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
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # TWICHT-VIDEO-DOWNLOADER
2
+
3
+ Library to download the videos, videos for subs and comments from twitch.
4
+
5
+ ## Fork changes
6
+
7
+ Added `fragment-downloaded` event & deleted broken chat download
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ $ pnpm add twitch-video-downloader
13
+ ```
14
+
15
+ ## Requirements
16
+
17
+ The [FFMPEG](https://www.ffmpeg.org/) library is needed to transcode video files from m3u8 to mkv
18
+
19
+ ## Usage
20
+
21
+ ```js
22
+ import { join } from "path";
23
+ import { writeFileSync } from "fs";
24
+
25
+ import { VideoDownloader, ensureDirectoryExists } from "twitch-video-downloader";
26
+
27
+ (async () => {
28
+ try {
29
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240");
30
+
31
+ downloader.on("fragment-downloaded", (file) => console.log(`${file}`));
32
+
33
+ downloader.on("progress-download", (progress) => console.log(`Downloaded ${progress.toFixed(2)}%`));
34
+ downloader.on("progress-transcode", (progress) => console.log(`Transcoded ${progress.toFixed(2)}%`));
35
+
36
+ downloader.on("start-download", (e) => console.log(`Download started! on `, e));
37
+ downloader.on("start-transcode", () => console.log(`Transcoded started!`));
38
+
39
+ // Get all resolutions available for this video
40
+ const resolutions = await downloader.getVideoResolutionsAvailable();
41
+
42
+ // Donwload specific resolution
43
+ const download = await downloader.download(resolutions[0]);
44
+
45
+ // Information and path of downloaded hls files
46
+ console.log(download);
47
+
48
+ // Trancoded video, from HLS to MKV
49
+ const transcode = await downloader.transcode(download);
50
+
51
+ // Information and path of trancoded video
52
+ console.log(transcode);
53
+
54
+ // Download offline chat
55
+ const comments = await downloader.downloadChat();
56
+
57
+ // Verify that the directory exists, if not create it
58
+ ensureDirectoryExists(join(__dirname, "./../downloads/chats"));
59
+
60
+ // Save all comments
61
+ writeFileSync(join(__dirname, `./../downloads/chats/${comments.vodID}.chat`), comments.content);
62
+ } catch (error) {
63
+ console.log(error);
64
+ }
65
+ })();
66
+ ```
67
+
68
+ This code can be very useful to have a quick overview of the library. If you clone the [repository](https://github.com/Alejandro095/twitch-video-downloader) you can find this same file in the following path.
69
+
70
+ ```
71
+ twitch-video-downloader
72
+
73
+ └───example
74
+ │ index.ts
75
+ ```
76
+
77
+ Once the project dependencies are installed with
78
+
79
+ ```bash
80
+ $ pnpm install
81
+ ```
82
+
83
+ You can play with this file by modifying it and downloading the videos that interest you. To run the script run the following command
84
+
85
+ ```bash
86
+ $ pnpm run start:watch
87
+ ```
88
+
89
+ If you want to know everything the library is doing, execute the following command to run the script in debug mode
90
+
91
+ ```bash
92
+ $ pnpm run dev:watch
93
+ ```
94
+
95
+ Or you can pass the debug parameter in the options
96
+
97
+ ```js
98
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240", {
99
+ debug: true
100
+ });
101
+ ```
102
+
103
+ ## API
104
+
105
+ VideoDownloader is the main class of the library, it is the entry point to start downloading videos or chat
106
+
107
+ ```js
108
+ const defaultOptions = {
109
+ clientID: "kimne78kx3ncx6brgo4mv6wki5h1ko",
110
+ debug: false,
111
+ downloadFolder: process.cwd(),
112
+ oAuthToken: "",
113
+ poolLimit: 20
114
+ };
115
+
116
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240", defaultOptions);
117
+ ```
118
+
119
+ | Option | Definition | Default |
120
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
121
+ | clientID | This is a parameter used by the twitch platform, the value is the same no matter what account is used, so you probably shouldn't change it | kimne78kx3ncx6brgo4mv6wki5h1ko |
122
+ | debug | To start the library in debug mode, a debug.log file will be created where everything the library is doing will be saved. | false |
123
+ | downloadFolder | The folder where the videos will be saved. Default is the working directory | process.cwd() |
124
+ | oAuthToken | This parameter is very important because with this you can download videos only for subscribers. It is not magic, before you must already have access to the video in your twitch account to be able to download it | "" |
125
+ | poolLimit | They are the maximum parallel downloads when downloading a video | 20 |
126
+
127
+ ### Where do I get my oAuthToken to download subscriber-only videos?
128
+
129
+ You have two options, you can extract it from Twitch cookies once you are logged in, the field is called auth-token. Here are the steps you must follow:
130
+
131
+ * Sign in to your Twitch account
132
+ * With the Twitch tab open, open the chrome devtools (press f12)
133
+ * With the devtools window open, now go to the application tab
134
+ * Select 'https://www.twitch.tv' in the Cookies section
135
+ * And look in the 'name' column for the field that says 'auth-token' and copy what is in the 'value' column
136
+
137
+ The second option (It is still in development, it is not recommended) is to use the TwitchOAuth class
138
+
139
+ ```js
140
+ import { TwitchOAuth, LoginOptions } from "twitch-video-downloader";
141
+
142
+ const loginDefaultOptions: LoginOptions = {
143
+ authy_token: "", // This is the only useful option. Use it when you have the Two-factor activated, copy the code from the Authenticator application. Make sure the code is still valid when you run this method
144
+ client_id: "kimne78kx3ncx6brgo4mv6wki5h1ko",
145
+ remember_me: true,
146
+ }
147
+
148
+ const twitchOAuth = new TwitchOAuth();
149
+
150
+ twitchOAuth.login("<YOUR TWITCH USER>", "<YOUR PASSWORD>", loginDefaultOptions).then(async (oAuthToken) => {
151
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240", {
152
+ oAuthToken: oAuthToken,
153
+ });
154
+
155
+ ...
156
+ });
157
+ ```
158
+
159
+ Warning: THIS CLASS IS NOT YET FINISHED DEVELOPING, so problems may arise. And we have not yet developed the option to solve the catchas when Twitch asks you to log in
160
+
161
+ IT IS RECOMMENDED THAT YOU USE THE FIRST METHOD TO GET YOUR OAUTH TOKEN FROM COOKIES BEFORE THIS METHOD
162
+
163
+ ## Events
164
+
165
+ | Event name | Description | Parameters |
166
+ | ------------------ | --------------------------------------------------------------- | ---------------------------------------------------- |
167
+ | progress-download | The event is called each time the download progress is updated | decimal |
168
+ | progress-transcode | The event is called each time the transcode progress is updated | decimal |
169
+ | start-download | The event is called when the download starts | { vodID: string, quality: string, folderPath:string} |
170
+ | start-transcode | The event is called when the transcode starts | void |
171
+
172
+ Example to register your listeners
173
+
174
+ ```js
175
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240");
176
+
177
+ downloader.on("<EVENT NAME>", (param) => console.log(param));
178
+ ```
179
+
180
+ ## Methods
181
+
182
+ ### getVideoResolutionsAvailable: Asynchronous method to get all available resolutions
183
+
184
+ ```js
185
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240");
186
+
187
+ const resolutions = await downloader.getVideoResolutionsAvailable();
188
+ ```
189
+
190
+ The function returns an array with the following information
191
+
192
+ ```js
193
+ [
194
+ {
195
+ quality: '1080p60',
196
+ resolution: '1920x1080',
197
+ url: 'https://...index-dvr.m3u8'
198
+ },
199
+ {
200
+ quality: '1080p',
201
+ resolution: '1920x1080',
202
+ url: 'https://...index-dvr.m3u8'
203
+ },
204
+ {
205
+ quality: '720p60',
206
+ resolution: '1080x720',
207
+ url: 'https://...index-dvr.m3u8'
208
+ },
209
+
210
+ ...
211
+ ]
212
+ ```
213
+
214
+ ### download: Asynchronous method to download a video
215
+
216
+ This function allows you to download a specific resolution, as a parameter you have to pass an object with the fields quality, resolution and url
217
+
218
+ ```js
219
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240");
220
+
221
+ const resolutions = await downloader.getVideoResolutionsAvailable();
222
+
223
+ // Donwload specific resolution
224
+ const download = await downloader.download(resolutions[0]);
225
+ ```
226
+
227
+ Once the function is finished executing, it returns an object with the following information
228
+
229
+ ```js
230
+ {
231
+ vodID: '800558240',
232
+ quality: '1080p60',
233
+ folderPath: 'D:\\Projects\\twitch-video-downloader\\downloads\\videos\\800558240\\hls\\1080p60'
234
+ }
235
+ ```
236
+
237
+ ### transcode: Asynchronous method to transcode a video
238
+
239
+ This function allows you to transcode a video, as a parameter you have to pass an object with the fields quality, resolution and folderPath, this fields are return by downloader method
240
+
241
+ ```js
242
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240");
243
+
244
+ const resolutions = await downloader.getVideoResolutionsAvailable();
245
+
246
+ const download = await downloader.download(resolutions[0]);
247
+
248
+ // Trancoded video, from HLS to MKV
249
+ const transcode = await downloader.transcode(download);
250
+ ```
251
+
252
+ The function also receives an optional second argument in the form of an object with additional settings.
253
+
254
+ ```js
255
+ const transcode = await downloader.transcode(download, {
256
+ deleteHslFiles: false, // Default value
257
+ outputPath: "<WORKING DIRECTORY>/downloads/videos/<VIDEO ID>/mkv/", // Default value
258
+ });
259
+ ```
260
+
261
+ Once the function is finished executing, it returns an object with the following information
262
+
263
+ ```js
264
+ {
265
+ vodID: '800558240',
266
+ quality: '1080p60',
267
+ filePath: 'D:\\Projects\\twitch-video-downloader\\downloads\\videos\\800558240\\mkv\\1080p60.mkv'
268
+ }
269
+ ```
270
+
271
+ ### downloadChat: Asynchronous method to download chat
272
+
273
+ This function allows you to download the chat of a video. The return of the function is the raw data from the twitch api
274
+
275
+ ```js
276
+ const downloader = new VideoDownloader("https://www.twitch.tv/videos/800558240");
277
+
278
+ // Download offline chat
279
+ const comments = await downloader.downloadChat();
280
+ ```
281
+
282
+ ## Credits
283
+
284
+ Some of the code is from libraries like [twitch-m3u8](https://github.com/dudik/twitch-m3u8) and [twitch-tools](https://github.com/HugoJF/twitch-tools)
@@ -0,0 +1,13 @@
1
+ import { CommentsPage } from "./interfaces/comment";
2
+ export declare class ChatDownloader {
3
+ private _vodID;
4
+ private _clientID;
5
+ private _allComments;
6
+ constructor(vodID: string, clientID: string);
7
+ private _api;
8
+ private _getEndpoint;
9
+ download(): Promise<{
10
+ vodID: string;
11
+ content: CommentsPage[];
12
+ }>;
13
+ }
@@ -0,0 +1,46 @@
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.ChatDownloader = void 0;
7
+ const cross_fetch_1 = __importDefault(require("cross-fetch"));
8
+ const logger_1 = require("./utils/logger");
9
+ class ChatDownloader {
10
+ constructor(vodID, clientID) {
11
+ this._allComments = [];
12
+ this._vodID = vodID;
13
+ this._clientID = clientID;
14
+ }
15
+ async _api(cursor) {
16
+ const headers = {
17
+ "Client-ID": this._clientID
18
+ };
19
+ return (0, cross_fetch_1.default)(this._getEndpoint(cursor), { headers });
20
+ }
21
+ _getEndpoint(cursor = "") {
22
+ return `https://api.twitch.tv/v5/videos/${this._vodID}/comments?cursor=${cursor}`;
23
+ }
24
+ async download() {
25
+ let cursor = undefined;
26
+ (0, logger_1.log)(`[ChatDownloader] Downloading chat for ${this._vodID}`);
27
+ do {
28
+ const response = (await this._api(cursor).then((resp) => resp.json()));
29
+ if (response.comments.length) {
30
+ this._allComments.push(response);
31
+ (0, logger_1.log)(`[ChatDownloader] ${response.comments.length} comments downloaded, cursor ${cursor || "initial"}.`);
32
+ cursor = response._next;
33
+ }
34
+ else {
35
+ cursor = undefined;
36
+ }
37
+ } while (cursor);
38
+ (0, logger_1.log)(`[ChatDownloader] Chat downloaded.`);
39
+ return {
40
+ vodID: this._vodID,
41
+ content: this._allComments
42
+ };
43
+ }
44
+ }
45
+ exports.ChatDownloader = ChatDownloader;
46
+ //# sourceMappingURL=chat-downloader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-downloader.js","sourceRoot":"","sources":["../src/chat-downloader.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAgC;AAGhC,2CAAqC;AAErC,MAAa,cAAc;IAMvB,YAAY,KAAa,EAAE,QAAgB;QAFnC,iBAAY,GAAmB,EAAE,CAAC;QAGtC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,MAAe;QAC9B,MAAM,OAAO,GAAG;YACZ,WAAW,EAAE,IAAI,CAAC,SAAS;SAC9B,CAAC;QAEF,OAAO,IAAA,qBAAK,EAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC;IAEO,YAAY,CAAC,SAAiB,EAAE;QACpC,OAAO,mCAAmC,IAAI,CAAC,MAAM,oBAAoB,MAAM,EAAE,CAAC;IACtF,CAAC;IAEM,KAAK,CAAC,QAAQ;QACjB,IAAI,MAAM,GAAG,SAAS,CAAC;QAEvB,IAAA,YAAG,EAAC,yCAAyC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAE5D,GAAG;YACC,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAQ,CAAC;YAE9E,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE;gBAC1B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACjC,IAAA,YAAG,EAAC,oBAAoB,QAAQ,CAAC,QAAQ,CAAC,MAAM,gCAAgC,MAAM,IAAI,SAAS,GAAG,CAAC,CAAC;gBACxG,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC;aAC3B;iBAAM;gBACH,MAAM,GAAG,SAAS,CAAC;aACtB;SACJ,QAAQ,MAAM,EAAE;QACjB,IAAA,YAAG,EAAC,mCAAmC,CAAC,CAAC;QACzC,OAAO;YACH,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,OAAO,EAAE,IAAI,CAAC,YAAY;SAC7B,CAAC;IACN,CAAC;CACJ;AA7CD,wCA6CC"}
@@ -0,0 +1,12 @@
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from "events";
3
+ export declare class Downloader extends EventEmitter {
4
+ private _url;
5
+ private _path;
6
+ private _tries;
7
+ private _maxTries;
8
+ constructor(url: string, path: string);
9
+ download(): Promise<void>;
10
+ private _handlerError;
11
+ private _init;
12
+ }
@@ -0,0 +1,92 @@
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.Downloader = void 0;
7
+ const https_1 = __importDefault(require("https"));
8
+ const events_1 = require("events");
9
+ const fs_1 = require("fs");
10
+ const attempt_1 = require("@lifeomic/attempt");
11
+ const logger_1 = require("./utils/logger");
12
+ class Downloader extends events_1.EventEmitter {
13
+ constructor(url, path) {
14
+ super();
15
+ this._tries = 0;
16
+ this._maxTries = 4;
17
+ this._url = url;
18
+ this._path = path;
19
+ }
20
+ async download() {
21
+ try {
22
+ await this._init();
23
+ }
24
+ catch (error) {
25
+ await this._handlerError(error);
26
+ }
27
+ }
28
+ async _handlerError(error, reject) {
29
+ this._tries = ++this._tries;
30
+ (0, logger_1.log)(`[Downloader] [handlerError] ${error}`);
31
+ if (this._tries > this._maxTries) {
32
+ const errorMessage = `Failed to download fragment with url ${this._url}`;
33
+ (0, logger_1.log)(errorMessage);
34
+ if (reject)
35
+ reject(errorMessage);
36
+ }
37
+ else {
38
+ const errorMessage = `Retrying fragment: ${this._url}, attempts made: ${this._tries}`;
39
+ (0, logger_1.log)(errorMessage);
40
+ await this.download();
41
+ }
42
+ }
43
+ _init() {
44
+ return new Promise((resolve, reject) => {
45
+ const filePathTemp = `${this._path}.progress`;
46
+ const filePath = this._path;
47
+ const fileExist = (0, fs_1.existsSync)(filePath);
48
+ if (fileExist) {
49
+ this.emit("fragment-downloaded", filePath);
50
+ (0, logger_1.log)(`[Downloader] Fragment download completed ${this._url}`);
51
+ resolve(filePath);
52
+ }
53
+ else {
54
+ const file = (0, fs_1.createWriteStream)(filePathTemp);
55
+ const handlerError = (error) => {
56
+ file.close();
57
+ (0, fs_1.unlink)(filePathTemp, () => {
58
+ this._handlerError(error, reject);
59
+ });
60
+ };
61
+ const request = https_1.default.get(this._url, (response) => {
62
+ if (response.statusCode != 200) {
63
+ handlerError(response.statusMessage);
64
+ }
65
+ response.pipe(file);
66
+ });
67
+ request.on("error", handlerError);
68
+ file.on("error", handlerError);
69
+ file.on("finish", () => {
70
+ handlerFinishFile();
71
+ });
72
+ const handlerFinishFile = async () => {
73
+ try {
74
+ await (0, attempt_1.retry)(async () => (0, fs_1.renameSync)(filePathTemp, filePath), {
75
+ delay: 500,
76
+ factor: 4,
77
+ maxDelay: 1000
78
+ });
79
+ }
80
+ catch (error) {
81
+ this._handlerError(error, reject);
82
+ }
83
+ this.emit("fragment-downloaded", filePath);
84
+ (0, logger_1.log)(`[Downloader] Fragment download completed ${this._url}`);
85
+ resolve(filePath);
86
+ };
87
+ }
88
+ });
89
+ }
90
+ }
91
+ exports.Downloader = Downloader;
92
+ //# sourceMappingURL=downloader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloader.js","sourceRoot":"","sources":["../src/downloader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare enum ERRORS {
2
+ CANT_GENERATE_ACCESS_TOKEN = "Can\u00B4t generate access token.",
3
+ MANIFIEST_IS_RESTRICRED = "Manifiest is restricted.",
4
+ INVALID_VIDEO_METADATA = "Invalid video metadata object.",
5
+ NOT_FOUND_FRAGMENTS = "Not found fragments.",
6
+ URL_MANIFIEST_REQUIRED = "URL manifiest requiered",
7
+ M3U8_DOES_NOT_EXISTS = "The file M3U8 does not exists"
8
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ERRORS = void 0;
4
+ var ERRORS;
5
+ (function (ERRORS) {
6
+ ERRORS["CANT_GENERATE_ACCESS_TOKEN"] = "Can\u00B4t generate access token.";
7
+ ERRORS["MANIFIEST_IS_RESTRICRED"] = "Manifiest is restricted.";
8
+ ERRORS["INVALID_VIDEO_METADATA"] = "Invalid video metadata object.";
9
+ ERRORS["NOT_FOUND_FRAGMENTS"] = "Not found fragments.";
10
+ ERRORS["URL_MANIFIEST_REQUIRED"] = "URL manifiest requiered";
11
+ ERRORS["M3U8_DOES_NOT_EXISTS"] = "The file M3U8 does not exists";
12
+ })(ERRORS = exports.ERRORS || (exports.ERRORS = {}));
13
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/enums/errors.ts"],"names":[],"mappings":";;;AAAA,IAAY,MAOX;AAPD,WAAY,MAAM;IACd,0EAA2D,CAAA;IAC3D,8DAAoD,CAAA;IACpD,mEAAyD,CAAA;IACzD,sDAA4C,CAAA;IAC5C,4DAAkD,CAAA;IAClD,gEAAsD,CAAA;AAC1D,CAAC,EAPW,MAAM,GAAN,cAAM,KAAN,cAAM,QAOjB"}
@@ -0,0 +1,3 @@
1
+ export declare enum TWITCH_API_ERROR {
2
+ VOD_MANIFIEST_RESTRICTED = "vod_manifest_restricted"
3
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TWITCH_API_ERROR = void 0;
4
+ var TWITCH_API_ERROR;
5
+ (function (TWITCH_API_ERROR) {
6
+ TWITCH_API_ERROR["VOD_MANIFIEST_RESTRICTED"] = "vod_manifest_restricted";
7
+ })(TWITCH_API_ERROR = exports.TWITCH_API_ERROR || (exports.TWITCH_API_ERROR = {}));
8
+ //# sourceMappingURL=twitch-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twitch-api.js","sourceRoot":"","sources":["../../src/enums/twitch-api.ts"],"names":[],"mappings":";;;AAAA,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IACxB,wEAAoD,CAAA;AACxD,CAAC,EAFW,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAE3B"}
@@ -0,0 +1,5 @@
1
+ export { VideoDownloader } from "./video-downloader";
2
+ export { TwitchOAuth } from "./twitch-oauth";
3
+ export { ensureDirectoryExists } from "./utils/filesystem";
4
+ export { ERRORS } from "./enums/errors";
5
+ export { TWITCH_API_ERROR } from "./enums/twitch-api";
package/build/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TWITCH_API_ERROR = exports.ERRORS = exports.ensureDirectoryExists = exports.TwitchOAuth = exports.VideoDownloader = void 0;
4
+ var video_downloader_1 = require("./video-downloader");
5
+ Object.defineProperty(exports, "VideoDownloader", { enumerable: true, get: function () { return video_downloader_1.VideoDownloader; } });
6
+ var twitch_oauth_1 = require("./twitch-oauth");
7
+ Object.defineProperty(exports, "TwitchOAuth", { enumerable: true, get: function () { return twitch_oauth_1.TwitchOAuth; } });
8
+ var filesystem_1 = require("./utils/filesystem");
9
+ Object.defineProperty(exports, "ensureDirectoryExists", { enumerable: true, get: function () { return filesystem_1.ensureDirectoryExists; } });
10
+ var errors_1 = require("./enums/errors");
11
+ Object.defineProperty(exports, "ERRORS", { enumerable: true, get: function () { return errors_1.ERRORS; } });
12
+ var twitch_api_1 = require("./enums/twitch-api");
13
+ Object.defineProperty(exports, "TWITCH_API_ERROR", { enumerable: true, get: function () { return twitch_api_1.TWITCH_API_ERROR; } });
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;AAA5C,mHAAA,eAAe,OAAA;AACxB,+CAA4C;AAAnC,2GAAA,WAAW,OAAA;AAGpB,iDAA2D;AAAlD,mHAAA,qBAAqB,OAAA;AAG9B,yCAAwC;AAA/B,gGAAA,MAAM,OAAA;AACf,iDAAsD;AAA7C,8GAAA,gBAAgB,OAAA"}
@@ -0,0 +1,32 @@
1
+ export interface Authorization {
2
+ forbidden: boolean;
3
+ reason: string;
4
+ }
5
+ export interface Chansub {
6
+ restricted_bitrates: any[];
7
+ }
8
+ export interface AccessToken {
9
+ authorization: Authorization;
10
+ chansub: Chansub;
11
+ device_id?: any;
12
+ expires: number;
13
+ https_required: boolean;
14
+ privileged: boolean;
15
+ user_id?: any;
16
+ version: number;
17
+ vod_id: number;
18
+ }
19
+ export interface PlaybackAccessToken {
20
+ data: {
21
+ videoPlaybackAccessToken: {
22
+ value: AccessToken;
23
+ signature: string;
24
+ __typename: string;
25
+ };
26
+ };
27
+ extenesion: {
28
+ durationMilliseconds: number;
29
+ operationName: string;
30
+ requestID: string;
31
+ };
32
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=access-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-token.js","sourceRoot":"","sources":["../../src/interfaces/access-token.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ export interface CommentsPage {
2
+ comments: Comment[];
3
+ _next?: string;
4
+ _prev?: string;
5
+ }
6
+ export interface Comment {
7
+ _id: string;
8
+ created_at: Date;
9
+ updated_at: Date;
10
+ channel_id: string;
11
+ content_type: any;
12
+ content_id: string;
13
+ content_offset_seconds: number;
14
+ commenter: any;
15
+ source: any;
16
+ state: any;
17
+ message: any;
18
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=comment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment.js","sourceRoot":"","sources":["../../src/interfaces/comment.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export interface GetFragmentsResponse {
2
+ rawContent: string;
3
+ fragments: [string, string][];
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=fragments-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragments-response.js","sourceRoot":"","sources":["../../src/interfaces/fragments-response.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface HslVideo {
2
+ vodID: string;
3
+ quality: string;
4
+ folderPath: string;
5
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=hls-video.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hls-video.js","sourceRoot":"","sources":["../../src/interfaces/hls-video.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export interface ManifiestResponseError {
2
+ url: string;
3
+ error: string;
4
+ error_code: string;
5
+ type: string;
6
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=manifiest-response.js.map