<commit_before>/*globals require, process */

(function () {
    'use strict';

    var reports = [],

    cli = require('commander'),
    fs = require('fs'),
    cr = require('./complexityReport'),
    check = require('check-types'),
    options,
    formatter,

    state = {
        reading: true,
        unread: 0,
        tooComplex: false
    };

    parseCommandLine();
    readSourceFiles();

    function parseCommandLine () {
        cli.
            usage('[options] <file...>').
            option(
                '-o, --output <file>',
                'specify an output file for the report'
            ).
            option(
                '-f, --format <format>',
                'specify the output format of the report'
            ).
            option(
                '-lo, --logicalor',
                'disregard operator || as source of cyclomatic complexity'
            ).
            option(
                '-sc, --switchcase',
                'disregard switch statements as source of cyclomatic complexity'
            ).
            option(
                '-fi, --forin',
                'treat for...in statements as source of cyclomatic complexity'
            ).
            option(
                '-tc, --trycatch',
                'treat catch clauses as source of cyclomatic complexity'
            ).
            option(
                '-th, --threshold <complexity>',
                'specifify the per-function complexity threshold',
                function (value) {
                    return parseInt(value, 10);
                }
            );

        cli.parse(process.argv);

        options = {
            logicalor: cli.logicalor,
            ternary: cli.ternary,
            switchcase: cli.switchcase,
            forin: cli.forin,
            trycatch: cli.trycatch,
        };

        if (check.isUnemptyString(cli.format) === false) {
            cli.format = 'plain';
        }
        formatter = require('./formats/' + cli.format);
    }

    function readSourceFiles () {
        var i;

        for (i = 0; i < cli.args.length; i += 1) {
            state.unread += 1;
            readSourceFile(cli.args[i]);
        }

        state.reading = false;
    }

    function readSourceFile (path) {
        fs.readFile(path, 'utf8', function (err, source) {
            if (err) {
                error('readSourceFile', err);
            }

            getReport(path, source);

            finish();
        });
    }

    function error (functionName, err) {
        fail('Fatal error [' + functionName + ']: ' + err.message);
    }

    function fail (message) {
        console.log(message);
        process.exit(1);
    }

    function getReport (path, source) {
        var report = cr.run(source, options);

        if (
            state.tooComplex === false &&
            check.isNumber(cli.threshold) &&
            isTooComplex(report)
        ) {
            state.tooComplex = true;
        }

        report.module = path;

        reports.push(report);
    }

    function isTooComplex (report) {
        var i;

        for (i = 0; i < report.functions.length; i += 1) {
            if (report.functions[i].complexity.cyclomatic > cli.threshold) {
                return true;
            }
        }

        return false;
    }

    function finish () {
        state.unread -= 1;

        if (state.reading === false && state.unread === 0) {
            writeReport();
        }
    }

    function writeReport () {
        var formatted = formatter.format(reports);

        if (check.isUnemptyString(cli.output)) {
            fs.writeFile(cli.output, formatted, 'utf8', function (err) {
                if (err) {
                    error('writeReport', err);
                }
            });
        } else {
            console.log(formatted);
        }

        exit();
    }

    function exit () {
        // TODO: Use Q

        if (state.tooComplex) {
            fail('Warning: Complexity threshold breached!');
        }
    }
}());

<commit_msg>Re-order options.<commit_after>/*globals require, process */

(function () {
    'use strict';

    var reports = [],

    cli = require('commander'),
    fs = require('fs'),
    cr = require('./complexityReport'),
    check = require('check-types'),
    options,
    formatter,

    state = {
        reading: true,
        unread: 0,
        tooComplex: false
    };

    parseCommandLine();
    readSourceFiles();

    function parseCommandLine () {
        cli.
            usage('[options] <file...>').
            option(
                '-o, --output <file>',
                'specify an output file for the report'
            ).
            option(
                '-f, --format <format>',
                'specify the output format of the report'
            ).
            option(
                '-th, --threshold <complexity>',
                'specifify the per-function complexity threshold',
                function (value) {
                    return parseInt(value, 10);
                }
            );
            option(
                '-lo, --logicalor',
                'disregard operator || as source of cyclomatic complexity'
            ).
            option(
                '-sc, --switchcase',
                'disregard switch statements as source of cyclomatic complexity'
            ).
            option(
                '-fi, --forin',
                'treat for...in statements as source of cyclomatic complexity'
            ).
            option(
                '-tc, --trycatch',
                'treat catch clauses as source of cyclomatic complexity'
            ).

        cli.parse(process.argv);

        options = {
            logicalor: cli.logicalor,
            ternary: cli.ternary,
            switchcase: cli.switchcase,
            forin: cli.forin,
            trycatch: cli.trycatch,
        };

        if (check.isUnemptyString(cli.format) === false) {
            cli.format = 'plain';
        }
        formatter = require('./formats/' + cli.format);
    }

    function readSourceFiles () {
        var i;

        for (i = 0; i < cli.args.length; i += 1) {
            state.unread += 1;
            readSourceFile(cli.args[i]);
        }

        state.reading = false;
    }

    function readSourceFile (path) {
        fs.readFile(path, 'utf8', function (err, source) {
            if (err) {
                error('readSourceFile', err);
            }

            getReport(path, source);

            finish();
        });
    }

    function error (functionName, err) {
        fail('Fatal error [' + functionName + ']: ' + err.message);
    }

    function fail (message) {
        console.log(message);
        process.exit(1);
    }

    function getReport (path, source) {
        var report = cr.run(source, options);

        if (
            state.tooComplex === false &&
            check.isNumber(cli.threshold) &&
            isTooComplex(report)
        ) {
            state.tooComplex = true;
        }

        report.module = path;

        reports.push(report);
    }

    function isTooComplex (report) {
        var i;

        for (i = 0; i < report.functions.length; i += 1) {
            if (report.functions[i].complexity.cyclomatic > cli.threshold) {
                return true;
            }
        }

        return false;
    }

    function finish () {
        state.unread -= 1;

        if (state.reading === false && state.unread === 0) {
            writeReport();
        }
    }

    function writeReport () {
        var formatted = formatter.format(reports);

        if (check.isUnemptyString(cli.output)) {
            fs.writeFile(cli.output, formatted, 'utf8', function (err) {
                if (err) {
                    error('writeReport', err);
                }
            });
        } else {
            console.log(formatted);
        }

        exit();
    }

    function exit () {
        // TODO: Use Q

        if (state.tooComplex) {
            fail('Warning: Complexity threshold breached!');
        }
    }
}());

<|endoftext|>


<commit_before>function LiveMatch() {

}

module.exports = LiveMatch;
<commit_msg>Adds LiveMatch constructor<commit_after>var Match = require('./Match');
var ScheduleList = require('./ScheduleList');
var ScheduleListItem = require('./ScheduleListItem');

function LiveMatch(options) {
    Match.call(this, options);

    // Classses
    this.Match = options.Match || Match;
    this.ScheduleList = options.ScheduleList || ScheduleList;
    this.ScheduleListItem = options.ScheduleListItem || ScheduleListItem;

    // Instances
    this.goalList = new this.ScheduleList();

    // Variables
}

LiveMatch.prototype = Object.create(Match.prototype);

(function (proto_, super_) {

    proto_.simulate = function () {
        return super_.simulate.call(this);
    };

}(LiveMatch.prototype, Match.prototype));

module.exports = LiveMatch;
<|endoftext|>


<commit_before>"use strict";

var fs = require('fs');

var Player = require('./player.js');
var Utils = require('./utils.js');

function World () {

	var _this = this;
	_this.state = resetState();

	function resetState () {
		var state = {
			session: "end",
			health: 50,
			players: {},
			ttl: 3000,
			controls: JSON.parse(fs.readFileSync('./controls.json', { encoding: 'utf8' })),
			outstanding: {},

			MINPLAYERS: 1,
			CONTROLS_PER_PLAYER: 6,
			COMMAND_TTL: 5000,

			HEALTH_TIMEOUT_PENALTY: -10,
			HEALTH_BONUS: 5,
		};

		state.controls = state.controls.unified;
		return state;
	}

	_this.setHealthIncr = function (incr) {
		var val = _this.state.health + incr;
		_this.state.health = Utils.clamp(val, 0, 100);
		_this.broadcast('health', _this.state.health);
	};

	_this.addPlayer = function (socket) {
		var player = new Player({
			socket: socket,
			world: _this,
		});

		var ip = socket.handshake.address.address;
		_this.state.players[ip] = player;

		socket.on('disconnect', function () {
			delete _this.state[ip];
			_this.endGame();
		});
	};

	_this.startGame = function () {
		var num_players = _this.numPlayersOnline();
		if (num_players < _this.state.MINPLAYERS) {
			_this.broadcast('alert', {
				text: Utils.plural(num_players, "You need " + (_this.state.MINPLAYERS - num_players) + " more player[[s]] to start."),
			});
			return;
		}

		_this.state.session = 'start';
		_this.broadcast('lifecycle', 'start');

		assignControls();

		_this.broadcast('health', _this.state.health);

		issueInitialCommands();
	};

	_this.endGame = function () {
		var players = _this.state.players;
		_this.state = resetState();
		_this.state.players = players;

		_this.broadcast('lifecycle', 'stop');
	};

	_this.broadcast = function (evt, payload) {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.send(evt, payload);
		});
	};

	_this.numPlayersOnline = function () {
		return Object.keys(_this.state.players).length;
	};

	function issueInitialCommands () {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.issueRandomCommand();
		});
	};

	function assignControls () {
		var controls = JSON.parse(JSON.stringify(_this.state.controls));
		var keys = Object.keys(controls);

		// Randomly assign controls
		Utils.forEach(_this.state.players, function (ip, player) {

			var player_controls = {};
			for (var ct = 0; ct < _this.state.CONTROLS_PER_PLAYER; ct++) {
				var control_id = Utils.random_choice_no_replacement(keys);
				var control = controls[control_id];
				player_controls[control_id] = control.label;
			}

			player.setControls(player_controls);
		});
	}
}

