wayBetter for X
This commit is contained in:
commit
2f1e35ba20
720 changed files with 147465 additions and 0 deletions
140
apps/desktop/tests/media.test.ts
Normal file
140
apps/desktop/tests/media.test.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { test } from 'node:test';
|
||||
import { bestMp4, extractTweetMedia, isAllowedMediaUrl, isTappedUrl, mediaFilename, origPhotoUrl } from '../src/inject/media/parse.ts';
|
||||
import { createMediaStore } from '../src/inject/media/store.ts';
|
||||
import { ingestText, installMediaTap } from '../src/inject/media/tap.ts';
|
||||
|
||||
test('origPhotoUrl rewrites every query/suffix form to name=orig', () => {
|
||||
assert.equal(origPhotoUrl('https://pbs.twimg.com/media/Abc.jpg'), 'https://pbs.twimg.com/media/Abc?format=jpg&name=orig');
|
||||
assert.equal(origPhotoUrl('https://pbs.twimg.com/media/Abc.png'), 'https://pbs.twimg.com/media/Abc?format=png&name=orig');
|
||||
assert.equal(origPhotoUrl('https://pbs.twimg.com/media/Abc?format=webp&name=small'), 'https://pbs.twimg.com/media/Abc?format=webp&name=orig');
|
||||
assert.equal(origPhotoUrl('https://pbs.twimg.com/media/Abc?format=jpg&name=900x900'), 'https://pbs.twimg.com/media/Abc?format=jpg&name=orig');
|
||||
assert.equal(origPhotoUrl('https://pbs.twimg.com/media/Abc.jpeg?name=large'), 'https://pbs.twimg.com/media/Abc?format=jpg&name=orig');
|
||||
assert.equal(origPhotoUrl('https://pbs.twimg.com/media/Abc'), 'https://pbs.twimg.com/media/Abc?format=jpg&name=orig');
|
||||
});
|
||||
|
||||
test('bestMp4 picks the highest-bitrate mp4 and ignores HLS', () => {
|
||||
assert.equal(bestMp4([
|
||||
{ content_type: 'application/x-mpegURL', url: 'https://video.twimg.com/a.m3u8' },
|
||||
{ bitrate: 632000, content_type: 'video/mp4', url: 'https://video.twimg.com/low.mp4' },
|
||||
{ bitrate: 2176000, content_type: 'video/mp4', url: 'https://video.twimg.com/high.mp4' },
|
||||
{ bitrate: 950000, content_type: 'video/mp4', url: 'https://video.twimg.com/mid.mp4' },
|
||||
]), 'https://video.twimg.com/high.mp4');
|
||||
assert.equal(bestMp4([{ bitrate: 0, content_type: 'video/mp4', url: 'https://video.twimg.com/gif.mp4' }]), 'https://video.twimg.com/gif.mp4');
|
||||
assert.equal(bestMp4([{ content_type: 'application/x-mpegURL', url: 'x' }]), null);
|
||||
assert.equal(bestMp4(undefined), null);
|
||||
});
|
||||
|
||||
test('mediaFilename follows @user_id_n.ext', () => {
|
||||
assert.equal(mediaFilename('jack', '20', 0, { kind: 'photo', url: 'https://pbs.twimg.com/media/A?format=jpg&name=orig' }), '@jack_20_1.jpg');
|
||||
assert.equal(mediaFilename('jack', '20', 1, { kind: 'photo', url: 'https://pbs.twimg.com/media/A?format=png&name=orig' }), '@jack_20_2.png');
|
||||
assert.equal(mediaFilename('jack', '20', 2, { kind: 'video', url: 'https://video.twimg.com/v.mp4' }), '@jack_20_3.mp4');
|
||||
assert.equal(mediaFilename('jack', '20', 0, { kind: 'animated_gif', url: 'https://video.twimg.com/g.mp4' }), '@jack_20_1.mp4');
|
||||
assert.equal(mediaFilename('../evil', '1/2', 0, { kind: 'video', url: '' }), '@evil_12_1.mp4');
|
||||
});
|
||||
|
||||
const tweet = (id: string, user: string, media: unknown[], extra: Record<string, unknown> = {}) => ({
|
||||
__typename: 'Tweet',
|
||||
rest_id: id,
|
||||
core: { user_results: { result: { __typename: 'User', core: { screen_name: user }, legacy: { screen_name: user } } } },
|
||||
legacy: { id_str: id, full_text: 'hi', extended_entities: { media } },
|
||||
...extra,
|
||||
});
|
||||
const photo = { type: 'photo', media_url_https: 'https://pbs.twimg.com/media/P1.jpg' };
|
||||
const video = { type: 'video', media_url_https: 'https://pbs.twimg.com/ext_tw_video_thumb/1/pu/img/t.jpg', video_info: { variants: [
|
||||
{ bitrate: 256000, content_type: 'video/mp4', url: 'https://video.twimg.com/v/256.mp4' },
|
||||
{ bitrate: 832000, content_type: 'video/mp4', url: 'https://video.twimg.com/v/832.mp4' },
|
||||
{ content_type: 'application/x-mpegURL', url: 'https://video.twimg.com/v/pl.m3u8' },
|
||||
] } };
|
||||
const gif = { type: 'animated_gif', video_info: { variants: [{ bitrate: 0, content_type: 'video/mp4', url: 'https://video.twimg.com/tweet_video/G.mp4' }] } };
|
||||
|
||||
const tweetDetail = { data: { threaded_conversation_with_injections_v2: { instructions: [
|
||||
{ type: 'TimelineAddEntries', entries: [
|
||||
{ entryId: 'tweet-100', content: { entryType: 'TimelineTimelineItem', itemContent: { itemType: 'TimelineTweet', tweet_results: { result: tweet('100', 'alice', [photo, video]) } } } },
|
||||
{ entryId: 'conversationthread-101', content: { entryType: 'TimelineTimelineModule', items: [
|
||||
{ entryId: 'x', item: { itemContent: { tweet_results: { result: { __typename: 'TweetWithVisibilityResults', tweet: tweet('101', 'bob', [gif]) } } } } },
|
||||
] } },
|
||||
{ entryId: 'tweet-102', content: { itemContent: { tweet_results: { result: { __typename: 'Tweet', rest_id: '102', legacy: { full_text: 'no media' } } } } } },
|
||||
] },
|
||||
] } } };
|
||||
|
||||
const homeTimeline = { data: { home: { home_timeline_urt: { instructions: [{ type: 'TimelineAddEntries', entries: [
|
||||
{ entryId: 'tweet-200', content: { itemContent: { tweet_results: { result: tweet('200', 'carol', [photo], {
|
||||
legacy: { id_str: '200', retweeted_status_result: { result: tweet('201', 'dave', [video]) } },
|
||||
}) } } } },
|
||||
] }] } } } };
|
||||
|
||||
test('extractTweetMedia finds nested tweets in TweetDetail', () => {
|
||||
const found = extractTweetMedia(tweetDetail);
|
||||
assert.deepEqual(found.map(t => [t.tweetId, t.username, t.media.map(m => m.kind)]), [
|
||||
['100', 'alice', ['photo', 'video']],
|
||||
['101', 'bob', ['animated_gif']],
|
||||
]);
|
||||
assert.equal(found[0].media[0].url, 'https://pbs.twimg.com/media/P1?format=jpg&name=orig');
|
||||
assert.equal(found[0].media[1].url, 'https://video.twimg.com/v/832.mp4');
|
||||
assert.equal(found[1].media[0].url, 'https://video.twimg.com/tweet_video/G.mp4');
|
||||
});
|
||||
|
||||
test('extractTweetMedia finds retweeted originals in HomeTimeline', () => {
|
||||
const found = extractTweetMedia(homeTimeline);
|
||||
assert.deepEqual(found.map(t => [t.tweetId, t.username]), [['201', 'dave']]);
|
||||
});
|
||||
|
||||
test('ingestText ignores malformed and oversize bodies', () => {
|
||||
const seen: string[] = [];
|
||||
ingestText('{not json', e => seen.push(e.tweetId));
|
||||
ingestText(JSON.stringify(tweetDetail), e => seen.push(e.tweetId));
|
||||
assert.deepEqual(seen, ['100', '101']);
|
||||
});
|
||||
|
||||
test('isTappedUrl / isAllowedMediaUrl', () => {
|
||||
assert.ok(isTappedUrl('https://x.com/i/api/graphql/abc123/TweetDetail?variables=%7B%7D'));
|
||||
assert.ok(isTappedUrl('https://x.com/i/api/graphql/abc/HomeLatestTimeline'));
|
||||
assert.ok(!isTappedUrl('https://x.com/i/api/graphql/abc/FavoriteTweet'));
|
||||
assert.ok(!isTappedUrl('https://x.com/i/api/graphql/abc/TweetDetailFoo'));
|
||||
assert.ok(isAllowedMediaUrl('https://pbs.twimg.com/media/A?format=jpg&name=orig'));
|
||||
assert.ok(isAllowedMediaUrl('https://video.twimg.com/v.mp4'));
|
||||
assert.ok(!isAllowedMediaUrl('https://x.com/i/api/1.1/foo'));
|
||||
assert.ok(!isAllowedMediaUrl('http://pbs.twimg.com/media/A'));
|
||||
});
|
||||
|
||||
test('media store is a bounded LRU', () => {
|
||||
const store = createMediaStore(2);
|
||||
store.set({ tweetId: 'a', username: '', media: [] });
|
||||
store.set({ tweetId: 'b', username: '', media: [] });
|
||||
store.get('a');
|
||||
store.set({ tweetId: 'c', username: '', media: [] });
|
||||
assert.equal(store.size, 2);
|
||||
assert.ok(store.get('a'));
|
||||
assert.equal(store.get('b'), undefined);
|
||||
});
|
||||
|
||||
test('fetch tap returns the original response untouched and reads a clone', async () => {
|
||||
const original = new Response(JSON.stringify(tweetDetail), { status: 200 });
|
||||
const target = { fetch: (async () => original) as typeof fetch };
|
||||
const seen: string[] = [];
|
||||
const handle = installMediaTap(target, e => seen.push(e.tweetId));
|
||||
const response = await target.fetch('https://x.com/i/api/graphql/q/TweetDetail?x=1');
|
||||
assert.equal(response, original);
|
||||
assert.equal(response.bodyUsed, false);
|
||||
assert.equal((await response.json()).data !== undefined, true);
|
||||
await new Promise(r => setTimeout(r, 10));
|
||||
assert.deepEqual(seen, ['100', '101']);
|
||||
const rejecting = { fetch: (async () => { throw new TypeError('net'); }) as typeof fetch };
|
||||
installMediaTap(rejecting, () => {});
|
||||
await assert.rejects(rejecting.fetch('https://x.com/i/api/graphql/q/TweetDetail'), TypeError);
|
||||
handle.dispose();
|
||||
});
|
||||
|
||||
test('the profile media tab (UserVideoTimeline, live 2026-10-01) is tapped and its posts get their media', () => {
|
||||
// X loads the profile's media tab ("Videos") with UserVideoTimeline instead of UserMedia; without
|
||||
// it in the list no post on that tab got a download button (seen live on /<user>/media).
|
||||
assert.ok(isTappedUrl('https://x.com/i/api/graphql/5A9PzD08T6PbvC2QlEYxMg/UserVideoTimeline?variables=%7B%7D'));
|
||||
assert.ok(!isTappedUrl('https://x.com/i/api/graphql/abc/UserVideoTimelineFoo'));
|
||||
// The response's shape as served live (ids, names and text invented).
|
||||
const body = JSON.parse(readFileSync(new URL('./fixtures/x/media/user-video-timeline.json', import.meta.url), 'utf8'));
|
||||
assert.deepEqual(extractTweetMedia(body), [{ tweetId: '1000000000000000001', username: 'someone', media: [
|
||||
{ kind: 'video', url: 'https://video.twimg.com/amplify_video/1000000000000000002/vid/avc1/1280x720/high.mp4' },
|
||||
] }]);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue