28 Commits

Author SHA1 Message Date
Godzil
fa0e8568b4 Last update for dependabot. More would need to do some actually work on the code, which is *not* on the menu for today, 2023-06-27 16:50:37 +02:00
Manoël Trapier
53c1940143 Merge pull request #132 from Godzil/dependabot/npm_and_yarn/json-schema-and-jsprim-0.4.0
Bump json-schema and jsprim
2023-06-27 15:34:50 +01:00
Manoël Trapier
1a1753ee5b Merge pull request #131 from Godzil/dependabot/npm_and_yarn/qs-6.5.3
Bump qs from 6.5.2 to 6.5.3
2023-06-27 15:34:41 +01:00
Manoël Trapier
99b5ec246b Merge pull request #129 from Godzil/dependabot/npm_and_yarn/css-what-2.1.3
Bump css-what from 2.1.0 to 2.1.3
2023-06-27 15:34:29 +01:00
Manoël Trapier
49e6f07237 Merge pull request #128 from Godzil/dependabot/npm_and_yarn/ajv-6.12.6
Bump ajv from 6.10.0 to 6.12.6
2023-06-27 15:34:07 +01:00
Godzil
62606005b5 Dependabot..... 2023-06-27 16:32:45 +02:00
dependabot[bot]
eb9fdc9177 Bump json-schema and jsprim
Bumps [json-schema](https://github.com/kriszyp/json-schema) and [jsprim](https://github.com/joyent/node-jsprim). These dependencies needed to be updated together.

Updates `json-schema` from 0.2.3 to 0.4.0
- [Commits](https://github.com/kriszyp/json-schema/compare/v0.2.3...v0.4.0)

Updates `jsprim` from 1.4.1 to 1.4.2
- [Changelog](https://github.com/TritonDataCenter/node-jsprim/blob/v1.4.2/CHANGES.md)
- [Commits](https://github.com/joyent/node-jsprim/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: json-schema
  dependency-type: indirect
- dependency-name: jsprim
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-27 14:29:52 +00:00
dependabot[bot]
546f1eeeb4 Bump qs from 6.5.2 to 6.5.3
Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3)

---
updated-dependencies:
- dependency-name: qs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-27 14:29:45 +00:00
dependabot[bot]
d92fbace08 Bump css-what from 2.1.0 to 2.1.3
Bumps [css-what](https://github.com/fb55/css-what) from 2.1.0 to 2.1.3.
- [Release notes](https://github.com/fb55/css-what/releases)
- [Commits](https://github.com/fb55/css-what/compare/v2.1.0...v2.1.3)

---
updated-dependencies:
- dependency-name: css-what
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-27 14:29:33 +00:00
dependabot[bot]
43c91eea08 Bump ajv from 6.10.0 to 6.12.6
Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.10.0 to 6.12.6.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/v6.10.0...v6.12.6)

---
updated-dependencies:
- dependency-name: ajv
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-27 14:29:05 +00:00
Godzil
9542e451f2 Make dependabot happy. And trying to stop the annoying notification from github. 2023-06-27 16:23:02 +02:00
Godzil
de3f9a1e9e 1.6.1 2020-04-28 00:03:51 +01:00
Godzil
f26ea70ef8 Bugfix: if there is no season, they put 0 instead of 1. Let's correct that 2020-04-28 00:03:25 +01:00
Godzil
baf15dc1b4 Minor changes in npmignore 2020-04-28 00:02:43 +01:00
Godzil
2c58e5e4ba update npm ignore file. 2020-04-27 23:38:14 +01:00
Godzil
c8f33e947d Update gitignore 2020-04-27 23:25:22 +01:00
Godzil
fc119acb1c 1.6.0 2020-04-27 23:06:04 +01:00
Godzil
2db34c3ed8 Update readme with some of the new informations 2020-04-27 23:05:16 +01:00
Godzil
64200a1da9 Add support for the NEW Way.
It started as just a way to fix subtitles download. It ended in a
complete new way to get info about video stream AND subtitles.

Lots of things have change on CR since the last major update, and on
some pages, the old subtitle fetch fail. They changed the player on the
page from the old flash one to a HTML based one and albeit most scrapped
info are still valid, some are no longer working on some new releases.

It should be more reliable, but there are some drawback. I'm currently
unable to select the resolution, it is 1080 by default. It will probably
not work for non premium account, but, you know, I ask clearly for you
to only use that tool if you have a premium account, so well, I'm not
going to try to support non premium account if it does not work.

Oh, and it add the possibility to download subtitles in the languages of
your choice!

The old mechanism is still there as fallback, but may be removed in the
futur to clean up the code.
2020-04-27 22:48:24 +01:00
Godzil
8655874097 Remove no longer used entry for the config. 2020-04-27 22:39:51 +01:00
Godzil
99ba051b7f Remove some too verbose messages when an error occur and we are not
in verbose mode.
2020-04-27 22:39:30 +01:00
Godzil
d692199d07 CF don't like us if we go to fast. Let's add some delays. 2020-04-27 22:39:03 +01:00
Godzil
e058b8e699 We want to save the video format/quality infos in the config file. 2020-04-27 22:37:23 +01:00
Godzil
b3a96cd0e7 Let's make the headers a bit better. 2020-04-27 22:34:13 +01:00
Godzil
f25a62234c Add more to the dub ignore regexp. 2020-04-27 22:33:16 +01:00
Manoël Trapier
e8a7856e44 Merge pull request #116 from elisha464/master
fix logUsingCookie
2020-04-13 20:27:08 +01:00
Manoël Trapier
f086b3593e Merge branch 'master' into master 2020-04-13 20:25:37 +01:00
elisha464
fa4c68c239 fix logUsingCookie
* it seems that the site only cares about the cookie `session_id`
* when checking if the user is authenticated the cookie jar was not being used
2019-11-30 16:32:24 +02:00
16 changed files with 9552 additions and 151 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,5 @@
dist/
node_modules/
package-lock.json
.idea/
test/
config.json

View File

@@ -1,8 +1,12 @@
extras/
node_modules/
src/
typings/
ts.js
tsconfig.json
tsd.json
tslint.json
.idea/
.github/
test/
config.json
.travis.yml

View File

@@ -79,7 +79,6 @@ The [command-line interface](http://en.wikipedia.org/wiki/Command-line_interface
-e, --episodes <s> Episode list. Read documentation on how to use
-f, --format <s> The subtitle format. (default: ass)
-o, --output <s> The output path.
-s, --series <s> The series name override.
--ignoredub Experimental: Ignore all seasons where the title end with 'Dub)'
-n, --nametmpl <s> Output name template (default: {SERIES_TITLE} - s{SEASON_NUMBER}e{EPISODE_NUMBER} - [{TAG}])
-t, --tag <s> The subgroup. (default: CrunchyRoll)
@@ -88,6 +87,8 @@ The [command-line interface](http://en.wikipedia.org/wiki/Command-line_interface
--verbose Make tool verbose
--rebuildcrp Rebuild the crpersistant file.
--retry <i> Number or time to retry fetching an episode. (default: 5)
-s, --sublang <items> Select the subtitle languages, multiple value separated by a comma are accepted (like: frFR,enUS )
--sleepTime <i> Minimum wait time between each http requests.
-h, --help output usage information
#### Batch-mode
@@ -98,7 +99,7 @@ When no sequence of series addresses is provided, the batch-mode source file wil
Starting from version 1.4.0, Crunchy store some information in a config.json file. The file which is use have to be in the folder you are calling Crunchy. This is partly by design and a limitation on where Crunchy can find files.
This file store some informations like your username and password.
This file store some information like your username and password.
You don't need to create that file as Crunchy will create it for you, the first time you run it. Each run will update the content of the file, so it you run crunchy with your credential on the command line, it will add them to config file.
@@ -115,6 +116,7 @@ Here are the list of valid parameter in the config file:
* `nametmpl` see `--nametmpl`
* `tag` see `--tag`
* `resolution` see `--resolution`
* `sublang` see `--sublang`
- Login related options:
* `pass` see `--user`
@@ -128,14 +130,15 @@ Here are the list of valid parameter in the config file:
* `crLocale`
* `crSessionKey`
* `crLoginUrl`
* `crUserId`
* `crUserKey`
- Other options:
* `sleepTime`: minimum delay (in ms) between each page load
- Generated values: don't touch them:
* `crDeviceId`
* `crSessionId`
Some of theses login related options are not going to be documented on what to put there for _legal_ reason.
Some of these login related options are not going to be documented on what to put there for _legal_ reason.
Crunchy will also create a `.cookie.jar` file in the output folder (by default the current folder) it is the file used by Crunchy to store the web cookies.
@@ -185,7 +188,24 @@ Download episodes starting from 42 to the last available of *Tail Fairy*:
crunchy -u login -p password http://www.cr.com/tail-fairy -e 42-
Download episode up to 42 (included) of *Tail Fairy* with italian subtitles:
crunchy -u login -p password http://www.cr.com/tail-fairy -e -42 -s itIT
Download episode up to 42 (included) of *Tail Fairy* with italian subtitles and fallback to english if no available:
crunchy -u login -p password http://www.cr.com/tail-fairy -e -42 -s itIT,enUS
#### Known valid subtitles language:
- `enUS` : English
- `frFR` : French
- `ptBR` : Portuguese (Brazil)
- `esES` : Spanish (Spain)
- `deDE` : German
- `esLA` : Spanish (Latin America)
- `itIT` : Italian
- `arME` : Armenian
- `ptPT` : Portuguese (Portugal)
#### Command line parameters
@@ -246,5 +266,5 @@ More information will be added at a later point. For now the recommendations are
* Atom with `atom-typescript` and `linter-tslint` (and dependencies).
Since this project uses TypeScript, compile with `node run compile` to build the tool and `npm run test` to run the linter.
Since this project uses TypeScript, compile with `node run build` to build the tool and `npm run test` to run the linter.

9152
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -15,41 +15,40 @@
"engines": {
"node": ">=5.0"
},
"version": "1.5.1",
"version": "1.6.1",
"bin": {
"crunchy": "./bin/crunchy",
"crunchy.sh": "./bin/crunchy.sh"
},
"dependencies": {
"big-integer": "^1.6.48",
"bluebird": "^3.7.2",
"brotli": "^1.3.2",
"cheerio": "^0.22.0",
"cloudscraper": "^4.6.0",
"commander": "^5.0.0",
"fs-extra": "^9.0.0",
"mkdirp": "^1.0.4",
"pjson": "^1.0.9",
"request": "^2.88.2",
"request-promise": "^4.2.5",
"tough-cookie-file-store": "^1.2.0",
"uuid": "^7.0.3",
"xml2js": "^0.4.23"
"big-integer": ">=1.6.51",
"bluebird": ">=3.7.2",
"brotli": ">=1.3.3",
"cloudscraper": ">=4.6.0",
"commander": ">=11.0.0",
"fs-extra": ">=11.1.1",
"mkdirp": ">=3.0.1",
"pjson": ">=1.0.9",
"request": ">=2.88.2",
"request-promise": ">=4.2.6",
"tough-cookie-file-store": ">=2.0.3",
"uuid": ">=9.0.0",
"xml2js": ">=0.6.0"
},
"devDependencies": {
"@types/bluebird": "^3.5.30",
"@types/cheerio": "^0.22.17",
"@types/fs-extra": "^8.1.0",
"@types/mkdirp": "^1.0.0",
"@types/node": "^13.11.1",
"@types/request": "^2.48.4",
"@types/request-promise": "^4.1.46",
"@types/uuid": "^7.0.2",
"@types/xml2js": "^0.4.5",
"npm-check": "^5.9.2",
"tsconfig-lint": "^0.12.0",
"tslint": "^6.1.1",
"typescript": "^3.8.3"
"@types/bluebird": ">=3.5.38",
"@types/cheerio": ">=0.22.31",
"@types/fs-extra": ">=11.0.1",
"@types/mkdirp": ">=2.0.0",
"@types/node": ">=20.3.2",
"@types/request": ">=2.48.8",
"@types/request-promise": ">=4.1.48",
"@types/uuid": ">=9.0.2",
"@types/xml2js": ">=0.4.11",
"cheerio": "^1.0.0-rc.12",
"npm-check": "^6.0.1",
"tslint": "^6.1.3",
"typescript": ">=5.1.3"
},
"scripts": {
"prepublishOnly": "npm run build",

View File

@@ -38,19 +38,14 @@ export default function(args: string[], done: (err?: Error) => void)
config.nametmpl = '{SERIES_TITLE} - s{SEASON_NUMBER}e{EPISODE_NUMBER} - {EPISODE_TITLE} - [{TAG}]';
}
// Update the config file with new parameters
cfg.save(config);
if (config.unlog)
if (config.tag === undefined)
{
config.crDeviceId = undefined;
config.user = undefined;
config.pass = undefined;
my_request.eatCookies(config);
cfg.save(config);
log.info('Unlogged!');
config.tag = 'CrunchyRoll';
}
process.exit(0);
if (config.sublang === undefined)
{
config.sublang = [ 'enUS' ];
}
// set resolution
@@ -75,6 +70,21 @@ export default function(args: string[], done: (err?: Error) => void)
config.video_quality = resol_table['1080'].quality;
}
// Update the config file with new parameters
cfg.save(config);
if (config.unlog)
{
config.crDeviceId = undefined;
config.user = undefined;
config.pass = undefined;
my_request.eatCookies(config);
cfg.save(config);
log.info('Unlogged!');
process.exit(0);
}
if (config.debug)
{
/* Ugly but meh */
@@ -133,7 +143,10 @@ export default function(args: string[], done: (err?: Error) => void)
}
else if (tasksArr[i].retry <= 0)
{
log.error(JSON.stringify(errin));
if (config.verbose)
{
log.error(JSON.stringify(errin));
}
if (config.debug)
{
log.dumpToDebug('BatchGiveUp', JSON.stringify(errin));
@@ -160,7 +173,7 @@ export default function(args: string[], done: (err?: Error) => void)
{
i += 1;
}
next();
setTimeout(next, config.sleepTime);
});
})();
});
@@ -366,6 +379,10 @@ function tasks(config: IConfigLine, batchPath: string, done: (err: Error, tasks?
});
}
function commaSeparatedList(value: any, dummyPrevious: any) {
return value.split(',');
}
/**
* Parses the arguments and returns a configuration.
*/
@@ -383,17 +400,20 @@ function parse(args: string[]): IConfigLine
.option('-e, --episodes <s>', 'Episode list. Read documentation on how to use')
// Settings
.option('-l, --crlang <s>', 'CR page language (valid: en, fr, es, it, pt, de, ru).')
.option('-s, --sublang <items>', 'Select the subtitle languages, multiple value separated by a comma ' +
'are accepted (like: frFR,enUS )', commaSeparatedList)
.option('-f, --format <s>', 'The subtitle format.', 'ass')
.option('-o, --output <s>', 'The output path.')
.option('-s, --series <s>', 'The series name override.')
.option('--ignoredub', 'Experimental: Ignore all seasons where the title end with \'Dub)\'')
.option('-n, --nametmpl <s>', 'Output name template')
.option('-t, --tag <s>', 'The subgroup.', 'CrunchyRoll')
.option('-r, --resolution <s>', 'The video resolution. (valid: 360, 480, 720, 1080)', '1080')
.option('-t, --tag <s>', 'The subgroup.')
.option('-r, --resolution <s>', 'The video resolution. (valid: 360, 480, 720, 1080)')
.option('-b, --batch <s>', 'Batch file', 'CrunchyRoll.txt')
.option('--verbose', 'Make tool verbose')
.option('--debug', 'Create a debug file. Use only if requested!')
.option('--rebuildcrp', 'Rebuild the crpersistant file.')
.option('--retry <i>', 'Number or time to retry fetching an episode.', '5')
.option('--sleepTime <i>', 'Minimum wait time between each http requests.')
.parse(args);
}

