From 2884bd908165232385b1b722afd94e4ebe25c5e0 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 1 Mar 2023 08:37:26 +0600 Subject: [PATCH] 5.1.1 - bigger video/audio duration limit (3 hours instead of 2 hours and 5 minutes). - no more unexpected errors when downloading audio from youtube. --- package.json | 4 +- src/config.json | 3 +- src/modules/changelog/changelog.json | 2 +- src/modules/config.js | 1 - src/modules/processing/services/soundcloud.js | 4 +- src/modules/processing/services/youtube.js | 38 +++++++++++-------- src/test/tests.json | 21 ++++++++++ 7 files changed, 49 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index d151875c..ab01ad51 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cobalt", "description": "save what you love", - "version": "5.1", + "version": "5.1.1", "author": "wukko", "exports": "./src/cobalt.js", "type": "module", @@ -33,6 +33,6 @@ "node-cache": "^5.1.2", "url-pattern": "1.0.3", "xml-js": "^1.6.11", - "youtubei.js": "^3.0.0" + "youtubei.js": "^3.1.0" } } diff --git a/src/config.json b/src/config.json index 34f6ec96..3caf79fb 100644 --- a/src/config.json +++ b/src/config.json @@ -1,7 +1,6 @@ { "streamLifespan": 120000, - "maxVideoDuration": 7500000, - "maxAudioDuration": 7500000, + "maxVideoDuration": 10800000, "genericUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", "authorInfo": { "name": "wukko", diff --git a/src/modules/changelog/changelog.json b/src/modules/changelog/changelog.json index 8a32a945..29ed5517 100644 --- a/src/modules/changelog/changelog.json +++ b/src/modules/changelog/changelog.json @@ -3,7 +3,7 @@ "version": "5.1", "title": "the evil has been defeated", "banner": "happymeowth.webp", - "content": "hey, ever wanted to download a youtube video without a hassle? cobalt is here to help. this update fixes all issues related to youtube downloads.\nnot only that, but it also introduces features never before seen in a downloader, such as youtube dub downloads! read below to see what's up :)\n\ntl;dr:\n*; audio in youtube videos FINALLY no longer gets cut off.\n*; you now can pick any video resolution you want (from 360p to 8k) and any possible youtube video codec (h264/av1/vp9).\n*; you now can download youtube videos with dubs in your native language. just check settings > audio.\n*; youtube processing has been vastly sped up.\n\nok, now onto the nerdy part of changelog. this update is pretty huge and includes improvements across the board.\n\nservice improvements:\n*; all youtube functionality has been reworked. cobalt now relies on innertube apis, not web scraping.\n*; random audio cut off issue has been fixed, let me know if it ever occurs again. (closes #62, #66, #75, #88).\n*; added support for youtube dubs. currently it's using your browser's default language when enabled, but i have plans on making a picker. i'll ask people on twitter and mastodon if this feature is needed, and add a picker in next updates.\n*; instead of adding more quality presets, i added granular quality options. pick whatever you like, from 360p up to 4320p (for all services, not just youtube).\n*; replaced a format picker with codec picker for youtube. you can pick h264, av1, or vp9. all of them should work as expected (closes #88).\n*; youtube audio files are now properly matched to corresponding video files.\n*; it's now always possible to download pristine h264 720p/360p videos from youtube. these videos will work ANYWHERE, so they're default for mobile.\n*; youtube requests are no longer permanently cached, ram usage should drop even further.\n*; youtube video and audio file names now include codec and dub language when applicable.\n*; general performance of entire youtube download process has been greatly improved.\n*; vk module has been reworked to be more compact and not make use of outdated technique of quality picking. should also be way more reliable.\n\ninternal improvements:\n*; cleaned up services config, all constants have been moved directly to modules for quicker access.\n*; matching module has been slightly cleaned up.\n\ninterface improvements:\n*; many descriptions and error messages have been slightly tuned to be less wordy.\n*; unnecessary title duplications in settings have been merged into one.\n*; added more clarity to quality and codec descriptions.\n\nif you use cobalt api, please note that you have to update your creation to support new features.\n\nthis is the second batch of 5.x improvements, there's way more to come. thank you for being here, i really appreciate your support.\n\nif you want to thank me (the developer), there's a nice tab under this changelog that has \"donations\" text on it. anything helps me continue developing and hosting the friendliest media downloader :D" + "content": "hey, ever wanted to download a youtube video without a hassle? cobalt is here to help. this update fixes all issues related to youtube downloads.\nnot only that, but it also introduces features never before seen in a downloader, such as youtube dub downloads! read below to see what's up :)\n\ntl;dr:\n*; audio in youtube videos FINALLY no longer gets cut off.\n*; you now can pick any video resolution you want (from 360p to 8k) and any possible youtube video codec (h264/av1/vp9).\n*; you now can download youtube videos with dubs in your native language. just check settings > audio.\n*; youtube processing has been vastly sped up.\n\nok, now onto the nerdy part of changelog. this update is pretty huge and includes improvements across the board.\n\nservice improvements:\n*; all youtube functionality has been reworked. cobalt now relies on innertube apis, not web scraping.\n*; random audio cut off issue has been fixed, let me know if it ever occurs again. (closes #62, #66, #75, #88).\n*; added support for youtube dubs. currently it's using your browser's default language when enabled, but i have plans on making a picker. i'll ask people on twitter and mastodon if this feature is needed, and add a picker in next updates.\n*; instead of adding more quality presets, i added granular quality options. pick whatever you like, from 360p up to 4320p (for all services, not just youtube).\n*; replaced a format picker with codec picker for youtube. you can pick h264, av1, or vp9. all of them should work as expected (closes #88).\n*; youtube audio files are now properly matched to corresponding video files.\n*; it's now always possible to download pristine h264 720p/360p videos from youtube. these videos will work ANYWHERE, so they're default for mobile.\n*; youtube requests are no longer permanently cached, ram usage should drop even further.\n*; youtube video and audio file names now include codec and dub language when applicable.\n*; max video and audio duration limits have been bumped up to 3 hours.\n*; general performance of entire youtube download process has been greatly improved.\n*; vk module has been reworked to be more compact and not make use of outdated technique of quality picking. should also be way more reliable.\n\ninternal improvements:\n*; cleaned up services config, all constants have been moved directly to modules for quicker access.\n*; matching module has been slightly cleaned up.\n\ninterface improvements:\n*; many descriptions and error messages have been slightly tuned to be less wordy.\n*; unnecessary title duplications in settings have been merged into one.\n*; added more clarity to quality and codec descriptions.\n\nif you use cobalt api, please note that you have to update your creation to support new features.\n\nthis is the second batch of 5.x improvements, there's way more to come. thank you for being here, i really appreciate your support.\n\nif you want to thank me (the developer), there's a nice tab under this changelog that has \"donations\" text on it. anything helps me continue developing and hosting the friendliest media downloader :D" }, "history": [{ "version": "5.0", diff --git a/src/modules/config.js b/src/modules/config.js index a6767da4..64dfca7d 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -10,7 +10,6 @@ export const version = packageJson.version, streamLifespan = config.streamLifespan, maxVideoDuration = config.maxVideoDuration, - maxAudioDuration = config.maxAudioDuration, genericUserAgent = config.genericUserAgent, repo = packageJson["bugs"]["url"].replace('/issues', ''), authorInfo = config.authorInfo, diff --git a/src/modules/processing/services/soundcloud.js b/src/modules/processing/services/soundcloud.js index aa3a695a..24f7f6ba 100644 --- a/src/modules/processing/services/soundcloud.js +++ b/src/modules/processing/services/soundcloud.js @@ -1,4 +1,4 @@ -import { maxAudioDuration } from "../../config.js"; +import { maxVideoDuration } from "../../config.js"; let cachedID = {}; @@ -58,7 +58,7 @@ export default async function(obj) { let fileUrl = `${fileUrlBase}${fileUrlBase.includes("?") ? "&" : "?"}client_id=${clientId}&track_authorization=${json.track_authorization}`; if (fileUrl.substring(0, 54) !== "https://api-v2.soundcloud.com/media/soundcloud:tracks:") return { error: 'ErrorEmptyDownload' }; - if (json.duration > maxAudioDuration) return { error: ['ErrorLengthAudioConvert', maxAudioDuration / 60000] }; + if (json.duration > maxVideoDuration) return { error: ['ErrorLengthAudioConvert', maxVideoDuration / 60000] }; let file = await fetch(fileUrl).then(async (r) => { return (await r.json()).url }).catch(() => { return false }); if (!file) return { error: 'ErrorCouldntFetch' }; diff --git a/src/modules/processing/services/youtube.js b/src/modules/processing/services/youtube.js index 2690a0db..9e4936ed 100644 --- a/src/modules/processing/services/youtube.js +++ b/src/modules/processing/services/youtube.js @@ -33,27 +33,20 @@ export default async function(o) { if (info.playability_status.status !== 'OK') return { error: 'ErrorYTUnavailable' }; if (info.basic_info.is_live) return { error: 'ErrorLiveVideo' }; - let adaptive_formats = info.streaming_data.adaptive_formats.filter((e) => { + let bestQuality, hasAudio, adaptive_formats = info.streaming_data.adaptive_formats.filter((e) => { if (e["mime_type"].includes(c[o.format].codec) || e["mime_type"].includes(c[o.format].aCodec)) return true }).sort((a, b) => Number(b.bitrate) - Number(a.bitrate)); - let bestQuality = adaptive_formats[0]['quality_label'].split('p')[0]; - let checkSingle = (i) => ((i['quality_label'].split('p')[0] === quality || i['quality_label'].split('p')[0] === bestQuality) && i["mime_type"].includes(c[o.format].codec)); - let checkBestAudio = (i) => (i["has_audio"] && !i["has_video"]); - let checkBestVideo = (i) => (i['quality_label'].split('p')[0] === bestQuality && !i["has_audio"] && i["has_video"]); - let checkRightVideo = (i) => (i['quality_label'].split('p')[0] === quality && !i["has_audio"] && i["has_video"]); - - if (!o.isAudioOnly && !o.isAudioMuted) { - let single = info.streaming_data.formats.find(i => checkSingle(i)); - if (single) return { - type: "bridge", - urls: single.url, - filename: `youtube_${o.id}_${single.width}x${single.height}_${o.format}.${c[o.format].container}` - } - }; + bestQuality = adaptive_formats.find(i => i["has_video"]); + hasAudio = adaptive_formats.find(i => i["has_audio"]); + if (bestQuality) bestQuality = bestQuality['quality_label'].split('p')[0]; + if (!bestQuality && !o.isAudioOnly || !hasAudio) return { error: 'ErrorYTTryOtherCodec' }; if (info.basic_info.duration > maxVideoDuration / 1000) return { error: ['ErrorLengthLimit', maxVideoDuration / 60000] }; + + let checkBestAudio = (i) => (i["has_audio"] && !i["has_video"]); let audio = adaptive_formats.find(i => checkBestAudio(i) && i["is_original"]); + if (o.dubLang) { let dubbedAudio = adaptive_formats.find(i => checkBestAudio(i) && i["language"] === o.dubLang); if (dubbedAudio) { @@ -61,7 +54,7 @@ export default async function(o) { isDubbed = true } } - if (o.isAudioOnly) { + if (hasAudio && o.isAudioOnly) { let r = { type: "render", isAudioOnly: true, @@ -79,6 +72,19 @@ export default async function(o) { if (descItems[4].startsWith("Released on:")) r.fileMetadata.date = descItems[4].replace("Released on: ", '').trim(); }; return r + } + + let checkSingle = (i) => ((i['quality_label'].split('p')[0] === quality || i['quality_label'].split('p')[0] === bestQuality) && i["mime_type"].includes(c[o.format].codec)); + let checkBestVideo = (i) => (i['quality_label'].split('p')[0] === bestQuality && !i["has_audio"] && i["has_video"]); + let checkRightVideo = (i) => (i['quality_label'].split('p')[0] === quality && !i["has_audio"] && i["has_video"]); + + if (!o.isAudioOnly && !o.isAudioMuted) { + let single = info.streaming_data.formats.find(i => checkSingle(i)); + if (single) return { + type: "bridge", + urls: single.url, + filename: `youtube_${o.id}_${single.width}x${single.height}_${o.format}.${c[o.format].container}` + } }; let video = adaptive_formats.find(i => ((Number(quality) > Number(bestQuality)) ? checkBestVideo(i) : checkRightVideo(i))); diff --git a/src/test/tests.json b/src/test/tests.json index db052782..d46baf87 100644 --- a/src/test/tests.json +++ b/src/test/tests.json @@ -365,6 +365,27 @@ "code": 200, "status": "stream" } + }, { + "name": "audio bitrate higher than video, no vp9 video in response (mp3, isAudioOnly)", + "url": "https://www.youtube.com/watch?v=t5nC_ucYBrc", + "params": { + "aFormat": "mp3", + "isAudioOnly": true + }, + "expected": { + "code": 200, + "status": "stream" + } + }, { + "name": "audio bitrate higher than video, no vp9 video in response (vp9)", + "url": "https://www.youtube.com/watch?v=t5nC_ucYBrc", + "params": { + "vCodec": "vp9" + }, + "expected": { + "code": 400, + "status": "error" + } }, { "name": "short, defaults", "url": "https://www.youtube.com/shorts/r5FpeOJItbw",