<commit_before>#!/usr/bin/env node

const package = require('./package.json');
const process = require('process');
const fs = require('fs');
const readline = require('readline');
const program = require('commander');
const chalk = require('chalk');
const ytdl = require('ytdl-core');
const youtubedl = require('youtube-dl');
const translate = require('translate-google');

const version = package.version;
const currentDir = process.cwd();
const log = console.log;

/**
 * Messages
 */

const extension = 'mp4';
const msg = {
    about: [
        chalk.dim(` VDL - ${version}`)
    ].join(' '),
    invalidUrl: (url) => [
            chalk.red(' !'),
            chalk.yellow('Please provide a valid url:'),
            chalk.dim(url)
        ].join(' '),
    getInfo: (url) => [
            chalk.blue(' +'),
            chalk.white('Getting info:'),
            chalk.dim(url)
        ].join(' '),
    errGetInfo: (url) => [
            chalk.red(' ✖'),
            chalk.yellow('Error getting video info:'),
            chalk.dim(url)
        ].join(' '),
    searchInfo: (keyword) => [
            chalk.blue(' +'),
            chalk.white('Searching video info:'),
            chalk.dim(keyword)
        ].join(' '),
    errSearchInfo: (keyword) => [
            chalk.red(' ✖'),
            chalk.yellow('Error searching video info:'),
            chalk.dim(keyword)
        ].join(' '),
    downloading: (title) => [
            chalk.blue(' +'),
            chalk.white('Downloading:'),
            chalk.dim(`${title}.${extension}`)
        ].join(' '),
    downloaded: (title) => [
            chalk.green(' ✔'),
            chalk.white('Saved:'),
            chalk.dim(`${title}.${extension}`)
        ].join(' '),
    progress: (downloaded, total) => [
            chalk.blue(' +'),
            chalk.dim('Progress:'),
            chalk.yellow(`[ ${downloaded} / ${total} ]`)
        ].join(' '),
    readFile: (filePath) => [
            chalk.blue(' +'),
            chalk.white('Reading file:'),
            chalk.dim(filePath)
        ].join(' '),
    itemsFound: (total) => [
            chalk.blue(' +'),
            chalk.white(`Found ${total} items`)
        ].join(' '),
    itemOfTotal: (current, total) => [
            chalk.blue(' +'),
            chalk.white(`[ ${current} / ${total} ]`)
        ].join(' '),
    commandNotFound: [
            chalk.red(' !'),
            chalk.yellow('Command Not Found'),
            chalk.red('\n !'),
            chalk.yellow('Use "vdl -h" for help')
        ].join(' '),
    done: [
            chalk.green(' ✔'),
            chalk.dim('Done\n')
        ].join(' ')
};

/**
 * Terminal output functions
 */

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.on('close', () => {
    process.exit(0);
});

const writeLine = (txt) => {
    readline.cursorTo(process.stdout, 0);
    readline.clearLine(process.stdout, 0);
    process.stdout.write(txt);
    process.stdout.pause();
};

const done = () => {
    log(msg.done);
    rl.close();
};

/**
 * Download video functions
 */

const isValidUrl = (url) => (
    url && url.toLowerCase().startsWith('http')
);

const validateUrl = url => {
    return new Promise(
        (resolve, reject) => {
            if (isValidUrl(url)) {
                resolve(
                    url.split('&')[0]
                );
            }
            else {
                reject(msg.invalidUrl);
            }
        }
    );
};

const getVideoInfo = (url) => {
    log(msg.getInfo(url));

    return new Promise((resolve, reject) => {
        ytdl.getInfo(url, (err, info) => {
            if (err) {
                reject(msg.errGetInfo(url));
            }
            else {
                resolve(info);
            }
        });
    });
};

const searchVideoInfo = (keyword) => {
    log(msg.searchInfo(keyword));

    const url = `gvsearch1:${keyword}`;

    return new Promise((resolve, reject) => {
        youtubedl.getInfo(url, (err, info) => {
            if (err) {
                reject(msg.errSearchInfo(keyword));
            }
            else {
                resolve({
                    title: info.title,
                    url: `https://www.youtube.com/watch?v=${info.id}`
                });
            }
        });
    });
};