View File

@@ -71,8 +71,6 @@ export function save(config: IConfig)
tmp.cache = undefined;
tmp.episodes = undefined;
tmp.series = undefined;
tmp.video_format = undefined;
tmp.video_quality = undefined;
tmp.rebuildcrp = undefined;
tmp.batch = undefined;
tmp.verbose = undefined;

View File

@@ -5,6 +5,7 @@ import mkdirp = require('mkdirp');
import my_request = require('./my_request');
import path = require('path');
import subtitle from './subtitle/index';
import vlos from './vlos';
import video from './video/index';
import xml2js = require('xml2js');
import log = require('./log');
@@ -21,15 +22,24 @@ export default function(config: IConfig, address: string, done: (err: Error, ign
return done(err, false);
}
scrapePlayer(config, address, page.id, (errS, player) =>
if (page.media != null)
{
if (errS)
/* No player to scrape */
download(config, page, null, done);
}
else
{
/* The old way */
scrapePlayer(config, address, page.id, (errS, player) =>
{
return done(errS, false);
}
if (errS)
{
return done(errS, false);
}
download(config, page, player, done);
});
download(config, page, player, done);
});
}
});
}
@@ -115,7 +125,7 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
if (ret)
{
log.dispEpisode(fileName, 'Fetching...', false);
downloadSubtitle(config, player, filePath, (errDS) =>
downloadSubtitle(config, page, player, filePath, (errDS) =>
{
if (errDS)
{
@@ -124,7 +134,8 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
}
const now = Date.now();
if (player.video.file !== undefined)
if ( ((page.media === null) && (player.video.file !== undefined))
|| ((page.media !== null) /* Do they still create page in advance for unreleased episodes? */) )
{
log.dispEpisode(fileName, 'Fetching video...', false);
downloadVideo(config, page, player, filePath, (errDV) =>
@@ -140,10 +151,28 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
return complete(fileName, 'Finished!', now, done);
}
const isSubtited = Boolean(player.subtitle);
let isSubtitled = true;
if (page.media === null)
{
isSubtitled = Boolean(player.subtitle);
}
else
{
if (page.media.subtitles.length === 0)
{
isSubtitled = false;
}
}
let videoExt = '.mp4';
if ( (page.media === null) && (player.video.mode === 'RTMP'))
{
videoExt = path.extname(player.video.file);
}
log.dispEpisode(fileName, 'Merging...', false);
video.merge(config, isSubtited, player.video.file, filePath, player.video.mode, config.verbose, (errVM) =>
video.merge(config, isSubtitled, videoExt, filePath, config.verbose, (errVM) =>
{
if (errVM)
{
@@ -164,7 +193,7 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
}
else
{
log.dispEpisode(fileName, 'Error creating folder \'" + filePath + "\'...', true);
log.dispEpisode(fileName, 'Error creating folder \'' + filePath + '\'...', true);
return done('Cannot create folder', false);
}
}
@@ -172,50 +201,125 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
/**
* Saves the subtitles to disk.
*/
function downloadSubtitle(config: IConfig, player: IEpisodePlayer, filePath: string, done: (err?: Error | string) => void)
function downloadSubtitle(config: IConfig, page: IEpisodePage, player: IEpisodePlayer,
filePath: string, done: (err?: Error | string) => void)
{
const enc = player.subtitle;
if (!enc)
if (page.media !== null)
{
return done();
}
subtitle.decode(enc.id, enc.iv, enc.data, (errSD, data) =>
{
if (errSD)
const subs = page.media.subtitles;
if (subs.length === 0)
{
return done(errSD);
/* No downloadable subtitles */
console.warn('Can\'t find subtitle ?!');
return done();
}
if (config.debug)
{
log.dumpToDebug('SubtitlesXML', data);
}
let i;
let j;
const formats = subtitle.formats;
const format = formats[config.format] ? config.format : 'ass';
formats[format](config, data, (errF: Error, decodedSubtitle: string) =>
/* Find a proper subtitles */
for (j = 0; j < config.sublang.length; j++)
{
if (errF)
const reqSubLang = config.sublang[j];
for (i = 0; i < subs.length; i++)
{
return done(errF);
const curSub = subs[i];
if (curSub.format === 'ass' && curSub.language === reqSubLang)
{
my_request.get(config, curSub.url, (err, result) =>
{
if (err)
{
log.error('An error occured while fetching subtitles...');
return done(err);
}
fs.writeFile(filePath + '.ass', '\ufeff' + result, done);
});
/* Break from the first loop */
j = config.sublang.length;
break;
}
}
}
if (i >= subs.length)
{
done('Cannot find subtitles with requested language(s)');
}
}
else
{
const enc = player.subtitle;
if (!enc)
{
return done();
}
subtitle.decode(enc.id, enc.iv, enc.data, (errSD, data) =>
{
if (errSD)
{
log.error('An error occured while getting subtitles...');
return done(errSD);
}
fs.writeFile(filePath + '.' + format, '\ufeff' + decodedSubtitle, done);
if (config.debug)
{
log.dumpToDebug('SubtitlesXML', data);
}
const formats = subtitle.formats;
const format = formats[config.format] ? config.format : 'ass';
formats[format](config, data, (errF: Error, decodedSubtitle: string) =>
{
if (errF)
{
return done(errF);
}
fs.writeFile(filePath + '.' + format, '\ufeff' + decodedSubtitle, done);
});
});
});
}
}
/**
* Streams the video to disk.
*/
function downloadVideo(config: IConfig, page: IEpisodePage, player: IEpisodePlayer,
filePath: string, done: (err: Error) => void)
filePath: string, done: (err: any) => void)
{
video.stream(player.video.host, player.video.file, page.swf, filePath,
path.extname(player.video.file), player.video.mode, config.verbose, done);
if (player == null)
{
/* new way */
const streams = page.media.streams;
let i;
/* Find a proper subtitles */
for (i = 0; i < streams.length; i++)
{
if (streams[i].format === 'vo_adaptive_hls' && streams[i].audio_lang === 'jaJP' &&
streams[i].hardsub_lang === null)
{
video.stream('', streams[i].url, '', filePath,
'mp4', 'HLS', config.verbose, done);
break;
}
}
if (i >= streams.length)
{
done('Cannot find a valid stream');
}
}
else
{
/* Old way */
video.stream(player.video.host, player.video.file, page.swf, filePath,
path.extname(player.video.file), player.video.mode, config.verbose, done);
}
}
/**
@@ -277,51 +381,73 @@ function scrapePage(config: IConfig, address: string, done: (err: Error, page?:
}
const $ = cheerio.load(result);
const swf = /^([^?]+)/.exec($('link[rel=video_src]').attr('href'));
const regexp = /\s*([^\n\r\t\f]+)\n?\s*[^0-9]*([0-9][\-0-9.]*)?,?\n?\s\s*[^0-9]*((PV )?[S0-9][P0-9.]*[a-fA-F]?)/;
const look = $('#showmedia_about_media').text();
const seasonTitle = $('span[itemprop="title"]').text();
const episodeTitle = $('#showmedia_about_name').text().replace(/[“”]/g, '');
const data = regexp.exec(look);
/* First check if we have the new player */
const vlosScript = $('#vilos-iframe-container');
if (config.debug)
if (vlosScript)
{
log.dumpToDebug('episode page', $.html());
}
const pageMetadata = JSON.parse($('script[type="application/ld+json"]')[0].children[0].data);
const divScript = $('div[id="showmedia_video_box_wide"]');
const scripts = divScript.find('script').toArray();
const script = scripts[2].children[0].data;
let seasonNumber = '1';
let seasonTitle = '';
if (!swf || !data)
{
log.warn('Somethig unexpected in the page at ' + address + ' (data are: ' + look + ')');
log.warn('Setting Season to 0 and episode to 0...');
if (config.debug)
if (pageMetadata.partOfSeason)
{
log.dumpToDebug('episode unexpected', look);
}
seasonNumber = pageMetadata.partOfSeason.seasonNumber;
if (seasonNumber === '0') { seasonNumber = '1'; }
done(null, {
episode: '0',
id: epId,
series: seasonTitle,
season: seasonTitle,
title: episodeTitle,
swf: swf[1],
volume: '0',
filename: '',
});
seasonTitle = pageMetadata.partOfSeason.name;
}
done(null, vlos.getMedia(script, seasonTitle, seasonNumber));
}
else
{
done(null, {
episode: data[3],
id: epId,
series: data[1],
season: seasonTitle,
title: episodeTitle,
swf: swf[1],
volume: data[2] || '1',
filename: '',
});
/* Use the old way */
const swf = /^([^?]+)/.exec($('link[rel=video_src]').attr('href'));
const regexp = /\s*([^\n\r\t\f]+)\n?\s*[^0-9]*([0-9][\-0-9.]*)?,?\n?\s\s*[^0-9]*((PV )?[S0-9][P0-9.]*[a-fA-F]?)/;
const seasonTitle = $('span[itemprop="title"]').text();
const look = $('#showmedia_about_media').text();
const episodeTitle = $('#showmedia_about_name').text().replace(/[“”]/g, '');
const data = regexp.exec(look);
if (config.debug) {
log.dumpToDebug('episode page', $.html());
}
if (!swf || !data) {
log.warn('Somethig unexpected in the page at ' + address + ' (data are: ' + look + ')');
log.warn('Setting Season to 0 and episode to 0...');
if (config.debug) {
log.dumpToDebug('episode unexpected', look);
}
done(null, {
episode: '0',
id: epId,
series: seasonTitle,
season: seasonTitle,
title: episodeTitle,
swf: swf[1],
volume: '0',
filename: '',
media: null,
});
} else {
done(null, {
episode: data[3],
id: epId,
series: data[1],
season: seasonTitle,
title: episodeTitle,
swf: swf[1],
volume: data[2] || '1',
filename: '',
media: null,
});
}
}
});
}

View File

@@ -8,6 +8,7 @@ interface IConfig {
episodes?: string;
// Settings
crlang?: string;
sublang?: any;
format?: string;
output?: string;
series?: string;
@@ -23,6 +24,7 @@ interface IConfig {
debug?: boolean;
unlog?: boolean;
retry?: number;
sleepTime?: number;
// Login options
userAgent?: string;
logUsingApi?: boolean;
@@ -33,9 +35,6 @@ interface IConfig {
crLocale?: string;
crSessionKey?: string;
crLoginUrl?: string;
// Third method, injecting data from cookies
crUserId?: string;
crUserKey?: string;
// Generated values
crDeviceId?: string;
crSessionId?: string;

View File

@@ -7,4 +7,5 @@ interface IEpisodePage {
title: string;
swf: string;
filename: string;
media: IVlosScript;
}

14
src/interface/IVlosScript.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
interface IVlosScript
{
metadata: {
episode_number: any;
id: any;
title: any;
};
confic: any;
subtitles: any;
streams: any;
series: {
title: any;
};
}

View File

@@ -25,9 +25,9 @@ export function localeToCC(locale: string): string
const dubignore_regexp: { [id: string]: RegExp; } =
{
en: /\(.*Dub(?:bed)?.*\)|(?:\(RU\))/i,
fr: /\(.*Dub(?:bed)?.*\)|(?:\(RU\))|\(?Doublage.*\)?/,
de: /\(.*isch\)|\(Dubbed\)|\(RU\)/
en: /\(.*Dub(?:bed)?.*\)|(?:\(RU\))|\(Russian\)/i,
fr: /\(.*Dub(?:bed)?.*\)|(?:\(RU\))|\(?Doublage.*\)|\(Russian\)?/,
de: /\(.*isch\)|\(Dubbed\)|\(RU\)|\(Russian\)/
};
export function get_diregexp(config: IConfig): RegExp

View File

@@ -232,10 +232,8 @@ export function post(config: IConfig, url: string, form: any, done: (err: any, r
function authUsingCookies(config: IConfig, done: (err: any) => void)
{
j.setCookie(request.cookie('c_userid=' + config.crUserId + '; Domain=crunchyroll.com; HttpOnly; hostOnly=false;'),
CR_COOKIE_DOMAIN);
j.setCookie(request.cookie('c_userkey=' + config.crUserKey + '; Domain=crunchyroll.com; HttpOnly; hostOnly=false;'),
CR_COOKIE_DOMAIN);
j.setCookie(request.cookie('session_id=' + config.crSessionId + '; Domain=crunchyroll.com; HttpOnly; hostOnly=false;'),
CR_COOKIE_DOMAIN);
checkIfUserIsAuth(config, (errCheckAuth2) =>
{
@@ -392,6 +390,9 @@ function getOptions(config: IConfig, form: any)
currentOptions = {};
currentOptions.headers = {};
currentOptions.headers['Cache-Control'] = 'private';
currentOptions.headers.Accept = 'application/xml,application/xhtml+xml,text/html;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5';
if (config.userAgent)
{
currentOptions.headers['User-Agent'] = config.userAgent;
@@ -408,7 +409,6 @@ function getOptions(config: IConfig, form: any)
currentOptions.decodeEmails = true;
currentOptions.jar = j;
optionsSet = true;
}

View File

@@ -106,7 +106,8 @@ export default function(config: IConfig, task: IConfigTask, done: (err: any) =>
'" - Retry ' + page.episodes[i].retry + ' / ' + config.retry);
page.episodes[i].retry -= 1;
}
next();
setTimeout(next, config.sleepTime);
return;
}
else
{
@@ -121,13 +122,15 @@ export default function(config: IConfig, task: IConfigTask, done: (err: any) =>
}
i += 1;
next();
setTimeout(next, config.sleepTime);
return;
});
}
else
{
i += 1;
next();
setTimeout(next, config.sleepTime);
return;
}
}
});

View File

@@ -9,21 +9,14 @@ import subtitle from '../subtitle/index';
/**
* Merges the subtitle and video files into a Matroska Multimedia Container.
*/
export default function(config: IConfig, isSubtitled: boolean, rtmpInputPath: string, filePath: string,
streamMode: string, verbose: boolean, done: (err: Error) => void)
export default function(config: IConfig, isSubtitled: boolean, videoFileExtention: string, filePath: string,
verbose: boolean, done: (err: Error) => void)
{
const subtitlePath = filePath + '.' + (subtitle.formats[config.format] ? config.format : 'ass');
let videoPath = filePath;
let cp;
if (streamMode === 'RTMP')
{
videoPath += path.extname(rtmpInputPath);
}
else
{
videoPath += '.mp4';
}
videoPath += videoFileExtention;
cp = childProcess.exec(command() + ' ' +
'-o "' + filePath + '.mkv" ' +

70
src/vlos.ts Normal file
View File

@@ -0,0 +1,70 @@
'use strict';
export default {getMedia};
function getMedia(vlosScript: string, seasonTitle: string, seasonNumber: string): IEpisodePage
{
let vlosMedia: IVlosScript;
function f(script: string) {
/* We need to scope things */
/* This is what will give us the medias */
function VilosPlayer() {
this.load = function(a: string, b: any, c: any)
{
vlosMedia = this.config.media;
vlosMedia.series = this.config.analytics.media_reporting_parent;
};
this.config = {};
this.config.player = {};
this.config.player.pause_screen = {};
this.config.language = '';
}
/* Let's stub what the script need */
const window = {
WM: {
UserConsent: {
getUserConsentAdvertisingState(): string { return ''; }
}
}
};
const document = {
getElementsByClassName(a: any): any { return {length: 0}; },
};
const localStorage = {
getItem(a: any): any { return null; },
};
const $ = {
cookie(a: any) { /* nothing */ },
};
/*
Evil ugly things. Need to run the script from a somewhat untrusted source.
Need to find a better way of doing.
*/
// tslint:disable-next-line:no-eval
eval(script);
}
f(vlosScript);
if (vlosMedia === undefined)
{
console.error('Error fetching vlos data - aborting - Please report the error if happen again.');
process.exit(-1);
}
return {
episode: vlosMedia.metadata.episode_number,
id: vlosMedia.metadata.id,
series: vlosMedia.series.title,
season: seasonTitle,
title: vlosMedia.metadata.title,
swf: '',
volume: seasonNumber,
filename: '',
media: vlosMedia,
};
}