diff --git a/api/src/processing/cookie/manager.js b/api/src/processing/cookie/manager.js index 25f41c2c..30bdab28 100644 --- a/api/src/processing/cookie/manager.js +++ b/api/src/processing/cookie/manager.js @@ -13,7 +13,8 @@ const VALID_SERVICES = new Set([ 'reddit', 'twitter', 'youtube', - 'youtube_oauth' + 'youtube_oauth', + 'threads' ]); const invalidCookies = {}; diff --git a/api/src/processing/match-action.js b/api/src/processing/match-action.js index 363cb403..33d1f65a 100644 --- a/api/src/processing/match-action.js +++ b/api/src/processing/match-action.js @@ -82,6 +82,7 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab switch (host) { case "instagram": case "twitter": + case "threads": case "snapchat": case "bsky": case "xiaohongshu": @@ -156,6 +157,7 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab case "streamable": case "snapchat": case "loom": + case "threads": case "twitch": responseType = "redirect"; break; diff --git a/api/src/processing/match.js b/api/src/processing/match.js index e2d6aa07..b6bfcb2f 100644 --- a/api/src/processing/match.js +++ b/api/src/processing/match.js @@ -26,6 +26,7 @@ import rutube from "./services/rutube.js"; import dailymotion from "./services/dailymotion.js"; import snapchat from "./services/snapchat.js"; import loom from "./services/loom.js"; +import threads from "./services/threads.js"; import facebook from "./services/facebook.js"; import bluesky from "./services/bluesky.js"; import xiaohongshu from "./services/xiaohongshu.js"; @@ -225,6 +226,15 @@ export default async function({ host, patternMatch, params }) { }); break; + case "threads": + r = await threads({ + ...patternMatch, + quality: params.videoQuality, + alwaysProxy: params.alwaysProxy, + dispatcher + }); + break; + case "facebook": r = await facebook({ ...patternMatch, diff --git a/api/src/processing/service-config.js b/api/src/processing/service-config.js index 1dc8bf30..94b64209 100644 --- a/api/src/processing/service-config.js +++ b/api/src/processing/service-config.js @@ -133,6 +133,10 @@ export const services = { "s/:id" ], }, + threads: { + patterns: [":user/post/:id"], + tld: "net", + }, tiktok: { patterns: [ ":user/video/:postId", diff --git a/api/src/processing/service-patterns.js b/api/src/processing/service-patterns.js index 8735f123..b8ca53aa 100644 --- a/api/src/processing/service-patterns.js +++ b/api/src/processing/service-patterns.js @@ -41,6 +41,9 @@ export const testers = { "streamable": pattern => pattern.id?.length <= 6, + "threads": pattern => + pattern.user?.length <= 33 && pattern.id?.length <= 32, + "tiktok": pattern => pattern.postId?.length <= 21 || pattern.shortLink?.length <= 13, diff --git a/api/src/processing/services/threads.js b/api/src/processing/services/threads.js new file mode 100644 index 00000000..90b97e21 --- /dev/null +++ b/api/src/processing/services/threads.js @@ -0,0 +1,142 @@ +import { createStream } from "../../stream/manage.js"; +import { getCookie, updateCookie } from "../cookie/manager.js"; +import { genericUserAgent } from "../../config.js"; + +const commonHeaders = { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.7", + "Cache-Control": "no-cache", + "Dnt": "1", + "Priority": "u=0, i", + "Sec-Ch-Ua": '"Not/A)Brand";v="8", "Chromium";v="126", "Brave";v="126"', + "Sec-Ch-Ua-Mobile": "?0", + "Sec-Ch-Ua-Model": '""', + "Sec-Ch-Ua-Platform": '"Windows"', + "Sec-Ch-Ua-Platform-Version": '"15.0.0"', + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "same-origin", + "Sec-Gpc": "1", + "Upgrade-Insecure-Requests": "1", + "User-Agent": genericUserAgent, +}; + +const DATA_REGEX = /