19 Commits

Author SHA1 Message Date
Godzil
cdf7f223db 1.3.1 2018-07-30 22:47:33 +01:00
Godzil
bb70161652 Add a really simple and stupid way to check if you are running the latest version. 2018-07-30 22:47:33 +01:00
Godzil
7f2f983f55 using log instead of console is nicer 2018-07-30 22:47:33 +01:00
Godzil
8dab83b3ef Add log when not adding an episode when rebuilding the .crpresistent. 2018-07-30 22:47:33 +01:00
Godzil
cbafa5bc90 Now that retrying is there; force ffmpeg to fail in case or error (should now avoid to silently download a corrupted video file) 2018-07-30 22:47:33 +01:00
Godzil
67735fb52a Add a similar mechanism for episodes downloads.
If it can't fetch it after a couple of retry (5 by default, can be changed on the command line) it will just ignore it and go to the next episode.
2018-07-30 22:47:33 +01:00
Godzil
7d6f762f59 Add retry mechanism in case of episodes list retrieve failure instead of just failing.
If it can't after a couple of retry (5 by default, can be changed on the command line) it will just ignore it and go to the next anime.
2018-07-30 22:47:33 +01:00
Godzil
f3a0d0129d Correct a bug with ffmpeg where it will wait forever for the user to answer a question when trying to overwrite a file.
Fix #68
2018-07-30 22:47:33 +01:00
Godzil
65c9032839 Add an option to make ffmpeg, mkvmerge and rtmpdump running verbosely. 2018-07-30 22:47:33 +01:00
Godzil
978a3282a4 1.3.0 2018-07-30 22:47:33 +01:00
Manoël Trapier
9f0195bebc Update README.md 2018-07-30 22:47:33 +01:00
Manoël Trapier
ea20108222 Update README.md 2018-07-30 22:47:33 +01:00
Godzil
4ee814864c Add support for changing the batchfile on the command line 2018-07-30 22:47:33 +01:00
Godzil
4cbfd691c3 add missing package 2018-07-30 22:47:32 +01:00
Godzil
7c04fb7282 Make tslint happy! 2018-07-30 22:47:29 +01:00
Godzil
849c7612aa A bit of code reformating and add an option to regenerate the .crpersistant file in case it become corrupted and Crunchy try to redownload everything. 2018-07-30 22:46:29 +01:00
Roei Elisha
6ad4cbed0a make login work 2018-07-30 22:45:28 +01:00
Godzil
9e2f5401d0 Update tslint.json 2018-07-30 22:44:18 +01:00
Roei Elisha
b064b97f2d fix linter problems 2018-05-22 23:55:10 +03:00
17 changed files with 467 additions and 313 deletions

View File

