support alwaysProxy in threads

This commit is contained in:
Snazzah 2024-10-08 15:23:29 -05:00
parent 687649a5e4
commit 1dd4903700
No known key found for this signature in database
GPG key ID: EA479766A94CEB61
2 changed files with 38 additions and 22 deletions

View file

@ -235,6 +235,7 @@ export default async function({ host, patternMatch, params }) {
r = await threads({ r = await threads({
...patternMatch, ...patternMatch,
quality: params.videoQuality, quality: params.videoQuality,
alwaysProxy: params.alwaysProxy,
dispatcher dispatcher
}); });
break; break;

View file

@ -22,8 +22,8 @@ const commonHeaders = {
const DATA_REGEX = /<script type="application\/json" {2}data-content-len="\d+" data-sjs>({"require":\[\["ScheduledServerJS","handle",null,\[{"__bbox":{"require":\[\["RelayPrefetchedStreamCache(?:(?:@|\\u0040)[0-9a-f]{32})?","next",\[],\["adp_BarcelonaPostPageQueryRelayPreloader_[0-9a-f]{23}",[^\n]+})<\/script>\n/; const DATA_REGEX = /<script type="application\/json" {2}data-content-len="\d+" data-sjs>({"require":\[\["ScheduledServerJS","handle",null,\[{"__bbox":{"require":\[\["RelayPrefetchedStreamCache(?:(?:@|\\u0040)[0-9a-f]{32})?","next",\[],\["adp_BarcelonaPostPageQueryRelayPreloader_[0-9a-f]{23}",[^\n]+})<\/script>\n/;
export default async function({ user, id, quality, dispatcher }) { export default async function({ user, id, quality, alwaysProxy, dispatcher }) {
const cookie = getCookie('threads'); const cookie = getCookie("threads");
const response = await fetch(`https://www.threads.net/${user}/post/${id}`, { const response = await fetch(`https://www.threads.net/${user}/post/${id}`, {
headers: { headers: {
...commonHeaders, ...commonHeaders,
@ -34,44 +34,46 @@ export default async function({ user, id, quality, dispatcher }) {
if (cookie) updateCookie(cookie, response.headers); if (cookie) updateCookie(cookie, response.headers);
if (response.status !== 200) { if (response.status !== 200) {
return { error: 'fetch.fail' }; return { error: "fetch.fail" };
} }
const html = await response.text(); const html = await response.text();
const dataString = html.match(DATA_REGEX)?.[1]; const dataString = html.match(DATA_REGEX)?.[1];
if (!dataString) { if (!dataString) {
return { error: 'fetch.fail' }; return { error: "fetch.fail" };
} }
const data = JSON.parse(dataString); const data = JSON.parse(dataString);
const post = data?.require?.[0]?.[3]?.[0]?.__bbox?.require?.[0]?.[3]?.[1]?.__bbox?.result?.data?.data?.edges[0]?.node?.thread_items[0]?.post; const post = data?.require?.[0]?.[3]?.[0]?.__bbox?.require?.[0]?.[3]?.[1]?.__bbox?.result?.data?.data?.edges[0]?.node?.thread_items[0]?.post;
if (!post) { if (!post) {
return { error: 'fetch.fail' }; return { error: "fetch.fail" };
} }
const filenameBase = `threads_${post.user.username}_${post.code}`;
// Video // Video
if (post.media_type === 2) { if (post.media_type === 2) {
if (!post.video_versions) { if (!post.video_versions) {
return { error: 'fetch.empty' }; return { error: "fetch.empty" };
} }
// types: 640p = 101, 480p = 102, 480p-low = 103 // types: 640p = 101, 480p = 102, 480p-low = 103
const selectedQualityType = quality === 'max' ? 101 : quality && parseInt(quality) <= 480 ? 102 : 101; const selectedQualityType = quality === "max" ? 101 : quality && parseInt(quality) <= 480 ? 102 : 101;
const video = post.video_versions.find((v) => v.type === selectedQualityType) || post.video_versions.sort((a, b) => a.type - b.type)[0]; const video = post.video_versions.find((v) => v.type === selectedQualityType) || post.video_versions.sort((a, b) => a.type - b.type)[0];
if (!video) { if (!video) {
return { error: 'fetch.empty' }; return { error: "fetch.empty" };
} }
return { return {
urls: video.url, urls: video.url,
filename: `threads_${user}_${id}.mp4`, filename: `${filenameBase}.mp4`,
audioFilename: `threads_${user}_${id}_audio` audioFilename: `${filenameBase}_audio`
} }
} }
// Photo // Photo
if (post.media_type === 1) { if (post.media_type === 1) {
if (!post.image_versions2?.candidates) { if (!post.image_versions2?.candidates) {
return { error: 'fetch.empty' }; return { error: "fetch.empty" };
} }
return { return {
@ -83,23 +85,36 @@ export default async function({ user, id, quality, dispatcher }) {
// Mixed // Mixed
if (post.media_type === 8) { if (post.media_type === 8) {
if (!post.carousel_media) { if (!post.carousel_media) {
return { error: 'fetch.empty' }; return { error: "fetch.empty" };
} }
return { return {
picker: post.carousel_media.map((media) => ({ picker: post.carousel_media.map((media, i) => {
type: media.video_versions ? 'video' : 'photo', const type = media.video_versions ? "video" : "photo";
url: media.video_versions ? media.video_versions[0].url : media.image_versions2.candidates[0].url, let url = media.video_versions ? media.video_versions[0].url : media.image_versions2.candidates[0].url;
/* thumbnails have `Cross-Origin-Resource-Policy` const thumbProxy = createStream({
** set to `same-origin`, so we need to proxy them */
thumb: createStream({
service: "threads", service: "threads",
type: "proxy", type: "proxy",
u: media.image_versions2.candidates[0].url u: media.image_versions2.candidates[0].url,
filename: `${filenameBase}_${i}.jpg`,
});
if (alwaysProxy) {
url = type === 'photo' ? thumbProxy : createStream({
service: "threads",
type: "proxy",
u: media.video_versions[0].url,
filename: `${filenameBase}_${i}.mp4`,
});
}
return {
type,
url,
thumb: thumbProxy
}
}) })
}))
} }
} }
return { error: 'fetch.fail' }; return { error: "fetch.fail" };
} }