module.exports = World;<commit_msg>Added 30 sec end of game.<commit_after>"use strict";

var fs = require('fs');

var Player = require('./player.js');
var Utils = require('./utils.js');

function World () {

	var _this = this;
	_this.state = resetState();

	function resetState () {
		var state = {
			session: "end",
			health: 50,
			players: {},
			ttl: 3000,
			controls: JSON.parse(fs.readFileSync('./controls.json', { encoding: 'utf8' })),
			outstanding: {},

			MINPLAYERS: 1,
			CONTROLS_PER_PLAYER: 6,
			COMMAND_TTL: 5000,

			HEALTH_TIMEOUT_PENALTY: -10,
			HEALTH_BONUS: 5,
		};

		state.controls = state.controls.unified;
		return state;
	}

	_this.setHealthIncr = function (incr) {
		var val = _this.state.health + incr;
		_this.state.health = Utils.clamp(val, 0, 100);
		_this.broadcast('health', _this.state.health);
	};

	_this.addPlayer = function (socket) {
		var player = new Player({
			socket: socket,
			world: _this,
		});

		var ip = socket.handshake.address.address;
		_this.state.players[ip] = player;

		socket.on('disconnect', function () {
			delete _this.state[ip];
			_this.endGame();
		});
	};

	_this.startGame = function () {
		var num_players = _this.numPlayersOnline();
		if (num_players < _this.state.MINPLAYERS) {
			_this.broadcast('alert', {
				text: Utils.plural(num_players, "You need " + (_this.state.MINPLAYERS - num_players) + " more player[[s]] to start."),
			});
			return;
		}

		_this.state.session = 'start';
		_this.broadcast('lifecycle', 'start');

		assignControls();

		_this.broadcast('health', _this.state.health);

		issueInitialCommands();

		setTimeout(function () {
			_this.endGame();
		}, 30000);
	};

	_this.endGame = function () {
		var players = _this.state.players;
		_this.state = resetState();
		_this.state.players = players;

		Utils.forEach(players, function (ip, player) {
			player.resetPlayerCommandState();
		});

		_this.broadcast('lifecycle', 'stop');
	};

	_this.broadcast = function (evt, payload) {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.send(evt, payload);
		});
	};

	_this.numPlayersOnline = function () {
		return Object.keys(_this.state.players).length;
	};

	function issueInitialCommands () {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.issueRandomCommand();
		});
	};

	function assignControls () {
		var controls = JSON.parse(JSON.stringify(_this.state.controls));
		var keys = Object.keys(controls);

		// Randomly assign controls
		Utils.forEach(_this.state.players, function (ip, player) {

			var player_controls = {};
			for (var ct = 0; ct < _this.state.CONTROLS_PER_PLAYER; ct++) {
				var control_id = Utils.random_choice_no_replacement(keys);
				var control = controls[control_id];
				player_controls[control_id] = control.label;
			}

			player.setControls(player_controls);
		});
	}
}

module.exports = World;<|endoftext|>


<commit_before>export default function createWorld() {
  const world = {
    entities: [],
    systems: [],
    add(entity) {
      this.entities.push(entity);
      return entity;
    },
    addSystem(system) {
      this.systems.push(system);
      return system;
    },
    update(deltaTime = 0) {
      this.systems.forEach((system) => {
        system.update(this.entities, deltaTime);
      });
    },
  };
  return world;
}
<commit_msg>feat(world): compute deltaTime if not provided<commit_after>export default function createWorld() {
  let prevTime = Date.now();
  const world = {
    entities: [],
    systems: [],
    add(entity) {
      this.entities.push(entity);
      return entity;
    },
    addSystem(system) {
      this.systems.push(system);
      return system;
    },
    update(deltaTime) {
      if (deltaTime == undefined) {
        const now = Date.now();
        deltaTime = (now - prevTime) / 1000;
        prevTime = now;
      }
      this.systems.forEach((system) => {
        system.update(this.entities, deltaTime);
      });
    },
  };
  return world;
}
<|endoftext|>


<commit_before>function _dubug(msg){
    DEBUG && console.log(msg);
}

function callBotAPI(newmsg,callback) {
   //YQL for cross region json ajax
    var yql_url = 'https://query.yahooapis.com/v1/public/yql';
    var url = 'http://www.tuling123.com/openapi/api?key=abab9d3783d6e367e71e56c721e3165a&info='+encodeURIComponent(newmsg);
    $.ajax({
        'url': yql_url,
        'data': {
              'q': 'SELECT * FROM json WHERE url="'+url+'"',
                   'format': 'json',
                   'jsonCompat': 'new',
        },
        'dataType': 'jsonp',
        'success': function(response) {
            ans = response.query.results.json.text;
            callback(ans);
        },
        'error': function() {
            _dubug("ajax error occured");
        }
    });
}

function sendmsg(ans) {
    _dubug("bot resp with: " + ans);
    $('#textInput')[0].value=ans;
    $('.chatSend')[0].click();
    _dubug("msg sent to: " + name);
    $('#conv_filehelper').click();
}

function installbot(chats){
    //add bot notes
    for (i=0;i<chats.length;i++) {
        var bot = document.createElement('div');
        bot.className='bot';
        chats[i].appendChild(bot);
    }

    //listen on bot click event
    $(chats).children('.bot').click(function(e){
        e.stopPropagation();
        $(this).toggleClass('active');
    });

    //add drag drop event to bot div
    jQuery.event.props.push('dataTransfer');
    $(chats).on({
        dragenter: function(e) {
            $(this).addClass('over');
        },
        dragover: function(e) {
            e.preventDefault();
        },
        dragleave: function(e) {
            $(this).removeClass('over');
        },
        drop: function() {
            $(this).find('.bot').toggleClass('active');
        },
    });

    _dubug("installed "+chats.length.toString()+" bots");
}

function buildbothome() {
    //add bot home
    $('#profile').append([
    '
',
        '',
        '',
            '
快把我拖到聊天列表上
',
            '
点击我可以 暂停/恢复
',
        '',
        '',
    '
'].join(''));

    //show tooltip for 10s
    $('img.botico').load(function(){
        $('#bot-wrapper span').show();
        setTimeout(function(){
            $('#bot-wrapper span').hide();
        },10*1000);
    });

    //clear tooltip on mousedown
    $('.bot-home').mousedown(function(){
        $('#bot-wrapper span').hide();
    });

    //listen on bot-home click event
    $('.bot-home').click(function(){
        $('.bot-home').toggleClass('paused');
    });

    //listen drag event
    jQuery.event.props.push('dataTransfer');
    $('.bot-home').on({
        dragstart: function() {
            $(this).css('opacity', '0.5');
            $('.chatListColumn *').css('pointer-events','none');
        },
        dragend: function() {
            $(this).css('opacity', '1');
            $('.chatListColumn *').css('pointer-events','');
            $('.chatListColumn').removeClass('over');
        },
    });
    _dubug("bot home constructed");
}

function botinit(){
    $(document).ready(function(){
        //init bot-home
        buildbothome();
        //init bot divs
        var bots = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")'].join(''))
        installbot(bots);
        _dubug(bots.length.toString()+" bots initiated");
    });
}

function botstart(interval){
    interval = typeof interval !== 'undefined' ? interval : 30;
    setInterval(chatbot,interval*1000);
    _dubug("started with interval: "+interval.toString()+"s");
}

function botupdate(){
    uninstalled = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")',
            ':not(:has(".bot"))'].join(''));
    uninstalled.length && _dubug(uninstalled.length.toString()+" nodes to update");
    uninstalled.length && installbot(uninstalled);
}

function chatbot() {
    botupdate();

    //if paused
    if ( $('.bot-home').hasClass('paused') ) return;
    //if no bot-took-overed
    if ( $('.bot.active').length == 0 )  return;

    //reply in current chat window
    if ( $('.activeColumn:has(".bot.active")').length ) {
        var observer = new MutationObserver(function(mutations) {
         mutations.forEach(function(mutation) {
           for (var i = 0; i < mutation.addedNodes.length; i++)
             console.log($(mutation.addedNodes[i]));
         });
        });
        observer.observe($('#chat_chatmsglist')[0], { childList: true, subtree: true});
    }

    //reply for red dotted item
    //$('#conv_filehelper').click();
    $('.unreadDot:visible,.unreadDotS:visible').each(function()
    {
        is_active = $(this).parent().find(".bot").hasClass("active");
        if (!is_active) return;
        name = $(this).parent().find(".left.name").text();
        _dubug("msg from: " + name);
        $(this).click();
        //Wait till chat box loaded
        setTimeout(function(){
            newmsg = $("#chat_chatmsglist").children(".chatItem.you").last().find("pre").text();
            if (newmsg) {
                _dubug("msg content: " + newmsg);
                callBotAPI(newmsg,sendmsg);
            } else {
                _dubug("no msg found")
            }
        },500);
    });
}