@@ -21,30 +21,39 @@ It is recommended to enable authentication (`-p` and `-u`) so your account permi
## Prerequisites ## Prerequisites
* NodeJS >= 5.x (http://nodejs.org/) * NodeJS >= 8.1 (http://nodejs.org/)
* NPM >= 2.5.x (https://www.npmjs.org/) * NPM >= 5.8 (https://www.npmjs.org/)
## Installation ## Installation
Use the applicable instructions to install. Is your operating system not listed? Please ask or contribute! Use the applicable instructions to install. Is your operating system not listed? Please ask or contribute!
### Debian (Mint, Ubuntu, etc) ### Linux (Debian, Mint, Ubuntu, etc)
1. Run in *Terminal*: `sudo apt-get install nodejs npm mkvtoolnix rtmpdump ffmpeg` 1. Run in *Terminal*: `sudo apt-get install nodejs npm mkvtoolnix rtmpdump ffmpeg`
2. Run in *Terminal*: `sudo ln -s /usr/bin/nodejs /usr/bin/node` 2. Run in *Terminal*: `sudo ln -s /usr/bin/nodejs /usr/bin/node`
3. Run in *Terminal*: `sudo npm install -g crunchy` 3. Run in *Terminal*: `sudo npm install -g crunchy`
#### Updating:
1. Run in *Terminal*: `sudo npm update -g crunchy`
### Mac OS X ### Mac OS X
1. Install *Homebrew* following the instructions at http://brew.sh/ 1. Install *Homebrew* following the instructions at http://brew.sh/
2. Run in *Terminal*: `brew install node mkvtoolnix rtmpdump ffmpeg` 2. Run in *Terminal*: `brew install node mkvtoolnix rtmpdump ffmpeg`
3. Run in *Terminal*: `npm install -g crunchy` 3. Run in *Terminal*: `npm install -g crunchy`
#### Updating:
1. Run in *Terminal*: `sudo npm update -g crunchy`
### Windows ### Windows
1. Install *NodeJS* following the instructions at http://nodejs.org/ 1. Install *NodeJS* following the instructions at http://nodejs.org/
3. Run in *Command Prompt*: `npm install -g crunchy` 3. Run in *Command Prompt*: `npm install -g crunchy`
#### Updating:
1. Run in *Command Prompt*: `npm update -g crunchy`
## Instructions ## Instructions
Use the applicable instructions for the interface of your choice (currently limited to command-line). Use the applicable instructions for the interface of your choice (currently limited to command-line).

215
package-lock.json generated
View File

@@ -1,9 +1,15 @@
{ {
"name": "crunchy", "name": "crunchy",
"version": "1.2.2", "version": "1.3.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@types/bluebird": {
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.20.tgz",
"integrity": "sha512-Wk41MVdF+cHBfVXj/ufUHJeO3BlIQr1McbHZANErMykaCWeDSZbH5erGjNBw2/3UlRdSxZbLfSuQTzFmPOYFsA==",
"dev": true
},
"@types/caseless": { "@types/caseless": {
"version": "0.12.1", "version": "0.12.1",
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz",
@@ -25,6 +31,15 @@
"@types/node": "8.5.2" "@types/node": "8.5.2"
} }
}, },
"@types/fs-extra": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.2.tgz",
"integrity": "sha512-Q3FWsbdmkQd1ib11A4XNWQvRD//5KpPoGawA8aB2DR7pWKoW9XQv3+dGxD/Z1eVFze23Okdo27ZQytVFlweKvQ==",
"dev": true,
"requires": {
"@types/node": "8.5.2"
}
},
"@types/mkdirp": { "@types/mkdirp": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-0.5.2.tgz", "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-0.5.2.tgz",
@@ -52,6 +67,16 @@
"@types/tough-cookie": "2.3.2" "@types/tough-cookie": "2.3.2"
} }
}, },
"@types/request-promise": {
"version": "4.1.41",
"resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.41.tgz",
"integrity": "sha512-qlx6COxSTdSFHY9oX9v2zL1I05hgz5lwqYiXa2SFL2nDxAiG5KK8rnllLBH7k6OqzS3Ck0bWbxlGVdrZhS6oNw==",
"dev": true,
"requires": {
"@types/bluebird": "3.5.20",
"@types/request": "2.47.0"
}
},
"@types/tough-cookie": { "@types/tough-cookie": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz",
@@ -61,7 +86,7 @@
"@types/xml2js": { "@types/xml2js": {
"version": "0.4.2", "version": "0.4.2",
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.2.tgz", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.2.tgz",
"integrity": "sha512-8aKUBSj3oGcnuiBmDLm3BIk09RYg01mz9HlQ2u4aS17oJ25DxjQrEUVGFSBVNOfM45pQW4OjcBPplq6r/exJdA==", "integrity": "sha1-pLhLOHn/1HEJU/2Syr/emopOhFY=",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "8.5.2" "@types/node": "8.5.2"
@@ -128,9 +153,9 @@
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
}, },
"aws4": { "aws4": {
"version": "1.6.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w=="
}, },
"babel-code-frame": { "babel-code-frame": {
"version": "6.26.0", "version": "6.26.0",
@@ -174,23 +199,20 @@
} }
}, },
"big-integer": { "big-integer": {
"version": "1.6.27", "version": "1.6.30",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.27.tgz", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.30.tgz",
"integrity": "sha512-NzUKMYW4SWme+H5K+mfEmBxEF/V04PhlzoxxXwSnDig78y2t7HLBVotfDBMUhRPRA3WWID3GmJB/OJSWPhVXtg==" "integrity": "sha512-LGDF7k/8yjS+GTbfFRGiSdcPnIwcjM6kQ0lmbja3tKJzVMmqHmUFnTuUOm/Lt2KVQ3mAZVupf9KNcsew0QV8Kw=="
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
}, },
"boolbase": { "boolbase": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
}, },
"boom": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
"requires": {
"hoek": "4.2.1"
}
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.8", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
@@ -213,14 +235,14 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
}, },
"chalk": { "chalk": {
"version": "2.3.2", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "3.2.1", "ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5", "escape-string-regexp": "1.0.5",
"supports-color": "5.3.0" "supports-color": "5.4.0"
}, },
"dependencies": { "dependencies": {
"ansi-styles": { "ansi-styles": {
@@ -233,9 +255,9 @@
} }
}, },
"supports-color": { "supports-color": {
"version": "5.3.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dev": true, "dev": true,
"requires": { "requires": {
"has-flag": "3.0.0" "has-flag": "3.0.0"
@@ -271,7 +293,7 @@
"resolved": "https://registry.npmjs.org/cloudscraper/-/cloudscraper-1.5.0.tgz", "resolved": "https://registry.npmjs.org/cloudscraper/-/cloudscraper-1.5.0.tgz",
"integrity": "sha512-bZagLhj59+N6Z6lD9zRksYu87GthLwXdKARULi4RZ6UVpotH39ruSFN3UQmw3uuqoj00iDxkGrapAvxeurmlQA==", "integrity": "sha512-bZagLhj59+N6Z6lD9zRksYu87GthLwXdKARULi4RZ6UVpotH39ruSFN3UQmw3uuqoj00iDxkGrapAvxeurmlQA==",
"requires": { "requires": {
"request": "2.85.0" "request": "2.87.0"
} }
}, },
"co": { "co": {
@@ -324,24 +346,6 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"cryptiles": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
"requires": {
"boom": "5.2.0"
},
"dependencies": {
"boom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
"requires": {
"hoek": "4.2.1"
}
}
}
},
"css-select": { "css-select": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
@@ -513,9 +517,9 @@
} }
}, },
"fs-extra": { "fs-extra": {
"version": "5.0.0", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz",
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==",
"requires": { "requires": {
"graceful-fs": "4.1.11", "graceful-fs": "4.1.11",
"jsonfile": "4.0.0", "jsonfile": "4.0.0",
@@ -584,22 +588,6 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true "dev": true
}, },
"hawk": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
"requires": {
"boom": "4.3.1",
"cryptiles": "3.1.2",
"hoek": "4.2.1",
"sntp": "2.1.0"
}
},
"hoek": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
},
"htmlparser2": { "htmlparser2": {
"version": "3.9.2", "version": "3.9.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
@@ -660,9 +648,9 @@
"dev": true "dev": true
}, },
"js-yaml": { "js-yaml": {
"version": "3.11.0", "version": "3.12.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
"integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
"dev": true, "dev": true,
"requires": { "requires": {
"argparse": "1.0.10", "argparse": "1.0.10",
@@ -709,6 +697,11 @@
"verror": "1.10.0" "verror": "1.10.0"
} }
}, },
"lodash": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"lodash.assignin": { "lodash.assignin": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
@@ -861,6 +854,11 @@
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
}, },
"pjson": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/pjson/-/pjson-1.0.9.tgz",
"integrity": "sha512-4hRJH3YzkUpOlShRzhyxAmThSNnAaIlWZCAb27hd0pVUAXNUAHAO7XZbsPPvsCYwBFEScTmCCL6DGE8NyZ8BdQ=="
},
"process-nextick-args": { "process-nextick-args": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
@@ -872,9 +870,9 @@
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
}, },
"qs": { "qs": {
"version": "6.5.1", "version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.3", "version": "2.3.3",
@@ -891,19 +889,18 @@
} }
}, },
"request": { "request": {
"version": "2.85.0", "version": "2.87.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
"integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
"requires": { "requires": {
"aws-sign2": "0.7.0", "aws-sign2": "0.7.0",
"aws4": "1.6.0", "aws4": "1.7.0",
"caseless": "0.12.0", "caseless": "0.12.0",
"combined-stream": "1.0.6", "combined-stream": "1.0.6",
"extend": "3.0.1", "extend": "3.0.1",
"forever-agent": "0.6.1", "forever-agent": "0.6.1",
"form-data": "2.3.2", "form-data": "2.3.2",
"har-validator": "5.0.3", "har-validator": "5.0.3",
"hawk": "6.0.2",
"http-signature": "1.2.0", "http-signature": "1.2.0",
"is-typedarray": "1.0.0", "is-typedarray": "1.0.0",
"isstream": "0.1.2", "isstream": "0.1.2",
@@ -911,14 +908,32 @@
"mime-types": "2.1.18", "mime-types": "2.1.18",
"oauth-sign": "0.8.2", "oauth-sign": "0.8.2",
"performance-now": "2.1.0", "performance-now": "2.1.0",
"qs": "6.5.1", "qs": "6.5.2",
"safe-buffer": "5.1.1", "safe-buffer": "5.1.1",
"stringstream": "0.0.5",
"tough-cookie": "2.3.4", "tough-cookie": "2.3.4",
"tunnel-agent": "0.6.0", "tunnel-agent": "0.6.0",
"uuid": "3.2.1" "uuid": "3.2.1"
} }
}, },
"request-promise": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",
"integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=",
"requires": {
"bluebird": "3.5.1",
"request-promise-core": "1.1.1",
"stealthy-require": "1.1.1",
"tough-cookie": "2.3.4"
}
},
"request-promise-core": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
"requires": {
"lodash": "4.17.10"
}
},
"resolve": { "resolve": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
@@ -944,14 +959,6 @@
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true "dev": true
}, },
"sntp": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
"integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
"requires": {
"hoek": "4.2.1"
}
},
"sprintf-js": { "sprintf-js": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz",
@@ -979,6 +986,11 @@
"integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=", "integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=",
"dev": true "dev": true
}, },
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
"string_decoder": { "string_decoder": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
@@ -987,11 +999,6 @@
"safe-buffer": "5.1.1" "safe-buffer": "5.1.1"
} }
}, },
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
},
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -1046,29 +1053,29 @@
} }
}, },
"tslib": { "tslib": {
"version": "1.9.0", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz",
"integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw==",
"dev": true "dev": true
}, },
"tslint": { "tslint": {
"version": "5.9.1", "version": "5.10.0",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz",
"integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=",
"dev": true, "dev": true,
"requires": { "requires": {
"babel-code-frame": "6.26.0", "babel-code-frame": "6.26.0",
"builtin-modules": "1.1.1", "builtin-modules": "1.1.1",
"chalk": "2.3.2", "chalk": "2.4.1",
"commander": "2.15.1", "commander": "2.15.1",
"diff": "3.5.0", "diff": "3.5.0",
"glob": "7.1.2", "glob": "7.1.2",
"js-yaml": "3.11.0", "js-yaml": "3.12.0",
"minimatch": "3.0.4", "minimatch": "3.0.4",
"resolve": "1.4.0", "resolve": "1.4.0",
"semver": "5.5.0", "semver": "5.5.0",
"tslib": "1.9.0", "tslib": "1.9.2",
"tsutils": "2.25.0" "tsutils": "2.27.1"
}, },
"dependencies": { "dependencies": {
"diff": { "diff": {
@@ -1080,12 +1087,12 @@
} }
}, },
"tsutils": { "tsutils": {
"version": "2.25.0", "version": "2.27.1",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.25.0.tgz", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz",
"integrity": "sha512-SPgUlOAUAe6fCyPi0QR4U0jRuDsHHKvzIR6/hHd0YR0bb8MzeLJgCagkPSmZeJjWImnpJ0xq6XHa9goTvMBBCQ==", "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==",
"dev": true, "dev": true,
"requires": { "requires": {
"tslib": "1.9.0" "tslib": "1.9.2"
} }
}, },
"tunnel-agent": { "tunnel-agent": {
@@ -1103,9 +1110,9 @@
"optional": true "optional": true
}, },
"typescript": { "typescript": {
"version": "2.8.1", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz",
"integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg==", "integrity": "sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA==",
"dev": true "dev": true
}, },
"underscore.string": { "underscore.string": {

View File

@@ -15,34 +15,41 @@
"engines": { "engines": {
"node": ">=5.0" "node": ">=5.0"
}, },
"version": "1.2.2", "version": "1.3.1",
"bin": { "bin": {
"crunchy": "./bin/crunchy", "crunchy": "./bin/crunchy",
"crunchy.sh": "./bin/crunchy.sh" "crunchy.sh": "./bin/crunchy.sh"
}, },
"dependencies": { "dependencies": {
"big-integer": "^1.6.27", "big-integer": "^1.6.30",
"bluebird": "^3.5.1",
"cheerio": "^0.22.0", "cheerio": "^0.22.0",
"cloudscraper": "^1.5.0", "cloudscraper": "^1.5.0",
"commander": "^2.15.1", "commander": "^2.15.1",
"fs-extra": "^5.0.0", "fs-extra": "^6.0.1",
"mkdirp": "^0.5.0", "mkdirp": "^0.5.0",
"request": "^2.85.0", "pjson": "^1.0.9",
"request": "^2.87.0",
"request-promise": "^4.2.2",
"xml2js": "^0.4.5" "xml2js": "^0.4.5"
}, },
"devDependencies": { "devDependencies": {
"@types/bluebird": "^3.5.20",
"@types/cheerio": "^0.22.7", "@types/cheerio": "^0.22.7",
"@types/fs-extra": "^5.0.2",
"@types/mkdirp": "^0.5.2", "@types/mkdirp": "^0.5.2",
"@types/request": "^2.47.0", "@types/request": "^2.47.0",
"@types/request-promise": "^4.1.41",
"@types/xml2js": "^0.4.2", "@types/xml2js": "^0.4.2",
"tsconfig-lint": "^0.12.0", "tsconfig-lint": "^0.12.0",
"tslint": "^5.9.1", "tslint": "^5.10.0",
"typescript": "^2.8.1" "typescript": "^2.9.1"
}, },
"scripts": { "scripts": {
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"compile": "tsc",
"build": "tsc", "build": "tsc",
"test": "tslint -c ./tslint.json --project ./tsconfig.json ./src/**/*.ts", "test": "tslint --project .",
"start": "node ./bin/crunchy" "start": "node ./bin/crunchy"
}, },
"bugs": { "bugs": {

View File

@@ -2,15 +2,16 @@
import commander = require('commander'); import commander = require('commander');
import fs = require('fs'); import fs = require('fs');
import path = require('path'); import path = require('path');
import series from './series';
import log = require('./log'); import log = require('./log');
import series from './series';
/* correspondances between resolution and value CR excpect */ /* correspondances between resolution and value CR excpect */
let resol_table: { [id: string]: IResolData; } = { const resol_table: { [id: string]: IResolData; } =
'360': {quality:'60', format:'106'}, {
'480': {quality:'61', format:'106'}, 360: {quality: '60', format: '106'},
'720': {quality:'62', format:'106'}, 480: {quality: '61', format: '106'},
'1080': {quality:'80', format:'108'}, 720: {quality: '62', format: '106'},
1080: {quality: '80', format: '108'},
}; };
/** /**
@@ -19,53 +20,69 @@ let resol_table: { [id: string]: IResolData; } = {
export default function(args: string[], done: (err?: Error) => void) export default function(args: string[], done: (err?: Error) => void)
{ {
const config = parse(args); const config = parse(args);
const batchPath = path.join(config.output || process.cwd(), 'CrunchyRoll.txt'); const batchPath = path.join(config.output || process.cwd(), config.batch);
// set resolution // set resolution
if (config.resolution) if (config.resolution)
{ {
try try
{ {
config.video_format = resol_table[config.resolution]['format']; config.video_format = resol_table[config.resolution].format;
config.video_quality = resol_table[config.resolution]['quality']; config.video_quality = resol_table[config.resolution].quality;
} }
catch(e) catch (e)
{ {
log.warn("Invalid resolution " + config.resolution + "p. Setting to 1080p") log.warn('Invalid resolution ' + config.resolution + 'p. Setting to 1080p');
config.video_format = resol_table['1080']['format']; config.video_format = resol_table['1080'].format;
config.video_quality = resol_table['1080']['quality']; config.video_quality = resol_table['1080'].quality;
} }
} }
else else
{ {
/* 1080 by default */ /* 1080 by default */
config.video_format = resol_table['1080']['format']; config.video_format = resol_table['1080'].format;
config.video_quality = resol_table['1080']['quality']; config.video_quality = resol_table['1080'].quality;
} }
tasks(config, batchPath, (err, tasks) => tasks(config, batchPath, (err, tasksArr) =>
{ {
if (err) if (err)
{ {
return done(err); return done(err);
} }
let i = 0; let i = 0;
(function next() (function next()
{ {
if (i >= tasks.length) if (i >= tasksArr.length)
{ {
return done(); return done();
} }
series(tasks[i].config, tasks[i].address, (errin) => series(tasksArr[i].config, tasksArr[i].address, (errin) =>
{ {
if (errin) if (errin)
{ {
return done(errin); if (tasksArr[i].retry <= 0)
{
console.error(err.stack || err);
log.error('Cannot get episodes from "' + tasksArr[i].address + '", please rerun later');
}
else
{
if (config.verbose)
{
console.error(err.stack || err);
}
log.warn('Retrying to fetch episodes ' + tasksArr[i].retry + ' / ' + config.retry);
tasksArr[i].retry -= 1;
}
}
else
{
i += 1;
} }
i += 1;
next(); next();
}); });
})(); })();
@@ -117,7 +134,7 @@ function tasks(config: IConfigLine, batchPath: string, done: (err: Error, tasks?
return done(null, config.args.map((addressIn) => return done(null, config.args.map((addressIn) =>
{ {
return {address: addressIn, config: configIn}; return {address: addressIn, config: configIn, retry: config.retry};
})); }));
} }
@@ -153,7 +170,7 @@ function tasks(config: IConfigLine, batchPath: string, done: (err: Error, tasks?
return; return;
} }
map.push({address: addressIn, config: lineConfig}); map.push({address: addressIn, config: lineConfig, retry: config.retry});
}); });
}); });
done(null, map); done(null, map);
@@ -181,7 +198,12 @@ function parse(args: string[]): IConfigLine
.option('-o, --output <s>', 'The output path.') .option('-o, --output <s>', 'The output path.')
.option('-s, --series <s>', 'The series override.') .option('-s, --series <s>', 'The series override.')
.option('-n, --filename <s>', 'The name override.') .option('-n, --filename <s>', 'The name override.')
.option('-t, --tag <s>', 'The subgroup. (Default: CrunchyRoll)') .option('-t, --tag <s>', 'The subgroup. (Default: CrunchyRoll)', 'CrunchyRoll')
.option('-r, --resolution <s>', 'The video resolution. (Default: 1080 (360, 480, 720, 1080))') .option('-r, --resolution <s>', 'The video resolution. (Default: 1080 (360, 480, 720, 1080))',
'1080')
.option('-g, --rebuildcrp', 'Rebuild the crpersistant file.')
.option('-b, --batch <s>', 'Batch file', 'CrunchyRoll.txt')
.option('--verbose', 'Make tool verbose')
.option('--retry <i>', 'Number or time to retry fetching an episode. Default: 5', 5)
.parse(args); .parse(args);
} }

