From cbfcfcfa18a1ebdc85cb283a6d670d68798e9832 Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Wed, 21 Feb 2024 11:48:00 +0000 Subject: [PATCH] tumblr: rewrite & fix audio support closes #342 --- src/modules/processing/matchActionDecider.js | 12 ++-- src/modules/processing/services/tumblr.js | 74 +++++++++++++++----- src/test/tests.json | 2 +- 3 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/modules/processing/matchActionDecider.js b/src/modules/processing/matchActionDecider.js index 7aa154b..2d8840a 100644 --- a/src/modules/processing/matchActionDecider.js +++ b/src/modules/processing/matchActionDecider.js @@ -143,7 +143,7 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di const isBestHostAudio = services[host]["bestAudio"] && (audioFormat === services[host]["bestAudio"]); const isTikTok = host === "tiktok" || host === "douyin"; - const isTumblr = host === "tumblr" && !r.filename; + const isTumblrAudio = host === "tumblr" && !r.filename; const isSoundCloud = host === "soundcloud"; if (isTikTok && services.tiktok.audioFormats.includes(audioFormat)) { @@ -168,11 +168,6 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di } } - if (isTumblr && isBestOrMp3) { - audioFormat = "mp3"; - processType = "bridge" - } - if (isBestAudioDefined || isBestHostAudio) { audioFormat = services[host]["bestAudio"]; processType = "bridge"; @@ -181,6 +176,11 @@ export default function(r, host, userFormat, isAudioOnly, lang, isAudioMuted, di copy = true } + if (isTumblrAudio && isBestOrMp3) { + audioFormat = "mp3"; + processType = "bridge" + } + if (r.isM3U8 || host === "vimeo") { copy = false; processType = "render" diff --git a/src/modules/processing/services/tumblr.js b/src/modules/processing/services/tumblr.js index 75d354e..05c7fd8 100644 --- a/src/modules/processing/services/tumblr.js +++ b/src/modules/processing/services/tumblr.js @@ -1,8 +1,26 @@ import psl from "psl"; import { genericUserAgent } from "../../config.js"; -export default async function(obj) { - let { subdomain } = psl.parse(obj.url.hostname); +const API_KEY = 'jrsCWX1XDuVxAFO4GkK147syAoN8BJZ5voz8tS80bPcj26Vc5Z'; +const API_BASE = 'https://api-http2.tumblr.com'; + +function request(domain, id) { + const url = new URL(`/v2/blog/${domain}/posts/${id}/permalink`, API_BASE); + url.searchParams.set('api_key', API_KEY); + url.searchParams.set('fields[blogs]', 'uuid,name,avatar,?description,?can_message,?can_be_followed,?is_adult,?reply_conditions,' + + '?theme,?title,?url,?is_blocked_from_primary,?placement_id,?primary,?updated,?followed,' + + '?ask,?can_subscribe,?paywall_access,?subscription_plan,?is_blogless_advertiser,?tumblrmart_accessories'); + + return fetch(url, { + headers: { + 'User-Agent': 'Tumblr/iPhone/33.3/333010/17.3.1/tumblr', + 'X-Version': 'iPhone/33.3/333010/17.3.1/tumblr' + } + }).then(a => a.json()).catch(() => {}); +} + +export default async function(input) { + let { subdomain } = psl.parse(input.url.hostname); if (subdomain?.includes('.')) { return { error: ['ErrorBrokenLink', 'tumblr'] } @@ -10,26 +28,44 @@ export default async function(obj) { subdomain = undefined } - let html = await fetch(`https://${subdomain ?? obj.user}.tumblr.com/post/${obj.id}`, { - headers: { "user-agent": genericUserAgent } - }).then((r) => { return r.text() }).catch(() => { return false }); + const domain = `${subdomain ?? input.user}.tumblr.com`; + const data = await request(domain, input.id); - if (!html) return { error: 'ErrorCouldntFetch' }; + const element = data?.response?.timeline?.elements?.[0]; + if (!element) return { error: 'ErrorEmptyDownload' }; - let r; - if (html.includes('property="og:video" content="https://va.media.tumblr.com/')) { - r = { - urls: `https://va.media.tumblr.com/${html.split('property="og:video" content="https://va.media.tumblr.com/')[1].split('"')[0]}`, - filename: `tumblr_${obj.id}.mp4`, - audioFilename: `tumblr_${obj.id}_audio` - } - } else if (html.includes('property="og:audio" content="https://a.tumblr.com/')) { - r = { - urls: `https://a.tumblr.com/${html.split('property="og:audio" content="https://a.tumblr.com/')[1].split('"')[0]}`, - audioFilename: `tumblr_${obj.id}`, + const contents = [ + ...element.content, + ...element?.trail?.map(t => t.content).flat() + ] + + const audio = contents.find(c => c.type === 'audio'); + if (audio && audio.provider === 'tumblr') { + const fileMetadata = { + title: audio?.title, + artist: audio?.artist + }; + + return { + urls: audio.media.url, + filenameAttributes: { + service: 'tumblr', + id: input.id, + title: fileMetadata.title, + author: fileMetadata.artist + }, isAudioOnly: true } - } else r = { error: 'ErrorEmptyDownload' }; + } - return r + const video = contents.find(c => c.type === 'video'); + if (video && video.provider === 'tumblr') { + return { + urls: video.media.url, + filename: `tumblr_${input.id}.mp4`, + audioFilename: `tumblr_${input.id}_audio` + } + } + + return { error: 'ErrorEmptyDownload' } } diff --git a/src/test/tests.json b/src/test/tests.json index a298d15..3b9a9ed 100644 --- a/src/test/tests.json +++ b/src/test/tests.json @@ -773,7 +773,7 @@ } }, { "name": "tumblr audio", - "url": "https://rf9weu8hjf789234hf9.tumblr.com/post/172006661342/everyone-thats-made-a-video-out-of-this-without", + "url": "https://www.tumblr.com/zedneon/737815079301562368/zedneon-ft-mr-sauceman-tech-n9ne-speed-of?source=share", "params": {}, "expected": { "code": 200,