const formatVideoInfo = (info) => {
    const stripCharacters = (str) => {
        let updatedStr = str;

        updatedStr = updatedStr.replace(/[\s|:?\.\\\/]/g, '-');
        updatedStr = updatedStr.replace(/[\"]/g, '');

        return updatedStr;
    };
    const strippedTitle = stripCharacters(info.title);

    return new Promise((resolve, reject) => {
        translate(strippedTitle, {to: 'en'}).then(
            translatedTitle => resolve({
                info,
                title: stripCharacters(translatedTitle)
            })
        ).catch(
            err => reject(err)
        );
    });
};

const downloadFromVideoInfo = (videoInfo) => {
    const title = videoInfo.title;
    let dlStream = ytdl.downloadFromInfo(videoInfo.info);

    log(msg.downloading(title));

    dlStream.pipe(
        fs.createWriteStream(`${currentDir}/${title}.${extension}`)
    );

    return new Promise((resolve, reject) => {
        dlStream.on('progress', (chunk, downloaded, total) => {
            writeLine(msg.progress(downloaded, total));

            if (downloaded === total) {
                readline.cursorTo(process.stdout, 0, null);
                readline.clearLine(process.stdout, 0);
                resolve(msg.downloaded(title));
            }
        });
    });
};

/**
 * Terminal command definitions
 */

program
    .version(version)
    .option('-u, --url [urlpath]', 'Add url')
    .option('-f, --file [filepath]', 'Add file')
    .parse(process.argv);

let commandFound = false;

const onCommandFound = () => {
    commandFound = true;
    log(msg.about);
};

if (program.url) {
    onCommandFound();

    validateUrl(program.url)
        .then(url => getVideoInfo(url))
        .then(info => formatVideoInfo(info))
        .then(videoInfo => downloadFromVideoInfo(videoInfo))
        .then(videoDownloadedMsg => log(videoDownloadedMsg))
        .then(() => done())
        .catch((err) => {
            log(err);
            done();
        });
}

if (program.file) {
    onCommandFound();

    const filePath = program.file;

    log(msg.readFile(filePath));

    const getFileContentAsList = pathToFile => {
        const newLineChars = /\r\n/g;
        const invalidChars = /\t\r\n\v\f/g;
        const content = fs.readFileSync(pathToFile, 'utf-8');
        let fileContentList = content.replace(newLineChars, '\n')
            .split('\n')
            .map(url => url ? url.replace(invalidChars, '') : '')
            .filter(url => url);

        return fileContentList;
    };

    const contentList = getFileContentAsList(filePath);
    const totalItems = contentList.length;

    log(msg.itemsFound(totalItems));

    const count = ((total) => {
        let counter = 1;

        return (
            () => {
                log(
                    msg.itemOfTotal(counter++, total)
                );
            }
        );
    })(totalItems);

    const searchAndDownload = (url) => {
        let searchDone = null;

        count();

        if (!url) {
            searchDone = Promise.reject(msg.invalidUrl);
        }
        else if (!isValidUrl(url)) {
            // If the suffix doesn't start with 'http',
            // assume it's a search.
            searchDone = searchVideoInfo(url);
        }
        else {
            searchDone = Promise.resolve({url});
        }

        searchDone
            .then(searchResult => getVideoInfo(searchResult.url))
            .then(info => formatVideoInfo(info))
            .then(videoInfo => downloadFromVideoInfo(videoInfo))
            .then(videoDownloadedMsg => log(videoDownloadedMsg))
            .then(downloadNextInFile)
            .catch(downloadNextInFile);
    };

    const downloadNextInFile = (err) => {
        const nextUrl = contentList.shift();

        if (err) {
            log(err);
        }

        if (nextUrl) {
            searchAndDownload(nextUrl);
        }
        else {
            done();
        }
    };

    if (totalItems) {
        searchAndDownload(contentList.shift());
    }
    else {
        done();
    }
}

if (!commandFound) {
    log(msg.about);
    log(msg.commandNotFound);
    rl.close();
}
<commit_msg>Improve error handling when translation fails<commit_after>#!/usr/bin/env node

const package = require('./package.json');
const process = require('process');
const fs = require('fs');
const readline = require('readline');
const program = require('commander');
const chalk = require('chalk');
const ytdl = require('ytdl-core');
const youtubedl = require('youtube-dl');
const translate = require('translate-google');

const version = package.version;
const currentDir = process.cwd();
const log = console.log;

/**
 * Messages
 */

const extension = 'mp4';
const msg = {
    about: [
        chalk.dim(` VDL - ${version}`)
    ].join(' '),
    invalidUrl: (url) => [
            chalk.red(' !'),
            chalk.yellow('Please provide a valid url:'),
            chalk.dim(url)
        ].join(' '),
    getInfo: (url) => [
            chalk.blue(' +'),
            chalk.white('Getting info:'),
            chalk.dim(url)
        ].join(' '),
    errGetInfo: (url) => [
            chalk.red(' ✖'),
            chalk.yellow('Error getting video info:'),
            chalk.dim(url)
        ].join(' '),
    searchInfo: (keyword) => [
            chalk.blue(' +'),
            chalk.white('Searching video info:'),
            chalk.dim(keyword)
        ].join(' '),
    errSearchInfo: (keyword) => [
            chalk.red(' ✖'),
            chalk.yellow('Error searching video info:'),
            chalk.dim(keyword)
        ].join(' '),
    errTranslateTitle: [
        chalk.red(' !'),
        chalk.yellow('Error translating video title'),
        chalk.red('\n !'),
        chalk.yellow('Using original title')
    ].join(' '),
    downloading: (title) => [
            chalk.blue(' +'),
            chalk.white('Downloading:'),
            chalk.dim(`${title}.${extension}`)
        ].join(' '),
    downloaded: (title) => [
            chalk.green(' ✔'),
            chalk.white('Saved:'),
            chalk.dim(`${title}.${extension}`)
        ].join(' '),
    progress: (downloaded, total) => [
            chalk.blue(' +'),
            chalk.dim('Progress:'),
            chalk.yellow(`[ ${downloaded} / ${total} ]`)
        ].join(' '),
    readFile: (filePath) => [
            chalk.blue(' +'),
            chalk.white('Reading file:'),
            chalk.dim(filePath)
        ].join(' '),
    itemsFound: (total) => [
            chalk.blue(' +'),
            chalk.white(`Found ${total} items`)
        ].join(' '),
    itemOfTotal: (current, total) => [
            chalk.blue(' +'),
            chalk.white(`[ ${current} / ${total} ]`)
        ].join(' '),
    commandNotFound: [
            chalk.red(' !'),
            chalk.yellow('Command Not Found'),
            chalk.red('\n !'),
            chalk.yellow('Use "vdl -h" for help')
        ].join(' '),
    done: [
            chalk.green(' ✔'),
            chalk.dim('Done\n')
        ].join(' ')
};

/**
 * Terminal output functions
 */

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.on('close', () => {
    process.exit(0);
});

const writeLine = (txt) => {
    readline.cursorTo(process.stdout, 0);
    readline.clearLine(process.stdout, 0);
    process.stdout.write(txt);
    process.stdout.pause();
};

const done = () => {
    log(msg.done);
    rl.close();
};

/**
 * Download video functions
 */

const isValidUrl = (url) => (
    url && url.toLowerCase().startsWith('http')
);

const validateUrl = url => {
    return new Promise(
        (resolve, reject) => {
            if (isValidUrl(url)) {
                resolve(
                    url.split('&')[0]
                );
            }
            else {
                reject(msg.invalidUrl);
            }
        }
    );
};

const getVideoInfo = (url) => {
    log(msg.getInfo(url));

    return new Promise((resolve, reject) => {
        ytdl.getInfo(url, (err, info) => {
            if (err) {
                reject(msg.errGetInfo(url));
            }
            else {
                resolve(info);
            }
        });
    });
};

const searchVideoInfo = (keyword) => {
    log(msg.searchInfo(keyword));

    const url = `gvsearch1:${keyword}`;

    return new Promise((resolve, reject) => {
        youtubedl.getInfo(url, (err, info) => {
            if (err) {
                reject(msg.errSearchInfo(keyword));
            }
            else {
                resolve({
                    title: info.title,
                    url: `https://www.youtube.com/watch?v=${info.id}`
                });
            }
        });
    });
};

const formatVideoInfo = (info) => {
    const stripCharacters = (str) => {
        let updatedStr = str;

        updatedStr = updatedStr.replace(/[\s|:?\.\\\/]/g, '-');
        updatedStr = updatedStr.replace(/[\"]/g, '');

        return updatedStr;
    };
    const strippedTitle = stripCharacters(info.title);

    return new Promise((resolve, reject) => {
        translate(strippedTitle, {to: 'en'}).then(
            translatedTitle => resolve({
                info,
                title: stripCharacters(translatedTitle)
            })
        ).catch(
            err => {
                log(msg.errTranslateTitle);
                resolve(strippedTitle);
            }
        );
    });
};

const downloadFromVideoInfo = (videoInfo) => {
    const title = videoInfo.title;
    let dlStream = ytdl.downloadFromInfo(videoInfo.info);

    log(msg.downloading(title));

    dlStream.pipe(
        fs.createWriteStream(`${currentDir}/${title}.${extension}`)
    );

    return new Promise((resolve, reject) => {
        dlStream.on('progress', (chunk, downloaded, total) => {
            writeLine(msg.progress(downloaded, total));

            if (downloaded === total) {
                readline.cursorTo(process.stdout, 0, null);
                readline.clearLine(process.stdout, 0);
                resolve(msg.downloaded(title));
            }
        });
    });
};

/**
 * Terminal command definitions
 */

program
    .version(version)
    .option('-u, --url [urlpath]', 'Add url')
    .option('-f, --file [filepath]', 'Add file')
    .parse(process.argv);

let commandFound = false;

const onCommandFound = () => {
    commandFound = true;
    log(msg.about);
};

if (program.url) {
    onCommandFound();

    validateUrl(program.url)
        .then(url => getVideoInfo(url))
        .then(info => formatVideoInfo(info))
        .then(videoInfo => downloadFromVideoInfo(videoInfo))
        .then(videoDownloadedMsg => log(videoDownloadedMsg))
        .then(() => done())
        .catch((err) => {
            log(err);
            done();
        });
}

if (program.file) {
    onCommandFound();

    const filePath = program.file;

    log(msg.readFile(filePath));

    const getFileContentAsList = pathToFile => {
        const newLineChars = /\r\n/g;
        const invalidChars = /\t\r\n\v\f/g;
        const content = fs.readFileSync(pathToFile, 'utf-8');
        let fileContentList = content.replace(newLineChars, '\n')
            .split('\n')
            .map(url => url ? url.replace(invalidChars, '') : '')
            .filter(url => url);

        return fileContentList;
    };

    const contentList = getFileContentAsList(filePath);
    const totalItems = contentList.length;

    log(msg.itemsFound(totalItems));

    const count = ((total) => {
        let counter = 1;

        return (
            () => {
                log(
                    msg.itemOfTotal(counter++, total)
                );
            }
        );
    })(totalItems);

    const searchAndDownload = (url) => {
        let searchDone = null;

        count();

        if (!url) {
            searchDone = Promise.reject(msg.invalidUrl);
        }
        else if (!isValidUrl(url)) {
            // If the suffix doesn't start with 'http',
            // assume it's a search.
            searchDone = searchVideoInfo(url);
        }
        else {
            searchDone = Promise.resolve({url});
        }

        searchDone
            .then(searchResult => getVideoInfo(searchResult.url))
            .then(info => formatVideoInfo(info))
            .then(videoInfo => downloadFromVideoInfo(videoInfo))
            .then(videoDownloadedMsg => log(videoDownloadedMsg))
            .then(downloadNextInFile)
            .catch(downloadNextInFile);
    };

    const downloadNextInFile = (err) => {
        const nextUrl = contentList.shift();

        if (err) {
            log(err);
        }

        if (nextUrl) {
            searchAndDownload(nextUrl);
        }
        else {
            done();
        }
    };

    if (totalItems) {
        searchAndDownload(contentList.shift());
    }
    else {
        done();
    }
}

if (!commandFound) {
    log(msg.about);
    log(msg.commandNotFound);
    rl.close();
}
<|endoftext|>


<commit_before>var each = require('turf-meta').coordEach;
var point = require('turf-point');

/**
 * Takes a {@link Feature} or {@link FeatureCollection} of any type and calculates the centroid using the arithmetic mean of all vertices.
 * This lessens the effect of small islands and artifacts when calculating
 * the centroid of a set of polygons.
 *
 * @module turf/centroid
 * @param {FeatureCollection} fc a {@link Feature} or FeatureCollection of any type
 * @return {Point} a Point feature at the centroid of the input feature(s)
 * @example
 * var poly = {
 *   "type": "Feature",
 *   "properties": {},
 *   "geometry": {
 *     "type": "Polygon",
 *     "coordinates": [[
 *       [105.818939,21.004714],
 *       [105.818939,21.061754],
 *       [105.890007,21.061754],
 *       [105.890007,21.004714],
 *       [105.818939,21.004714]
 *     ]]
 *   }
 * };
 *
 * var centroidPt = turf.centroid(poly);
 *
 * var result = {
 *   "type": "FeatureCollection",
 *   "features": [poly, centroidPt]
 * };
 *
 * //=result
 */
module.exports = function(features){
  var xSum = 0, ySum = 0, len = 0;
  each(features, function(coord) {
    xSum += coord[0];
    ySum += coord[1];
    len++;
  });
  return point([xSum / len, ySum / len]);
};
<commit_msg>Add category<commit_after>var each = require('turf-meta').coordEach;
var point = require('turf-point');

/**
 * Takes a {@link Feature} or {@link FeatureCollection} of any type and calculates the centroid using the arithmetic mean of all vertices.
 * This lessens the effect of small islands and artifacts when calculating
 * the centroid of a set of polygons.
 *
 * @module turf/centroid
 * @category measurement
 * @param {FeatureCollection} fc a {@link Feature} or FeatureCollection of any type
 * @return {Point} a Point feature at the centroid of the input feature(s)
 * @example
 * var poly = {
 *   "type": "Feature",
 *   "properties": {},
 *   "geometry": {
 *     "type": "Polygon",
 *     "coordinates": [[
 *       [105.818939,21.004714],
 *       [105.818939,21.061754],
 *       [105.890007,21.061754],
 *       [105.890007,21.004714],
 *       [105.818939,21.004714]
 *     ]]
 *   }
 * };
 *
 * var centroidPt = turf.centroid(poly);
 *
 * var result = {
 *   "type": "FeatureCollection",
 *   "features": [poly, centroidPt]
 * };
 *
 * //=result
 */
module.exports = function(features){
  var xSum = 0, ySum = 0, len = 0;
  each(features, function(coord) {
    xSum += coord[0];
    ySum += coord[1];
    len++;
  });
  return point([xSum / len, ySum / len]);
};
<|endoftext|>


<commit_before>#!/usr/bin/env node --es_staging
"use strict";

const ChildProcess = require("child_process");
const getOpts      = require("get-options");
const fs           = require("fs");

/** Read CLI args */
let _ = getOpts(process.argv.slice(2), {
	"-?, -h, --help":                       "",
	"-m, --mutilate":                       "<bool>",
	"-u, --underline-urls":                 "<bool>",
	"-i, --indent":                         "<size>",
	"-c, --colour, --colours, --colourise": "<bool>",
	"-p, --paged":                          "",
	"-a, --alphabetise":                    ""
});

/** Would've used `let {options, argv} = getOpts`, but hey, no native destructuring support yet */
let options       = _.options;
let argv          = _.argv;

/** Print help and bail if that's what the user wanted */
if(options.help){
	const path = require("path");
	let name   = path.basename(process.argv[1]);
	let help   = `
	Usage: ${name} [options] <filename>

	Pretty-print JSON data for terminal output.

	Options:

	  -m, --mutilate <bool=1>       Unquote property identifiers
	  -u, --underline-urls <bool=1> Add underlines to URLs
	  -c, --colour <bool=1>         Colourise the prettified output
	  -i, --indent <size=4>         Indentation width, expressed in spaces
	  -a, --alphabetise             Order properties alphabetically
	  -p, --paged                   Show content in pager if longer than screen

	Run \`man ppjson' for full documentation.
	`.replace(/\t+/g, "    ");

	console.log(help);
	process.exit(0);
}

/** Parse our options and normalise their defaults */
let mutilate      = options.m === undefined ? true  : bool(options.m);
let underlineURLs = options.u === undefined ? true  : bool(options.u);
let colourise     = options.c === undefined ? true  : bool(options.c);
let pagedView     = options.p === undefined ? false : bool(options.p);
let indentSize    = options.i === undefined ? 4     : options.indent;
let alphabetise   = options.a === undefined ? false : options.alphabetise;
let indent        = Array(Math.max(1, indentSize) + 1).join(" ");

/** Configure colour palette */
let env               = process.env;
const COLOUR_STRINGS  = "\x1B[38;5;" + (+env.PPJSON_COLOUR_STRINGS || 2) + "m";
const COLOUR_NUMBERS  = "\x1B[38;5;" + (+env.PPJSON_COLOUR_NUMBERS || 2) + "m";
const COLOUR_TRUE     = "\x1B[38;5;" + (+env.PPJSON_COLOUR_TRUE    || 6) + "m";
const COLOUR_FALSE    = "\x1B[38;5;" + (+env.PPJSON_COLOUR_FALSE   || 6) + "m";
const COLOUR_NULL     = "\x1B[38;5;" + (+env.PPJSON_COLOUR_NULL    || 6) + "m";
const COLOUR_PUNCT    = "\x1B[38;5;" + (+env.PPJSON_COLOUR_PUNCT   || 8) + "m";
const COLOUR_ERROR    = "\x1B[38;5;" + (+env.PPJSON_COLOUR_ERROR   || 1) + "m";

let input  = "";
let output = "";

/** An input file was specified on command-line */
if(argv.length){

	/** Cycle through each file and prettify their contents */
	for(let i = 0, l = argv.length; i < l; ++i){
		let path = argv[i];

		try{ fs.accessSync(path); }
		catch(error){
			/** If there was an access error, hit eject */
			process.stderr.write(COLOUR_ERROR);
			console.error("\x1B[1mERROR\x1B[22m: " + error.message);
			process.stderr.write("\x1B[0m");
			process.exit(2);
		}

		/** Otherwise, go for it */
		output += prettifyJSON(fs.readFileSync(path, {encoding: "utf8"}));

		/** Make sure there's enough whitespace between files */
		if(i < l - 1) output += "\n\n";
	}

	/** Send the compiled result to STDOUT */
	print(output);
}

/** No file specified, just read from STDIN instead */
else{
	process.stdin.setEncoding("utf8");
	process.stdin.on("readable", () => {
		let chunk = process.stdin.read();
		if(chunk !== null)
		input += chunk;
	});

	process.stdin.on("end", e => print(prettifyJSON(input)));
}

/**
 * Interpret a string as a boolean value.
 *
 * @param {String} input
 * @return {Boolean}
 */
function bool(input){
	let num = +input;

	/** NaN: String was supplied, check for keywords equating to "false" */
	if(num !== num)
		return !/^(?:false|off|no|disabled?|nah)$/i.test(input);
	return !!num;
}

/**
 * Recursively alphabetise the enumerable properties of an object.
 *
 * This function returns a copy of the original object with all properties
 * listed in alphabetic order, rather than enumeration order. The original
 * object is unmodified.
 *
 * @param {Object}  input
 * @param {Boolean} strictCase - If TRUE, will order case-sensitively (capitals first)
 * @return {Object}
 */
function alphabetiseProperties(input, strictCase){
	let stringTag = Object.prototype.toString.call(input);

	/** Regular JavaScript object; enumerate properties */
	if("[object Object]" === stringTag){
		let keys = Object.keys(input);

		keys = strictCase ? keys.sort() : keys.sort((a, b) => {
			let A = a.toLowerCase();
			let B = b.toLowerCase();
			if(A < B) return -1;
			if(A > B) return 1;
			return 0;
		});

		let result = {};
		for(let i of keys)
			result[i] = alphabetiseProperties(input[i]);
		return result;
	}

	/** This is an array; make sure the properties of its values are sorted too */
	else if("[object Array]" === stringTag)
		return Array.prototype.map.call(input, e => alphabetiseProperties(e));

	/** Just return it untouched */
	return input;
}

/**
 * Spruce up JSON for console display.
 *
 * @param {String}
 * @return {String}
 */
function prettifyJSON(input){
	let output = JSON.parse(input);

	/** Order the properties of objects by alphabetical order, not enumeration order */
	if(alphabetise)
		output = alphabetiseProperties(output);

	output = JSON.stringify(output, null, indent);

	/** Unquote property identifiers in object literals */
	if(mutilate){
		let pattern = new RegExp(indent + '"([\\w\\$]+)":', "g");
		output      = output.replace(pattern, indent + "$1:");
	}

	/** Colourise the output */
	if(colourise){
		const RESET = "\x1B[0m";

		output = output
			.replace(/("([^\\"]|\\.)*")/g, COLOUR_STRINGS + "$1" + RESET)  /** Strings  */
			.replace(/(\d+,)$/gm,          COLOUR_NUMBERS + "$1" + RESET)  /** Numerals */

			/** Constants */
			.replace(/true(,)?$/gm,        COLOUR_TRUE  + "true"  + RESET + "$1")
			.replace(/false(,)?$/gm,       COLOUR_FALSE + "false" + RESET + "$1")
			.replace(/null(,)?$/gm,        COLOUR_NULL  + "null"  + RESET + "$1")

			/** Greyed-out unimportant bits */
			.replace(new RegExp("^((?:"+indent+")*)([\\[\\{\\}\\]],?)", "gm"), "$1"+COLOUR_PUNCT+"$2"+RESET)
			.replace(/((?:\[\]|\{\})?,)$/gm, COLOUR_PUNCT+"$1"+RESET)
			.replace(/(\[|\{)$/gm, COLOUR_PUNCT+"$1"+RESET);
	}

	/** Underline URLs */
	if(underlineURLs){
		const UNDERLINE_ON  = "\x1B[4m";
		const UNDERLINE_OFF = "\x1B[24m";
		let rURL = /(\s*(https?:)?\/\/([^:]+:[^@]+@)?([\w-]+)(\.[\w-]+)*(:\d+)?(\/\S+)?\s*(?="))/gm;
		output   = output.replace(rURL, UNDERLINE_ON+"$1"+UNDERLINE_OFF);
	}

	return output + "\n";
}

/**
 * Send a string to STDOUT.
 *
 * The content is sent through to the less program if it's too long to
 * show without scrolling, unless the --paged option's been disabled.
 *
 * @param {String} input
 */
function print(input){

	/** Determine if the output should be piped through to a pager */
	if(pagedView && input.match(/\n/g).length > process.stdout.rows){
		let less  = ChildProcess.spawn("less", ["-Ri"], {
			stdio: ["pipe", process.stdout, process.stderr]
		});
		less.stdin.write(input);
		less.stdin.end();
	}

	else process.stdout.write(input);
}
<commit_msg>Allow boolean options to be set to false with "N"<commit_after>#!/usr/bin/env node --es_staging
"use strict";

const ChildProcess = require("child_process");
const getOpts      = require("get-options");
const fs           = require("fs");

/** Read CLI args */
let _ = getOpts(process.argv.slice(2), {
	"-?, -h, --help":                       "",
	"-m, --mutilate":                       "<bool>",
	"-u, --underline-urls":                 "<bool>",
	"-i, --indent":                         "<size>",
	"-c, --colour, --colours, --colourise": "<bool>",
	"-p, --paged":                          "",
	"-a, --alphabetise":                    ""
});

/** Would've used `let {options, argv} = getOpts`, but hey, no native destructuring support yet */
let options       = _.options;
let argv          = _.argv;

/** Print help and bail if that's what the user wanted */
if(options.help){
	const path = require("path");
	let name   = path.basename(process.argv[1]);
	let help   = `
	Usage: ${name} [options] <filename>

	Pretty-print JSON data for terminal output.

	Options:

	  -m, --mutilate <bool=1>       Unquote property identifiers
	  -u, --underline-urls <bool=1> Add underlines to URLs
	  -c, --colour <bool=1>         Colourise the prettified output
	  -i, --indent <size=4>         Indentation width, expressed in spaces
	  -a, --alphabetise             Order properties alphabetically
	  -p, --paged                   Show content in pager if longer than screen

	Run \`man ppjson' for full documentation.
	`.replace(/\t+/g, "    ");

	console.log(help);
	process.exit(0);
}

/** Parse our options and normalise their defaults */
let mutilate      = options.m === undefined ? true  : bool(options.m);
let underlineURLs = options.u === undefined ? true  : bool(options.u);
let colourise     = options.c === undefined ? true  : bool(options.c);
let pagedView     = options.p === undefined ? false : bool(options.p);
let indentSize    = options.i === undefined ? 4     : options.indent;
let alphabetise   = options.a === undefined ? false : options.alphabetise;
let indent        = Array(Math.max(1, indentSize) + 1).join(" ");

/** Configure colour palette */
let env               = process.env;
const COLOUR_STRINGS  = "\x1B[38;5;" + (+env.PPJSON_COLOUR_STRINGS || 2) + "m";
const COLOUR_NUMBERS  = "\x1B[38;5;" + (+env.PPJSON_COLOUR_NUMBERS || 2) + "m";
const COLOUR_TRUE     = "\x1B[38;5;" + (+env.PPJSON_COLOUR_TRUE    || 6) + "m";
const COLOUR_FALSE    = "\x1B[38;5;" + (+env.PPJSON_COLOUR_FALSE   || 6) + "m";
const COLOUR_NULL     = "\x1B[38;5;" + (+env.PPJSON_COLOUR_NULL    || 6) + "m";
const COLOUR_PUNCT    = "\x1B[38;5;" + (+env.PPJSON_COLOUR_PUNCT   || 8) + "m";
const COLOUR_ERROR    = "\x1B[38;5;" + (+env.PPJSON_COLOUR_ERROR   || 1) + "m";

let input  = "";
let output = "";

/** An input file was specified on command-line */
if(argv.length){

	/** Cycle through each file and prettify their contents */
	for(let i = 0, l = argv.length; i < l; ++i){
		let path = argv[i];

		try{ fs.accessSync(path); }
		catch(error){
			/** If there was an access error, hit eject */
			process.stderr.write(COLOUR_ERROR);
			console.error("\x1B[1mERROR\x1B[22m: " + error.message);
			process.stderr.write("\x1B[0m");
			process.exit(2);
		}

		/** Otherwise, go for it */
		output += prettifyJSON(fs.readFileSync(path, {encoding: "utf8"}));

		/** Make sure there's enough whitespace between files */
		if(i < l - 1) output += "\n\n";
	}

	/** Send the compiled result to STDOUT */
	print(output);
}

/** No file specified, just read from STDIN instead */
else{
	process.stdin.setEncoding("utf8");
	process.stdin.on("readable", () => {
		let chunk = process.stdin.read();
		if(chunk !== null)
		input += chunk;
	});

	process.stdin.on("end", e => print(prettifyJSON(input)));
}

/**
 * Interpret a string as a boolean value.
 *
 * @param {String} input
 * @return {Boolean}
 */
function bool(input){
	let num = +input;

	/** NaN: String was supplied, check for keywords equating to "false" */
	if(num !== num)
		return !/^(?:false|off|no?|disabled?|nah)$/i.test(input);
	return !!num;
}

/**
 * Recursively alphabetise the enumerable properties of an object.
 *
 * This function returns a copy of the original object with all properties
 * listed in alphabetic order, rather than enumeration order. The original
 * object is unmodified.
 *
 * @param {Object}  input
 * @param {Boolean} strictCase - If TRUE, will order case-sensitively (capitals first)
 * @return {Object}
 */
function alphabetiseProperties(input, strictCase){
	let stringTag = Object.prototype.toString.call(input);

	/** Regular JavaScript object; enumerate properties */
	if("[object Object]" === stringTag){
		let keys = Object.keys(input);

		keys = strictCase ? keys.sort() : keys.sort((a, b) => {
			let A = a.toLowerCase();
			let B = b.toLowerCase();
			if(A < B) return -1;
			if(A > B) return 1;
			return 0;
		});

		let result = {};
		for(let i of keys)
			result[i] = alphabetiseProperties(input[i]);
		return result;
	}

	/** This is an array; make sure the properties of its values are sorted too */
	else if("[object Array]" === stringTag)
		return Array.prototype.map.call(input, e => alphabetiseProperties(e));

	/** Just return it untouched */
	return input;
}

/**
 * Spruce up JSON for console display.
 *
 * @param {String}
 * @return {String}
 */
function prettifyJSON(input){
	let output = JSON.parse(input);

	/** Order the properties of objects by alphabetical order, not enumeration order */
	if(alphabetise)
		output = alphabetiseProperties(output);

	output = JSON.stringify(output, null, indent);

	/** Unquote property identifiers in object literals */
	if(mutilate){
		let pattern = new RegExp(indent + '"([\\w\\$]+)":', "g");
		output      = output.replace(pattern, indent + "$1:");
	}

	/** Colourise the output */
	if(colourise){
		const RESET = "\x1B[0m";

		output = output
			.replace(/("([^\\"]|\\.)*")/g, COLOUR_STRINGS + "$1" + RESET)  /** Strings  */
			.replace(/(\d+,)$/gm,          COLOUR_NUMBERS + "$1" + RESET)  /** Numerals */

			/** Constants */
			.replace(/true(,)?$/gm,        COLOUR_TRUE  + "true"  + RESET + "$1")
			.replace(/false(,)?$/gm,       COLOUR_FALSE + "false" + RESET + "$1")
			.replace(/null(,)?$/gm,        COLOUR_NULL  + "null"  + RESET + "$1")

			/** Greyed-out unimportant bits */
			.replace(new RegExp("^((?:"+indent+")*)([\\[\\{\\}\\]],?)", "gm"), "$1"+COLOUR_PUNCT+"$2"+RESET)
			.replace(/((?:\[\]|\{\})?,)$/gm, COLOUR_PUNCT+"$1"+RESET)
			.replace(/(\[|\{)$/gm, COLOUR_PUNCT+"$1"+RESET);
	}

	/** Underline URLs */
	if(underlineURLs){
		const UNDERLINE_ON  = "\x1B[4m";
		const UNDERLINE_OFF = "\x1B[24m";
		let rURL = /(\s*(https?:)?\/\/([^:]+:[^@]+@)?([\w-]+)(\.[\w-]+)*(:\d+)?(\/\S+)?\s*(?="))/gm;
		output   = output.replace(rURL, UNDERLINE_ON+"$1"+UNDERLINE_OFF);
	}

	return output + "\n";
}

/**
 * Send a string to STDOUT.
 *
 * The content is sent through to the less program if it's too long to
 * show without scrolling, unless the --paged option's been disabled.
 *
 * @param {String} input
 */
function print(input){

	/** Determine if the output should be piped through to a pager */
	if(pagedView && input.match(/\n/g).length > process.stdout.rows){
		let less  = ChildProcess.spawn("less", ["-Ri"], {
			stdio: ["pipe", process.stdout, process.stderr]
		});
		less.stdin.write(input);
		less.stdin.end();
	}

	else process.stdout.write(input);
}
<|endoftext|>


<commit_before>'use strict'
module.exports = writeFile
module.exports.sync = writeFileSync
module.exports._getTmpname = getTmpname // for testing
module.exports._cleanupOnExit = cleanupOnExit

var fs = require('graceful-fs')
var MurmurHash3 = require('imurmurhash')
var onExit = require('signal-exit')
var path = require('path')
var activeFiles = {}

// if we run inside of a worker_thread, `process.pid` is not unique
/* istanbul ignore next */
var threadId = (function getId () {
  try {
    var workerThreads = require('worker_threads')

    /// if we are in main thread, this is set to `0`
    return workerThreads.threadId
  } catch (e) {
    // worker_threads are not available, fallback to 0
    return 0
  }
})()

var invocations = 0
function getTmpname (filename) {
  return filename + '.' +
    MurmurHash3(__filename)
      .hash(String(process.pid))
      .hash(String(threadId))
      .hash(String(++invocations))
      .result()
}

function cleanupOnExit (tmpfile) {
  return function () {
    try {
      fs.unlinkSync(typeof tmpfile === 'function' ? tmpfile() : tmpfile)
    } catch (_) {}
  }
}

function writeFile (filename, data, options, callback) {
  if (options instanceof Function) {
    callback = options
    options = null
  }
  if (!options) options = {}

  var Promise = options.Promise || global.Promise
  var truename
  var fd
  var tmpfile
  var removeOnExit = cleanupOnExit(() => tmpfile)
  var absoluteName = path.resolve(filename)

  new Promise(function serializeSameFile (resolve) {
    // make a queue if it doesn't already exist
    if (!activeFiles[absoluteName]) activeFiles[absoluteName] = []

    activeFiles[absoluteName].push(resolve) // add this job to the queue
    if (activeFiles[absoluteName].length === 1) resolve() // kick off the first one
  }).then(function getRealPath () {
    return new Promise(function (resolve) {
      fs.realpath(filename, function (_, realname) {
        truename = realname || filename
        tmpfile = getTmpname(truename)
        resolve()
      })
    })
  }).then(function stat () {
    return new Promise(function stat (resolve) {
      if (options.mode && options.chown) resolve()
      else {
        // Either mode or chown is not explicitly set
        // Default behavior is to copy it from original file
        fs.stat(truename, function (err, stats) {
          if (err || !stats) resolve()
          else {
            options = Object.assign({}, options)

            if (!options.mode) {
              options.mode = stats.mode
            }
            if (!options.chown && process.getuid) {
              options.chown = { uid: stats.uid, gid: stats.gid }
            }
            resolve()
          }
        })
      }
    })
  }).then(function thenWriteFile () {
    return new Promise(function (resolve, reject) {
      fs.open(tmpfile, 'w', options.mode, function (err, _fd) {
        fd = _fd
        if (err) reject(err)
        else resolve()
      })
    })
  }).then(function write () {
    return new Promise(function (resolve, reject) {
      if (Buffer.isBuffer(data)) {
        fs.write(fd, data, 0, data.length, 0, function (err) {
          if (err) reject(err)
          else resolve()
        })
      } else if (data != null) {
        fs.write(fd, String(data), 0, String(options.encoding || 'utf8'), function (err) {
          if (err) reject(err)
          else resolve()
        })
      } else resolve()
    })
  }).then(function syncAndClose () {
    if (options.fsync !== false) {
      return new Promise(function (resolve, reject) {
        fs.fsync(fd, function (err) {
          if (err) reject(err)
          else fs.close(fd, resolve)
        })
      })
    }
  }).then(function chown () {
    if (options.chown) {
      return new Promise(function (resolve, reject) {
        fs.chown(tmpfile, options.chown.uid, options.chown.gid, function (err) {
          if (err) reject(err)
          else resolve()
        })
      })
    }
  }).then(function chmod () {
    if (options.mode) {
      return new Promise(function (resolve, reject) {
        fs.chmod(tmpfile, options.mode, function (err) {
          if (err) reject(err)
          else resolve()
        })
      })
    }
  }).then(function rename () {
    return new Promise(function (resolve, reject) {
      fs.rename(tmpfile, truename, function (err) {
        if (err) reject(err)
        else resolve()
      })
    })
  }).then(function success () {
    removeOnExit()
    callback()
  }).catch(function fail (err) {
    removeOnExit()
    fs.unlink(tmpfile, function () {
      callback(err)
    })
  }).then(function checkQueue () {
    activeFiles[absoluteName].shift() // remove the element added by serializeSameFile
    if (activeFiles[absoluteName].length > 0) {
      activeFiles[absoluteName][0]() // start next job if one is pending
    } else delete activeFiles[absoluteName]
  })
}

function writeFileSync (filename, data, options) {
  if (!options) options = {}
  try {
    filename = fs.realpathSync(filename)
  } catch (ex) {
    // it's ok, it'll happen on a not yet existing file
  }
  var tmpfile = getTmpname(filename)

  try {
    if (!options.mode || !options.chown) {
      // Either mode or chown is not explicitly set
      // Default behavior is to copy it from original file
      try {
        var stats = fs.statSync(filename)
        options = Object.assign({}, options)
        if (!options.mode) {
          options.mode = stats.mode
        }
        if (!options.chown && process.getuid) {
          options.chown = { uid: stats.uid, gid: stats.gid }
        }
      } catch (ex) {
        // ignore stat errors
      }
    }

    var removeOnExit = onExit(cleanupOnExit(tmpfile))
    var fd = fs.openSync(tmpfile, 'w', options.mode)
    if (Buffer.isBuffer(data)) {
      fs.writeSync(fd, data, 0, data.length, 0)
    } else if (data != null) {
      fs.writeSync(fd, String(data), 0, String(options.encoding || 'utf8'))
    }
    if (options.fsync !== false) {
      fs.fsyncSync(fd)
    }
    fs.closeSync(fd)
    if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid)
    if (options.mode) fs.chmodSync(tmpfile, options.mode)
    fs.renameSync(tmpfile, filename)
    removeOnExit()
  } catch (err) {
    removeOnExit()
    try { fs.unlinkSync(tmpfile) } catch (e) {}
    throw err
  }
}
<commit_msg>Remove unneeded removeOnExit call<commit_after>'use strict'
module.exports = writeFile
module.exports.sync = writeFileSync
module.exports._getTmpname = getTmpname // for testing
module.exports._cleanupOnExit = cleanupOnExit

var fs = require('graceful-fs')
var MurmurHash3 = require('imurmurhash')
var onExit = require('signal-exit')
var path = require('path')
var activeFiles = {}

// if we run inside of a worker_thread, `process.pid` is not unique
/* istanbul ignore next */
var threadId = (function getId () {
  try {
    var workerThreads = require('worker_threads')

    /// if we are in main thread, this is set to `0`
    return workerThreads.threadId
  } catch (e) {
    // worker_threads are not available, fallback to 0
    return 0
  }
})()

var invocations = 0
function getTmpname (filename) {
  return filename + '.' +
    MurmurHash3(__filename)
      .hash(String(process.pid))
      .hash(String(threadId))
      .hash(String(++invocations))
      .result()
}

function cleanupOnExit (tmpfile) {
  return function () {
    try {
      fs.unlinkSync(typeof tmpfile === 'function' ? tmpfile() : tmpfile)
    } catch (_) {}
  }
}

function writeFile (filename, data, options, callback) {
  if (options instanceof Function) {
    callback = options
    options = null
  }
  if (!options) options = {}

  var Promise = options.Promise || global.Promise
  var truename
  var fd
  var tmpfile
  var removeOnExit = cleanupOnExit(() => tmpfile)
  var absoluteName = path.resolve(filename)

  new Promise(function serializeSameFile (resolve) {
    // make a queue if it doesn't already exist
    if (!activeFiles[absoluteName]) activeFiles[absoluteName] = []

    activeFiles[absoluteName].push(resolve) // add this job to the queue
    if (activeFiles[absoluteName].length === 1) resolve() // kick off the first one
  }).then(function getRealPath () {
    return new Promise(function (resolve) {
      fs.realpath(filename, function (_, realname) {
        truename = realname || filename
        tmpfile = getTmpname(truename)
        resolve()
      })
    })
  }).then(function stat () {
    return new Promise(function stat (resolve) {
      if (options.mode && options.chown) resolve()
      else {
        // Either mode or chown is not explicitly set
        // Default behavior is to copy it from original file
        fs.stat(truename, function (err, stats) {
          if (err || !stats) resolve()
          else {
            options = Object.assign({}, options)

            if (!options.mode) {
              options.mode = stats.mode
            }
            if (!options.chown && process.getuid) {
              options.chown = { uid: stats.uid, gid: stats.gid }
            }
            resolve()
          }
        })
      }
    })
  }).then(function thenWriteFile () {
    return new Promise(function (resolve, reject) {
      fs.open(tmpfile, 'w', options.mode, function (err, _fd) {
        fd = _fd
        if (err) reject(err)
        else resolve()
      })
    })
  }).then(function write () {
    return new Promise(function (resolve, reject) {
      if (Buffer.isBuffer(data)) {
        fs.write(fd, data, 0, data.length, 0, function (err) {
          if (err) reject(err)
          else resolve()
        })
      } else if (data != null) {
        fs.write(fd, String(data), 0, String(options.encoding || 'utf8'), function (err) {
          if (err) reject(err)
          else resolve()
        })
      } else resolve()
    })
  }).then(function syncAndClose () {
    if (options.fsync !== false) {
      return new Promise(function (resolve, reject) {
        fs.fsync(fd, function (err) {
          if (err) reject(err)
          else fs.close(fd, resolve)
        })
      })
    }
  }).then(function chown () {
    if (options.chown) {
      return new Promise(function (resolve, reject) {
        fs.chown(tmpfile, options.chown.uid, options.chown.gid, function (err) {
          if (err) reject(err)
          else resolve()
        })
      })
    }
  }).then(function chmod () {
    if (options.mode) {
      return new Promise(function (resolve, reject) {
        fs.chmod(tmpfile, options.mode, function (err) {
          if (err) reject(err)
          else resolve()
        })
      })
    }
  }).then(function rename () {
    return new Promise(function (resolve, reject) {
      fs.rename(tmpfile, truename, function (err) {
        if (err) reject(err)
        else resolve()
      })
    })
  }).then(function success () {
    callback()
  }).catch(function fail (err) {
    removeOnExit()
    fs.unlink(tmpfile, function () {
      callback(err)
    })
  }).then(function checkQueue () {
    activeFiles[absoluteName].shift() // remove the element added by serializeSameFile
    if (activeFiles[absoluteName].length > 0) {
      activeFiles[absoluteName][0]() // start next job if one is pending
    } else delete activeFiles[absoluteName]
  })
}

function writeFileSync (filename, data, options) {
  if (!options) options = {}
  try {
    filename = fs.realpathSync(filename)
  } catch (ex) {
    // it's ok, it'll happen on a not yet existing file
  }
  var tmpfile = getTmpname(filename)

  try {
    if (!options.mode || !options.chown) {
      // Either mode or chown is not explicitly set
      // Default behavior is to copy it from original file
      try {
        var stats = fs.statSync(filename)
        options = Object.assign({}, options)
        if (!options.mode) {
          options.mode = stats.mode
        }
        if (!options.chown && process.getuid) {
          options.chown = { uid: stats.uid, gid: stats.gid }
        }
      } catch (ex) {
        // ignore stat errors
      }
    }

    var removeOnExit = onExit(cleanupOnExit(tmpfile))
    var fd = fs.openSync(tmpfile, 'w', options.mode)
    if (Buffer.isBuffer(data)) {
      fs.writeSync(fd, data, 0, data.length, 0)
    } else if (data != null) {
      fs.writeSync(fd, String(data), 0, String(options.encoding || 'utf8'))
    }
    if (options.fsync !== false) {
      fs.fsyncSync(fd)
    }
    fs.closeSync(fd)
    if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid)
    if (options.mode) fs.chmodSync(tmpfile, options.mode)
    fs.renameSync(tmpfile, filename)
    removeOnExit()
  } catch (err) {
    removeOnExit()
    try { fs.unlinkSync(tmpfile) } catch (e) {}
    throw err
  }
}
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var got = require('got');
var app = express();
var port = process.env.PORT;
var url = 'https://www.googleapis.com/urlshortener/v1/url?key=' + process.env.APIKEY

app.get('/shorten/', function (req, res) {
  var opts = {
    body: JSON.stringify({longUrl: req.query.longurl}),
    headers: {
      'content-type': 'application/json'
    },
    strictSSL: true,
    json: true
  };

  got.post(url, opts, function(err, data) {
    if (err) {
      res.status(400).end(data);
    } else {
      res.json(data);
    }
  });
});

var server = app.listen(port, function() {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Server is started at http://:%s:%s', host, port);
});
<commit_msg>Add log<commit_after>'use strict';

var express = require('express');
var got = require('got');
var app = express();
var port = process.env.PORT;
var url = 'https://www.googleapis.com/urlshortener/v1/url?key=' + process.env.APIKEY

app.get('/shorten/', function (req, res) {
  var opts = {
    body: JSON.stringify({longUrl: req.query.longurl}),
    headers: {
      'content-type': 'application/json'
    },
    strictSSL: true,
    json: true
  };

  console.log('Request shorten job with longurl', opts.body);

  got.post(url, opts, function(err, data) {
    if (err) {
      console.log('Got an error', err);
      res.status(400);
    }

    console.log('Got a response', JSON.stringify(data));
    res.json(data);
  });
});

var server = app.listen(port, function() {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Server is started at http://:%s:%s', host, port);
});
<|endoftext|>


<commit_before>var express = require('express');
var ECT = require("ect");
var fs = require("fs");
var session = require("express-session");
var crypto = require("crypto");
var https = require("https");
var qs = require("querystring");
var levelup = require("levelup");
var bodyParser = require("body-parser");
var levelMultiply = require("level-multiply");
var sass = require("node-sass");

//constants
var SESSION_SECRET = process.env.SHPAPAD_SESSION_SECRET;
var OAUTH_CLIENTID = process.env.SHPAPAD_OAUTH_CLIENTID;
var OAUTH_SECRET = process.env.SHPAPAD_OAUTH_CLIENTSECRET;

//environments
var app = express();
var rawdb = levelup("./data/db/shpapad_db");
var db = levelMultiply(rawdb);
var ectRenderer = ECT({ watch: true, root: __dirname + '/views', ext : '.ect'});
app.set('view engine', 'ect');
app.engine("ect", ectRenderer.render);
app.use(session({ secret: SESSION_SECRET, resave: false, saveUninitialized: true, cookie: { maxAge: null, expires: false } }));
app.use(bodyParser.urlencoded({ extended: false }));

//assets
app.get("/index.css", function(req, res) {
  var f = sass.renderSync({file: __dirname + "/assets/index.css"});
  res.writeHead(200, {"Content-Type":"text/css"});
  res.end(f.css, "utf-8");
});
app.get("/superagent.js", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/superagent.js"); res.writeHead(200, {"Content-Type":"text/javascript"}); res.end(f, "utf-8"); });
app.get("/shpapad.js", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/shpapad.js"); res.writeHead(200, {"Content-Type":"text/javascript"}); res.end(f, "utf-8"); });
app.get("/siwg.png", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/siwg.png"); res.writeHead(200, {"Content-Type":"image/png"}); res.end(f); });
app.get("/logout.png", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/logout.png"); res.writeHead(200, {"Content-Type":"image/png"}); res.end(f); });
app.get("/shpapad-logo-w.png", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/shpapad-logo-555.png"); res.writeHead(200, {"Content-Type":"image/png"}); res.end(f); });

//routes
app.get("/", function(req, res) {
  if (req.session.user) {
    res.redirect(303, "/list");
  } else {
    res.redirect(303, "/login");
  }
});

app.get("/login", function(req, res) {
  if (req.session.user) {
    res.redirect(303, "/list");
  } else {
    req.session.oauthState = crypto.randomBytes(32).toString("hex");
    var oauthParameter = {
        client_id: OAUTH_CLIENTID
      , response_type: "code"
      , scope: "openid"
      , redirect_uri: "https://shpapad.sevensenses.jp/oauth2callback"
      , state: req.session.oauthState
    };
    res.render("login", { menu: [], logoutable: false, oauth: oauthParameter });
  }
});

app.get("/logout", function(req, res) {
  req.session.destroy();
  res.redirect(303, "/login");
});

app.get("/oauth2callback", function(req, res) {
  if (req.session.user) {
    res.redirect(303, "/list");
  } else {

      //for CSRF
      if (req.query["state"] && req.query["code"]) {
          if (req.query["state"] === req.session.oauthState) {
            //state OK
          } else {
            res.redirect(303, "/login");
            return;
          }
      } else {
        res.redirect(303, "/login");
        return;
      }

      //get user information from google with OpenID-Connect
      var parameters = qs.stringify({"code":req.query["code"], "client_id":OAUTH_CLIENTID, "client_secret":OAUTH_SECRET, "redirect_uri":"https://shpapad.sevensenses.jp/oauth2callback", "grant_type":"authorization_code"});
      var options = {hostname:"accounts.google.com", port:443, path:"/o/oauth2/token", method:"POST", headers:{"Content-Type":"application/x-www-form-urlencoded"}};
      var request = https.request(options, function(response) {
        var d = "";
        response.on("data", function(chunk) {
          d += chunk;
        });
        response.on("end", function() {
          //parse id_token
          var rawjson = JSON.parse(d.toString());
          https.get("https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=" + rawjson["id_token"], function(vres) {
            vres.on("data", function(vd) {
              var userid = (JSON.parse(vd.toString()))["user_id"];
              var hashedID = crypto.createHash("sha256").update(userid).digest("hex");
              db.get(hashedID, function(err, value) {
                if (err) {
                  if (err.notFound) {
                    //create new user
                    db.put(hashedID, "", function(err) {
                      if (err) {
                        res.redirect(303, "/login");
                      } else {
                        //auth OK
                        req.session.user = userid;
                        res.redirect(303, "/list");
                      }
                    });
                  } else {
                    res.redirect(303, "/login");
                  }
                } else {
                  //auth OK
                  req.session.user = userid;
                  res.redirect(303, "/list");
                }
              });
            });
          });
        });
      });
      request.write(parameters);
      request.end();
  }
});

app.get("/list", function(req, res) {
  if (req.session.user) {
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");

    //get lists
    var lists = [];
    db.createReadStream({gt: hashedID + "!", lte: hashedID + "!9999999999999" })
      .on("data", function(data) {
        var obj = JSON.parse(data["value"]);
        lists.push({key:data["key"], name:obj["name"], count:obj["count"]});
      })
      .on("end", function(data) {
        res.render("list", { menu: [{href: "/list", value:"リスト"}], logoutable: true, lists: lists });
      });

  } else {
    res.redirect(303, "/login");
  }
});

app.post("/list", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //create list
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var hashedListKey = hashedID + "!" + (new Date()).getTime();
    var listValue = {name: req.body.value, count: 0};
    db.put(hashedListKey, JSON.stringify(listValue), function(err) {
      if (err) {
        res.send("add error! (1)");
      } else {
        res.json({"id":hashedListKey});
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.put("/list", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value && req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //update list name
    db.get(req.query.id, function(err, list) {
      if (err) {
        res.send("update list error! (2)");
      } else {
        var listobj = JSON.parse(list);
        var updated = {name:req.body.value, count:listobj["count"]};
        db.put(req.query.id, JSON.stringify(updated), function(err) {
          if (err) {
            res.send("update list error! (2)");
          } else {
            res.end();
          }
        });
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.delete("/list", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.id).substring(0, req.query.id.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //delete list
    db.del(req.query.id, function(err) {
      if (err) {
        res.send("delete error! (1)");
      } else {
        res.end();
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.get("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.query.l) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //get list name
    db.get(req.query.l, function(err, list) {
      if (err) {
        res.send("add error! (2)");
      } else {
        var listobj = JSON.parse(list);

        //get tasks
        var tkey = crypto.createHash("sha256").update(req.query.l).digest("hex");
        var tasks = [];
        db.createReadStream({gt: tkey + "!", lte: tkey + "!9999999999999" })
          .on("data", function(data) {
            tasks.push(data);
          })
          .on("end", function(data) {
            res.render("task", { menu: [{href: "/list", value:"リスト"}, {href: "/task?l=" + req.query.l, value: listobj.name}], logoutable: true, tasks: tasks });
          });
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.post("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //create task
    var key = crypto.createHash("sha256").update(req.query.l).digest("hex") + "!" +  (new Date()).getTime();
    db.put(key, req.body.value, function(err) {
      if (err) {
        res.send("create task error! (1)");
      } else {

        //update task count
        db.get(req.query.l, function(err, list) {
          if (err) {
            res.send("create task error! (2)");
          } else {
            var listobj = JSON.parse(list);
            var updated = {name:listobj["name"], count:listobj["count"]+1};
            db.put(req.query.l, JSON.stringify(updated), function(err) {
              if (err) {
                res.send("create task error! (2)");
              } else {
                res.json({"id":key});
              }
            });
          }
        });

      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

//update task value (requires query.id/query.l/body.value)
app.put("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value) {
    } else {
      res.status(400);
      res.end();
      return;
    }
    if (req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //update task
    db.put(req.query.id, req.body.value, function(err) {
      if (err) res.send("update task error!");
      res.end();
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.delete("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //delete task
    db.del(req.query.id, function(err) {
      if (err) {
        res.send("delete error! (1)");
      } else {

        //update task count
        db.get(req.query.l, function(err, list) {
          if (err) {
            res.send("create task error! (2)");
          } else {
            var listobj = JSON.parse(list);
            var updated = {name:listobj["name"], count:listobj["count"]-1};
            db.put(req.query.l, JSON.stringify(updated), function(err) {
              if (err) {
                res.send("create task error! (2)");
              } else {
                res.end();
              }
            });
          }
        });

        res.end();
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.listen(3000);
<commit_msg>catch up for levelup<commit_after>var express = require('express');
var ECT = require("ect");
var fs = require("fs");
var session = require("express-session");
var crypto = require("crypto");
var https = require("https");
var qs = require("querystring");
var levelup = require("levelup");
var leveldown = require("leveldown");
var bodyParser = require("body-parser");
var levelMultiply = require("level-multiply");
var sass = require("node-sass");

//constants
var SESSION_SECRET = process.env.SHPAPAD_SESSION_SECRET;
var OAUTH_CLIENTID = process.env.SHPAPAD_OAUTH_CLIENTID;
var OAUTH_SECRET = process.env.SHPAPAD_OAUTH_CLIENTSECRET;

//environments
var app = express();
var rawdb = levelup(leveldown("./data/db/shpapad_db"));
var db = levelMultiply(rawdb);
var ectRenderer = ECT({ watch: true, root: __dirname + '/views', ext : '.ect'});
app.set('view engine', 'ect');
app.engine("ect", ectRenderer.render);
app.use(session({ secret: SESSION_SECRET, resave: false, saveUninitialized: true, cookie: { maxAge: null, expires: false } }));
app.use(bodyParser.urlencoded({ extended: false }));

//assets
app.get("/index.css", function(req, res) {
  var f = sass.renderSync({file: __dirname + "/assets/index.css"});
  res.writeHead(200, {"Content-Type":"text/css"});
  res.end(f.css, "utf-8");
});
app.get("/superagent.js", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/superagent.js"); res.writeHead(200, {"Content-Type":"text/javascript"}); res.end(f, "utf-8"); });
app.get("/shpapad.js", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/shpapad.js"); res.writeHead(200, {"Content-Type":"text/javascript"}); res.end(f, "utf-8"); });
app.get("/siwg.png", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/siwg.png"); res.writeHead(200, {"Content-Type":"image/png"}); res.end(f); });
app.get("/logout.png", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/logout.png"); res.writeHead(200, {"Content-Type":"image/png"}); res.end(f); });
app.get("/shpapad-logo-w.png", function(req, res) { var f = fs.readFileSync(__dirname + "/assets/shpapad-logo-555.png"); res.writeHead(200, {"Content-Type":"image/png"}); res.end(f); });

//routes
app.get("/", function(req, res) {
  if (req.session.user) {
    res.redirect(303, "/list");
  } else {
    res.redirect(303, "/login");
  }
});

app.get("/login", function(req, res) {
  if (req.session.user) {
    res.redirect(303, "/list");
  } else {
    req.session.oauthState = crypto.randomBytes(32).toString("hex");
    var oauthParameter = {
        client_id: OAUTH_CLIENTID
      , response_type: "code"
      , scope: "openid"
      , redirect_uri: "https://shpapad.sevensenses.jp/oauth2callback"
      , state: req.session.oauthState
    };
    res.render("login", { menu: [], logoutable: false, oauth: oauthParameter });
  }
});

app.get("/logout", function(req, res) {
  req.session.destroy();
  res.redirect(303, "/login");
});

app.get("/oauth2callback", function(req, res) {
  if (req.session.user) {
    res.redirect(303, "/list");
  } else {

      //for CSRF
      if (req.query["state"] && req.query["code"]) {
          if (req.query["state"] === req.session.oauthState) {
            //state OK
          } else {
            res.redirect(303, "/login");
            return;
          }
      } else {
        res.redirect(303, "/login");
        return;
      }

      //get user information from google with OpenID-Connect
      var parameters = qs.stringify({"code":req.query["code"], "client_id":OAUTH_CLIENTID, "client_secret":OAUTH_SECRET, "redirect_uri":"https://shpapad.sevensenses.jp/oauth2callback", "grant_type":"authorization_code"});
      var options = {hostname:"accounts.google.com", port:443, path:"/o/oauth2/token", method:"POST", headers:{"Content-Type":"application/x-www-form-urlencoded"}};
      var request = https.request(options, function(response) {
        var d = "";
        response.on("data", function(chunk) {
          d += chunk;
        });
        response.on("end", function() {
          //parse id_token
          var rawjson = JSON.parse(d.toString());
          https.get("https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=" + rawjson["id_token"], function(vres) {
            vres.on("data", function(vd) {
              var userid = (JSON.parse(vd.toString()))["user_id"];
              var hashedID = crypto.createHash("sha256").update(userid).digest("hex");
              db.get(hashedID, function(err, value) {
                if (err) {
                  if (err.notFound) {
                    //create new user
                    db.put(hashedID, "", function(err) {
                      if (err) {
                        res.redirect(303, "/login");
                      } else {
                        //auth OK
                        req.session.user = userid;
                        res.redirect(303, "/list");
                      }
                    });
                  } else {
                    res.redirect(303, "/login");
                  }
                } else {
                  //auth OK
                  req.session.user = userid;
                  res.redirect(303, "/list");
                }
              });
            });
          });
        });
      });
      request.write(parameters);
      request.end();
  }
});

app.get("/list", function(req, res) {
  if (req.session.user) {
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");

    //get lists
    var lists = [];
    db.createReadStream({gt: hashedID + "!", lte: hashedID + "!9999999999999" })
      .on("data", function(data) {
        var obj = JSON.parse(data["value"]);
        lists.push({key:data["key"], name:obj["name"], count:obj["count"]});
      })
      .on("end", function(data) {
        res.render("list", { menu: [{href: "/list", value:"リスト"}], logoutable: true, lists: lists });
      });

  } else {
    res.redirect(303, "/login");
  }
});

app.post("/list", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //create list
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var hashedListKey = hashedID + "!" + (new Date()).getTime();
    var listValue = {name: req.body.value, count: 0};
    db.put(hashedListKey, JSON.stringify(listValue), function(err) {
      if (err) {
        res.send("add error! (1)");
      } else {
        res.json({"id":hashedListKey});
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.put("/list", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value && req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //update list name
    db.get(req.query.id, function(err, list) {
      if (err) {
        res.send("update list error! (2)");
      } else {
        var listobj = JSON.parse(list);
        var updated = {name:req.body.value, count:listobj["count"]};
        db.put(req.query.id, JSON.stringify(updated), function(err) {
          if (err) {
            res.send("update list error! (2)");
          } else {
            res.end();
          }
        });
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.delete("/list", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.id).substring(0, req.query.id.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //delete list
    db.del(req.query.id, function(err) {
      if (err) {
        res.send("delete error! (1)");
      } else {
        res.end();
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.get("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.query.l) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //get list name
    db.get(req.query.l, function(err, list) {
      if (err) {
        res.send("add error! (2)");
      } else {
        var listobj = JSON.parse(list);

        //get tasks
        var tkey = crypto.createHash("sha256").update(req.query.l).digest("hex");
        var tasks = [];
        db.createReadStream({gt: tkey + "!", lte: tkey + "!9999999999999" })
          .on("data", function(data) {
            tasks.push(data);
          })
          .on("end", function(data) {
            res.render("task", { menu: [{href: "/list", value:"リスト"}, {href: "/task?l=" + req.query.l, value: listobj.name}], logoutable: true, tasks: tasks });
          });
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.post("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //create task
    var key = crypto.createHash("sha256").update(req.query.l).digest("hex") + "!" +  (new Date()).getTime();
    db.put(key, req.body.value, function(err) {
      if (err) {
        res.send("create task error! (1)");
      } else {

        //update task count
        db.get(req.query.l, function(err, list) {
          if (err) {
            res.send("create task error! (2)");
          } else {
            var listobj = JSON.parse(list);
            var updated = {name:listobj["name"], count:listobj["count"]+1};
            db.put(req.query.l, JSON.stringify(updated), function(err) {
              if (err) {
                res.send("create task error! (2)");
              } else {
                res.json({"id":key});
              }
            });
          }
        });

      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

//update task value (requires query.id/query.l/body.value)
app.put("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.body.value) {
    } else {
      res.status(400);
      res.end();
      return;
    }
    if (req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //update task
    db.put(req.query.id, req.body.value, function(err) {
      if (err) res.send("update task error!");
      res.end();
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.delete("/task", function(req, res) {
  if (req.session.user) {

    //parameter existence
    if (req.query.id) {
    } else {
      res.status(400);
      res.end();
      return;
    }

    //parameter validation (owner?)
    var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex");
    var parameterID = (req.query.l).substring(0, req.query.l.indexOf("!"));
    if (hashedID !== parameterID) {
      res.status(403);
      res.end();
      return;
    }

    //delete task
    db.del(req.query.id, function(err) {
      if (err) {
        res.send("delete error! (1)");
      } else {

        //update task count
        db.get(req.query.l, function(err, list) {
          if (err) {
            res.send("create task error! (2)");
          } else {
            var listobj = JSON.parse(list);
            var updated = {name:listobj["name"], count:listobj["count"]-1};
            db.put(req.query.l, JSON.stringify(updated), function(err) {
              if (err) {
                res.send("create task error! (2)");
              } else {
                res.end();
              }
            });
          }
        });

        res.end();
      }
    });

  } else {
    res.redirect(303, "/logout");
  }
});

app.listen(3000);
<|endoftext|>


<commit_before>var mkdirp = require('mkdirp');
var url = require('url');
var fs = require('fs');
var ffmpeg = require('fluent-ffmpeg');
var creds = require(process.env.HOME + '/.spotifyCreds.json');
var stream = require('stream');

var xml2js = require('xml2js');
var spotifyWeb = require('spotify-web');

var spotifyBackend = {};
spotifyBackend.name = 'spotify';

var config, player, logger;

// TODO: seeking
var encodeSong = function(origStream, seek, songID, progCallback, errCallback) {
    var incompletePath = config.songCachePath + '/spotify/incomplete/' + songID + '.opus';
    var incompleteStream = fs.createWriteStream(incompletePath, {flags: 'w'});
    var encodedPath = config.songCachePath + '/spotify/' + songID + '.opus';

    var command = ffmpeg(origStream)
        .noVideo()
        //.inputFormat('s16le')
        //.inputOption('-ac 2')
        .audioCodec('libopus')
        .audioBitrate('192')
        .format('opus')
        .on('error', function(err) {
            logger.error('error while transcoding ' + songID + ': ' + err);
            if(fs.existsSync(incompletePath))
                fs.unlinkSync(incompletePath);
            errCallback(err);
        })

    var opusStream = command.pipe(null, {end: true});
    opusStream.on('data', function(chunk) {
        incompleteStream.write(chunk, undefined, function() {
            progCallback(chunk.length, false);
        });
    });
    opusStream.on('end', function() {
        incompleteStream.end(undefined, undefined, function() {
            logger.verbose('transcoding ended for ' + songID);

            // TODO: we don't know if transcoding ended successfully or not,
            // and there might be a race condition between errCallback deleting
            // the file and us trying to move it to the songCache

            // atomically move result to encodedPath
            if(fs.existsSync(incompletePath)) {
                fs.renameSync(incompletePath, encodedPath);
                progCallback(0, true);
            } else {
                progCallback(0, false);
            }
        });
    });

    logger.verbose('transcoding ' + songID + '...');
    return function(err) {
        command.kill();
        logger.verbose('canceled preparing: ' + songID + ': ' + err);
        if(fs.existsSync(incompletePath))
            fs.unlinkSync(incompletePath);
        errCallback('canceled preparing: ' + songID + ': ' + err);
    };
};

var spotifyDownload = function(songID, progCallback, errCallback) {
    var cancelEncoding;
    spotifyBackend.spotify.get(songID, function(err, track) {
        if(err) {
            errCallback(err);
        } else {
            cancelEncoding = encodeSong(track.play(), 0, songID, progCallback, errCallback);
        }
    });
    return function(err) {
        cancelEncoding(err);
    };
};

// cache songID to disk.
// on success: progCallback must be called with true as argument
// on failure: errCallback must be called with error message
// returns a function that cancels preparing
spotifyBackend.prepareSong = function(songID, progCallback, errCallback) {
    var filePath = config.songCachePath + '/spotify/' + songID + '.opus';

    if(fs.existsSync(filePath)) {
        // true as first argument because there is song data
        progCallback(true, true);
    } else {
        return spotifyDownload(songID, progCallback, errCallback);
    }
};

spotifyBackend.isPrepared = function(songID) {
    var filePath = config.songCachePath + '/spotify/' + songID + '.opus';
    return fs.existsSync(filePath);
};

spotifyBackend.search = function(query, callback, errCallback) {
    var results = {};
    results.songs = {};

    var offset = 0;
    spotifyBackend.spotify.search(query.terms, function(err, xml) {
        if(err) {
            errCallback('error while searching spotify: ' + err);
        } else {
            var parser = new xml2js.Parser();
            parser.on('end', function(searchResult) {
                // this format is 100% WTF
                var tracks = searchResult.result.tracks[0].track;

                if(tracks) {
                    for(var i = 0; i < tracks.length; i++) {
                        var track = tracks[i];
                        var trackUri = spotifyWeb.id2uri('track', track.id[0].toString());
                        results.songs[trackUri] = {
                            artist: track.artist ? track.artist[0] : null,
                            title: track.title ? track.title[0] : null,
                            album: track.album ? track.album[0] : null,
                            albumArt: null, // TODO
                            duration: track.length ? track.length[0] : null,
                            songID: trackUri,
                            score: track.popularity ? 100 * track.popularity[0] : null,
                            backendName: spotifyBackend.name,
                            format: 'opus'
                        };
                    }
                }

                callback(results);
            });
            parser.parseString(xml);
        }
    });
};

spotifyBackend.init = function(_player, _logger, callback) {
    player = _player;
    config = _player.config;
    logger = _logger;

    mkdirp(config.songCachePath + '/spotify/incomplete');

    // initialize google play music backend
    spotifyWeb.login(creds.login, creds.password, function(err, spotifySession) {
        if(err) {
            callback(err);
        } else {
            spotifyBackend.spotify = spotifySession;
            callback();
        }
    });
};
module.exports = spotifyBackend;
<commit_msg>be compatible with the core refactor<commit_after>var mkdirp = require('mkdirp');
var url = require('url');
var fs = require('fs');
var ffmpeg = require('fluent-ffmpeg');
var creds = require(process.env.HOME + '/.spotifyCreds.json');
var stream = require('stream');

var xml2js = require('xml2js');
var spotifyWeb = require('spotify-web');

var spotifyBackend = {};
spotifyBackend.name = 'spotify';

var config, player, logger;

// TODO: seeking
var encodeSong = function(origStream, seek, song, progCallback, errCallback) {
    var incompletePath = config.songCachePath + '/spotify/incomplete/' + song.songID + '.opus';
    var incompleteStream = fs.createWriteStream(incompletePath, {flags: 'w'});
    var encodedPath = config.songCachePath + '/spotify/' + song.songID + '.opus';

    var command = ffmpeg(origStream)
        .noVideo()
        //.inputFormat('s16le')
        //.inputOption('-ac 2')
        .audioCodec('libopus')
        .audioBitrate('192')
        .format('opus')
        .on('error', function(err) {
            logger.error('error while transcoding ' + song.songID + ': ' + err);
            if(fs.existsSync(incompletePath))
                fs.unlinkSync(incompletePath);
            errCallback(song, err);
        })

    var opusStream = command.pipe(null, {end: true});
    opusStream.on('data', function(chunk) {
        incompleteStream.write(chunk, undefined, function() {
            progCallback(song, chunk.length, false);
        });
    });
    opusStream.on('end', function() {
        incompleteStream.end(undefined, undefined, function() {
            logger.verbose('transcoding ended for ' + song.songID);

            // TODO: we don't know if transcoding ended successfully or not,
            // and there might be a race condition between errCallback deleting
            // the file and us trying to move it to the songCache

            // atomically move result to encodedPath
            if(fs.existsSync(incompletePath)) {
                fs.renameSync(incompletePath, encodedPath);
                progCallback(song, 0, true);
            } else {
                progCallback(song, 0, false);
            }
        });
    });

    logger.verbose('transcoding ' + song.songID + '...');
    return function(err) {
        command.kill();
        logger.verbose('canceled preparing: ' + song.songID + ': ' + err);
        if(fs.existsSync(incompletePath))
            fs.unlinkSync(incompletePath);
        errCallback(song, 'canceled preparing: ' + song.songID + ': ' + err);
    };
};

var spotifyDownload = function(song, progCallback, errCallback) {
    var cancelEncoding;
    spotifyBackend.spotify.get(song.songID, function(err, track) {
        if(err) {
            errCallback(song, err);
        } else {
            cancelEncoding = encodeSong(track.play(), 0, song, progCallback, errCallback);
        }
    });
    return function(err) {
        cancelEncoding(err);
    };
};

// cache song to disk.
// on success: progCallback must be called with true as argument
// on failure: errCallback must be called with error message
// returns a function that cancels preparing
spotifyBackend.prepareSong = function(song, progCallback, errCallback) {
    var filePath = config.songCachePath + '/spotify/' + song.songID + '.opus';

    if(fs.existsSync(filePath)) {
        // true as first argument because there is song data
        progCallback(song, true, true);
    } else {
        return spotifyDownload(song, progCallback, errCallback);
    }
};

spotifyBackend.isPrepared = function(song) {
    var filePath = config.songCachePath + '/spotify/' + song.songID + '.opus';
    return fs.existsSync(filePath);
};

spotifyBackend.search = function(query, callback, errCallback) {
    var results = {};
    results.songs = {};

    var offset = 0;
    spotifyBackend.spotify.search(query.terms, function(err, xml) {
        if(err) {
            errCallback('error while searching spotify: ' + err);
        } else {
            var parser = new xml2js.Parser();
            parser.on('end', function(searchResult) {
                // this format is 100% WTF
                var tracks = searchResult.result.tracks[0].track;

                if(tracks) {
                    for(var i = 0; i < tracks.length; i++) {
                        var track = tracks[i];
                        var trackUri = spotifyWeb.id2uri('track', track.id[0].toString());
                        results.songs[trackUri] = {
                            artist: track.artist ? track.artist[0] : null,
                            title: track.title ? track.title[0] : null,
                            album: track.album ? track.album[0] : null,
                            albumArt: null, // TODO
                            duration: track.length ? track.length[0] : null,
                            songID: trackUri,
                            score: track.popularity ? 100 * track.popularity[0] : null,
                            backendName: spotifyBackend.name,
                            format: 'opus'
                        };
                    }
                }

                callback(results);
            });
            parser.parseString(xml);
        }
    });
};

spotifyBackend.init = function(_player, _logger, callback) {
    player = _player;
    config = _player.config;
    logger = _logger;

    mkdirp(config.songCachePath + '/spotify/incomplete');

    // initialize google play music backend
    spotifyWeb.login(creds.login, creds.password, function(err, spotifySession) {
        if(err) {
            callback(err);
        } else {
            spotifyBackend.spotify = spotifySession;
            callback();
        }
    });
};
module.exports = spotifyBackend;
<|endoftext|>


<commit_before>/* jshint node: true */
'use strict';

var url = '//maps.googleapis.com/maps/api/js?sensor=false';

module.exports = {
  name: 'ember-cli-google-maps-script',
  contentFor: function(type, config) {
    var env = config.environment;
    var configuration = config['google-maps-script'];
    var clientId = configuration.clientId;

    if (type === 'head-footer' && env !== 'test') {
      if (clientId) {
        url += '&client=' + clientId;
      }
    }

    return '<script src="' + url + '"></script>';
  }
};
<commit_msg>Fixing critical error<commit_after>/* jshint node: true */
'use strict';

var url = '//maps.googleapis.com/maps/api/js?sensor=false';

module.exports = {
  name: 'ember-cli-google-maps-script',
  contentFor: function(type, config) {
    var env = config.environment;
    var configuration = config['google-maps-script'];
    var clientId = configuration.clientId;

    if (type === 'head-footer' && env !== 'test') {
      if (clientId) {
        url += '&client=' + clientId;
      }

      return '<script src="' + url + '"></script>';
    }
  }
};
<|endoftext|>


<commit_before>/*jslint node: true, vars: true, indent: 4 */
'use strict';

var util = require('util'),
    Imap = require('imap'),
    debug = require('debug'),
    MailParser = require('mailparser').MailParser,
    EventEmitter = require('events').EventEmitter;

var dbg = debug('mailnotifier');

function Notifier(opts, dbg) {
    EventEmitter.call(this);
    var self = this;
    self.options = opts;
    if (self.options.username) { //backward compat
        self.options.user = self.options.username;
    }
    self.options.box = self.options.box || 'INBOX';
    self.options.debug = self.options.debug || debug('imap');

    if(dbg) {
        self.dbg = dbg;
    }
}
util.inherits(Notifier, EventEmitter);

module.exports = function (opts, customDbg) {
    return new Notifier(opts, customDbg);
};

Notifier.prototype.start = function () {
    var self = this;
    self.imap = new Imap(self.options);
    self.imap.once('end', function () {
        self.dbg('imap end');
        self.emit('end');
    });
    self.imap.once('error', function (err) {
        self.dbg('imap error : %s', err);
        self.emit('error', err);
    });
    self.imap.once('close', function (haserr) {
        self.dbg('imap close : %s', haserr ? 'errored' : 'normal');
    });
    self.imap.on('uidvalidity', function (uidvalidity) {
        self.dbg('new uidvalidity : %s', uidvalidity);
    });
    self.imap.once('ready', function () {
        self.emit('connected');
        self.imap.openBox(self.options.box, false, function (err, box) {
            if (err) {
                self.dbg('unable to open box : %s', err);
                self.emit('error', err);
                return;
            }
            self.scan();
            self.imap.on('mail', function (id) {
                self.dbg('mail event : %s', id);
                self.scan();
            });
        });
    });
    self.imap.connect();
    return this;
};

Notifier.prototype.scan = function () {
    var self = this, search = self.options.search || ['UNSEEN'];
    self.dbg('scanning %s with filter `%s`.', self.options.box,  search);
    self.imap.search(search, function (err, seachResults) {
        if (err) {
            self.emit('error', err);
            return;
        }
        if (!seachResults || seachResults.length === 0) {
            self.dbg('no new mail in %s', self.options.box);
            return;
        }
        self.dbg('found %d new messages', seachResults.length);
        var fetch = self.imap.fetch(seachResults, {
            markSeen: self.options.markSeen !== false,
            bodies: ''
        });
        fetch.on('message', function (msg) {
            var uid, flags;
            msg.on('attributes', function(attrs) {
                uid = attrs.uid;
                flags = attrs.flags;
                self.dbg("Message uid", attrs.uid);
            });
            var mp = new MailParser();
            mp.once('end', function (mail) {
                mail.uid = uid;
                mail.flags = flags;
                self.emit('mail', mail);
            });
            msg.once('body', function (stream, info) {
                stream.pipe(mp);
            });
        });
        fetch.once('end', function () {
            self.dbg('Done fetching all messages!');
        });
        fetch.once('error', function (err) {
            self.dbg('fetch error : ', err);
            self.emit('error', err);
        });
    });
    return this;
};

Notifier.prototype.stop = function () {
    var self = this;
    self.dbg('imap.state before stopping: %s', this.imap.state);

    if (this.imap.state !== 'disconnected') {
        this.imap.end();
    }

    self.dbg('notifier stopped');
    return this;
};

Notifier.prototype.dbg = function (...args) {
    dbg(...args);
}
<commit_msg>log error on close, export dbg in prototype<commit_after>/*jslint node: true, vars: true, indent: 4 */
'use strict';

var util = require('util'),
    Imap = require('imap'),
    debug = require('debug'),
    MailParser = require('mailparser').MailParser,
    EventEmitter = require('events').EventEmitter;

var dbg = debug('mailnotifier');

function Notifier(opts, dbg) {
    EventEmitter.call(this);
    var self = this;
    self.options = opts;
    if (self.options.username) { //backward compat
        self.options.user = self.options.username;
    }
    self.options.box = self.options.box || 'INBOX';
    self.options.debug = self.options.debug || debug('imap');

    if(dbg) {
        self.dbg = dbg;
    }
}
util.inherits(Notifier, EventEmitter);

module.exports = function (opts, customDbg) {
    return new Notifier(opts, customDbg);
};

Notifier.prototype.start = function () {
    var self = this;
    self.imap = new Imap(self.options);
    self.imap.once('end', function () {
        self.dbg('imap end');
        self.emit('end');
    });
    self.imap.once('error', function (err) {
        self.dbg('imap error : %s', err);
        self.emit('error', err);
    });
    self.imap.once('close', function (haserr) {
        self.dbg('imap close : %s', haserr ? 'errored ' + haserr : 'normal');
    });
    self.imap.on('uidvalidity', function (uidvalidity) {
        self.dbg('new uidvalidity : %s', uidvalidity);
    });
    self.imap.once('ready', function () {
        self.emit('connected');
        self.imap.openBox(self.options.box, false, function (err, box) {
            if (err) {
                self.dbg('unable to open box : %s', err);
                self.emit('error', err);
                return;
            }
            self.scan();
            self.imap.on('mail', function (id) {
                self.dbg('mail event : %s', id);
                self.scan();
            });
        });
    });
    self.imap.connect();
    return this;
};

Notifier.prototype.scan = function () {
    var self = this, search = self.options.search || ['UNSEEN'];
    self.dbg('scanning %s with filter `%s`.', self.options.box,  search);
    self.imap.search(search, function (err, seachResults) {
        if (err) {
            self.emit('error', err);
            return;
        }
        if (!seachResults || seachResults.length === 0) {
            self.dbg('no new mail in %s', self.options.box);
            return;
        }
        self.dbg('found %d new messages', seachResults.length);
        var fetch = self.imap.fetch(seachResults, {
            markSeen: self.options.markSeen !== false,
            bodies: ''
        });
        fetch.on('message', function (msg) {
            var uid, flags;
            msg.on('attributes', function(attrs) {
                uid = attrs.uid;
                flags = attrs.flags;
                self.dbg("Message uid", attrs.uid);
            });
            var mp = new MailParser();
            mp.once('end', function (mail) {
                mail.uid = uid;
                mail.flags = flags;
                self.emit('mail', mail);
            });
            msg.once('body', function (stream, info) {
                stream.pipe(mp);
            });
        });
        fetch.once('end', function () {
            self.dbg('Done fetching all messages!');
        });
        fetch.once('error', function (err) {
            self.dbg('fetch error : ', err);
            self.emit('error', err);
        });
    });
    return this;
};

Notifier.prototype.stop = function () {
    var self = this;
    self.dbg('imap.state before stopping: %s', this.imap.state);

    if (this.imap.state !== 'disconnected') {
        this.imap.end();
    }

    self.dbg('notifier stopped');
    return this;
};

Notifier.prototype.dbg = dbg;
<|endoftext|>