AlttiRi / twitter-click-and-save

[userscript] Adds buttons to save images and videos in Twitter, also does some other enhancements. (Twitter image and video downloader)
https://greasyfork.org/en/scripts/430132
GNU General Public License v3.0
104 stars 10 forks source link

Unable to download video from sponsored (ad/paid) tweets #6

Open biggestsonicfan opened 2 years ago

biggestsonicfan commented 2 years ago

ArknightsEN has a video tweet here and after playing the video, the red script button doesn't seem to do anything.

AlttiRi commented 2 years ago

Never seen that. Ah, yeah, because it's just an advertisement and I use uBlock Origin.

AlttiRi commented 2 years ago

API endpoint* that I use (when you click on the button over the video) does not contain card info.

It requires to use a different endpoint**, but it also requires an additional investigations for me to implement it.

* https://twitter.com/i/api/2/timeline/conversation/1559446864214183936.json?tweet_mode=extended ** https://twitter.com/i/api/graphql/UrnAThw_XuPHLdlayDWxfQ/TweetDetail?variables=...

biggestsonicfan commented 3 months ago

Found another weird ad tweet.

AlttiRi commented 3 months ago

I added a fix to correctly show the error. https://github.com/AlttiRi/twitter-click-and-save/commit/eb8856bc523383e81c14df98b96ad0302786f268


It's possible to download it, it requires to do some extra action when result.length === 0 in getTweetMedias.

It's so boring to parse Twitter's JSON responses. Here is the unfinished fix (see TODO):

        /**
         * Returns an array like this (https://x.com/kirachem/status/1805456475893928166):
         * [
             {
              "screen_name": "kirachem",
              "tweet_id": "1805456475893928166",
              "download_url": "https://video.twimg.com/amplify_video/1805450004041285634/vid/avc1/1080x1080/2da-wiS9XJ42-9rv.mp4?tag=16",
              "type": "video",
              "type_original": "video",
              "index": 0,
              "type_index": 0,
              "type_index_original": 0,
              "preview_url": "https://pbs.twimg.com/media/GQ4_SPoakAAnW8e.jpg",
              "media_id": "1805450004041285634",
              "media_key": "13_1805450004041285634",
              "expanded_url": "https://twitter.com/kirachem/status/1805456475893928166/video/1",
              "short_expanded_url": "pic.twitter.com/VnOcUSsGaC",
              "short_tweet_url": "https://t.co/VnOcUSsGaC",
              "tweet_text": "Bunny Tifa (Cloud's POV)"
             }
            ]
         */
        static async getTweetMedias(tweetId) {
            const tweetJson = await API.getTweetJson(tweetId);
            const {tweetResult, tweetLegacy, tweetUser} = API.parseTweetJson(tweetJson, tweetId);

            let result = API.parseTweetLegacyMedias(tweetResult, tweetLegacy, tweetUser);

            if (tweetResult.quoted_status_result && tweetResult.quoted_status_result.result /* check is the qouted tweet not deleted */) {
                const tweetResultQuoted = tweetResult.quoted_status_result.result;
                const tweetLegacyQuoted = tweetResultQuoted.legacy;
                const tweetUserQuoted   = tweetResultQuoted.core.user_results.result;
                result = [...result, ...API.parseTweetLegacyMedias(tweetResultQuoted, tweetLegacyQuoted, tweetUserQuoted)];
            }

            // --- HERE ---
            if (result.length === 0) {
                if (tweetResult.card?.legacy?.binding_values?.[0]) {
                    const obj = tweetResult.card.legacy.binding_values[0];
                    if (obj.key === "unified_card" && obj.value && obj.value.type === "STRING") {
                        const value = JSON.parse(obj.value.string_value);
                        console.log("!!!", "todo continue here", value);
                        // TODO: find mp4 and the related data, then return a fake result (an array with a custom object, see the example return value)
                    }
                }
            }
            // --- HERE ---

            return result;
        }

Maybe it's possible to reuse parseTweetLegacyMedias.

AlttiRi commented 3 months ago

Possible, it's just better to find mp4 url with RegEx, then just download it using the username and tweeId from the location url for the filename, without spending time for the parsing large json.