twitch-video-downloader-plus 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +284 -0
  3. package/build/chat-downloader.d.ts +13 -0
  4. package/build/chat-downloader.js +46 -0
  5. package/build/chat-downloader.js.map +1 -0
  6. package/build/downloader.d.ts +12 -0
  7. package/build/downloader.js +92 -0
  8. package/build/downloader.js.map +1 -0
  9. package/build/enums/errors.d.ts +8 -0
  10. package/build/enums/errors.js +13 -0
  11. package/build/enums/errors.js.map +1 -0
  12. package/build/enums/twitch-api.d.ts +3 -0
  13. package/build/enums/twitch-api.js +8 -0
  14. package/build/enums/twitch-api.js.map +1 -0
  15. package/build/index.d.ts +5 -0
  16. package/build/index.js +14 -0
  17. package/build/index.js.map +1 -0
  18. package/build/interfaces/access-token.d.ts +32 -0
  19. package/build/interfaces/access-token.js +3 -0
  20. package/build/interfaces/access-token.js.map +1 -0
  21. package/build/interfaces/comment.d.ts +18 -0
  22. package/build/interfaces/comment.js +3 -0
  23. package/build/interfaces/comment.js.map +1 -0
  24. package/build/interfaces/fragments-response.d.ts +4 -0
  25. package/build/interfaces/fragments-response.js +3 -0
  26. package/build/interfaces/fragments-response.js.map +1 -0
  27. package/build/interfaces/hls-video.d.ts +5 -0
  28. package/build/interfaces/hls-video.js +3 -0
  29. package/build/interfaces/hls-video.js.map +1 -0
  30. package/build/interfaces/manifiest-response.d.ts +6 -0
  31. package/build/interfaces/manifiest-response.js +3 -0
  32. package/build/interfaces/manifiest-response.js.map +1 -0
  33. package/build/interfaces/mkv-video.d.ts +5 -0
  34. package/build/interfaces/mkv-video.js +3 -0
  35. package/build/interfaces/mkv-video.js.map +1 -0
  36. package/build/interfaces/transcode-options.d.ts +4 -0
  37. package/build/interfaces/transcode-options.js +3 -0
  38. package/build/interfaces/transcode-options.js.map +1 -0
  39. package/build/interfaces/video-download-information.d.ts +5 -0
  40. package/build/interfaces/video-download-information.js +3 -0
  41. package/build/interfaces/video-download-information.js.map +1 -0
  42. package/build/twitch-oauth.d.ts +9 -0
  43. package/build/twitch-oauth.js +37 -0
  44. package/build/twitch-oauth.js.map +1 -0
  45. package/build/utils/filesystem.d.ts +1 -0
  46. package/build/utils/filesystem.js +11 -0
  47. package/build/utils/filesystem.js.map +1 -0
  48. package/build/utils/is-json.d.ts +1 -0
  49. package/build/utils/is-json.js +14 -0
  50. package/build/utils/is-json.js.map +1 -0
  51. package/build/utils/logger.d.ts +1 -0
  52. package/build/utils/logger.js +39 -0
  53. package/build/utils/logger.js.map +1 -0
  54. package/build/utils/parse-url.d.ts +1 -0
  55. package/build/utils/parse-url.js +14 -0
  56. package/build/utils/parse-url.js.map +1 -0
  57. package/build/video-downloader.d.ts +32 -0
  58. package/build/video-downloader.js +158 -0
  59. package/build/video-downloader.js.map +1 -0
  60. package/build/video-fragments.d.ts +7 -0
  61. package/build/video-fragments.js +33 -0
  62. package/build/video-fragments.js.map +1 -0
  63. package/build/video-manifiest.d.ts +12 -0
  64. package/build/video-manifiest.js +83 -0
  65. package/build/video-manifiest.js.map +1 -0
  66. package/package.json +52 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifiest-response.js","sourceRoot":"","sources":["../../src/interfaces/manifiest-response.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface MkvVideo {
2
+ vodID: string;
3
+ quality: string;
4
+ filePath: string;
5
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=mkv-video.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mkv-video.js","sourceRoot":"","sources":["../../src/interfaces/mkv-video.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export interface TranscodeOptions {
2
+ deleteHslFiles?: boolean;
3
+ outputPath?: string;
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=transcode-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcode-options.js","sourceRoot":"","sources":["../../src/interfaces/transcode-options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface VideoDownloadInformation {
2
+ quality: string;
3
+ resolution: string;
4
+ url: string;
5
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=video-download-information.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-download-information.js","sourceRoot":"","sources":["../../src/interfaces/video-download-information.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ export interface LoginOptions {
2
+ authy_token?: string;
3
+ client_id?: string;
4
+ remember_me?: boolean;
5
+ }
6
+ export declare class TwitchOAuth {
7
+ private readonly PASSPORT_ENDPOINT;
8
+ login(username: string, password: string, options?: LoginOptions): Promise<any>;
9
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TwitchOAuth = void 0;
7
+ const cross_fetch_1 = __importDefault(require("cross-fetch"));
8
+ class TwitchOAuth {
9
+ constructor() {
10
+ this.PASSPORT_ENDPOINT = "https://passport.twitch.tv/login";
11
+ }
12
+ async login(username, password, options) {
13
+ const payload = {
14
+ username: username,
15
+ password: password,
16
+ authy_token: options === null || options === void 0 ? void 0 : options.authy_token,
17
+ client_id: (options === null || options === void 0 ? void 0 : options.client_id) || "kimne78kx3ncx6brgo4mv6wki5h1ko",
18
+ remember_me: (options === null || options === void 0 ? void 0 : options.remember_me) || true,
19
+ undelete_user: false
20
+ };
21
+ const passportFetchOptions = {
22
+ method: "POST",
23
+ body: JSON.stringify(payload)
24
+ };
25
+ return (0, cross_fetch_1.default)(this.PASSPORT_ENDPOINT, passportFetchOptions)
26
+ .then((response) => response.json())
27
+ .then((response) => {
28
+ if (response && response.error_code)
29
+ throw new Error(`[TwitchOAuth] Twitch server say: ${response.error}`);
30
+ if (!response.access_token)
31
+ throw new Error(`[TwitchOAuth] Unknown error.`);
32
+ return response.access_token;
33
+ });
34
+ }
35
+ }
36
+ exports.TwitchOAuth = TwitchOAuth;
37
+ //# sourceMappingURL=twitch-oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twitch-oauth.js","sourceRoot":"","sources":["../src/twitch-oauth.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAgC;AAqBhC,MAAa,WAAW;IAAxB;QACqB,sBAAiB,GAAW,kCAAkC,CAAC;IA2BpF,CAAC;IAzBU,KAAK,CAAC,KAAK,CAAC,QAAgB,EAAE,QAAgB,EAAE,OAAsB;QACzE,MAAM,OAAO,GAAiB;YAC1B,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,QAAQ;YAClB,WAAW,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW;YACjC,SAAS,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,KAAI,gCAAgC;YACjE,WAAW,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,KAAI,IAAI;YACzC,aAAa,EAAE,KAAK;SACvB,CAAC;QAEF,MAAM,oBAAoB,GAAgB;YACtC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAChC,CAAC;QAEF,OAAO,IAAA,qBAAK,EAAC,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,CAAC;aACrD,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;aACnC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YACf,IAAI,QAAQ,IAAI,QAAQ,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;YAE3G,IAAI,CAAC,QAAQ,CAAC,YAAY;gBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAE5E,OAAO,QAAQ,CAAC,YAAY,CAAC;QACjC,CAAC,CAAC,CAAC;IACX,CAAC;CACJ;AA5BD,kCA4BC"}
@@ -0,0 +1 @@
1
+ export declare function ensureDirectoryExists(path: string): void;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureDirectoryExists = void 0;
4
+ const fs_1 = require("fs");
5
+ function ensureDirectoryExists(path) {
6
+ if (!(0, fs_1.existsSync)(path)) {
7
+ (0, fs_1.mkdirSync)(path, { recursive: true });
8
+ }
9
+ }
10
+ exports.ensureDirectoryExists = ensureDirectoryExists;
11
+ //# sourceMappingURL=filesystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../../src/utils/filesystem.ts"],"names":[],"mappings":";;;AAAA,2BAA2C;AAE3C,SAAgB,qBAAqB,CAAC,IAAY;IAC9C,IAAI,CAAC,IAAA,eAAU,EAAC,IAAI,CAAC,EAAE;QACnB,IAAA,cAAS,EAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;KACxC;AACL,CAAC;AAJD,sDAIC"}
@@ -0,0 +1 @@
1
+ export declare function parseIfIsJSON<T>(text: string): T | null;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseIfIsJSON = void 0;
4
+ function parseIfIsJSON(text) {
5
+ try {
6
+ const data = JSON.parse(text);
7
+ return data;
8
+ }
9
+ catch (error) {
10
+ return null;
11
+ }
12
+ }
13
+ exports.parseIfIsJSON = parseIfIsJSON;
14
+ //# sourceMappingURL=is-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-json.js","sourceRoot":"","sources":["../../src/utils/is-json.ts"],"names":[],"mappings":";;;AAAA,SAAgB,aAAa,CAAI,IAAY;IACzC,IAAI;QACA,MAAM,IAAI,GAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEjC,OAAO,IAAI,CAAC;KACf;IAAC,OAAO,KAAK,EAAE;QACZ,OAAO,IAAI,CAAC;KACf;AACL,CAAC;AARD,sCAQC"}
@@ -0,0 +1 @@
1
+ export declare const log: (message: string) => void;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.log = void 0;
23
+ const winston_1 = __importStar(require("winston"));
24
+ const longFormat = winston_1.default.format.combine(winston_1.format.colorize(), winston_1.format.timestamp(), winston_1.format.align(), winston_1.format.printf((info) => `${info.timestamp} ${info.level} ${info.message}`));
25
+ const logger = (0, winston_1.createLogger)({
26
+ format: longFormat,
27
+ transports: [new winston_1.transports.Console({ level: "debug" })]
28
+ });
29
+ const log = (message) => {
30
+ const isDebug = process.env.TWITCH_VIDEO_DOWNLOADER_DEBUG === "true";
31
+ if (isDebug) {
32
+ if (logger.transports.length < 2) {
33
+ logger.add(new winston_1.transports.File({ filename: "debug.log", level: "debug" }));
34
+ }
35
+ logger.log({ level: "debug", message });
36
+ }
37
+ };
38
+ exports.log = log;
39
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,mDAAoE;AAEpE,MAAM,UAAU,GAAG,iBAAO,CAAC,MAAM,CAAC,OAAO,CACrC,gBAAM,CAAC,QAAQ,EAAE,EACjB,gBAAM,CAAC,SAAS,EAAE,EAClB,gBAAM,CAAC,KAAK,EAAE,EACd,gBAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,CAC7E,CAAC;AAEF,MAAM,MAAM,GAAG,IAAA,sBAAY,EAAC;IACxB,MAAM,EAAE,UAAU;IAClB,UAAU,EAAE,CAAC,IAAI,oBAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;CAC3D,CAAC,CAAC;AAEI,MAAM,GAAG,GAAG,CAAC,OAAe,EAAE,EAAE;IACnC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,KAAK,MAAM,CAAC;IAErE,IAAI,OAAO,EAAE;QACT,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9B,MAAM,CAAC,GAAG,CAAC,IAAI,oBAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;SAC9E;QAED,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;KAC3C;AACL,CAAC,CAAC;AAVW,QAAA,GAAG,OAUd"}
@@ -0,0 +1 @@
1
+ export declare function parseURL(url: string): string;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseURL = void 0;
4
+ function parseURL(url) {
5
+ const REGEX = /(http(s)?:\/\/)?(www.)?twitch.tv\/videos\/(\d+)/;
6
+ const search = url.match(REGEX);
7
+ let result = "";
8
+ if (search) {
9
+ result = search[4];
10
+ }
11
+ return result;
12
+ }
13
+ exports.parseURL = parseURL;
14
+ //# sourceMappingURL=parse-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-url.js","sourceRoot":"","sources":["../../src/utils/parse-url.ts"],"names":[],"mappings":";;;AAAA,SAAgB,QAAQ,CAAC,GAAW;IAChC,MAAM,KAAK,GAAG,iDAAiD,CAAC;IAChE,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEhC,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,IAAI,MAAM,EAAE;QACR,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;KACtB;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAXD,4BAWC"}
@@ -0,0 +1,32 @@
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from "events";
3
+ import { MkvVideo } from "./interfaces/mkv-video";
4
+ import { HslVideo } from "./interfaces/hls-video";
5
+ import { VideoDownloadInformation } from "./interfaces/video-download-information";
6
+ import { TranscodeOptions } from "./interfaces/transcode-options";
7
+ interface options {
8
+ poolLimit?: number;
9
+ downloadFolder?: string;
10
+ clientID?: string;
11
+ oAuthToken?: string;
12
+ debug?: boolean;
13
+ }
14
+ export declare class VideoDownloader extends EventEmitter {
15
+ private _clientID;
16
+ private _oAuthToken;
17
+ private _pathFolder;
18
+ private _poolLimit;
19
+ private _quality;
20
+ private _rootProjectPath;
21
+ private _totalVideoFragments;
22
+ private _videoFragmentsDownloaded;
23
+ private _videoURL;
24
+ private _vodID;
25
+ constructor(videoURL: string, options?: options);
26
+ getVideoResolutionsAvailable(): Promise<VideoDownloadInformation[]>;
27
+ download(video: VideoDownloadInformation): Promise<HslVideo>;
28
+ private _downloadFragments;
29
+ private _downloadFragment;
30
+ transcode(videoSaved: HslVideo, options?: TranscodeOptions): Promise<MkvVideo>;
31
+ }
32
+ export {};
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.VideoDownloader = void 0;
7
+ const events_1 = require("events");
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const tiny_async_pool_1 = __importDefault(require("tiny-async-pool"));
11
+ const fluent_ffmpeg_1 = __importDefault(require("fluent-ffmpeg"));
12
+ const downloader_1 = require("./downloader");
13
+ const video_fragments_1 = require("./video-fragments");
14
+ const video_manifiest_1 = require("./video-manifiest");
15
+ const filesystem_1 = require("./utils/filesystem");
16
+ const logger_1 = require("./utils/logger");
17
+ const parse_url_1 = require("./utils/parse-url");
18
+ const errors_1 = require("./enums/errors");
19
+ class VideoDownloader extends events_1.EventEmitter {
20
+ constructor(videoURL, options) {
21
+ super();
22
+ this._clientID = "kimne78kx3ncx6brgo4mv6wki5h1ko";
23
+ this._oAuthToken = "";
24
+ this._pathFolder = "";
25
+ this._poolLimit = 20;
26
+ this._quality = "";
27
+ this._rootProjectPath = process.cwd();
28
+ this._totalVideoFragments = 0;
29
+ this._videoFragmentsDownloaded = [];
30
+ if (options) {
31
+ this._poolLimit = options.poolLimit ? options.poolLimit : this._poolLimit;
32
+ this._rootProjectPath = options.downloadFolder ? options.downloadFolder : this._rootProjectPath;
33
+ this._clientID = options.clientID ? options.clientID : this._clientID;
34
+ this._oAuthToken = options.oAuthToken ? options.oAuthToken : this._oAuthToken;
35
+ if (process.env.TWITCH_VIDEO_DOWNLOADER_DEBUG == null) {
36
+ process.env.TWITCH_VIDEO_DOWNLOADER_DEBUG = options.debug ? "true" : "false";
37
+ }
38
+ (0, logger_1.log)(`[VideoDownloader] Options:
39
+ ${JSON.stringify(options, undefined, 2)}`);
40
+ }
41
+ this._videoURL = videoURL;
42
+ this._vodID = (0, parse_url_1.parseURL)(this._videoURL);
43
+ (0, logger_1.log)(`[VideoDownloader] VodID is ${this._vodID}.`);
44
+ }
45
+ async getVideoResolutionsAvailable() {
46
+ const manifiests = new video_manifiest_1.VideoManifiest(this._vodID, this._clientID, this._oAuthToken);
47
+ const resolutions = await manifiests.getVideoResolutions();
48
+ return resolutions;
49
+ }
50
+ async download(video) {
51
+ if (!video || !video.quality || !video.resolution || !video.url) {
52
+ (0, logger_1.log)(`[VideoDownloader] Video download request failed, invalid video information, object must have quality, resolution, and url fields`);
53
+ (0, logger_1.log)(JSON.stringify(video, undefined, 2));
54
+ throw new Error(errors_1.ERRORS.INVALID_VIDEO_METADATA);
55
+ }
56
+ (0, logger_1.log)(`[VideoDownloader] Video download request, with information`);
57
+ (0, logger_1.log)(JSON.stringify(video, undefined, 2));
58
+ const videoFragmentsFetcher = new video_fragments_1.VideoFragmentsFetcher();
59
+ const { rawContent, fragments } = await videoFragmentsFetcher.getFragments(video);
60
+ this._quality = video.quality;
61
+ this._pathFolder = (0, path_1.join)(this._rootProjectPath, `downloads/videos/${this._vodID}/hls/${video.quality}`);
62
+ (0, filesystem_1.ensureDirectoryExists)(this._pathFolder);
63
+ (0, logger_1.log)(`[VideoDownloader] Path of downloading ${this._pathFolder}`);
64
+ (0, fs_1.writeFileSync)(`${this._pathFolder}/index.m3u8`, rawContent);
65
+ if (fragments.length > 0) {
66
+ this._videoFragmentsDownloaded = [];
67
+ this._totalVideoFragments = fragments.length;
68
+ (0, logger_1.log)(`[VideoDownloader] Total fragments of video is ${this._totalVideoFragments}.`);
69
+ this.emit("start-download", {
70
+ vodID: this._vodID,
71
+ quality: this._quality,
72
+ folderPath: this._pathFolder
73
+ });
74
+ await this._downloadFragments(fragments);
75
+ }
76
+ else {
77
+ (0, logger_1.log)(`[VideoDownloader] Not found fragments of VodID ${this._vodID}.`);
78
+ throw new Error(errors_1.ERRORS.NOT_FOUND_FRAGMENTS);
79
+ }
80
+ return {
81
+ vodID: this._vodID,
82
+ quality: this._quality,
83
+ folderPath: this._pathFolder
84
+ };
85
+ }
86
+ async _downloadFragments(fragments) {
87
+ await (0, tiny_async_pool_1.default)(this._poolLimit, fragments, this._downloadFragment.bind(this));
88
+ }
89
+ async _downloadFragment(fragmentData) {
90
+ const [name, url] = fragmentData;
91
+ const path = `${this._pathFolder}/${name}`;
92
+ const downloader = new downloader_1.Downloader(url, path);
93
+ downloader.on("fragment-downloaded", (fragment) => {
94
+ this.emit("fragment-downloaded", fragment);
95
+ this._videoFragmentsDownloaded.push(fragment);
96
+ const percentage = (this._videoFragmentsDownloaded.length / this._totalVideoFragments) * 100;
97
+ if (percentage)
98
+ this.emit("progress-download", percentage);
99
+ });
100
+ await downloader.download();
101
+ return name;
102
+ }
103
+ transcode(videoSaved, options) {
104
+ (0, logger_1.log)("[VideoDownloader] Video transcode, with options");
105
+ if (options)
106
+ (0, logger_1.log)(JSON.stringify(options, undefined, 2));
107
+ const m3u8FilePath = (0, path_1.join)(videoSaved.folderPath, "index.m3u8");
108
+ if (!(0, fs_1.existsSync)(m3u8FilePath)) {
109
+ throw new Error(errors_1.ERRORS.M3U8_DOES_NOT_EXISTS);
110
+ }
111
+ const outputPathFolder = (0, path_1.join)(this._rootProjectPath, `downloads/videos/${videoSaved.vodID}/mkv`);
112
+ const outputPath = (options === null || options === void 0 ? void 0 : options.outputPath)
113
+ ? (0, path_1.join)(options.outputPath, `${videoSaved.vodID}-${videoSaved.quality}.mkv`)
114
+ : (0, path_1.join)(outputPathFolder, `${videoSaved.quality}.mkv`);
115
+ (0, logger_1.log)(`[VideoDownloader] Transcode output path: ${outputPath}`);
116
+ (0, filesystem_1.ensureDirectoryExists)(outputPathFolder);
117
+ return new Promise((resolve, reject) => {
118
+ const transcoder = (0, fluent_ffmpeg_1.default)();
119
+ transcoder
120
+ .addInput(m3u8FilePath)
121
+ .outputOptions(["-codec copy", "-preset ultrafast"])
122
+ .output(outputPath)
123
+ .on("start", (commands) => {
124
+ (0, logger_1.log)(`[VideoDownloader] Transcode start, commands: ${commands}`);
125
+ this.emit("start-transcode", commands);
126
+ })
127
+ .on("end", (err, stdout, stderr) => {
128
+ if (err) {
129
+ (0, logger_1.log)(`[VideoDownloader] Transcode error: ${err}`);
130
+ return reject(err);
131
+ }
132
+ if (options === null || options === void 0 ? void 0 : options.deleteHslFiles) {
133
+ (0, logger_1.log)(`[VideoDownloader] Delete HLS files`);
134
+ (0, fs_1.rmSync)(videoSaved.folderPath, { recursive: true, force: true });
135
+ }
136
+ (0, logger_1.log)(`[VideoDownloader] Transcode end`);
137
+ return resolve({
138
+ vodID: videoSaved.vodID,
139
+ quality: videoSaved.quality,
140
+ filePath: outputPath
141
+ });
142
+ })
143
+ .on("progress", (progress) => {
144
+ if (progress.percent) {
145
+ (0, logger_1.log)(`[VideoDownloader] Transcode progress: ${progress.percent}%`);
146
+ this.emit("progress-transcode", progress.percent);
147
+ }
148
+ })
149
+ .on("error", (error) => {
150
+ (0, logger_1.log)(`[VideoDownloader] Transcode error: ${error}`);
151
+ reject(error);
152
+ })
153
+ .run();
154
+ });
155
+ }
156
+ }
157
+ exports.VideoDownloader = VideoDownloader;
158
+ //# sourceMappingURL=video-downloader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-downloader.js","sourceRoot":"","sources":["../src/video-downloader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { GetFragmentsResponse } from "./interfaces/fragments-response";
2
+ import { VideoDownloadInformation } from "./interfaces/video-download-information";
3
+ export declare class VideoFragmentsFetcher {
4
+ private readonly MANIFIEST_URL_BASE;
5
+ private _getManifiest;
6
+ getFragments(manifiest: VideoDownloadInformation): Promise<GetFragmentsResponse>;
7
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.VideoFragmentsFetcher = void 0;
7
+ const cross_fetch_1 = __importDefault(require("cross-fetch"));
8
+ const logger_1 = require("./utils/logger");
9
+ const errors_1 = require("./enums/errors");
10
+ class VideoFragmentsFetcher {
11
+ constructor() {
12
+ this.MANIFIEST_URL_BASE = /^https?:\/\/.*\.net\/.*\//;
13
+ }
14
+ async _getManifiest(url) {
15
+ (0, logger_1.log)(`[VideoFragmentsFetcher] Downloading specific manifiest, url: \n ${url}`);
16
+ return (0, cross_fetch_1.default)(url).then((response) => response.text());
17
+ }
18
+ async getFragments(manifiest) {
19
+ const manifiestUrlBase = (manifiest.url.match(this.MANIFIEST_URL_BASE) || [])[0];
20
+ if (!manifiestUrlBase)
21
+ throw new Error(errors_1.ERRORS.URL_MANIFIEST_REQUIRED);
22
+ const streamManifiest = await this._getManifiest(manifiest.url);
23
+ let fragmentsMatched = streamManifiest.matchAll(/#EXTINF.*?\n(.*?\.ts)/g) || [];
24
+ let fragments = Array.from(fragmentsMatched).map((fragment) => [fragment[1], `${manifiestUrlBase}${fragment[1]}`]);
25
+ (0, logger_1.log)(`[VideoFragmentsFetcher] All fragments found: \n ${JSON.stringify(fragments, undefined, 2)}`);
26
+ return {
27
+ rawContent: streamManifiest,
28
+ fragments: fragments
29
+ };
30
+ }
31
+ }
32
+ exports.VideoFragmentsFetcher = VideoFragmentsFetcher;
33
+ //# sourceMappingURL=video-fragments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-fragments.js","sourceRoot":"","sources":["../src/video-fragments.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAgC;AAEhC,2CAAqC;AAKrC,2CAAwC;AAExC,MAAa,qBAAqB;IAAlC;QACqB,uBAAkB,GAAG,2BAA2B,CAAC;IAyBtE,CAAC;IAvBW,KAAK,CAAC,aAAa,CAAC,GAAW;QACnC,IAAA,YAAG,EAAC,mEAAmE,GAAG,EAAE,CAAC,CAAC;QAC9E,OAAO,IAAA,qBAAK,EAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAmC;QAClD,MAAM,gBAAgB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjF,IAAI,CAAC,gBAAgB;YAAE,MAAM,IAAI,KAAK,CAAC,eAAM,CAAC,sBAAsB,CAAC,CAAC;QAEtE,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAEhE,IAAI,gBAAgB,GAAG,eAAe,CAAC,QAAQ,CAAC,wBAAwB,CAAC,IAAI,EAAE,CAAC;QAEhF,IAAI,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,gBAAgB,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAuB,CAAC;QAEzI,IAAA,YAAG,EAAC,mDAAmD,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAElG,OAAO;YACH,UAAU,EAAE,eAAe;YAC3B,SAAS,EAAE,SAAS;SACvB,CAAC;IACN,CAAC;CACJ;AA1BD,sDA0BC"}
@@ -0,0 +1,12 @@
1
+ import { VideoDownloadInformation } from "./interfaces/video-download-information";
2
+ export declare class VideoManifiest {
3
+ private readonly _vodID;
4
+ private readonly _clientID;
5
+ private readonly _oAuthToken;
6
+ private readonly GRAPHQL_ENDPOINT;
7
+ constructor(vodID: string | number, clientID: string | number, oAuthToken?: string | number);
8
+ private _getPlaybackAccessToken;
9
+ private _getVideoManifiest;
10
+ private _parseVideoManifiest;
11
+ getVideoResolutions(): Promise<VideoDownloadInformation[]>;
12
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.VideoManifiest = void 0;
7
+ const cross_fetch_1 = __importDefault(require("cross-fetch"));
8
+ const logger_1 = require("./utils/logger");
9
+ const is_json_1 = require("./utils/is-json");
10
+ const errors_1 = require("./enums/errors");
11
+ const twitch_api_1 = require("./enums/twitch-api");
12
+ class VideoManifiest {
13
+ constructor(vodID, clientID, oAuthToken = "") {
14
+ this.GRAPHQL_ENDPOINT = "https://gql.twitch.tv/gql";
15
+ this._vodID = String(vodID);
16
+ this._clientID = String(clientID);
17
+ this._oAuthToken = String(oAuthToken);
18
+ }
19
+ _getPlaybackAccessToken() {
20
+ const graphqlQuery = {
21
+ operationName: "PlaybackAccessToken_Template",
22
+ query: 'query PlaybackAccessToken_Template($login: String!, $isLive: Boolean!, $vodID: ID!, $isVod: Boolean!, $playerType: String!) { streamPlaybackAccessToken(channelName: $login, params: {platform: "web", playerBackend: "mediaplayer", playerType: $playerType}) @include(if: $isLive) { value signature __typename } videoPlaybackAccessToken(id: $vodID, params: {platform: "web", playerBackend: "mediaplayer", playerType: $playerType}) @include(if: $isVod) { value signature __typename }}',
23
+ variables: {
24
+ isLive: false,
25
+ login: "",
26
+ isVod: true,
27
+ vodID: this._vodID,
28
+ playerType: "site"
29
+ }
30
+ };
31
+ const graphqlFentchOptions = {
32
+ method: "POST",
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ Authorization: "OAuth " + this._oAuthToken,
36
+ "Client-ID": this._clientID
37
+ },
38
+ body: JSON.stringify(graphqlQuery)
39
+ };
40
+ (0, logger_1.log)(`[VideoManifiest] Generating access token.`);
41
+ return (0, cross_fetch_1.default)(this.GRAPHQL_ENDPOINT, graphqlFentchOptions).then((response) => response.json());
42
+ }
43
+ _getVideoManifiest(signature, accessToken) {
44
+ (0, logger_1.log)(`[VideoManifiest] Fetching video manifiest, with parameters: \n Signature: ${signature}, \n Access Token: ${JSON.stringify(accessToken, undefined, 4)}.`);
45
+ const MANIFIEST_ENPOINT = `https://usher.ttvnw.net/vod/${this._vodID}.m3u8?allow_source=true&player_backend=mediaplayer&playlist_include_framerate=true&reassignments_supported=true&sig=${signature}&supported_codecs=avc1&token=${accessToken}&cdm=wv&player_version=1.7.0`;
46
+ return (0, cross_fetch_1.default)(MANIFIEST_ENPOINT).then((response) => response.text());
47
+ }
48
+ _parseVideoManifiest(manifiest) {
49
+ const isJsonResponse = (0, is_json_1.parseIfIsJSON)(manifiest);
50
+ if ((isJsonResponse === null || isJsonResponse === void 0 ? void 0 : isJsonResponse.length) && isJsonResponse[0].error_code == twitch_api_1.TWITCH_API_ERROR.VOD_MANIFIEST_RESTRICTED) {
51
+ (0, logger_1.log)(`[VideoManifiest] Can´t get video maifiest, you probably don't have permission to view the video. Pass your OAuth token from twitch to VideoDownloader options to try get the video.`);
52
+ throw new Error(errors_1.ERRORS.MANIFIEST_IS_RESTRICRED);
53
+ }
54
+ const parsedPlaylist = [];
55
+ const lines = manifiest.split("\n");
56
+ for (let i = 4; i < lines.length; i += 3) {
57
+ parsedPlaylist.push({
58
+ quality: lines[i - 2].split('NAME="')[1].split('"')[0],
59
+ resolution: lines[i - 1].indexOf("RESOLUTION") != -1 ? lines[i - 1].split("RESOLUTION=")[1].split(",")[0] : null,
60
+ url: lines[i]
61
+ });
62
+ }
63
+ (0, logger_1.log)(`[VideoManifiest] Resolutions found`);
64
+ (0, logger_1.log)(JSON.stringify(parsedPlaylist, undefined, 2));
65
+ return parsedPlaylist;
66
+ }
67
+ getVideoResolutions() {
68
+ return this._getPlaybackAccessToken()
69
+ .then((response) => {
70
+ var _a, _b, _c, _d;
71
+ const signature = ((_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.videoPlaybackAccessToken) === null || _b === void 0 ? void 0 : _b.signature) || null;
72
+ const token = ((_d = (_c = response === null || response === void 0 ? void 0 : response.data) === null || _c === void 0 ? void 0 : _c.videoPlaybackAccessToken) === null || _d === void 0 ? void 0 : _d.value) || null;
73
+ if (!signature || !token) {
74
+ (0, logger_1.log)(`[VideoManifiest] Can´t generante access token, with clientId ${this._clientID}.`);
75
+ throw new Error(errors_1.ERRORS.CANT_GENERATE_ACCESS_TOKEN);
76
+ }
77
+ return this._getVideoManifiest(signature, token);
78
+ })
79
+ .then(this._parseVideoManifiest.bind(this));
80
+ }
81
+ }
82
+ exports.VideoManifiest = VideoManifiest;
83
+ //# sourceMappingURL=video-manifiest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-manifiest.js","sourceRoot":"","sources":["../src/video-manifiest.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "twitch-video-downloader-plus",
3
+ "version": "1.6.3",
4
+ "description": "A library to download twitch videos",
5
+ "main": "./build/index.js",
6
+ "types": "./build/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Alejandro095/twitch-video-downloader.git"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc -p ./tsconfig.json",
13
+ "dev:watch": "cross-env TWITCH_VIDEO_DOWNLOADER_DEBUG=true nodemon",
14
+ "format": "prettier --write .",
15
+ "prepare": "npm run build",
16
+ "start:example": "ts-node example/index.ts",
17
+ "start:hls-server": "node ./hls-server.js",
18
+ "start:watch": "nodemon"
19
+ },
20
+ "keywords": [
21
+ "Twitch",
22
+ "video",
23
+ "downloader"
24
+ ],
25
+ "author": "Alejandro Tovar",
26
+ "license": "MIT",
27
+ "devDependencies": {
28
+ "@types/fluent-ffmpeg": "^2.1.20",
29
+ "@types/tiny-async-pool": "^1.0.0",
30
+ "cross-env": "^7.0.3",
31
+ "hls-server": "^1.5.0",
32
+ "nodemon": "^2.0.15",
33
+ "prettier": "^2.5.1",
34
+ "ts-node": "^10.4.0",
35
+ "typescript": "^4.5.4"
36
+ },
37
+ "dependencies": {
38
+ "@lifeomic/attempt": "^3.0.1",
39
+ "cross-fetch": "^3.1.4",
40
+ "fluent-ffmpeg": "^2.1.2",
41
+ "http-attach": "^1.0.0",
42
+ "tiny-async-pool": "^1.2.0",
43
+ "winston": "^3.3.3"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/Alejandro095/twitch-video-downloader/issues"
47
+ },
48
+ "homepage": "https://github.com/Alejandro095/twitch-video-downloader#readme",
49
+ "directories": {
50
+ "example": "example"
51
+ }
52
+ }