View File

@@ -1,13 +1,34 @@
'use strict'; 'use strict';
import batch from './batch'; import batch from './batch';
import request = require('request');
import log = require('./log');
import pjson = require('pjson');
const current_version = pjson.version;
/* Check if the current version is the latest */
log.info('Crunchy version ' + current_version);
request.get({ uri: 'https://raw.githubusercontent.com/Godzil/Crunchy/master/package.json' },
(error: Error, response: any, body: any) =>
{
const onlinepkg = JSON.parse(body);
let tmp = current_version.split('.');
const cur = (Number(tmp[0]) * 10000) + (Number(tmp[1]) * 100) + Number(tmp[2]);
tmp = onlinepkg.version.split('.');
const dist = (Number(tmp[0]) * 10000) + (Number(tmp[1]) * 100) + Number(tmp[2]);
if (dist > cur)
{
log.warn('There is a newer version of crunchy (v' + onlinepkg.version + '), you should update!');
}
});
batch(process.argv, (err: any) => batch(process.argv, (err: any) =>
{ {
if (err) if (err)
{ {
console.error(err.stack || err); console.error(err.stack || err);
process.exit(-1) process.exit(-1);
} }
console.info("Done!") console.info('Done!');
process.exit(0) process.exit(0);
}); });

View File