DEBUG = true;
botinit();
botstart(runtimeGlobal.interval);
botupdate();
<commit_msg>测试mutation observer<commit_after>function _dubug(msg){
    DEBUG && console.log(msg);
}

function callBotAPI(newmsg,callback) {
   //YQL for cross region json ajax
    var yql_url = 'https://query.yahooapis.com/v1/public/yql';
    var url = 'http://www.tuling123.com/openapi/api?key=abab9d3783d6e367e71e56c721e3165a&info='+encodeURIComponent(newmsg);
    $.ajax({
        'url': yql_url,
        'data': {
              'q': 'SELECT * FROM json WHERE url="'+url+'"',
                   'format': 'json',
                   'jsonCompat': 'new',
        },
        'dataType': 'jsonp',
        'success': function(response) {
            ans = response.query.results.json.text;
            callback(ans);
        },
        'error': function() {
            _dubug("ajax error occured");
        }
    });
}

function sendmsg(ans) {
    _dubug("bot resp with: " + ans);
    $('#textInput')[0].value=ans;
    $('.chatSend')[0].click();
    _dubug("msg sent to: " + name);
    $('#conv_filehelper').click();
}

function installbot(chats){
    //add bot notes
    for (i=0;i<chats.length;i++) {
        var bot = document.createElement('div');
        bot.className='bot';
        chats[i].appendChild(bot);
    }

    //listen on bot click event
    $(chats).children('.bot').click(function(e){
        e.stopPropagation();
        $(this).toggleClass('active');
    });

    //add drag drop event to bot div
    jQuery.event.props.push('dataTransfer');
    $(chats).on({
        dragenter: function(e) {
            $(this).addClass('over');
        },
        dragover: function(e) {
            e.preventDefault();
        },
        dragleave: function(e) {
            $(this).removeClass('over');
        },
        drop: function() {
            $(this).find('.bot').toggleClass('active');
        },
    });

    _dubug("installed "+chats.length.toString()+" bots");
}

function buildbothome() {
    //add bot home
    $('#profile').append([
    '
',
        '',
        '',
            '
快把我拖到聊天列表上
',
            '
点击我可以 暂停/恢复
',
        '',
        '',
    '
'].join(''));

    //show tooltip for 10s
    $('img.botico').load(function(){
        $('#bot-wrapper span').show();
        setTimeout(function(){
            $('#bot-wrapper span').hide();
        },10*1000);
    });

    //clear tooltip on mousedown
    $('.bot-home').mousedown(function(){
        $('#bot-wrapper span').hide();
    });

    //listen on bot-home click event
    $('.bot-home').click(function(){
        $('.bot-home').toggleClass('paused');
    });

    //listen drag event
    jQuery.event.props.push('dataTransfer');
    $('.bot-home').on({
        dragstart: function() {
            $(this).css('opacity', '0.5');
            $('.chatListColumn *').css('pointer-events','none');
        },
        dragend: function() {
            $(this).css('opacity', '1');
            $('.chatListColumn *').css('pointer-events','');
            $('.chatListColumn').removeClass('over');
        },
    });
    _dubug("bot home constructed");
}

function botinit(){
    $(document).ready(function(){
        //init bot-home
        buildbothome();
        //init bot divs
        var bots = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")'].join(''))
        installbot(bots);
        _dubug(bots.length.toString()+" bots initiated");
    });
}

function botstart(interval){
    interval = typeof interval !== 'undefined' ? interval : 30;
    setInterval(chatbot,interval*1000);
    _dubug("started with interval: "+interval.toString()+"s");
}

function botupdate(){
    uninstalled = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")',
            ':not(:has(".bot"))'].join(''));
    uninstalled.length && _dubug(uninstalled.length.toString()+" nodes to update");
    uninstalled.length && installbot(uninstalled);
}

function chatbot() {
    botupdate();

    //if paused
    if ( $('.bot-home').hasClass('paused') ) return;
    //if no bot-took-overed
    if ( $('.bot.active').length == 0 )  return;

    //reply in current chat window
    if ( $('.activeColumn:has(".bot.active")').length ) {
        var observer = new MutationObserver(function(mutations) {
         mutations.forEach(function(mutation) {
           for (var i = 0; i < mutation.addedNodes.length; i++)
             console.log(mutation.addedNodes[i]);
         });
        });
        observer.observe($('#chat_chatmsglist')[0], { childList: true});
    }

    //reply for red dotted item
    //$('#conv_filehelper').click();
    $('.unreadDot:visible,.unreadDotS:visible').each(function()
    {
        is_active = $(this).parent().find(".bot").hasClass("active");
        if (!is_active) return;
        name = $(this).parent().find(".left.name").text();
        _dubug("msg from: " + name);
        $(this).click();
        //Wait till chat box loaded
        setTimeout(function(){
            newmsg = $("#chat_chatmsglist").children(".chatItem.you").last().find("pre").text();
            if (newmsg) {
                _dubug("msg content: " + newmsg);
                callBotAPI(newmsg,sendmsg);
            } else {
                _dubug("no msg found")
            }
        },500);
    });
}

DEBUG = true;
botinit();
botstart(runtimeGlobal.interval);
botupdate();
<|endoftext|>


<commit_before>function _debug(msg){
    DEBUG && console.log(msg);
}

function callBotAPI(newmsg,callback) {
   //YQL for cross region json ajax
    var yql_url = 'https://query.yahooapis.com/v1/public/yql';
    var url = 'http://www.tuling123.com/openapi/api?key=abab9d3783d6e367e71e56c721e3165a&info='+encodeURIComponent(newmsg);
    $.ajax({
        'url': yql_url,
        'data': {
              'q': 'SELECT * FROM json WHERE url="'+url+'"',
                   'format': 'json',
                   'jsonCompat': 'new',
        },
        'dataType': 'jsonp',
        'success': function(response) {
            ans = response.query.results.json.text;
            callback(ans);
        },
        'error': function() {
            _debug("ajax error occured");
        }
    });
}

function sendmsg(ans) {
    _debug("bot resp with: " + ans);
    $('#textInput')[0].value=ans;
    $('.chatSend')[0].click();
    _debug("msg sent to: " + name);
}

function installbot(chats){
    //add bot notes
    for (i=0;i<chats.length;i++) {
        var bot = document.createElement('div');
        bot.className='bot';
        chats[i].appendChild(bot);
    }

    //listen on bot click event
    $(chats).children('.bot').click(function(e){
        e.stopPropagation();
        $(this).toggleClass('active');
        typeof(observer) !== 'undefined' && observer.disconnect();
    });

    //add drag drop event to bot div
    jQuery.event.props.push('dataTransfer');
    $(chats).on({
        dragenter: function(e) {
            $(this).addClass('over');
        },
        dragover: function(e) {
            e.preventDefault();
        },
        dragleave: function(e) {
            $(this).removeClass('over');
        },
        drop: function() {
            $(this).find('.bot').toggleClass('active');
        },
    });

    _debug("installed "+chats.length.toString()+" bots");
}

function buildbothome() {
    //add bot home
    $('#profile').append([
    '
',
        '',
        '',
            '
快把我拖到聊天列表上
',
            '
点击我可以 暂停/恢复
',
        '',
        '',
    '
'].join(''));

    //show tooltip for 10s
    $('img.botico').load(function(){
        $('#bot-wrapper span').show();
        setTimeout(function(){
            $('#bot-wrapper span').hide();
        },10*1000);
    });

    //clear tooltip on mousedown
    $('.bot-home').mousedown(function(){
        $('#bot-wrapper span').hide();
    });

    //listen on bot-home click event
    $('.bot-home').click(function(){
        $('.bot-home').toggleClass('paused');
    });

    //listen drag event
    jQuery.event.props.push('dataTransfer');
    $('.bot-home').on({
        dragstart: function() {
            $(this).css('opacity', '0.5');
            $('.chatListColumn *').css('pointer-events','none');
        },
        dragend: function() {
            $(this).css('opacity', '1');
            $('.chatListColumn *').css('pointer-events','');
            $('.chatListColumn').removeClass('over');
        },
    });
    _debug("bot home constructed");
}

function botinit(){
    $(document).ready(function(){
        //init bot-home
        buildbothome();
        //init bot divs
        var bots = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")'].join(''))
        installbot(bots);
        _debug(bots.length.toString()+" bots initiated");
        //set chat list update listener
        setupdater();
    });
}

function botstart(interval){
    interval = typeof interval !== 'undefined' ? interval : 30;
    setInterval(chatbot,interval*1000);
    _debug("started with interval: "+interval.toString()+"s");
}

function botupdate(){
    uninstalled = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")',
            ':not(:has(".bot"))'].join(''));
    uninstalled.length && _debug(uninstalled.length.toString()+" nodes to update");
    uninstalled.length && installbot(uninstalled);
}

function setupdater() {
    updater = new MutationObserver(function(mutations) {
        botupdate();
    });
    updater.observe($('#conversationContainer')[0], { childList: true});
    _debug('updater set');
}

