diff --git a/src/modules/processing/matchActionDecider.js b/src/modules/processing/matchActionDecider.js index 2d8840a3..44a4a81b 100644 --- a/src/modules/processing/matchActionDecider.js +++ b/src/modules/processing/matchActionDecider.js @@ -137,40 +137,22 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di audioFormat = "best" } + const serviceBestAudio = r.bestAudio || services[host]["bestAudio"]; const isBestAudio = audioFormat === "best"; - const isBestOrMp3 = audioFormat === "mp3" || isBestAudio; - const isBestAudioDefined = isBestAudio && services[host]["bestAudio"]; - const isBestHostAudio = services[host]["bestAudio"] && (audioFormat === services[host]["bestAudio"]); + const isBestOrMp3 = isBestAudio || audioFormat === "mp3"; + const isBestAudioDefined = isBestAudio && serviceBestAudio; + const isBestHostAudio = serviceBestAudio && (audioFormat === serviceBestAudio); - const isTikTok = host === "tiktok" || host === "douyin"; const isTumblrAudio = host === "tumblr" && !r.filename; const isSoundCloud = host === "soundcloud"; - if (isTikTok && services.tiktok.audioFormats.includes(audioFormat)) { - if (r.isMp3 && isBestOrMp3) { - audioFormat = "mp3"; - processType = "bridge" - } else if (isBestAudio) { - audioFormat = "m4a"; - processType = "bridge" - } - } - - if (isSoundCloud && services.soundcloud.audioFormats.includes(audioFormat)) { - if (r.isMp3 && isBestOrMp3) { - audioFormat = "mp3"; - processType = "render" - copy = true - } else if (isBestAudio || audioFormat === "opus") { - audioFormat = "opus"; - processType = "render" - copy = true - } - } - if (isBestAudioDefined || isBestHostAudio) { - audioFormat = services[host]["bestAudio"]; + audioFormat = serviceBestAudio; processType = "bridge"; + if (isSoundCloud) { + processType = "render" + copy = true + } } else if (isBestAudio && !isSoundCloud) { audioFormat = "m4a"; copy = true diff --git a/src/modules/processing/services/soundcloud.js b/src/modules/processing/services/soundcloud.js index 37baee54..6a20acc6 100644 --- a/src/modules/processing/services/soundcloud.js +++ b/src/modules/processing/services/soundcloud.js @@ -4,7 +4,7 @@ import { cleanString } from "../../sub/utils.js"; const cachedID = { version: '', id: '' -}; +} async function findClientID() { try { @@ -32,9 +32,7 @@ async function findClientID() { cachedID.id = clientid; return clientid; - } catch (e) { - return false; - } + } catch {} } export default async function(obj) { @@ -58,27 +56,30 @@ export default async function(obj) { let json = await fetch(`https://api-v2.soundcloud.com/resolve?url=${link}&client_id=${clientId}`).then((r) => { return r.status === 200 ? r.json() : false - }).catch(() => { return false }); + }).catch(() => {}); + if (!json) return { error: 'ErrorCouldntFetch' }; if (!json["media"]["transcodings"]) return { error: 'ErrorEmptyDownload' }; - let isMp3, - selectedStream = json.media.transcodings.filter(v => v.preset === "opus_0_0") + let bestAudio = "opus", + selectedStream = json.media.transcodings.find(v => v.preset === "opus_0_0"); // fall back to mp3 if no opus is available if (selectedStream.length === 0) { - selectedStream = json.media.transcodings.filter(v => v.preset === "mp3_0_0") - isMp3 = true + selectedStream = json.media.transcodings.find(v => v.preset === "mp3_0_0"); + bestAudio = "mp3" } - let fileUrlBase = selectedStream[0]["url"]; + + let fileUrlBase = selectedStream.url; 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 > maxVideoDuration) return { error: ['ErrorLengthAudioConvert', maxVideoDuration / 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 }); + let file = await fetch(fileUrl).then(async (r) => { return (await r.json()).url }).catch(() => {}); if (!file) return { error: 'ErrorCouldntFetch' }; let fileMetadata = { @@ -94,7 +95,7 @@ export default async function(obj) { title: fileMetadata.title, author: fileMetadata.artist }, - isMp3, + bestAudio, fileMetadata } } diff --git a/src/modules/processing/services/tiktok.js b/src/modules/processing/services/tiktok.js index c0dbd8a8..159cfd87 100644 --- a/src/modules/processing/services/tiktok.js +++ b/src/modules/processing/services/tiktok.js @@ -41,8 +41,9 @@ export default async function(obj) { detail = detail?.aweme_list?.find(v => v.aweme_id === postId); if (!detail) return { error: 'ErrorCouldntFetch' }; - let video, videoFilename, audioFilename, isMp3, audio, images, - filenameBase = `tiktok_${detail.author.unique_id}_${postId}`; + let video, videoFilename, audioFilename, audio, images, + filenameBase = `tiktok_${detail.author.unique_id}_${postId}`, + bestAudio = 'm4a'; images = detail.image_post_info?.images; @@ -56,12 +57,12 @@ export default async function(obj) { } else { let fallback = playAddr.url_list[0]; audio = fallback; - audioFilename = `${filenameBase}_audio_fv`; // fv - from video + audioFilename = `${filenameBase}_audio`; if (obj.fullAudio || fallback.includes("music")) { audio = detail.music.play_url.url_list[0] - audioFilename = `${filenameBase}_audio` + audioFilename = `${filenameBase}_audio_original` } - if (audio.slice(-4) === ".mp3") isMp3 = true; + if (audio.slice(-4) === ".mp3") bestAudio = 'mp3'; } if (video) return { @@ -72,7 +73,7 @@ export default async function(obj) { urls: audio, audioFilename: audioFilename, isAudioOnly: true, - isMp3: isMp3 + bestAudio } if (images) { let imageLinks = []; @@ -86,13 +87,13 @@ export default async function(obj) { urls: audio, audioFilename: audioFilename, isAudioOnly: true, - isMp3: isMp3 + bestAudio } } if (audio) return { urls: audio, audioFilename: audioFilename, isAudioOnly: true, - isMp3: isMp3 + bestAudio } } diff --git a/src/modules/processing/services/youtube.js b/src/modules/processing/services/youtube.js index a844f976..ff4ea0ae 100644 --- a/src/modules/processing/services/youtube.js +++ b/src/modules/processing/services/youtube.js @@ -4,7 +4,7 @@ import { cleanString } from '../../sub/utils.js'; const yt = await Innertube.create(); -const c = { +const codecMatch = { h264: { codec: "avc1", aCodec: "mp4a", @@ -23,8 +23,8 @@ const c = { } export default async function(o) { - let info, isDubbed, - quality = o.quality === "max" ? "9000" : o.quality; // 9000(p) - max quality + let info, isDubbed, format = o.format || "h264"; + let quality = o.quality === "max" ? "9000" : o.quality; // 9000(p) - max quality function qual(i) { if (!i.quality_label) { @@ -56,10 +56,16 @@ export default async function(o) { let bestQuality, hasAudio; - let adaptive_formats = info.streaming_data.adaptive_formats.filter(e => - e.mime_type.includes(c[o.format].codec) || e.mime_type.includes(c[o.format].aCodec) + const filterByCodec = (formats) => formats.filter(e => + e.mime_type.includes(codecMatch[format].codec) || e.mime_type.includes(codecMatch[format].aCodec) ).sort((a, b) => Number(b.bitrate) - Number(a.bitrate)); + let adaptive_formats = filterByCodec(info.streaming_data.adaptive_formats); + if (adaptive_formats.length === 0 && format === "vp9") { + format = "h264" + adaptive_formats = filterByCodec(info.streaming_data.adaptive_formats) + } + bestQuality = adaptive_formats.find(i => i.has_video); hasAudio = adaptive_formats.find(i => i.has_audio); @@ -105,14 +111,15 @@ export default async function(o) { isAudioOnly: true, urls: audio.decipher(yt.session.player), filenameAttributes: filenameAttributes, - fileMetadata: fileMetadata + fileMetadata: fileMetadata, + bestAudio: format === "h264" ? 'm4a' : 'opus' } const matchingQuality = Number(quality) > Number(bestQuality) ? bestQuality : quality, - checkSingle = i => qual(i) === matchingQuality && i.mime_type.includes(c[o.format].codec), + checkSingle = i => qual(i) === matchingQuality && i.mime_type.includes(codecMatch[format].codec), checkRender = i => qual(i) === matchingQuality && i.has_video && !i.has_audio; let match, type, urls; - if (!o.isAudioOnly && !o.isAudioMuted && o.format === 'h264') { + if (!o.isAudioOnly && !o.isAudioMuted && format === 'h264') { match = info.streaming_data.formats.find(checkSingle); type = "bridge"; urls = match?.decipher(yt.session.player); @@ -128,8 +135,8 @@ export default async function(o) { if (match) { filenameAttributes.qualityLabel = match.quality_label; filenameAttributes.resolution = `${match.width}x${match.height}`; - filenameAttributes.extension = c[o.format].container; - filenameAttributes.youtubeFormat = o.format; + filenameAttributes.extension = codecMatch[format].container; + filenameAttributes.youtubeFormat = format; return { type, urls, diff --git a/src/modules/processing/servicesConfig.json b/src/modules/processing/servicesConfig.json index 4d61f7be..413854a2 100644 --- a/src/modules/processing/servicesConfig.json +++ b/src/modules/processing/servicesConfig.json @@ -57,7 +57,6 @@ "alias": "tiktok videos, photos & audio", "patterns": [":user/video/:postId", ":id", "t/:id", ":user/photo/:postId"], "subdomains": ["vt", "vm"], - "audioFormats": ["best", "m4a", "mp3"], "enabled": true }, "douyin": { @@ -74,7 +73,6 @@ "soundcloud": { "patterns": [":author/:song/s-:accessKey", ":author/:song", ":shortLink"], "subdomains": ["on", "m"], - "audioFormats": ["best", "opus", "mp3"], "enabled": true }, "instagram": {