@@ -57,7 +57,8 @@ function fileExist(path: string)
{ {
fs.statSync(path); fs.statSync(path);
return true; return true;
} catch (e) }
catch (e)
{ {
return false; return false;
} }
@@ -82,6 +83,13 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
if (fileExist(filePath + '.mkv')) if (fileExist(filePath + '.mkv'))
{ {
let count = 0; let count = 0;
if (config.rebuildcrp)
{
log.warn('Adding \'' + fileName + '\' to the DB...');
return done(null, false);
}
log.warn('File \'' + fileName + '\' already exist...'); log.warn('File \'' + fileName + '\' already exist...');
do do
@@ -94,6 +102,12 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
log.warn('Renaming to \'' + fileName + '\'...'); log.warn('Renaming to \'' + fileName + '\'...');
} }
if (config.rebuildcrp)
{
log.warn('Ignoring \'' + fileName + '\' as it does not exist...');
return done(null, true);
}
mkdirp(path.dirname(filePath), (errM: Error) => mkdirp(path.dirname(filePath), (errM: Error) =>
{ {
if (errM) if (errM)
@@ -128,7 +142,7 @@ function download(config: IConfig, page: IEpisodePage, player: IEpisodePlayer, d
const isSubtited = Boolean(player.subtitle); const isSubtited = Boolean(player.subtitle);
log.dispEpisode(fileName, 'Merging...', false); log.dispEpisode(fileName, 'Merging...', false);
video.merge(config, isSubtited, player.video.file, filePath, player.video.mode, (errVM) => video.merge(config, isSubtited, player.video.file, filePath, player.video.mode, config.verbose, (errVM) =>
{ {
if (errVM) if (errVM)
{ {
@@ -185,11 +199,11 @@ function downloadSubtitle(config: IConfig, player: IEpisodePlayer, filePath: str
/** /**
* Streams the video to disk. * Streams the video to disk.
*/ */
function downloadVideo(ignored/*config*/: IConfig, page: IEpisodePage, player: IEpisodePlayer, function downloadVideo(config: IConfig, page: IEpisodePage, player: IEpisodePlayer,
filePath: string, done: (err: Error) => void) filePath: string, done: (err: Error) => void)
{ {
video.stream(player.video.host, player.video.file, page.swf, filePath, video.stream(player.video.host, player.video.file, page.swf, filePath,
path.extname(player.video.file), player.video.mode, done); path.extname(player.video.file), player.video.mode, config.verbose, done);
} }
/** /**
@@ -258,7 +272,7 @@ function scrapePage(config: IConfig, address: string, done: (err: Error, page?:
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 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 look = $('#showmedia_about_media').text();
const seasonTitle = $('span[itemprop="title"]').text(); const seasonTitle = $('span[itemprop="title"]').text();
let episodeTitle = $('#showmedia_about_name').text().replace(/[“”]/g, ''); const episodeTitle = $('#showmedia_about_name').text().replace(/[“”]/g, '');
const data = regexp.exec(look); const data = regexp.exec(look);
if (!swf || !data) if (!swf || !data)

View File

@@ -17,4 +17,8 @@ interface IConfig {
resolution?: string; resolution?: string;
video_format?: string; video_format?: string;
video_quality?: string; video_quality?: string;
rebuildcrp?: boolean;
batch?: string;
verbose?: boolean;
retry?: number;
} }

View File

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

View File

@@ -2,4 +2,5 @@ interface ISeriesEpisode {
address: string; address: string;
episode: string; episode: string;
volume: number; volume: number;
retry: number;
} }

View File

@@ -1,7 +1,13 @@
'use strict'; 'use strict';
import request = require('request');
import cheerio = require('cheerio'); import cheerio = require('cheerio');
import request = require('request');
import rp = require('request-promise');
import Promise = require('bluebird');
import log = require('./log'); import log = require('./log');
import { RequestPromise } from 'request-promise';
import { Response } from 'request';
// tslint:disable-next-line:no-var-requires
const cloudscraper = require('cloudscraper'); const cloudscraper = require('cloudscraper');
let isAuthenticated = false; let isAuthenticated = false;
@@ -10,30 +16,84 @@ let isPremium = false;
const defaultHeaders: request.Headers = const defaultHeaders: request.Headers =
{ {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; x64; rv:58.0) Gecko/20100101 Firefox/58.0', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; x64; rv:58.0) Gecko/20100101 Firefox/58.0',
// Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
'Connection': 'keep-alive', 'Connection': 'keep-alive',
'Referer': 'https://www.crunchyroll.com/login' 'Referer': 'https://www.crunchyroll.com/login',
}; };
function generateDeviceId(): string
{
let id = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++)
{
id += possible.charAt(Math.floor(Math.random() * possible.length));
}
return id;
}
function startSession(): Promise<string>
{
return rp(
{
method: 'GET',
url: 'CR_SESSION_URL',
qs:
{
device_id: generateDeviceId(),
device_type: 'CR_DEVICE_TYPE',
access_token: 'CR_SESSION_KEY',
version: 'CR_API_VERSION',
locale: 'CR_LOCALE',
},
json: true,
})
.then((response: any) =>
{
return response.data.session_id;
});
}
function login(sessionId: string, user: string, pass: string): Promise<any>
{
return rp(
{
method: 'POST',
url: 'CR_LOGIN_URL',
form:
{
account: user,
password: pass,
session_id: sessionId,
version: 'CR_API_VERSION',
},
json: true,
})
.then((response) =>
{
if (response.error) throw new Error('Login failed: ' + response.message);
return response.data;
});
}
// TODO: logout
/** /**
* Performs a GET request for the resource. * Performs a GET request for the resource.
*/ */
export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void) export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void)
{ {
authenticate(config, err => authenticate(config, (err) =>
{ {
if (err) if (err)
{ {
return done(err); return done(err);
} }
cloudscraper.request(modify(options, 'GET'), (err: Error, response: any, body: any) => cloudscraper.request(modify(options, 'GET'), (error: Error, response: any, body: any) =>
{ {
if (err) if (error) return done(error);
{
return done(err);
}
done(null, typeof body === 'string' ? body : String(body)); done(null, typeof body === 'string' ? body : String(body));
}); });
}); });
@@ -44,20 +104,19 @@ export function get(config: IConfig, options: string|request.Options, done: (err
*/ */
export function post(config: 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 => authenticate(config, (err) =>
{ {
if (err) if (err)
{ {
return done(err); return done(err);
} }
cloudscraper.request(modify(options, 'POST'), (err: Error, response: any, body: any) => cloudscraper.request(modify(options, 'POST'), (error: Error, response: any, body: any) =>
{ {
if (err) if (error)
{ {
return done(err); return done(error);
} }
done(null, typeof body === 'string' ? body : String(body)); done(null, typeof body === 'string' ? body : String(body));
}); });
}); });
@@ -73,107 +132,70 @@ function authenticate(config: IConfig, done: (err: Error) => void)
return done(null); return done(null);
} }
/* Bypass the login page and send a login request directly */ startSession()
let options = .then((sessionId: string) =>
{ {
headers: defaultHeaders, defaultHeaders.Cookie = `sess_id=${sessionId}; c_locale=enUS`;
jar: true, return login(sessionId, config.user, config.pass);
gzip: false, })
method: 'GET', .then((userData) =>
url: 'https://www.crunchyroll.com/login'
};
cloudscraper.request(options, (err: Error, rep: string, body: string) =>
{ {
if (err) return done(err); /**
* The page return with a meta based redirection, as we wan't to check that everything is fine, reload
const $ = cheerio.load(body); * the main page. A bit convoluted, but more sure.
*/
/* Get the token from the login page */ const options =
const token = $('input[name="login_form[_token]"]').attr('value');
if (token === '')
{
return done(new Error('Can`t find token!'));
}
let options =
{ {
headers: defaultHeaders, headers: defaultHeaders,
form:
{
'login_form[name]': config.user,
'login_form[password]': config.pass,
'login_form[redirect_url]': '/',
'login_form[_token]': token
},
jar: true, jar: true,
gzip: false, url: 'http://www.crunchyroll.com/',
method: 'POST', method: 'GET',
url: 'https://www.crunchyroll.com/login'
}; };
cloudscraper.request(options, (err: Error, rep: string, body: string) => cloudscraper.request(options, (err: Error, rep: string, body: string) =>
{ {
if (err) if (err)
{ {
return done(err); return done(err);
} }
/* The page return with a meta based redirection, as we wan't to check that everything is fine, reload const $ = cheerio.load(body);
* the main page. A bit convoluted, but more sure.
*/ /* Check if auth worked */
let options = const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g;
const dims = regexps.exec($('script').text());
for (let i = 1; i < 5; i++)
{ {
headers: defaultHeaders, if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered'))
jar: true, {
url: 'http://www.crunchyroll.com/', isAuthenticated = true;
method: 'GET' }
};
cloudscraper.request(options, (err: Error, rep: string, body: string) => if ((dims[i] === 'premium') || (dims[i] === 'premiumplus'))
{
isPremium = true;
}
}
if (isAuthenticated === false)
{ {
if (err) const error = $('ul.message, li.error').text();
{ return done(new Error('Authentication failed: ' + error));
return done(err); }
}
let $ = cheerio.load(body); if (isPremium === false)
{
/* Check if auth worked */ log.warn('Do not use this app without a premium account.');
const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g; }
const dims = regexps.exec($('script').text()); else
{
for (let i = 1; i < 5; i++) log.info('You have a premium account! Good!');
{ }
if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered')) done(null);
{
isAuthenticated = true;
}
if ((dims[i] === 'premium') || (dims[i] === 'premiumplus'))
{
isPremium = true;
}
}
if (isAuthenticated === false)
{
const error = $('ul.message, li.error').text();
return done(new Error('Authentication failed: ' + error));
}
if (isPremium === false)
{
log.warn('Do not use this app without a premium account.');
}
else
{
log.info('You have a premium account! Good!');
}
done(null);
});
}); });
}); })
.catch(done);
} }
/** /**
@@ -188,5 +210,10 @@ function modify(options: string|request.Options, reqMethod: string): request.Opt
options.method = reqMethod; options.method = reqMethod;
return options; return options;
} }
return { jar: true, headers: defaultHeaders, url: options.toString(), method: reqMethod }; return {
} jar: true,
headers: defaultHeaders,
url: options.toString(),
method: reqMethod
};
}

View File

@@ -2,7 +2,7 @@
import cheerio = require('cheerio'); import cheerio = require('cheerio');
import episode from './episode'; import episode from './episode';
import fs = require('fs'); import fs = require('fs');
const fse = require('fs-extra'); import fse = require('fs-extra');
import my_request = require('./my_request'); import my_request = require('./my_request');
import path = require('path'); import path = require('path');
import url = require('url'); import url = require('url');
@@ -56,27 +56,45 @@ export default function(config: IConfig, address: string, done: (err: Error) =>
{ {
if (errD) if (errD)
{ {
return done(errD); if (page.episodes[i].retry <= 0)
}
if ((ignored === false) || (ignored === undefined))
{
const newCache = JSON.stringify(cache, null, ' ');
fs.writeFile(persistentPath, newCache, (errW: Error) =>
{ {
if (errW) console.error(err.stack || err);
log.error('Cannot fetch episode "s' + page.episodes[i].volume + 'e' + page.episodes[i].episode +
'", please rerun later');
}
else
{
if (config.verbose)
{ {
return done(errW); console.error(errD.stack || errD);
} }
log.warn('Retrying to fetch episode "s' + page.episodes[i].volume + 'e' + page.episodes[i].episode +
i += 1; '" - Retry ' + page.episodes[i].retry + ' / ' + config.retry);
next(); page.episodes[i].retry -= 1;
}); }
next();
} }
else else
{ {
i += 1; if ((ignored === false) || (ignored === undefined))
next(); {
const newCache = JSON.stringify(cache, null, ' ');
fs.writeFile(persistentPath, newCache, (errW: Error) =>
{
if (errW)
{
return done(errW);
}
i += 1;
next();
});
}
else
{
i += 1;
next();
}
} }
}); });
})(); })();
@@ -152,8 +170,9 @@ function page(config: IConfig, address: string, done: (err: Error, result?: ISer
address: address.substr(1), address: address.substr(1),
episode: '', episode: '',
volume: 0, volume: 0,
retry: config.retry,
}); });
done(null, {episodes: episodes.reverse(), series: ""}); done(null, {episodes: episodes.reverse(), series: ''});
} }
else else
{ {
@@ -191,11 +210,12 @@ function page(config: IConfig, address: string, done: (err: Error, result?: ISer
address: url, address: url,
episode: episode[1], episode: episode[1],
volume: volume ? parseInt(volume[0], 10) : 1, volume: volume ? parseInt(volume[0], 10) : 1,
retry: config.retry,
}); });
}); });
if (episodeCount === 0) if (episodeCount === 0)
{ {
log.warn("No episodes found for " + title + ". Could it be a movie?"); log.warn('No episodes found for ' + title + '. Could it be a movie?');
} }
done(null, {episodes: episodes.reverse(), series: title}); done(null, {episodes: episodes.reverse(), series: title});
}); });

View File

@@ -33,10 +33,10 @@ export default function(input: string|Buffer, done: (err: Error, subtitle?: stri
*/ */
function event(block: ISubtitleEvent): string function event(block: ISubtitleEvent): string
{ {
var format = 'Layer,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text'; const format = 'Layer,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text';
return '[Events]\n' + return '[Events]\n' +
'Format: ' + format + '\n' + [].concat(block.event).map(style => ('Dialogue: 0,' + 'Format: ' + format + '\n' + [].concat(block.event).map((style) => ('Dialogue: 0,' +
style.$.start + ',' + style.$.start + ',' +
style.$.end + ',' + style.$.end + ',' +
style.$.style + ',' + style.$.style + ',' +
@@ -70,13 +70,13 @@ function script(block: ISubtitle): string
*/ */
function style(block: ISubtitleStyle): string function style(block: ISubtitleStyle): string
{ {
var format = 'Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,' + const format = 'Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,' +
'OutlineColour,BackColour,Bold,Italic,Underline,StrikeOut,ScaleX,' + 'OutlineColour,BackColour,Bold,Italic,Underline,StrikeOut,ScaleX,' +
'ScaleY,Spacing,Angle,BorderStyle,Outline,Shadow,Alignment,' + 'ScaleY,Spacing,Angle,BorderStyle,Outline,Shadow,Alignment,' +
'MarginL,MarginR,MarginV,Encoding'; 'MarginL,MarginR,MarginV,Encoding';
return '[V4+ Styles]\n' + return '[V4+ Styles]\n' +
'Format: ' + format + '\n' + [].concat(block.style).map(style => 'Style: ' + 'Format: ' + format + '\n' + [].concat(block.style).map((style) => 'Style: ' +
style.$.name + ',' + style.$.name + ',' +
style.$.font_name + ',' + style.$.font_name + ',' +
style.$.font_size + ',' + style.$.font_size + ',' +

View File

@@ -2,7 +2,7 @@
import ass from './ass'; import ass from './ass';
import srt from './srt'; import srt from './srt';
export default <IFormatterTable> { export default {
ass: ass, ass,
srt: srt srt
}; } as IFormatterTable;

View File

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

View File

@@ -9,32 +9,42 @@ import log = require('../log');
* Streams the video to disk. * Streams the video to disk.
*/ */
export default function(rtmpUrl: string, rtmpInputPath: string, swfUrl: string, filePath: string, export default function(rtmpUrl: string, rtmpInputPath: string, swfUrl: string, filePath: string,
fileExt: string, mode: string, done: (err: Error) => void) fileExt: string, mode: string, verbose: boolean, done: (err: Error) => void)
{ {
if (mode === 'RTMP') let cp;
{ let cmd;
childProcess.exec(command('rtmpdump') + ' ' + if (mode === 'RTMP')
'-r "' + rtmpUrl + '" ' + {
'-y "' + rtmpInputPath + '" ' + cmd = command('rtmpdump') + ' ' +
'-W "' + swfUrl + '" ' + '-r "' + rtmpUrl + '" ' +
'-o "' + filePath + fileExt + '"', { '-y "' + rtmpInputPath + '" ' +
maxBuffer: Infinity, '-W "' + swfUrl + '" ' +
}, done); '-o "' + filePath + fileExt + '"';
} }
else if (mode === 'HLS') else if (mode === 'HLS')
{ {
const cmd = command('ffmpeg') + ' ' + cmd = command('ffmpeg') + ' ' +
'-i "' + rtmpInputPath + '" ' + '-y -xerror ' +
'-c copy -bsf:a aac_adtstoasc ' + '-i "' + rtmpInputPath + '" ' +
'"' + filePath + '.mp4"'; '-c copy -bsf:a aac_adtstoasc ' +
childProcess.exec(cmd, { '"' + filePath + '.mp4"';
maxBuffer: Infinity, }
}, done); else
} {
else log.error('No such mode: ' + mode);
{ }
log.error('No such mode: ' + mode);
} cp = childProcess.exec(cmd,
{
maxBuffer: Infinity,
}, done);
if (verbose === true)
{
cp.stdin.pipe(process.stdin);
cp.stdout.pipe(process.stdout);
cp.stderr.pipe(process.stderr);
}
} }
/** /**
@@ -44,7 +54,7 @@ function command(exe: string): string
{ {
if (os.platform() !== 'win32') if (os.platform() !== 'win32')
{ {
return exe; return exe;
} }
return '"' + path.join(__dirname, '../../bin/' + exe + '.exe') + '"'; return '"' + path.join(__dirname, '../../bin/' + exe + '.exe') + '"';

View File

@@ -6,6 +6,9 @@
"removeComments": false, "removeComments": false,
"module": "commonjs", "module": "commonjs",
"outDir": "dist", "outDir": "dist",
"sourceMap": true "sourceMap": true,
"lib": [
"es2015"
]
} }
} }

View File

@@ -9,23 +9,22 @@
"curly": false, "curly": false,
"eofline": false, "eofline": false,
"forin": true, "forin": true,
"indent": [true, 2], "indent": [true, "spaces", 2],
"interface-name": true, "interface-name": true,
"jsdoc-format": true, "jsdoc-format": true,
"label-position": true, "label-position": true,
"max-line-length": [true, 140], "max-line-length": [true, 140],
"member-ordering": [true, "member-ordering": false,
"public-before-private", "no-shadowed-variable": false,
"static-before-instance",
"variables-before-functions"
],
"array-type": [true, "array"], "array-type": [true, "array"],
"trailing-comma": false,
"no-any": false, "no-any": false,
"no-arg": true, "no-arg": true,
"no-bitwise": true, "no-bitwise": true,
"space-within-parens": false,
"no-object-literal-type-assertion": false,
"no-console": [true, "no-console": [true,
"debug", "debug",
"info",
"time", "time",
"timeEnd", "timeEnd",
"trace" "trace"
@@ -42,7 +41,6 @@
"no-use-before-declare": false, "no-use-before-declare": false,
"no-var-requires": true, "no-var-requires": true,
"one-line": [true, "one-line": [true,
"check-catch",
"check-whitespace" "check-whitespace"
], ],
"quotemark": [true, "single"], "quotemark": [true, "single"],
@@ -67,6 +65,8 @@
"check-operator", "check-operator",
"check-separator", "check-separator",
"check-type" "check-type"
] ],
"object-literal-sort-keys": false,
"ordered-imports": false
} }
} }