function chatbot() {
    //if paused
    if ( $('.bot-home').hasClass('paused') ) return;
    //if no bot-took-overed
    if ( $('.bot.active').length == 0 )  return;

    //record current chat
    var activechat = $('.activeColumn');

    //reply in current chat window
    typeof(observer) !== 'undefined' && observer.disconnect();
    if ( $('.activeColumn:has(".bot.active")').length ) {
        observer = new MutationObserver(function(mutations) {
            _debug('current chat observer triggered');
            var m = mutations.pop();
            if (m.nextSibling) {
                var newnode = m.addedNodes[1];
                if (newnode.className == 'chatItem me') return;
                var newmsg = $(newnode).find('pre').text();
                var name = $(activechat).find('.left.name').text();
                _debug("msg from: " + name);
                if (newmsg) {
                    _debug("msg content: " + newmsg);
                    callBotAPI(newmsg,sendmsg);
                } else {
                    _debug("no msg found");
                }
            }
        });
        observer.observe($('#chat_chatmsglist')[0], { childList: true});
    }

    //reply for red dotted item
    $('chatListColumn .unreadDot:visible, chatListColumn .unreadDotS:visible').each(function()
    {
        is_active = $(this).parent().find(".bot").hasClass("active");
        if (!is_active) return;
        var name = $(this).parent().find(".left.name").text();
        _debug("msg from: " + name);

        $(this).parent().click(function(){
            //Wait till chat box loaded
            setTimeout(function(){
                newmsg = $("#chat_chatmsglist").children(".chatItem.you").last().find("pre").text();
                if (newmsg) {
                    _debug("msg content: " + newmsg);
                    callBotAPI(newmsg,sendmsg);
                } else {
                    _debug("no msg found");
                }
                $(activechat).click();
            },500);
        });
    });
}

DEBUG = true;
botinit();
botstart(runtimeGlobal.interval);
botupdate();
<commit_msg>for debug<commit_after>function _debug(msg){
    DEBUG && console.log(msg);
}

function callBotAPI(newmsg,callback) {
   //YQL for cross region json ajax
    var yql_url = 'https://query.yahooapis.com/v1/public/yql';
    var url = 'http://www.tuling123.com/openapi/api?key=abab9d3783d6e367e71e56c721e3165a&info='+encodeURIComponent(newmsg);
    $.ajax({
        'url': yql_url,
        'data': {
              'q': 'SELECT * FROM json WHERE url="'+url+'"',
                   'format': 'json',
                   'jsonCompat': 'new',
        },
        'dataType': 'jsonp',
        'success': function(response) {
            ans = response.query.results.json.text;
            callback(ans);
        },
        'error': function() {
            _debug("ajax error occured");
        }
    });
}

function sendmsg(ans) {
    _debug("bot resp with: " + ans);
    $('#textInput')[0].value=ans;
    $('.chatSend')[0].click();
    _debug("msg sent to: " + name);
}

function installbot(chats){
    //add bot notes
    for (i=0;i<chats.length;i++) {
        var bot = document.createElement('div');
        bot.className='bot';
        chats[i].appendChild(bot);
    }

    //listen on bot click event
    $(chats).children('.bot').click(function(e){
        e.stopPropagation();
        $(this).toggleClass('active');
        typeof(observer) !== 'undefined' && observer.disconnect();
    });

    //add drag drop event to bot div
    jQuery.event.props.push('dataTransfer');
    $(chats).on({
        dragenter: function(e) {
            $(this).addClass('over');
        },
        dragover: function(e) {
            e.preventDefault();
        },
        dragleave: function(e) {
            $(this).removeClass('over');
        },
        drop: function() {
            $(this).find('.bot').toggleClass('active');
        },
    });

    _debug("installed "+chats.length.toString()+" bots");
}

