Update to TS1.5

This commit is contained in:
Roel van Uden 2015-05-23 19:07:14 +02:00
parent 5fdee94b38
commit 18375d3d22
29 changed files with 194 additions and 207 deletions

View File

@ -11,7 +11,7 @@
"type": "git",
"url": "git://github.com/Deathspike/crunchyroll.js.git"
},
"version": "1.1.3",
"version": "1.1.4",
"bin": {
"crunchyroll": "./bin/crunchyroll"
},

View File

@ -1,15 +1,13 @@
'use strict';
export = main;
import commander = require('commander');
import fs = require('fs');
import path = require('path');
import series = require('./series');
import typings = require('./typings');
import series from './series';
/**
* Streams the batch of series to disk.
*/
function main(args: string[], done: (err?: Error) => void) {
export default function(args: string[], done: (err?: Error) => void) {
var config = parse(args);
var batchPath = path.join(config.output || process.cwd(), 'CrunchyRoll.txt');
tasks(config, batchPath, (err, tasks) => {
@ -41,14 +39,15 @@ function split(value: string): string[] {
previous = i + 1;
}
}
pieces.push(value.substring(previous, i).match(/^"?(.+?)"?$/)[1]);
var lastPiece = value.substring(previous, i).match(/^"?(.+?)"?$/);
if (lastPiece) pieces.push(lastPiece[1]);
return pieces;
}
/**
* Parses the configuration or reads the batch-mode file for tasks.
*/
function tasks(config: typings.IConfigLine, batchPath: string, done: (err: Error, tasks?: typings.IConfigTask[]) => void) {
function tasks(config: IConfigLine, batchPath: string, done: (err: Error, tasks?: IConfigTask[]) => void) {
if (config.args.length) {
return done(null, config.args.map(address => {
return {address: address, config: config};
@ -58,7 +57,7 @@ function tasks(config: typings.IConfigLine, batchPath: string, done: (err: Error
if (!exists) return done(null, []);
fs.readFile(batchPath, 'utf8', (err, data) => {
if (err) return done(err);
var map: typings.IConfigTask[] = [];
var map: IConfigTask[] = [];
data.split(/\r?\n/).forEach(line => {
if (/^(\/\/|#)/.test(line)) return;
var lineConfig = parse(process.argv.concat(split(line)));
@ -75,7 +74,7 @@ function tasks(config: typings.IConfigLine, batchPath: string, done: (err: Error
/**
* Parses the arguments and returns a configuration.
*/
function parse(args: string[]): typings.IConfigLine {
function parse(args: string[]): IConfigLine {
return new commander.Command().version(require('../package').version)
// Authentication
.option('-p, --pass <s>', 'The password.')

View File

@ -1,5 +1,5 @@
'use strict';
import batch = require('./batch');
import batch from './batch';
batch(process.argv, (err: any) => {
if (err) console.error(err.stack || err);

View File

@ -1,19 +1,17 @@
'use strict';
export = main;
import cheerio = require('cheerio');
import fs = require('fs');
import mkdirp = require('mkdirp');
import request = require('./request');
import path = require('path');
import subtitle = require('./subtitle/index');
import typings = require('./typings');
import video = require('./video/index');
import subtitle from './subtitle/index';
import video from './video/index';
import xml2js = require('xml2js');
/**
* Streams the episode to disk.
*/
function main(config: typings.IConfig, address: string, done: (err: Error) => void) {
export default function(config: IConfig, address: string, done: (err: Error) => void) {
scrapePage(config, address, (err, page) => {
if (err) return done(err);
scrapePlayer(config, address, page.id, (err, player) => {
@ -38,7 +36,7 @@ function complete(message: string, begin: number, done: (err: Error) => void) {
/**
* Downloads the subtitle and video.
*/
function download(config: typings.IConfig, page: typings.IEpisodePage, player: typings.IEpisodePlayer, done: (err: Error) => void) {
function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, done: (err: Error) => void) {
var series = config.series || page.series;
var fileName = name(config, page, series);
var filePath = path.join(config.output || process.cwd(), series, fileName);
@ -64,7 +62,7 @@ function download(config: typings.IConfig, page: typings.IEpisodePage, player: t
/**
* Saves the subtitles to disk.
*/
function downloadSubtitle(config: typings.IConfig, player: typings.IEpisodePlayer, filePath: string, done: (err?: Error) => void) {
function downloadSubtitle(config: IConfig, player: IEpisodePlayer, filePath: string, done: (err?: Error) => void) {
var enc = player.subtitle;
if (!enc) return done();
subtitle.decode(enc.id, enc.iv, enc.data, (err, data) => {
@ -81,9 +79,9 @@ function downloadSubtitle(config: typings.IConfig, player: typings.IEpisodePlaye
/**
* Streams the video to disk.
*/
function downloadVideo(config: typings.IConfig,
page: typings.IEpisodePage,
player: typings.IEpisodePlayer,
function downloadVideo(config: IConfig,
page: IEpisodePage,
player: IEpisodePlayer,
filePath: string,
done: (err: Error) => void) {
video.stream(
@ -97,7 +95,7 @@ function downloadVideo(config: typings.IConfig,
/**
* Names the file based on the config, page, series and tag.
*/
function name(config: typings.IConfig, page: typings.IEpisodePage, series: string) {
function name(config: IConfig, page: IEpisodePage, series: string) {
var episode = (page.episode < 10 ? '0' : '') + page.episode;
var volume = (page.volume < 10 ? '0' : '') + page.volume;
var tag = config.tag || 'CrunchyRoll';
@ -116,7 +114,7 @@ function prefix(value: number|string, length: number) {
/**
* Requests the page data and scrapes the id, episode, series and swf.
*/
function scrapePage(config: typings.IConfig, address: string, done: (err: Error, page?: typings.IEpisodePage) => void) {
function scrapePage(config: IConfig, address: string, done: (err: Error, page?: IEpisodePage) => void) {
var id = parseInt((address.match(/[0-9]+$/) || ['0'])[0], 10);
if (!id) return done(new Error('Invalid address.'));
request.get(config, address, (err, result) => {
@ -139,7 +137,7 @@ function scrapePage(config: typings.IConfig, address: string, done: (err: Error,
/**
* Requests the player data and scrapes the subtitle and video data.
*/
function scrapePlayer(config: typings.IConfig, address: string, id: number, done: (err: Error, player?: typings.IEpisodePlayer) => void) {
function scrapePlayer(config: IConfig, address: string, id: number, done: (err: Error, player?: IEpisodePlayer) => void) {
var url = address.match(/^(https?:\/\/[^\/]+)/);
if (!url) return done(new Error('Invalid address.'));
request.post(config, {
@ -150,7 +148,7 @@ function scrapePlayer(config: typings.IConfig, address: string, id: number, done
xml2js.parseString(result, {
explicitArray: false,
explicitRoot: false
}, (err: Error, player: typings.IEpisodePlayerConfig) => {
}, (err: Error, player: IEpisodePlayerConfig) => {
if (err) return done(err);
try {
var isSubtitled = Boolean(player['default:preload'].subtitle);

View File

@ -1,4 +1,5 @@
'use strict';
export import batch = require('./batch');
export import episode = require('./episode');
export import series = require('./series');
import batch from './batch';
import episode from './episode';
import series from './series';
export {batch, episode, series};

16
src/interface/IConfig.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
interface IConfig {
// Authentication
pass?: string;
user?: string;
// Disables
cache?: boolean;
merge?: boolean;
// Filters
episode?: number;
volume?: number;
// Settings
format?: string;
output?: string;
series?: string;
tag?: string;
}

3
src/interface/IConfigLine.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
interface IConfigLine extends IConfig {
args: string[];
}

4
src/interface/IConfigTask.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
interface IConfigTask {
address: string;
config: IConfigLine;
}

7
src/interface/IEpisodePage.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
interface IEpisodePage {
id: number;
episode: number;
series: string;
volume: number;
swf: string;
}

11
src/interface/IEpisodePlayer.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
interface IEpisodePlayer {
subtitle?: {
id: number;
iv: string;
data: string;
};
video: {
file: string;
host: string;
};
}

15
src/interface/IEpisodePlayerConfig.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
interface IEpisodePlayerConfig {
'default:preload': {
subtitle: {
$: {
id: string;
};
iv: string;
data: string;
};
stream_info: {
file: string;
host: string;
};
};
}

3
src/interface/IFormatterTable.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
interface IFormatterTable {
[key: string]: (input: string|Buffer, done: (err: Error, subtitle?: string) => void) => void;
}

4
src/interface/ISeries.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
interface ISeries {
episodes: ISeriesEpisode[];
series: string;
}

5
src/interface/ISeriesEpisode.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
interface ISeriesEpisode {
address: string;
episode: number;
volume: number;
}

13
src/interface/ISubtitle.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
interface ISubtitle {
$: {
title: string;
wrap_style: string;
play_res_x: string;
play_res_y: string;
id: string;
lang_string: string;
created: string;
};
events: ISubtitleEvent;
styles: ISubtitleStyle;
}

15
src/interface/ISubtitleEvent.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
interface ISubtitleEvent {
event: {
$: {
end: string;
start: string;
style: string;
name: string;
margin_l: string;
margin_r: string;
margin_v: string;
effect: string;
text: string;
};
}[];
}

29
src/interface/ISubtitleStyle.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
interface ISubtitleStyle {
style: {
$: {
name: string;
font_name: string;
font_size: string;
primary_colour: string;
secondary_colour: string;
outline_colour: string;
back_colour: string;
bold: string;
italic: string;
underline: string;
strikeout: string;
scale_x: string;
scale_y: string;
spacing: string;
angle: string;
border_style: string;
outline: string;
shadow: string;
alignment: string;
margin_l: string;
margin_r: string;
margin_v: string;
encoding: string;
};
}[];
}

View File

@ -1,12 +1,11 @@
'use strict';
import request = require('request');
import typings = require('./typings');
var isAuthenticated = false;
/**
* Performs a GET request for the resource.
*/
export function get(config: typings.IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
export function get(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
authenticate(config, err => {
if (err) return done(err);
request.get(modify(options), (err: Error, response: any, body: any) => {
@ -19,7 +18,7 @@ export function get(config: typings.IConfig, options: request.Options, done: (er
/**
* Performs a POST request for the resource.
*/
export function post(config: typings.IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) {
authenticate(config, err => {
if (err) return done(err);
request.post(modify(options), (err: Error, response: any, body: any) => {
@ -32,7 +31,7 @@ export function post(config: typings.IConfig, options: request.Options, done: (e
/**
* Authenticates using the configured pass and user.
*/
function authenticate(config: typings.IConfig, done: (err: Error) => void) {
function authenticate(config: IConfig, done: (err: Error) => void) {
if (isAuthenticated || !config.pass || !config.user) return done(null);
var options = {
form: {

View File

@ -1,18 +1,16 @@
'use strict';
export = main;
import cheerio = require('cheerio');
import episode = require('./episode');
import episode from './episode';
import fs = require('fs');
import request = require('./request');
import path = require('path');
import typings = require('./typings');
import url = require('url');
var persistent = '.crpersistent';
/**
* Streams the series to disk.
*/
function main(config: typings.IConfig, address: string, done: (err: Error) => void) {
export default function(config: IConfig, address: string, done: (err: Error) => void) {
var persistentPath = path.join(config.output || process.cwd(), persistent);
fs.readFile(persistentPath, 'utf8', (err, contents) => {
var cache = config.cache ? {} : JSON.parse(contents || '{}');
@ -39,9 +37,9 @@ function main(config: typings.IConfig, address: string, done: (err: Error) => vo
* Downloads the episode.
*/
function download(cache: {[address: string]: number},
config: typings.IConfig,
config: IConfig,
baseAddress: string,
item: typings.ISeriesEpisode,
item: ISeriesEpisode,
done: (err: Error) => void) {
if (!filter(config, item)) return done(null);
var address = url.resolve(baseAddress, item.address);
@ -56,7 +54,7 @@ function download(cache: {[address: string]: number},
/**
* Filters the item based on the configuration.
*/
function filter(config: typings.IConfig, item: typings.ISeriesEpisode) {
function filter(config: IConfig, item: ISeriesEpisode) {
// Filter on chapter.
var episodeFilter = config.episode;
if (episodeFilter > 0 && item.episode <= episodeFilter) return false;
@ -72,13 +70,13 @@ function filter(config: typings.IConfig, item: typings.ISeriesEpisode) {
/**
* Requests the page and scrapes the episodes and series.
*/
function page(config: typings.IConfig, address: string, done: (err: Error, result?: typings.ISeries) => void) {
function page(config: IConfig, address: string, done: (err: Error, result?: ISeries) => void) {
request.get(config, address, (err, result) => {
if (err) return done(err);
var $ = cheerio.load(result);
var title = $('span[itemprop=name]').text();
if (!title) return done(new Error('Invalid page.'));
var episodes: typings.ISeriesEpisode[] = [];
var episodes: ISeriesEpisode[] = [];
$('.episode').each((i, el) => {
if ($(el).children('img[src*=coming_soon]').length) return;
var volume = /([0-9]+)\s*$/.exec($(el).closest('ul').prev('a').text());

View File

@ -1,6 +1,5 @@
/* tslint:disable:no-bitwise false */
'use strict';
export = main;
import crypto = require('crypto');
import bigInt = require('big-integer');
import zlib = require('zlib');
@ -8,7 +7,7 @@ import zlib = require('zlib');
/**
* Decodes the data.
*/
function main(id: number, iv: Buffer|string, data: Buffer|string, done: (err?: Error, result?: Buffer) => void) {
export default function(id: number, iv: Buffer|string, data: Buffer|string, done: (err?: Error, result?: Buffer) => void) {
try {
decompress(decrypt(id, iv, data), done);
} catch (e) {

View File

@ -1,16 +1,14 @@
'use strict';
export = main;
import xml2js = require('xml2js');
import typings = require('../../typings');
/**
* Converts an input buffer to a SubStation Alpha subtitle.
*/
function main(input: string|Buffer, done: (err: Error, subtitle?: string) => void) {
export default function(input: string|Buffer, done: (err: Error, subtitle?: string) => void) {
xml2js.parseString(input.toString(), {
explicitArray: false,
explicitRoot: false
}, (err: Error, xml: typings.ISubtitle) => {
}, (err: Error, xml: ISubtitle) => {
if (err) return done(err);
try {
done(null, script(xml) + '\n' +
@ -25,7 +23,7 @@ function main(input: string|Buffer, done: (err: Error, subtitle?: string) => voi
/**
* Converts the event block.
*/
function event(block: typings.ISubtitleEvent): string {
function event(block: ISubtitleEvent): string {
var format = 'Layer,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text';
return '[Events]\n' +
'Format: ' + format + '\n' +
@ -44,7 +42,7 @@ function event(block: typings.ISubtitleEvent): string {
/**
* Converts the script block.
*/
function script(block: typings.ISubtitle): string {
function script(block: ISubtitle): string {
return '[Script Info]\n' +
'Title: ' + block.$.title + '\n' +
'ScriptType: v4.00+\n' +
@ -59,7 +57,7 @@ function script(block: typings.ISubtitle): string {
/**
* Converts the style block.
*/
function style(block: typings.ISubtitleStyle): string {
function style(block: ISubtitleStyle): string {
var format = 'Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,' +
'OutlineColour,BackColour,Bold,Italic,Underline,StrikeOut,ScaleX,' +
'ScaleY,Spacing,Angle,BorderStyle,Outline,Shadow,Alignment,' +

View File

@ -1,10 +1,8 @@
'use strict';
export = main;
import ass = require('./ass');
import srt = require('./srt');
import typings = require('../../typings');
import ass from './ass';
import srt from './srt';
var main: typings.IFormatterTable = {
export default <IFormatterTable> {
ass: ass,
srt: srt
};

View File

@ -1,14 +1,12 @@
'use strict';
export = srt;
import xml2js = require('xml2js');
import typings = require('../../typings');
/**
* Converts an input buffer to a SubRip subtitle.
*/
function srt(input: Buffer|string, done: (err: Error, subtitle?: string) => void) {
export default function(input: Buffer|string, done: (err: Error, subtitle?: string) => void) {
var options = {explicitArray: false, explicitRoot: false};
xml2js.parseString(input.toString(), options, (err: Error, xml: typings.ISubtitle) => {
xml2js.parseString(input.toString(), options, (err: Error, xml: ISubtitle) => {
try {
if (err) return done(err);
done(null, xml.events.event.map((event, index) => {

View File

@ -1,3 +1,4 @@
'use strict';
export import decode = require('./decode');
export import formats = require('./formats/index');
import decode from './decode';
import formats from './formats/index';
export default {decode, formats};

View File

@ -1,136 +0,0 @@
export interface IConfig {
// Authentication
pass?: string;
user?: string;
// Disables
cache?: boolean;
merge?: boolean;
// Filters
episode?: number;
volume?: number;
// Settings
format?: string;
output?: string;
series?: string;
tag?: string;
}
export interface IConfigLine extends IConfig {
args: string[];
}
export interface IConfigTask {
address: string;
config: IConfigLine;
}
export interface IEpisodePage {
id: number;
episode: number;
series: string;
volume: number;
swf: string;
}
export interface IEpisodePlayer {
subtitle?: {
id: number;
iv: string;
data: string;
};
video: {
file: string;
host: string;
};
}
export interface IEpisodePlayerConfig {
'default:preload': {
subtitle: {
$: {
id: string;
};
iv: string;
data: string;
};
stream_info: {
file: string;
host: string;
};
};
}
export interface IFormatterTable {
[key: string]: (input: string|Buffer, done: (err: Error, subtitle?: string) => void) => void;
}
export interface ISeries {
episodes: ISeriesEpisode[];
series: string;
}
export interface ISeriesEpisode {
address: string;
episode: number;
volume: number;
}
export interface ISubtitle {
$: {
title: string;
wrap_style: string;
play_res_x: string;
play_res_y: string;
id: string;
lang_string: string;
created: string;
};
events: ISubtitleEvent;
styles: ISubtitleStyle;
}
export interface ISubtitleEvent {
event: {
$: {
end: string;
start: string;
style: string;
name: string;
margin_l: string;
margin_r: string;
margin_v: string;
effect: string;
text: string;
};
}[];
}
export interface ISubtitleStyle {
style: {
$: {
name: string;
font_name: string;
font_size: string;
primary_colour: string;
secondary_colour: string;
outline_colour: string;
back_colour: string;
bold: string;
italic: string;
underline: string;
strikeout: string;
scale_x: string;
scale_y: string;
spacing: string;
angle: string;
border_style: string;
outline: string;
shadow: string;
alignment: string;
margin_l: string;
margin_r: string;
margin_v: string;
encoding: string;
};
}[];
}

View File

@ -1,3 +1,4 @@
'use strict';
export import merge = require('./merge');
export import stream = require('./stream');
import merge from './merge';
import stream from './stream';
export default {merge, stream};

View File

@ -1,16 +1,14 @@
'use strict';
export = main;
import childProcess = require('child_process');
import fs = require('fs');
import path = require('path');
import os = require('os');
import subtitle = require('../subtitle/index');
import typings = require('../typings');
import subtitle from '../subtitle/index';
/**
* Merges the subtitle and video files into a Matroska Multimedia Container.
*/
function main(config: typings.IConfig, isSubtitled: boolean, rtmpInputPath: string, filePath: string, done: (err: Error) => void) {
export default function(config: IConfig, isSubtitled: boolean, rtmpInputPath: string, filePath: string, done: (err: Error) => void) {
var subtitlePath = filePath + '.' + (subtitle.formats[config.format] ? config.format : 'ass');
var videoPath = filePath + path.extname(rtmpInputPath);
childProcess.exec(command() + ' ' +

View File

@ -1,5 +1,4 @@
'use strict';
export = main;
import childProcess = require('child_process');
import path = require('path');
import os = require('os');
@ -7,7 +6,7 @@ import os = require('os');
/**
* Streams the video to disk.
*/
function main(rtmpUrl: string, rtmpInputPath: string, swfUrl: string, filePath: string, done: (err: Error) => void) {
export default function(rtmpUrl: string, rtmpInputPath: string, swfUrl: string, filePath: string, done: (err: Error) => void) {
childProcess.exec(command() + ' ' +
'-r "' + rtmpUrl + '" ' +
'-y "' + rtmpInputPath + '" ' +

View File

@ -1,5 +1,5 @@
{
"version": "1.4.1",
"version": "1.5.1-beta",
"compilerOptions": {
"declaration": true,
"noImplicitAny": true,
@ -18,6 +18,18 @@
"src/cli.ts",
"src/episode.ts",
"src/index.ts",
"src/interface/IConfig.d.ts",
"src/interface/IConfigLine.d.ts",
"src/interface/IConfigTask.d.ts",
"src/interface/IEpisodePage.d.ts",
"src/interface/IEpisodePlayer.d.ts",
"src/interface/IEpisodePlayerConfig.d.ts",
"src/interface/IFormatterTable.d.ts",
"src/interface/ISeries.d.ts",
"src/interface/ISeriesEpisode.d.ts",
"src/interface/ISubtitle.d.ts",
"src/interface/ISubtitleEvent.d.ts",
"src/interface/ISubtitleStyle.d.ts",
"src/request.ts",
"src/series.ts",
"src/subtitle/decode.ts",
@ -25,7 +37,6 @@
"src/subtitle/formats/index.ts",
"src/subtitle/formats/srt.ts",
"src/subtitle/index.ts",
"src/typings.ts",
"src/video/index.ts",
"src/video/merge.ts",
"src/video/stream.ts",