Skip to content

Commit 2cf1df7

Browse files
🔖 v0.5.21
1 parent faca02b commit 2cf1df7

File tree

2 files changed

+20
-33
lines changed

2 files changed

+20
-33
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "twitch-dlp",
3-
"version": "0.5.20",
3+
"version": "0.5.21",
44
"author": "DmitryScaletta",
55
"description": "Download any twitch VODs from start during live broadcast",
66
"license": "MIT",

twitch-dlp.js

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import os from "node:os";
99
import stream from "node:stream";
1010
import crypto from "node:crypto";
1111

12-
//#region node_modules/.pnpm/twitch-gql-queries@0.1.13/node_modules/twitch-gql-queries/dist/index.js
12+
//#region node_modules/.pnpm/twitch-gql-queries@0.1.17/node_modules/twitch-gql-queries/dist/index.js
1313
var CLIENT_ID = "kimne78kx3ncx6brgo4mv6wki5h1ko";
1414
var MAX_QUERIES_PER_REQUEST = 35;
1515
var gqlRequest = async (queries, requestInit) => {
@@ -48,7 +48,7 @@ var getQueryShareClipRenderStatus = (variables) => ({
4848
variables,
4949
extensions: { persistedQuery: {
5050
version: 1,
51-
sha256Hash: "f130048a462a0ac86bb54d653c968c514e9ab9ca94db52368c1179e97b0f16eb"
51+
sha256Hash: "e0a46b287d760c6890a39d1ccd736af5ec9479a267d02c710e9ac33326b651d2"
5252
} }
5353
});
5454
var getQueryStreamMetadata = (variables) => ({
@@ -258,18 +258,16 @@ const parseMediaPlaylist = (lines) => {
258258
duration: Number.parseFloat(segLines[i].replace(EXTINF, "").replace(",", "")),
259259
map
260260
});
261-
const endlist = lines.includes("#EXT-X-ENDLIST");
262261
return {
263262
type: "playlist",
264263
isMasterPlaylist: false,
265-
endlist,
264+
endlist: lines.includes("#EXT-X-ENDLIST"),
266265
segments
267266
};
268267
};
269268
const parse = (playlist) => {
270269
const lines = playlist.split("\n").map((line) => line.trim()).filter(Boolean);
271-
const isMasterPlaylist = MASTER_PLAYLIST_TAGS.some((s) => lines.some((line) => line.startsWith(`#${s}:`)));
272-
return isMasterPlaylist ? parseMasterPlaylist(lines) : parseMediaPlaylist(lines);
270+
return MASTER_PLAYLIST_TAGS.some((s) => lines.some((line) => line.startsWith(`#${s}:`))) ? parseMasterPlaylist(lines) : parseMediaPlaylist(lines);
273271
};
274272

275273
//#endregion
@@ -316,8 +314,7 @@ const ILLEGAL_PATH_CHARS_MAP = {
316314
"|": "|"
317315
};
318316
const sanitizeFilename = (str) => {
319-
const chars = Object.keys(ILLEGAL_PATH_CHARS_MAP);
320-
const regex = `[${chars.map((c) => c === "\\" ? "\\\\" : c).join("")}]`;
317+
const regex = `[${Object.keys(ILLEGAL_PATH_CHARS_MAP).map((c) => c === "\\" ? "\\\\" : c).join("")}]`;
321318
return str.replace(new RegExp(regex, "g"), (c) => ILLEGAL_PATH_CHARS_MAP[c]);
322319
};
323320
const getOutputPath = (template, videoInfo) => {
@@ -513,8 +510,7 @@ const createLogger = (logPath) => (event) => {
513510
const nameEq = (name) => (event) => event[0] === name;
514511
const getLog = async (logPath) => {
515512
try {
516-
const logContent = await fsp.readFile(logPath, "utf8");
517-
return logContent.split("\n").filter(Boolean).map((line) => line.split(" ").map((v) => JSON.parse(v)));
513+
return (await fsp.readFile(logPath, "utf8")).split("\n").filter(Boolean).map((line) => line.split(" ").map((v) => JSON.parse(v)));
518514
} catch {
519515
return null;
520516
}
@@ -755,8 +751,7 @@ const parseRateLimit = (rateLimit) => {
755751
};
756752
const isUrlsAvailableFetch = async (urls, gzip) => {
757753
try {
758-
const responses = await Promise.all(urls.map((url) => fetch(url, { headers: { "Accept-Encoding": gzip ? "deflate, gzip" : "" } })));
759-
return responses.map((res) => res.ok);
754+
return (await Promise.all(urls.map((url) => fetch(url, { headers: { "Accept-Encoding": gzip ? "deflate, gzip" : "" } })))).map((res) => res.ok);
760755
} catch (e) {
761756
return urls.map(() => false);
762757
}
@@ -886,7 +881,7 @@ const downloadFrag = async (downloader, url, destPath, limitRateArg, gzip, type
886881
//#region src/utils/getDlFormat.ts
887882
const getDlFormat = (formats, formatArg) => {
888883
const dlFormat = formatArg === "best" ? formats[0] : formats.find((f) => f.format_id.toLowerCase() === formatArg.toLowerCase());
889-
if (!dlFormat) throw new Error("Wrong format");
884+
if (!dlFormat) throw new Error(`Wrong format: ${formatArg}`);
890885
return dlFormat;
891886
};
892887

@@ -1091,13 +1086,11 @@ const timeFmt = new Intl.DateTimeFormat(LOCALE, {
10911086
});
10921087
const formatSpeed = (n) => {
10931088
const i = n === 0 ? 0 : Math.floor(Math.log(n) / Math.log(1024));
1094-
const value = n / Math.pow(1024, i);
1095-
return `${value.toFixed(2)}${UNITS[i]}/s`;
1089+
return `${(n / Math.pow(1024, i)).toFixed(2)}${UNITS[i]}/s`;
10961090
};
10971091
const formatSize = (n) => {
10981092
const i = n === 0 ? 0 : Math.floor(Math.log(n) / Math.log(1024));
1099-
const value = n / Math.pow(1024, i);
1100-
return `${value.toFixed(2)}${UNITS[i]}`;
1093+
return `${(n / Math.pow(1024, i)).toFixed(2)}${UNITS[i]}`;
11011094
};
11021095
const showProgress = (downloadedFrags, fragsCount) => {
11031096
const dlFrags = Array.from(downloadedFrags.values());
@@ -1130,6 +1123,7 @@ const showProgress = (downloadedFrags, fragsCount) => {
11301123
const WAIT_BETWEEN_CYCLES_SEC = 60;
11311124
const RETRY_MESSAGE = `Retry every ${WAIT_BETWEEN_CYCLES_SEC} second(s)`;
11321125
const downloadVideo = async (formats, videoInfo, args) => {
1126+
if (formats.length === 0) throw new Error("Cannot get video formats");
11331127
if (args["list-formats"]) {
11341128
showFormats(formats);
11351129
process.exit();
@@ -1297,8 +1291,7 @@ const getVideoInfoByClipMeta = (clipMeta) => ({
12971291
//#region src/utils/downloadWithStreamlink.ts
12981292
const DEFAULT_STREAMLINK_ARGS = ["--twitch-force-client-integrity", "--twitch-access-token-param=playerType=frontpage"];
12991293
const getDefaultOutputTemplate = () => {
1300-
const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", " ").replace(":", "_");
1301-
return `%(uploader)s (live) ${now} [%(id)s].%(ext)s`;
1294+
return `%(uploader)s (live) ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", " ").replace(":", "_")} [%(id)s].%(ext)s`;
13021295
};
13031296
const downloadWithStreamlink = async (link, streamMeta, channelLogin, args) => {
13041297
if (!await isInstalled("streamlink")) throw new Error("streamlink is not installed. Install it from https://streamlink.github.io/");
@@ -1412,12 +1405,10 @@ const getVideoFormats = async (videoId) => {
14121405
if (!accessToken) return [];
14131406
const manifest = await getManifest(videoId, accessToken);
14141407
if (!manifest) return [];
1415-
const formats = parseDownloadFormats(manifest);
1416-
return formats;
1408+
return parseDownloadFormats(manifest);
14171409
};
14181410
const getFullVodPath = (vodPath) => {
1419-
const hashedVodPath = crypto.createHash("sha1").update(vodPath).digest("hex").slice(0, 20);
1420-
return `${hashedVodPath}_${vodPath}`;
1411+
return `${crypto.createHash("sha1").update(vodPath).digest("hex").slice(0, 20)}_${vodPath}`;
14211412
};
14221413
const getVodUrl = (vodDomain, fullVodPath, broadcastType = "ARCHIVE", videoId = "", format = "chunked") => {
14231414
const playlistName = broadcastType === "HIGHLIGHT" ? `highlight-${videoId}` : "index-dvr";
@@ -1449,11 +1440,10 @@ const getAvailableFormats = async (vodDomain, fullVodPath, broadcastType, videoI
14491440
return formats;
14501441
};
14511442
const getVideoFormatsByFullVodPath = async (fullVodPath, broadcastType, videoId) => {
1452-
const responses = await Promise.all(VOD_DOMAINS.map((domain) => {
1443+
const vodDomainIdx = (await Promise.all(VOD_DOMAINS.map((domain) => {
14531444
const url = getVodUrl(domain, fullVodPath, broadcastType, videoId);
14541445
return fetch(url, { method: "HEAD" }).catch(() => null);
1455-
}));
1456-
const vodDomainIdx = responses.findIndex((res) => res?.ok);
1446+
}))).findIndex((res) => res?.ok);
14571447
if (vodDomainIdx === -1) return [];
14581448
return getAvailableFormats(VOD_DOMAINS[vodDomainIdx], fullVodPath, broadcastType, videoId);
14591449
};
@@ -1589,8 +1579,7 @@ const downloadClip = async (slug, args) => {
15891579
const res = await fetch(dlFormat.url, { method: "HEAD" });
15901580
const size = Number.parseInt(res.headers.get("content-length") || "0");
15911581
console.log(`[download] Downloading clip (${(size / 1024 / 1024).toFixed(2)} MB)`);
1592-
const result = await downloadFrag(args.downloader, dlFormat.url, destPath, args["limit-rate"], void 0, "mp4");
1593-
if (!result) throw new Error("[download] Download failed");
1582+
if (!await downloadFrag(args.downloader, dlFormat.url, destPath, args["limit-rate"], void 0, "mp4")) throw new Error("[download] Download failed");
15941583
console.log("[download] Done");
15951584
};
15961585

@@ -1657,13 +1646,11 @@ const mergeFragments = async (outputPath, args) => {
16571646
//#region src/commands/showHelp.ts
16581647
const showHelp = async () => {
16591648
const readmePath = path.resolve(import.meta.dirname, "README.md");
1660-
const readme = await fsp.readFile(readmePath, "utf8");
1661-
const entries = readme.split(/\s## (.*)/g).slice(1);
1649+
const entries = (await fsp.readFile(readmePath, "utf8")).split(/\s## (.*)/g).slice(1);
16621650
const sections = {};
16631651
for (let i = 0; i < entries.length; i += 2) {
16641652
const header = entries[i];
1665-
const content = entries[i + 1].trim();
1666-
sections[header] = content;
1653+
sections[header] = entries[i + 1].trim();
16671654
}
16681655
const help = [
16691656
"Options:",

0 commit comments

Comments
 (0)