function buildbothome() {
    //add bot home
    $('#profile').append([
    '
',
        '',
        '',
            '
快把我拖到聊天列表上
',
            '
点击我可以 暂停/恢复
',
        '',
        '',
    '
'].join(''));

    //show tooltip for 10s
    $('img.botico').load(function(){
        $('#bot-wrapper span').show();
        setTimeout(function(){
            $('#bot-wrapper span').hide();
        },10*1000);
    });

    //clear tooltip on mousedown
    $('.bot-home').mousedown(function(){
        $('#bot-wrapper span').hide();
    });

    //listen on bot-home click event
    $('.bot-home').click(function(){
        $('.bot-home').toggleClass('paused');
    });

    //listen drag event
    jQuery.event.props.push('dataTransfer');
    $('.bot-home').on({
        dragstart: function() {
            $(this).css('opacity', '0.5');
            $('.chatListColumn *').css('pointer-events','none');
        },
        dragend: function() {
            $(this).css('opacity', '1');
            $('.chatListColumn *').css('pointer-events','');
            $('.chatListColumn').removeClass('over');
        },
    });
    _debug("bot home constructed");
}

function botinit(){
    $(document).ready(function(){
        //init bot-home
        buildbothome();
        //init bot divs
        var bots = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")'].join(''))
        installbot(bots);
        _debug(bots.length.toString()+" bots initiated");
        //set chat list update listener
        setupdater();
    });
}

function botstart(interval){
    interval = typeof interval !== 'undefined' ? interval : 30;
    setInterval(chatbot,interval*1000);
    _debug("started with interval: "+interval.toString()+"s");
}

function botupdate(){
    uninstalled = $(['.chatListColumn',
            ':not([un="newsapp"])',
            ':not([un="filehelper"])',
            ':not([un="fmessage"])',
            ':not(".loadMoreConv")',
            ':not(:has(".bot"))'].join(''));
    uninstalled.length && installbot(uninstalled);
}

function setupdater() {
    updater = new MutationObserver(function(mutations) {
        botupdate();
    });
    updater.observe($('#conversationContainer')[0], { childList: true});
    _debug('updater set');
}

function chatbot() {
    //if paused
    if ( $('.bot-home').hasClass('paused') ) return;
    //if no bot-took-overed
    if ( $('.bot.active').length == 0 )  return;

    //record current chat
    var activechat = $('.activeColumn');

    //reply in current chat window
    typeof(observer) !== 'undefined' && observer.disconnect();
    if ( $('.activeColumn:has(".bot.active")').length ) {
        observer = new MutationObserver(function(mutations) {
            var m = mutations.pop();
            console.log(m);
            var newnode = m.addedNodes[1];
            if (newnode.className == 'chatItem you') {
                var newmsg = $(newnode).find('pre').text();
                var name = $(activechat).find('.left.name').text();
                _debug("msg from: " + name);
                if (newmsg) {
                    _debug("msg content: " + newmsg);
                    callBotAPI(newmsg,sendmsg);
                } else {
                    _debug("no msg found");
                }
            }
        });
        observer.observe($('#chat_chatmsglist')[0], { childList: true});
    }

    //reply for red dotted item
    $('.unreadDot:visible, .unreadDotS:visible').each(function()
    {
        is_active = $(this).parent().find(".bot").hasClass("active");
        if (!is_active) return;
        var name = $(this).parent().find(".left.name").text();
        _debug("msg from: " + name);

        $(this).parent().click();
        //Wait till chat box loaded
        setTimeout(function(){
            newmsg = $("#chat_chatmsglist").children(".chatItem.you").last().find("pre").text();
            if (newmsg) {
                _debug("msg content: " + newmsg);
                callBotAPI(newmsg,sendmsg);
            } else {
                _debug("no msg found");
            }
            $(activechat).click();
        },500);
    });
}

DEBUG = true;
botinit();
botstart(runtimeGlobal.interval);
botupdate();
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import range from 'lodash/range'

export default class Paginator extends Component {
  static propTypes = {
    initialPage: PropTypes.number,
    numberOfPages: PropTypes.number.isRequired,
    children: PropTypes.func.isRequired
  }

  static defaultProps = { initialPage: 1 }

  state = { page: this.props.initialPage }

  getPageRange = (offset = 3) => {
    if (typeof offset !== 'number') {
      throw new Error(`offset should be a number`)
    }
    if (offset <= 0) {
      throw new Error(`offset should be equal or greater than 1`)
    }
    const start = this.state.page - offset
    const end = this.state.page + offset
    return range(
      start > 1 ? start : 1,
      (end < this.props.numberOfPages ? end : this.props.numberOfPages) + 1
    )
  }

  setPage = page => this.setState({ page })

  incrementPage = () => {
    if (this.state.page >= this.props.numberOfPages) {
      return
    }
    this.setPage(this.state.page + 1)
  }

  decrementPage = () => {
    if (this.state.page <= 1) {
      return
    }
    this.setPage(this.state.page - 1)
  }

  render() {
    return this.props.children({
      ...this.state,
      getPageRange: this.getPageRange,
      setPage: this.setPage,
      incrementPage: this.incrementPage,
      decrementPage: this.decrementPage
    })
  }
}
<commit_msg>Paginator: add onPageChange and improve type checks<commit_after>import React, { Component, PropTypes } from 'react'
import range from 'lodash/range'

export default class Paginator extends Component {
  static propTypes = {
    initialPage: PropTypes.number,
    numberOfPages: PropTypes.number.isRequired,
    onPageChange: PropTypes.func,
    children: PropTypes.func.isRequired
  }

  static defaultProps = { initialPage: 1 }

  state = { page: this.props.initialPage }

  componentWillMount() {
    if (this.props.initialPage < 1) {
      throw new Error('initialPage should be greater than or equal to 1')
    }
    if (this.props.initialPage > this.props.numberOfPages) {
      throw new Error(
        `initialPage should be less than or equal to ${this.props.numberOfPages}`
      )
    }
  }

  getPageRange = (offset = 3) => {
    if (typeof offset !== 'number') {
      throw new Error('offset should be a number')
    }
    const start = this.state.page - offset
    const end = this.state.page + offset
    return range(
      start > 1 ? start : 1,
      (end < this.props.numberOfPages ? end : this.props.numberOfPages) + 1
    )
  }

  setPage = page => {
    if (typeof page !== 'number') {
      throw new Error('page should be a number')
    }
    if (page < 1) {
      throw new Error('page should be greater than or equal to 1')
    }
    if (page > this.props.numberOfPages) {
      throw new Error(
        `page should be less than or equal to ${this.props.numberOfPages}`
      )
    }
    this.setState({ page }, () => {
      if (this.props.onPageChange) {
        this.props.onPageChange(page)
      }
    })
  }

  incrementPage = () => {
    if (this.state.page === this.props.numberOfPages) {
      return
    }
    this.setPage(this.state.page + 1)
  }

  decrementPage = () => {
    if (this.state.page === 1) {
      return
    }
    this.setPage(this.state.page - 1)
  }

  render() {
    return this.props.children({
      ...this.state,
      getPageRange: this.getPageRange,
      setPage: this.setPage,
      incrementPage: this.incrementPage,
      decrementPage: this.decrementPage
    })
  }
}
<|endoftext|>


<commit_before>/*
cacophony-api: The Cacophony Project API server
Copyright (C) 2018  The Cacophony Project

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

const jsonwebtoken = require("jsonwebtoken");
const mime = require("mime");
const moment = require("moment");
const urljoin = require("url-join");

const { ClientError } = require("../customErrors");
const config = require("../../config");
const models = require("../../models");
const responseUtil = require("./responseUtil");
const util = require("./util");

function makeUploadHandler(mungeData) {
  return util.multipartUpload("raw", (request, data, key) => {
    if (mungeData) {
      data = mungeData(data);
    }

    const recording = models.Recording.buildSafely(data);
    recording.rawFileKey = key;
    recording.rawMimeType = guessRawMimeType(data.type, data.filename);
    recording.DeviceId = request.device.id;
    recording.GroupId = request.device.GroupId;
    recording.processingState = models.Recording.processingStates[data.type][0];
    if (typeof request.device.public === "boolean") {
      recording.public = request.device.public;
    }
    return recording;
  });
}

// Returns a promise for the recordings query specified in the
// request.
async function query(request, type) {
  if (type) {
    request.query.where.type = type;
  }

  const builder = await new models.Recording.queryBuilder().init(
    request.user,
    request.query.where,
    request.query.tagMode,
    request.query.tags,
    request.query.offset,
    request.query.limit,
    request.query.order
  );
  const result = await models.Recording.findAndCountAll(builder.get());

  const filterOptions = models.Recording.makeFilterOptions(
    request.user,
    request.filterOptions
  );
  result.rows = result.rows.map(rec => {
    rec.filterData(filterOptions);
    return handleLegacyTagFieldsForGetOnRecording(rec);
  });
  return result;
}

// Returns a promise for report rows for a set of recordings. Takes
// the same parameters as query() above.
async function report(request) {
  const builder = (await new models.Recording.queryBuilder().init(
    request.user,
    request.query.where,
    request.query.tagMode,
    request.query.tags,
    request.query.offset,
    request.query.limit,
    request.query.order
  ))
    .addColumn("comment")
    .addAudioEvents();

  const result = await models.Recording.findAll(builder.get());

  const filterOptions = models.Recording.makeFilterOptions(
    request.user,
    request.filterOptions
  );

  // Our DB schema doesn't allow us to easily get from a audio event
  // recording to a audio file name so do some work first to look these up.
  const audioEvents = new Map();
  const audioFileIds = new Set();
  for (const r of result) {
    const event = findLatestEvent(r.Device.Events);
    if (event) {
      const fileId = event.EventDetail.details.fileId;
      audioEvents[r.id] = {
        timestamp: event.dateTime,
        volume: event.EventDetail.details.volume,
        fileId
      };
      audioFileIds.add(fileId);
    }
  }

  // Bulk look up file details of played audio events.
  const audioFileNames = new Map();
  for (const f of await models.File.getMultiple(Array.from(audioFileIds))) {
    audioFileNames[f.id] = f.details.name;
  }

  console.log(audioEvents);
  console.log(audioFileIds);
  console.log(audioFileNames);

  const recording_url_base = config.server.recording_url_base || "";

  const out = [
    [
      "id",
      "type",
      "group",
      "device",
      "timestamp",
      "duration",
      "comment",
      "track_count",
      "automatic_track_tags",
      "human_track_tags",
      "recording_tags",
      "audio_bait",
      "audio_bait_time",
      "mins_since_audio_bait",
      "audio_bait_volume",
      "url"
    ]
  ];

  for (const r of result) {
    r.filterData(filterOptions);

    const automatic_track_tags = new Set();
    const human_track_tags = new Set();
    for (const track of r.Tracks) {
      for (const tag of track.TrackTags) {
        const subject = tag.what || tag.detail;
        if (tag.automatic) {
          automatic_track_tags.add(subject);
        } else {
          human_track_tags.add(subject);
        }
      }
    }

    const recording_tags = r.Tags.map(t => t.what || t.detail);

    let audioBaitName = "";
    let audioBaitTime = null;
    let audioBaitDelta = null;
    let audioBaitVolume = null;
    const audioEvent = audioEvents[r.id];
    if (audioEvent) {
      audioBaitName = audioFileNames[audioEvent.fileId];
      audioBaitTime = moment(audioEvent.timestamp);
      audioBaitDelta = moment
        .duration(r.recordingDateTime - audioBaitTime)
        .asMinutes()
        .toFixed(1);
      audioBaitVolume = audioEvent.volume;
    }

    out.push([
      r.id,
      r.type,
      r.Group.groupname,
      r.Device.devicename,
      moment(r.recordingDateTime).format(),
      r.duration,
      r.comment,
      r.Tracks.length,
      formatTags(automatic_track_tags),
      formatTags(human_track_tags),
      formatTags(recording_tags),
      audioBaitName,
      audioBaitTime ? audioBaitTime.format() : "",
      audioBaitDelta,
      audioBaitVolume,
      urljoin(recording_url_base, r.id.toString())
    ]);
  }
  return out;
}

function findLatestEvent(events) {
  if (!events) {
    return null;
  }

  let latest = events[0];
  for (const event of events) {
    if (event.dateTime > latest.dateTime) {
      latest = event;
    }
  }
  return latest;
}

function formatTags(tags) {
  const out = Array.from(tags);
  out.sort();
  return out.join("+");
}

async function get(request, type) {
  const recording = await models.Recording.get(
    request.user,
    request.params.id,
    models.Recording.Perms.VIEW,
    {
      type: type,
      filterOptions: request.query.filterOptions
    }
  );
  if (!recording) {
    throw new ClientError("No file found with given datapoint.");
  }

  const downloadFileData = {
    _type: "fileDownload",
    key: recording.fileKey,
    filename: recording.getFileName(),
    mimeType: recording.fileMimeType
  };

  const downloadRawData = {
    _type: "fileDownload",
    key: recording.rawFileKey,
    filename: recording.getRawFileName(),
    mimeType: recording.rawMimeType
  };
  delete recording.rawFileKey;

  return {
    recording: handleLegacyTagFieldsForGetOnRecording(recording),
    cookedJWT: jsonwebtoken.sign(
      downloadFileData,
      config.server.passportSecret,
      { expiresIn: 60 * 10 }
    ),
    rawJWT: jsonwebtoken.sign(downloadRawData, config.server.passportSecret, {
      expiresIn: 60 * 10
    })
  };
}

async function delete_(request, response) {
  const deleted = await models.Recording.deleteOne(
    request.user,
    request.params.id
  );
  if (deleted) {
    responseUtil.send(response, {
      statusCode: 200,
      messages: ["Deleted recording."]
    });
  } else {
    responseUtil.send(response, {
      statusCode: 400,
      messages: ["Failed to delete recording."]
    });
  }
}

function guessRawMimeType(type, filename) {
  const mimeType = mime.getType(filename);
  if (mimeType) {
    return mimeType;
  }
  switch (type) {
    case "thermalRaw":
      return "application/x-cptv";
    case "audio":
      return "audio/mpeg";
    default:
      return "application/octet-stream";
  }
}

async function addTag(user, recording, tag, response) {
  if (!recording) {
    throw new ClientError("No such recording.");
  }

  // If old tag fields are used, convert to new field names.
  tag = handleLegacyTagFieldsForCreate(tag);

  const tagInstance = models.Tag.buildSafely(tag);
  tagInstance.RecordingId = recording.id;
  if (user) {
    tagInstance.taggerId = user.id;
  }
  await tagInstance.save();

  responseUtil.send(response, {
    statusCode: 200,
    messages: ["Added new tag."],
    tagId: tagInstance.id
  });
}

function handleLegacyTagFieldsForCreate(tag) {
  tag = moveLegacyField(tag, "animal", "what");
  tag = moveLegacyField(tag, "event", "detail");
  return tag;
}

function moveLegacyField(o, oldName, newName) {
  if (o[oldName]) {
    if (o[newName]) {
      throw new ClientError(
        `can't specify both '${oldName}' and '${newName}' fields at the same time`
      );
    }
    o[newName] = o[oldName];
    delete o[oldName];
  }
  return o;
}

function handleLegacyTagFieldsForGet(tag) {
  tag.animal = tag.what;
  tag.event = tag.detail;
  return tag;
}

function handleLegacyTagFieldsForGetOnRecording(recording) {
  recording = recording.get({ plain: true });
  recording.Tags = recording.Tags.map(handleLegacyTagFieldsForGet);
  return recording;
}

const statusCode = {
  Success: 1,
  Fail: 2,
  Both: 3
};

// reprocessAll expects request.body.recordings to be a list of recording_ids
// will mark each recording to be reprocessed
async function reprocessAll(request, response) {
  const recordings = request.body.recordings;
  const responseMessage = {
    statusCode: 200,
    messages: [],
    reprocessed: [],
    fail: []
  };

  let status = 0;
  for (let i = 0; i < recordings.length; i++) {
    const resp = await reprocessRecording(request.user, recordings[i]);
    if (resp.statusCode != 200) {
      status = status | statusCode.Fail;
      responseMessage.messages.push(resp.messages[0]);
      responseMessage.statusCode = resp.statusCode;
      responseMessage.fail.push(resp.recordingId);
    } else {
      responseMessage.reprocessed.push(resp.recordingId);
      status = status | statusCode.Success;
    }
  }
  responseMessage.messages.splice(0, 0, getReprocessMessage(status));
  responseUtil.send(response, responseMessage);
  return;
}

function getReprocessMessage(status) {
  switch (status) {
    case statusCode.Success:
      return "All recordings scheduled for reprocessing";
    case statusCode.Fail:
      return "Recordings could not be scheduled for reprocessing";
    case statusCode.Both:
      return "Some recordings could not be scheduled for reprocessing";
    default:
      return "";
  }
}

// reprocessRecording marks supplied recording_id for reprocessing,
// under supplied user privileges
async function reprocessRecording(user, recording_id) {
  const recording = await models.Recording.get(
    user,
    recording_id,
    models.Recording.Perms.UPDATE
  );

  if (!recording) {
    return {
      statusCode: 400,
      messages: ["No such recording: " + recording_id],
      recordingId: recording_id
    };
  }

  await recording.reprocess(user);

  return {
    statusCode: 200,
    messages: ["Recording scheduled for reprocessing"],
    recordingId: recording_id
  };
}

// reprocess a recording defined by request.user and request.params.id
async function reprocess(request, response) {
  const responseInfo = await reprocessRecording(
    request.user,
    request.params.id
  );
  responseUtil.send(response, responseInfo);
}

exports.makeUploadHandler = makeUploadHandler;
exports.query = query;
exports.report = report;
exports.get = get;
exports.delete_ = delete_;
exports.addTag = addTag;
exports.reprocess = reprocess;
exports.reprocessAll = reprocessAll;
<commit_msg>Removed some unwanted log output<commit_after>/*
cacophony-api: The Cacophony Project API server
Copyright (C) 2018  The Cacophony Project

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

const jsonwebtoken = require("jsonwebtoken");
const mime = require("mime");
const moment = require("moment");
const urljoin = require("url-join");

const { ClientError } = require("../customErrors");
const config = require("../../config");
const models = require("../../models");
const responseUtil = require("./responseUtil");
const util = require("./util");

function makeUploadHandler(mungeData) {
  return util.multipartUpload("raw", (request, data, key) => {
    if (mungeData) {
      data = mungeData(data);
    }

    const recording = models.Recording.buildSafely(data);
    recording.rawFileKey = key;
    recording.rawMimeType = guessRawMimeType(data.type, data.filename);
    recording.DeviceId = request.device.id;
    recording.GroupId = request.device.GroupId;
    recording.processingState = models.Recording.processingStates[data.type][0];
    if (typeof request.device.public === "boolean") {
      recording.public = request.device.public;
    }
    return recording;
  });
}

// Returns a promise for the recordings query specified in the
// request.
async function query(request, type) {
  if (type) {
    request.query.where.type = type;
  }

  const builder = await new models.Recording.queryBuilder().init(
    request.user,
    request.query.where,
    request.query.tagMode,
    request.query.tags,
    request.query.offset,
    request.query.limit,
    request.query.order
  );
  const result = await models.Recording.findAndCountAll(builder.get());

  const filterOptions = models.Recording.makeFilterOptions(
    request.user,
    request.filterOptions
  );
  result.rows = result.rows.map(rec => {
    rec.filterData(filterOptions);
    return handleLegacyTagFieldsForGetOnRecording(rec);
  });
  return result;
}

// Returns a promise for report rows for a set of recordings. Takes
// the same parameters as query() above.
async function report(request) {
  const builder = (await new models.Recording.queryBuilder().init(
    request.user,
    request.query.where,
    request.query.tagMode,
    request.query.tags,
    request.query.offset,
    request.query.limit,
    request.query.order
  ))
    .addColumn("comment")
    .addAudioEvents();

  const result = await models.Recording.findAll(builder.get());

  const filterOptions = models.Recording.makeFilterOptions(
    request.user,
    request.filterOptions
  );

  // Our DB schema doesn't allow us to easily get from a audio event
  // recording to a audio file name so do some work first to look these up.
  const audioEvents = new Map();
  const audioFileIds = new Set();
  for (const r of result) {
    const event = findLatestEvent(r.Device.Events);
    if (event) {
      const fileId = event.EventDetail.details.fileId;
      audioEvents[r.id] = {
        timestamp: event.dateTime,
        volume: event.EventDetail.details.volume,
        fileId
      };
      audioFileIds.add(fileId);
    }
  }

  // Bulk look up file details of played audio events.
  const audioFileNames = new Map();
  for (const f of await models.File.getMultiple(Array.from(audioFileIds))) {
    audioFileNames[f.id] = f.details.name;
  }

  const recording_url_base = config.server.recording_url_base || "";

  const out = [
    [
      "id",
      "type",
      "group",
      "device",
      "timestamp",
      "duration",
      "comment",
      "track_count",
      "automatic_track_tags",
      "human_track_tags",
      "recording_tags",
      "audio_bait",
      "audio_bait_time",
      "mins_since_audio_bait",
      "audio_bait_volume",
      "url"
    ]
  ];

  for (const r of result) {
    r.filterData(filterOptions);

    const automatic_track_tags = new Set();
    const human_track_tags = new Set();
    for (const track of r.Tracks) {
      for (const tag of track.TrackTags) {
        const subject = tag.what || tag.detail;
        if (tag.automatic) {
          automatic_track_tags.add(subject);
        } else {
          human_track_tags.add(subject);
        }
      }
    }

    const recording_tags = r.Tags.map(t => t.what || t.detail);

    let audioBaitName = "";
    let audioBaitTime = null;
    let audioBaitDelta = null;
    let audioBaitVolume = null;
    const audioEvent = audioEvents[r.id];
    if (audioEvent) {
      audioBaitName = audioFileNames[audioEvent.fileId];
      audioBaitTime = moment(audioEvent.timestamp);
      audioBaitDelta = moment
        .duration(r.recordingDateTime - audioBaitTime)
        .asMinutes()
        .toFixed(1);
      audioBaitVolume = audioEvent.volume;
    }

    out.push([
      r.id,
      r.type,
      r.Group.groupname,
      r.Device.devicename,
      moment(r.recordingDateTime).format(),
      r.duration,
      r.comment,
      r.Tracks.length,
      formatTags(automatic_track_tags),
      formatTags(human_track_tags),
      formatTags(recording_tags),
      audioBaitName,
      audioBaitTime ? audioBaitTime.format() : "",
      audioBaitDelta,
      audioBaitVolume,
      urljoin(recording_url_base, r.id.toString())
    ]);
  }
  return out;
}

function findLatestEvent(events) {
  if (!events) {
    return null;
  }

  let latest = events[0];
  for (const event of events) {
    if (event.dateTime > latest.dateTime) {
      latest = event;
    }
  }
  return latest;
}

function formatTags(tags) {
  const out = Array.from(tags);
  out.sort();
  return out.join("+");
}

async function get(request, type) {
  const recording = await models.Recording.get(
    request.user,
    request.params.id,
    models.Recording.Perms.VIEW,
    {
      type: type,
      filterOptions: request.query.filterOptions
    }
  );
  if (!recording) {
    throw new ClientError("No file found with given datapoint.");
  }

  const downloadFileData = {
    _type: "fileDownload",
    key: recording.fileKey,
    filename: recording.getFileName(),
    mimeType: recording.fileMimeType
  };

  const downloadRawData = {
    _type: "fileDownload",
    key: recording.rawFileKey,
    filename: recording.getRawFileName(),
    mimeType: recording.rawMimeType
  };
  delete recording.rawFileKey;

  return {
    recording: handleLegacyTagFieldsForGetOnRecording(recording),
    cookedJWT: jsonwebtoken.sign(
      downloadFileData,
      config.server.passportSecret,
      { expiresIn: 60 * 10 }
    ),
    rawJWT: jsonwebtoken.sign(downloadRawData, config.server.passportSecret, {
      expiresIn: 60 * 10
    })
  };
}

async function delete_(request, response) {
  const deleted = await models.Recording.deleteOne(
    request.user,
    request.params.id
  );
  if (deleted) {
    responseUtil.send(response, {
      statusCode: 200,
      messages: ["Deleted recording."]
    });
  } else {
    responseUtil.send(response, {
      statusCode: 400,
      messages: ["Failed to delete recording."]
    });
  }
}

function guessRawMimeType(type, filename) {
  const mimeType = mime.getType(filename);
  if (mimeType) {
    return mimeType;
  }
  switch (type) {
    case "thermalRaw":
      return "application/x-cptv";
    case "audio":
      return "audio/mpeg";
    default:
      return "application/octet-stream";
  }
}

async function addTag(user, recording, tag, response) {
  if (!recording) {
    throw new ClientError("No such recording.");
  }

  // If old tag fields are used, convert to new field names.
  tag = handleLegacyTagFieldsForCreate(tag);

  const tagInstance = models.Tag.buildSafely(tag);
  tagInstance.RecordingId = recording.id;
  if (user) {
    tagInstance.taggerId = user.id;
  }
  await tagInstance.save();

  responseUtil.send(response, {
    statusCode: 200,
    messages: ["Added new tag."],
    tagId: tagInstance.id
  });
}

function handleLegacyTagFieldsForCreate(tag) {
  tag = moveLegacyField(tag, "animal", "what");
  tag = moveLegacyField(tag, "event", "detail");
  return tag;
}

function moveLegacyField(o, oldName, newName) {
  if (o[oldName]) {
    if (o[newName]) {
      throw new ClientError(
        `can't specify both '${oldName}' and '${newName}' fields at the same time`
      );
    }
    o[newName] = o[oldName];
    delete o[oldName];
  }
  return o;
}

function handleLegacyTagFieldsForGet(tag) {
  tag.animal = tag.what;
  tag.event = tag.detail;
  return tag;
}

function handleLegacyTagFieldsForGetOnRecording(recording) {
  recording = recording.get({ plain: true });
  recording.Tags = recording.Tags.map(handleLegacyTagFieldsForGet);
  return recording;
}

const statusCode = {
  Success: 1,
  Fail: 2,
  Both: 3
};

// reprocessAll expects request.body.recordings to be a list of recording_ids
// will mark each recording to be reprocessed
async function reprocessAll(request, response) {
  const recordings = request.body.recordings;
  const responseMessage = {
    statusCode: 200,
    messages: [],
    reprocessed: [],
    fail: []
  };

  let status = 0;
  for (let i = 0; i < recordings.length; i++) {
    const resp = await reprocessRecording(request.user, recordings[i]);
    if (resp.statusCode != 200) {
      status = status | statusCode.Fail;
      responseMessage.messages.push(resp.messages[0]);
      responseMessage.statusCode = resp.statusCode;
      responseMessage.fail.push(resp.recordingId);
    } else {
      responseMessage.reprocessed.push(resp.recordingId);
      status = status | statusCode.Success;
    }
  }
  responseMessage.messages.splice(0, 0, getReprocessMessage(status));
  responseUtil.send(response, responseMessage);
  return;
}

function getReprocessMessage(status) {
  switch (status) {
    case statusCode.Success:
      return "All recordings scheduled for reprocessing";
    case statusCode.Fail:
      return "Recordings could not be scheduled for reprocessing";
    case statusCode.Both:
      return "Some recordings could not be scheduled for reprocessing";
    default:
      return "";
  }
}

// reprocessRecording marks supplied recording_id for reprocessing,
// under supplied user privileges
async function reprocessRecording(user, recording_id) {
  const recording = await models.Recording.get(
    user,
    recording_id,
    models.Recording.Perms.UPDATE
  );

  if (!recording) {
    return {
      statusCode: 400,
      messages: ["No such recording: " + recording_id],
      recordingId: recording_id
    };
  }

  await recording.reprocess(user);

  return {
    statusCode: 200,
    messages: ["Recording scheduled for reprocessing"],
    recordingId: recording_id
  };
}

// reprocess a recording defined by request.user and request.params.id
async function reprocess(request, response) {
  const responseInfo = await reprocessRecording(
    request.user,
    request.params.id
  );
  responseUtil.send(response, responseInfo);
}

exports.makeUploadHandler = makeUploadHandler;
exports.query = query;
exports.report = report;
exports.get = get;
exports.delete_ = delete_;
exports.addTag = addTag;
exports.reprocess = reprocess;
exports.reprocessAll = reprocessAll;
<|endoftext|>


<commit_before>/*
 * copyright (c) 2015 Susisu
 */

(function () {
    "use strict";

    var settings = {
        "server_url": "http://localhost:3000"
    };

    chrome.browserAction.onClicked.addListener(function (tab) {
        chrome.tabs.sendMessage(tab.id, { action: "getCharacterSet" }, function (charset) {
            // TODO: send the current (appropriate) character set to the server

            // console.log(charset);
        });
    });

    chrome.webRequest.onHeadersReceived.addListener(function (details) {
        for (var i = 0; i < details.responseHeaders.length; i++) {
            var header = details.responseHeaders[i];
            if (header.name.toLowerCase() === "content-type" && header.value.toLowerCase().indexOf("text") >= 0) {
                var request = new XMLHttpRequest();
                request.open("GET",
                    settings["server_url"] + "/api/charset?url=" + encodeURIComponent(details.url),
                    false
                );
                request.send(null);
                if (request.status === 200) {
                    var charset = request.responseText;
                    if (header.value.toLowerCase().indexOf("charset=") >= 0) {
                        header.value = header.value.replace(/charset\=[^\s;]+/, "charset=" + charset)
                    }
                    else {
                        header.value = header.value + "; charset=" + charset;
                    }
                }
                break;
            }
        }
        return { "responseHeaders": details.responseHeaders };
    }, { "urls": ["<all_urls>"] }, ["blocking", "responseHeaders"]);
})();
<commit_msg>Send registration request to the server<commit_after>/*
 * copyright (c) 2015 Susisu
 */

(function () {
    "use strict";

    var settings = {
        "server_url": "http://localhost:3000"
    };

    chrome.browserAction.onClicked.addListener(function (tab) {
        chrome.tabs.sendMessage(tab.id, { action: "getCharacterSet" }, function (charset) {
            var params = "url=" + encodeURIComponent(tab.url) + "&charset=" + encodeURIComponent(charset);
            var request = new XMLHttpRequest();
            request.open("POST",
                settings["server_url"] + "/api/register"
            );
            request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            request.send(params);
        });
    });

    chrome.webRequest.onHeadersReceived.addListener(function (details) {
        for (var i = 0; i < details.responseHeaders.length; i++) {
            var header = details.responseHeaders[i];
            if (header.name.toLowerCase() === "content-type" && header.value.toLowerCase().indexOf("text") >= 0) {
                var request = new XMLHttpRequest();
                request.open("GET",
                    settings["server_url"] + "/api/charset?url=" + encodeURIComponent(details.url),
                    false
                );
                request.send(null);
                if (request.status === 200) {
                    var charset = request.responseText;
                    if (header.value.toLowerCase().indexOf("charset=") >= 0) {
                        header.value = header.value.replace(/charset\=[^\s;]+/, "charset=" + charset)
                    }
                    else {
                        header.value = header.value + "; charset=" + charset;
                    }
                }
                break;
            }
        }
        return { "responseHeaders": details.responseHeaders };
    }, { "urls": ["<all_urls>"] }, ["blocking", "responseHeaders"]);
})();
<|endoftext|>


<commit_before>import tumblrClient from '../tumblrClient';
import youtube from '../youtubeClient';
import range from 'lodash/range';
import without from 'lodash/without';
import map from 'lodash/map';
import toNumber from 'lodash/toNumber';
import { redisClient } from '../api';
import config from 'config';
const playlist = config.youtubePlaylistId;
// import redis from 'redis';

// function getBlogPostsPromise({ limit = 20, offset }) {
//   return new Promise((resolve, reject) => {
//     tumblrClient.blogPosts('vega-june.tumblr.com', {
//       limit,
//       offset: offset * 20,
//     }, (err, data) => {
//       if (err) reject(err);
//       resolve(data);
//     });
//   });
// }
//
// function getRedisMultiPromise(multi) {
//   return new Promise((resolve, reject) => {
//     multi.exec((err, data) => {
//       if (err) reject(err);
//       resolve(data);
//     });
//   });
// }
//
// function superagentGetPromise(superAgent) {
//   return new Promise((resolve, reject) => {
//     superAgent.end((err, data) => {
//       if (err) reject(err);
//       resolve(data);
//     });
//   });
// }
//
// function mapTumblrToIds(tumblrData) {
//   const multi = redisClient.multi();
//   const ids = map(tumblrData.posts, ({ id, type, photos, thumbnail_url }) => {
//     multi.get(id);
//     const url = type === 'video'
//       ? thumbnail_url
//       : photos[0].alt_sizes[5].url
//     return { id, url };
//   });
// }

export function loadInfo(req) {
  const offset = req && req.query && req.query.offset
    ? req.query.offset
    : 0;
  return new Promise((resolve, reject) => {
    tumblrClient.blogPosts('vega-june.tumblr.com', {
      limit: 20,
      offset: offset * 20,
    }, (err, data) => {
      if (err) return reject(err);
      return resolve(data);
    });
  });
}

export function loadAll(req) {
  console.log('loadingall!!!');
  return new Promise((masterResolve) => {
    const multi = redisClient.multi();
    multi.get('latestTumblr');
    multi.get('latestYoutube');

    multi.exec( (err, [data, youtube]) => {

      const tumblrPromise = new Promise((tumblrResolve, tumblrReject) => {
        if (data) {
          console.log('resolving data');
          return tumblrResolve(JSON.parse(data));
        } else {
          fetchAllPostsFromTumblr(req).then((tumblrData) => {
            redisClient.set('latestTumblr', JSON.stringify(tumblrData), (setErr, setData) => {
              console.log('setData', setData);
              tumblrResolve(tumblrData);
            });
          });
        }
      }).catch(console.log);
      const youtubePromise = new Promise((youtubeResolve, youtubeReject) => {
        if (youtube) {
          console.log('resolving data');
          return youtubeResolve(JSON.parse(youtube));
        } else {
          fetchAllYoutubeVideos().then((youtubeData) => {
            redisClient.set('latestYoutube', JSON.stringify(youtubeData), (setErr, setData) => {
              console.log('setData', setData);
              youtubeResolve(youtubeData);
            });
          });
        }
      }).catch(console.log);
      return Promise.all([tumblrPromise, youtubePromise]).then(([tumblr, youtube]) => {
        masterResolve({tumblr, youtube});
      }).catch(console.log);
    });
      // console.error('data, youtube', youtube);

    // }).catch(console.log);
  });
}

export function fetchAllPostsFromTumblr(req) {
  return new Promise((masterResolve, masterReject) => {
    const pages = req && req.query && req.query.pages
      ? map(req.query.pages.split(','), toNumber)
      : -1;
    const totalPosts = req && req.query && req.query.totalPosts
      ? req.query.totalPosts
      : null;
    const firstPromise = totalPosts
      ? Promise.resolve(totalPosts)
      : new Promise((resolve, reject) => {
        tumblrClient.blogPosts('vega-june.tumblr.com', {
          limit: 20,
        }, (err, data) => {
          if (err) return reject(err);
          return resolve(data.blog.posts);
        });
      });
    firstPromise.then((promiseResponse) => {
      const pagesNeeded = Math.floor(promiseResponse / 20 + 1);
      const allPagesNeededFetch = without(range(pagesNeeded), ...pages);
      return Promise.all(
        allPagesNeededFetch.reduce((out, page) => [
          ...out,
          new Promise((resolve, reject) => {
            console.log('getting page', page);
            tumblrClient.blogPosts('vega-june.tumblr.com', {
              limit: 20,
              offset: 20 * page
            }, (err, data) => {
              if (err) return reject(err);
              return resolve(data);
            });
          })
        ], [])
      ).then(resultArray => {
        redisClient.set('latestTumblr', JSON.stringify(resultArray), (setErr, setData) => {
          console.log('setData', setData);
          masterResolve(resultArray);
        });
      }).catch((err) => {
        masterReject(err);
      });
    });
  }).catch(err => console.error(err));
}

export function fetchAllYoutubeVideos() {
  return new Promise((resolve, reject) => {
    youtube.playlistItems.list({
      part: 'contentDetails',
      playlistId: 'PLDxchoMUm8NhAkt6c0pkdowAunWUaq_f8',
      mine: true,
    }, (err, res) => {
      if (err || !res.items.length) {
        reject(err);
      }
      const ids = [];
      res.items.forEach((item) => ids.push(item.contentDetails.videoId));
      console.error('ids', ids);
      youtube.videos.list({
        part: 'contentDetails,snippet,player',
        id: ids.join(','),
      }, (err, res) => {
        if (err || !res) {
          reject(err);
        }
        console.error('err', err);
        console.error('res', res);
        redisClient.set('latestYoutube', JSON.stringify(res), (setErr, setData) => {
          console.log('setData', setData);
          resolve(res);
        });
      });
    });
  }).catch(console.log);
}
<commit_msg>allowed 50 videaz.<commit_after>import tumblrClient from '../tumblrClient';
import youtube from '../youtubeClient';
import range from 'lodash/range';
import without from 'lodash/without';
import map from 'lodash/map';
import toNumber from 'lodash/toNumber';
import { redisClient } from '../api';
import config from 'config';
const playlist = config.youtubePlaylistId;
// import redis from 'redis';

// function getBlogPostsPromise({ limit = 20, offset }) {
//   return new Promise((resolve, reject) => {
//     tumblrClient.blogPosts('vega-june.tumblr.com', {
//       limit,
//       offset: offset * 20,
//     }, (err, data) => {
//       if (err) reject(err);
//       resolve(data);
//     });
//   });
// }
//
// function getRedisMultiPromise(multi) {
//   return new Promise((resolve, reject) => {
//     multi.exec((err, data) => {
//       if (err) reject(err);
//       resolve(data);
//     });
//   });
// }
//
// function superagentGetPromise(superAgent) {
//   return new Promise((resolve, reject) => {
//     superAgent.end((err, data) => {
//       if (err) reject(err);
//       resolve(data);
//     });
//   });
// }
//
// function mapTumblrToIds(tumblrData) {
//   const multi = redisClient.multi();
//   const ids = map(tumblrData.posts, ({ id, type, photos, thumbnail_url }) => {
//     multi.get(id);
//     const url = type === 'video'
//       ? thumbnail_url
//       : photos[0].alt_sizes[5].url
//     return { id, url };
//   });
// }

export function loadInfo(req) {
  const offset = req && req.query && req.query.offset
    ? req.query.offset
    : 0;
  return new Promise((resolve, reject) => {
    tumblrClient.blogPosts('vega-june.tumblr.com', {
      limit: 20,
      offset: offset * 20,
    }, (err, data) => {
      if (err) return reject(err);
      return resolve(data);
    });
  });
}

export function loadAll(req) {
  console.log('loadingall!!!');
  return new Promise((masterResolve) => {
    const multi = redisClient.multi();
    multi.get('latestTumblr');
    multi.get('latestYoutube');

    multi.exec( (err, [data, youtube]) => {

      const tumblrPromise = new Promise((tumblrResolve, tumblrReject) => {
        if (data) {
          console.log('resolving data');
          return tumblrResolve(JSON.parse(data));
        } else {
          fetchAllPostsFromTumblr(req).then((tumblrData) => {
            redisClient.set('latestTumblr', JSON.stringify(tumblrData), (setErr, setData) => {
              console.log('setData', setData);
              tumblrResolve(tumblrData);
            });
          });
        }
      }).catch(console.log);
      const youtubePromise = new Promise((youtubeResolve, youtubeReject) => {
        if (youtube) {
          console.log('resolving data');
          return youtubeResolve(JSON.parse(youtube));
        } else {
          fetchAllYoutubeVideos().then((youtubeData) => {
            redisClient.set('latestYoutube', JSON.stringify(youtubeData), (setErr, setData) => {
              console.log('setData', setData);
              youtubeResolve(youtubeData);
            });
          });
        }
      }).catch(console.log);
      return Promise.all([tumblrPromise, youtubePromise]).then(([tumblr, youtube]) => {
        masterResolve({tumblr, youtube});
      }).catch(console.log);
    });
      // console.error('data, youtube', youtube);

    // }).catch(console.log);
  });
}

export function fetchAllPostsFromTumblr(req) {
  return new Promise((masterResolve, masterReject) => {
    const pages = req && req.query && req.query.pages
      ? map(req.query.pages.split(','), toNumber)
      : -1;
    const totalPosts = req && req.query && req.query.totalPosts
      ? req.query.totalPosts
      : null;
    const firstPromise = totalPosts
      ? Promise.resolve(totalPosts)
      : new Promise((resolve, reject) => {
        tumblrClient.blogPosts('vega-june.tumblr.com', {
          limit: 20,
        }, (err, data) => {
          if (err) return reject(err);
          return resolve(data.blog.posts);
        });
      });
    firstPromise.then((promiseResponse) => {
      const pagesNeeded = Math.floor(promiseResponse / 20 + 1);
      const allPagesNeededFetch = without(range(pagesNeeded), ...pages);
      return Promise.all(
        allPagesNeededFetch.reduce((out, page) => [
          ...out,
          new Promise((resolve, reject) => {
            console.log('getting page', page);
            tumblrClient.blogPosts('vega-june.tumblr.com', {
              limit: 20,
              offset: 20 * page
            }, (err, data) => {
              if (err) return reject(err);
              return resolve(data);
            });
          })
        ], [])
      ).then(resultArray => {
        redisClient.set('latestTumblr', JSON.stringify(resultArray), (setErr, setData) => {
          console.log('setData', setData);
          masterResolve(resultArray);
        });
      }).catch((err) => {
        masterReject(err);
      });
    });
  }).catch(err => console.error(err));
}

export function fetchAllYoutubeVideos() {
  return new Promise((resolve, reject) => {
    youtube.playlistItems.list({
      part: 'contentDetails',
      playlistId: 'PLDxchoMUm8NhAkt6c0pkdowAunWUaq_f8',
      mine: true,
      maxResults: 50,
    }, (err, res) => {
      if (err || !res.items.length) {
        reject(err);
      }
      const ids = [];
      res.items.forEach((item) => ids.push(item.contentDetails.videoId));
      console.error('ids', ids);
      youtube.videos.list({
        part: 'contentDetails,snippet,player',
        id: ids.join(','),
      }, (err, res) => {
        if (err || !res) {
          reject(err);
        }
        console.error('err', err);
        console.error('res', res);
        redisClient.set('latestYoutube', JSON.stringify(res), (setErr, setData) => {
          console.log('setData', setData);
          resolve(res);
        });
      });
    });
  }).catch(console.log);
}
<|endoftext|>