diff --git a/package.json b/package.json index 6ed165f9..296a4264 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cobalt", "description": "save what you love", - "version": "7.6.5", + "version": "7.6.6", "author": "wukko", "exports": "./src/cobalt.js", "type": "module", diff --git a/src/modules/processing/match.js b/src/modules/processing/match.js index c0d7ccf9..9857cdb1 100644 --- a/src/modules/processing/match.js +++ b/src/modules/processing/match.js @@ -32,8 +32,7 @@ export default async function(host, patternMatch, url, lang, obj) { switch (host) { case "twitter": r = await twitter({ - id: patternMatch["id"] ? patternMatch["id"] : false, - spaceId: patternMatch["spaceId"] ? patternMatch["spaceId"] : false + id: patternMatch["id"] }); break; case "vk": @@ -87,7 +86,7 @@ export default async function(host, patternMatch, url, lang, obj) { r = await tumblr({ id: patternMatch["id"], url: url, - user: patternMatch["user"] ? patternMatch["user"] : false + user: patternMatch["user"] || false }); break; case "vimeo": @@ -101,10 +100,11 @@ export default async function(host, patternMatch, url, lang, obj) { case "soundcloud": isAudioOnly = true; r = await soundcloud({ + url: url, author: patternMatch["author"], - song: patternMatch["song"], url: url, - shortLink: patternMatch["shortLink"] ? patternMatch["shortLink"] : false, - accessKey: patternMatch["accessKey"] ? patternMatch["accessKey"] : false, + song: patternMatch["song"], + shortLink: patternMatch["shortLink"] || false, + accessKey: patternMatch["accessKey"] || false, format: obj.aFormat }); break; @@ -115,10 +115,14 @@ export default async function(host, patternMatch, url, lang, obj) { }) break; case "vine": - r = await vine({ id: patternMatch["id"] }); + r = await vine({ + id: patternMatch["id"] + }); break; case "pinterest": - r = await pinterest({ id: patternMatch["id"] }); + r = await pinterest({ + id: patternMatch["id"] + }); break; case "streamable": r = await streamable({ @@ -129,7 +133,7 @@ export default async function(host, patternMatch, url, lang, obj) { break; case "twitch": r = await twitch({ - clipId: patternMatch["clip"] ? patternMatch["clip"] : false, + clipId: patternMatch["clip"] || false, quality: obj.vQuality, isAudioOnly: obj.isAudioOnly }); @@ -148,9 +152,11 @@ export default async function(host, patternMatch, url, lang, obj) { if (r.isAudioOnly) isAudioOnly = true; let isAudioMuted = isAudioOnly ? false : obj.isAudioMuted; - if (r.error) return apiJSON(0, { t: Array.isArray(r.error) ? loc(lang, r.error[0], r.error[1]) : loc(lang, r.error) }); + if (r.error) return apiJSON(0, { + t: Array.isArray(r.error) ? loc(lang, r.error[0], r.error[1]) : loc(lang, r.error) + }) - return matchActionDecider(r, host, obj.aFormat, isAudioOnly, lang, isAudioMuted, disableMetadata, obj.filenamePattern); + return matchActionDecider(r, host, obj.aFormat, isAudioOnly, lang, isAudioMuted, disableMetadata, obj.filenamePattern) } catch (e) { return apiJSON(0, { t: genericError(lang, host) }) } diff --git a/src/modules/processing/services/twitter.js b/src/modules/processing/services/twitter.js index a2510413..da3b2b44 100644 --- a/src/modules/processing/services/twitter.js +++ b/src/modules/processing/services/twitter.js @@ -15,8 +15,7 @@ export default async function(obj) { }; let activateURL = `https://api.twitter.com/1.1/guest/activate.json`; - let graphqlTweetURL = `https://twitter.com/i/api/graphql/0hWvDhmW8YQ-S_ib3azIrw/TweetResultByRestId`; - let graphqlSpaceURL = `https://twitter.com/i/api/graphql/Gdz2uCtmIGMmhjhHG3V7nA/AudioSpaceById`; + let graphqlTweetURL = `https://twitter.com/i/api/graphql/5GOHgZe-8U2j5sVHQzEm9A/TweetResultByRestId`; let req_act = await fetch(activateURL, { method: "POST", @@ -30,92 +29,59 @@ export default async function(obj) { _headers["x-guest-token"] = req_act["guest_token"]; _headers["cookie"] = `guest_id=v1%3A${req_act["guest_token"]}`; - if (obj.id) { - let query = { - variables: {"tweetId": obj.id, "withCommunity": false, "includePromotedContent": false, "withVoice": false}, - features: {"creator_subscriptions_tweet_preview_api_enabled":true,"tweetypie_unmention_optimization_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"responsive_web_twitter_article_tweet_consumption_enabled":false,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":true,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":true,"longform_notetweets_rich_text_read_enabled":true,"longform_notetweets_inline_media_enabled":true,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"responsive_web_media_download_video_enabled":false,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"responsive_web_enhance_cards_enabled":false} - } - query.variables = new URLSearchParams(JSON.stringify(query.variables)).toString().slice(0, -1); - query.features = new URLSearchParams(JSON.stringify(query.features)).toString().slice(0, -1); - query = `${graphqlTweetURL}?variables=${query.variables}&features=${query.features}`; - - let TweetResultByRestId = await fetch(query, { headers: _headers }).then((r) => { return r.status === 200 ? r.json() : false }).catch((e) => { return false }); - - // {"data":{"tweetResult":{"result":{"__typename":"TweetUnavailable","reason":"Protected"}}}} - if (!TweetResultByRestId || TweetResultByRestId.data.tweetResult.result.__typename !== "Tweet") return { error: 'ErrorTweetUnavailable' }; - - let baseMedia, - baseTweet = TweetResultByRestId.data.tweetResult.result.legacy; - - if (baseTweet.retweeted_status_result && baseTweet.retweeted_status_result.result.legacy.extended_entities.media) { - baseMedia = baseTweet.retweeted_status_result.result.legacy.extended_entities - } else if (baseTweet.extended_entities && baseTweet.extended_entities.media) { - baseMedia = baseTweet.extended_entities - } - if (!baseMedia) return { error: 'ErrorNoVideosInTweet' }; - - let single, multiple = [], media = baseMedia["media"]; - media = media.filter((i) => { if (i["type"] === "video" || i["type"] === "animated_gif") return true }); - - if (media.length > 1) { - for (let i in media) { multiple.push({type: "video", thumb: media[i]["media_url_https"], url: bestQuality(media[i]["video_info"]["variants"])}) } - } else if (media.length === 1) { - single = bestQuality(media[0]["video_info"]["variants"]) - } else { - return { error: 'ErrorNoVideosInTweet' } - } - - if (single) { - return { - urls: single, - filename: `twitter_${obj.id}.mp4`, - audioFilename: `twitter_${obj.id}_audio` - } - } else if (multiple) { - return { picker: multiple } - } else { - return { error: 'ErrorNoVideosInTweet' } - } + let query = { + variables: { "tweetId": obj.id, "withCommunity": false, "includePromotedContent": false, "withVoice": false }, + features: { "creator_subscriptions_tweet_preview_api_enabled": true, "c9s_tweet_anatomy_moderator_badge_enabled": true, "tweetypie_unmention_optimization_enabled": true, "responsive_web_edit_tweet_api_enabled": true, "graphql_is_translatable_rweb_tweet_is_translatable_enabled": true, "view_counts_everywhere_api_enabled": true, "longform_notetweets_consumption_enabled": true, "responsive_web_twitter_article_tweet_consumption_enabled": false, "tweet_awards_web_tipping_enabled": false, "responsive_web_home_pinned_timelines_enabled": true, "freedom_of_speech_not_reach_fetch_enabled": true, "standardized_nudges_misinfo": true, "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": true, "longform_notetweets_rich_text_read_enabled": true, "longform_notetweets_inline_media_enabled": true, "responsive_web_graphql_exclude_directive_enabled": true, "verified_phone_label_enabled": false, "responsive_web_media_download_video_enabled": false, "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false, "responsive_web_graphql_timeline_navigation_enabled": true, "responsive_web_enhance_cards_enabled": false } } - // spaces no longer work with guest authorization - if (obj.spaceId) { - _headers["host"] = "twitter.com"; - _headers["content-type"] = "application/json"; + query.variables = encodeURIComponent(JSON.stringify(query.variables)); + query.features = encodeURIComponent(JSON.stringify(query.features)); + query = `${graphqlTweetURL}?variables=${query.variables}&features=${query.features}`; - let query = { - variables: {"id": obj.spaceId,"isMetatagsQuery":true,"withDownvotePerspective":false,"withReactionsMetadata":false,"withReactionsPerspective":false,"withReplays":true}, - features: {"spaces_2022_h2_clipping":true,"spaces_2022_h2_spaces_communities":true,"responsive_web_twitter_blue_verified_badge_is_enabled":true,"responsive_web_graphql_exclude_directive_enabled":true,"verified_phone_label_enabled":false,"responsive_web_graphql_skip_user_profile_image_extensions_enabled":false,"tweetypie_unmention_optimization_enabled":true,"vibe_api_enabled":true,"responsive_web_edit_tweet_api_enabled":true,"graphql_is_translatable_rweb_tweet_is_translatable_enabled":true,"view_counts_everywhere_api_enabled":true,"longform_notetweets_consumption_enabled":true,"tweet_awards_web_tipping_enabled":false,"freedom_of_speech_not_reach_fetch_enabled":false,"standardized_nudges_misinfo":true,"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled":false,"responsive_web_graphql_timeline_navigation_enabled":true,"interactive_text_enabled":true,"responsive_web_text_conversations_enabled":false,"longform_notetweets_richtext_consumption_enabled":false,"responsive_web_enhance_cards_enabled":false} + let tweet = await fetch(query, { headers: _headers }).then((r) => { + return r.status === 200 ? r.json() : false + }).catch((e) => { return false }); + + // {"data":{"tweetResult":{"result":{"__typename":"TweetUnavailable","reason":"Protected"}}}} + if (tweet?.data?.tweetResult?.result?.__typename !== "Tweet") { + return { error: 'ErrorTweetUnavailable' } + } + + let baseMedia, + baseTweet = tweet.data.tweetResult.result.legacy; + + if (baseTweet.retweeted_status_result?.result.legacy.extended_entities.media) { + baseMedia = baseTweet.retweeted_status_result.result.legacy.extended_entities + } else if (baseTweet.extended_entities?.media) { + baseMedia = baseTweet.extended_entities + } + if (!baseMedia) return { error: 'ErrorNoVideosInTweet' }; + + let single, multiple = [], media = baseMedia["media"]; + media = media.filter((i) => { if (i["type"] === "video" || i["type"] === "animated_gif") return true }); + + if (media.length > 1) { + for (let i in media) { + multiple.push({ + type: "video", + thumb: media[i]["media_url_https"], + url: bestQuality(media[i]["video_info"]["variants"]) + }) } - query.variables = new URLSearchParams(JSON.stringify(query.variables)).toString().slice(0, -1); - query.features = new URLSearchParams(JSON.stringify(query.features)).toString().slice(0, -1); - query = `${graphqlSpaceURL}?variables=${query.variables}&features=${query.features}`; - - let AudioSpaceById = await fetch(query, { headers: _headers }).then((r) => {return r.status === 200 ? r.json() : false}).catch((e) => { return false }); - if (!AudioSpaceById) return { error: 'ErrorEmptyDownload' }; - - if (!AudioSpaceById.data.audioSpace.metadata) return { error: 'ErrorEmptyDownload' }; - if (AudioSpaceById.data.audioSpace.metadata.is_space_available_for_replay !== true) return { error: 'TwitterSpaceWasntRecorded' }; - - let streamStatus = await fetch( - `https://twitter.com/i/api/1.1/live_video_stream/status/${AudioSpaceById.data.audioSpace.metadata.media_key}`, { headers: _headers } - ).then((r) =>{ return r.status === 200 ? r.json() : false }).catch(() => { return false }); - if (!streamStatus) return { error: 'ErrorCouldntFetch' }; - - let participants = AudioSpaceById.data.audioSpace.participants.speakers, - listOfParticipants = `Twitter Space speakers: `; - for (let i in participants) { listOfParticipants += `@${participants[i]["twitter_screen_name"]}, ` } - listOfParticipants = listOfParticipants.slice(0, -2); + } else if (media.length === 1) { + single = bestQuality(media[0]["video_info"]["variants"]) + } else { + return { error: 'ErrorNoVideosInTweet' } + } + if (single) { return { - urls: streamStatus.source.noRedirectPlaybackUrl, - audioFilename: `twitterspaces_${obj.spaceId}`, - isAudioOnly: true, - fileMetadata: { - title: AudioSpaceById.data.audioSpace.metadata.title, - artist: `Twitter Space by @${AudioSpaceById.data.audioSpace.metadata.creator_results.result.legacy.screen_name}`, - comment: listOfParticipants, - // cover: AudioSpaceById.data.audioSpace.metadata.creator_results.result.legacy.profile_image_url_https.replace("_normal", "") - } + urls: single, + filename: `twitter_${obj.id}.mp4`, + audioFilename: `twitter_${obj.id}_audio` } + } else if (multiple) { + return { picker: multiple } + } else { + return { error: 'ErrorNoVideosInTweet' } } } diff --git a/src/modules/processing/servicesConfig.json b/src/modules/processing/servicesConfig.json index 1deece4d..6425f0f7 100644 --- a/src/modules/processing/servicesConfig.json +++ b/src/modules/processing/servicesConfig.json @@ -13,7 +13,7 @@ }, "twitter": { "alias": "twitter videos & voice", - "patterns": [":user/status/:id", ":user/status/:id/video/:v", "i/spaces/:spaceId"], + "patterns": [":user/status/:id", ":user/status/:id/video/:v"], "enabled": true }, "vk": { diff --git a/src/modules/processing/servicesPatternTesters.js b/src/modules/processing/servicesPatternTesters.js index 89d0f03f..b4c75a8c 100644 --- a/src/modules/processing/servicesPatternTesters.js +++ b/src/modules/processing/servicesPatternTesters.js @@ -1,6 +1,5 @@ export const testers = { - "twitter": (patternMatch) => (patternMatch["id"] && patternMatch["id"].length < 20) - || (patternMatch["spaceId"] && patternMatch["spaceId"].length === 13), + "twitter": (patternMatch) => (patternMatch["id"] && patternMatch["id"].length < 20), "vk": (patternMatch) => (patternMatch["userId"] && patternMatch["videoId"] && patternMatch["userId"].length <= 10 && patternMatch["videoId"].length <= 10),