feat(website): add the TMDB metadata guide with the required attribution

New guide at /blog/tmdb-metadata-guide/: a sent/not-sent table for the
opt-in enrichment (title, year, app language and the build's API key leave
the machine; nothing about the user, the provider or the playlist does),
how to switch it on, when to use your own free key, what the feature
unlocks, and what the local cache holds. Eight FAQ entries, and a section
for readers who would rather leave it off.

The post states plainly that TMDB is a metadata database and not a content
source, and carries TMDB's required attribution through a reusable
TmdbAttribution component (their logo plus "This product uses the TMDB API
but is not endorsed or certified by TMDB."), the same wording the app shows
in Settings and About.

Its screenshot is the settings section itself: the capture's G5 guard keeps
enrichment disabled for the whole run, so no licensed poster or still can
reach a published frame. The new action stages the switch in the form only,
which reveals the key field, the cache panel and the attribution while the
stored setting stays off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5 committed 2026-09-08 09:12:38 +02:00
1 parent 93e759e1da
commit a7f3860102
10 files changed
+230

No files matched your search

@@ -260,6 +260,39 @@ async function waitForRemoteControlServer(): Promise<void> {
);
}
/**
* Settings › Metadata (TMDB) with the switch staged ON, which is what reveals
* the API-key field, the cache panel and the required TMDB attribution. The
* value is never saved: enrichment must stay off for the whole capture (G5),
* and `discardUnsavedSettings` drops the form before the next action.
*/
async function openSettingsTmdb(page: Page): Promise<void> {
await openSettings(page);
const sectionLink = page
.locator('[data-test-id="settings-section-tmdb"]')
.first();
await sectionLink.waitFor({ state: 'visible', timeout: 15_000 });
await sectionLink.click({ timeout: 10_000 });
await page.waitForURL(/\/workspace\/settings\/tmdb/, { timeout: 15_000 });
const section = page.locator('#tmdb');
await section.waitFor({ state: 'visible', timeout: 15_000 });
const toggle = section.locator(
'[data-test-id="tmdb-enabled"] input[type="checkbox"]'
);
if (!(await toggle.isChecked())) {
await section.locator('[data-test-id="tmdb-enabled"]').click();
}
await section
.locator('[data-test-id="tmdb-api-key"]')
.waitFor({ state: 'visible', timeout: 10_000 });
await page.waitForTimeout(500);
}
export const SETUP_ACTIONS: Readonly<Record<string, CaptureAction>> = {
'open-settings': openSettings,
'open-dashboard': openDashboard,
@@ -268,6 +301,7 @@ export const SETUP_ACTIONS: Readonly<Record<string, CaptureAction>> = {
'open-add-playlist-stalker': openAddPlaylistStalker,
'open-add-playlist-m3u-url': openAddPlaylistM3uUrl,
'open-settings-epg': openSettingsEpg,
'open-settings-tmdb': openSettingsTmdb,
'open-settings-remote-control': openSettingsRemoteControl,
'enable-remote-control': enableRemoteControl,
};
+1
View File
@@ -63,6 +63,7 @@ export const KNOWN_ACTIONS = [
'load-demo-epg',
'open-m3u-channel-menu',
'open-epg-mapping-dialog',
'open-settings-tmdb',
];
/**
+8
View File
@@ -195,6 +195,14 @@
"setup": [
"open-settings-epg-offset"
]
},
{
"slug": "guide-tmdb-settings",
"title": "Settings: Metadata (TMDB)",
"group": "guides",
"setup": [
"open-settings-tmdb"
]
}
]
}
+4
View File
@@ -65,6 +65,10 @@ const GUIDES = [
'blog/guides/screenshots/guide-epg-offset-dark.png',
],
},
{
slug: 'tmdb-metadata-guide',
screenshots: ['blog/guides/screenshots/guide-tmdb-settings-dark.png'],
},
];
const readDist = (relativePath) => readFile(new URL(relativePath, distRoot), 'utf8');