diff --git a/apps/website/README.md b/apps/website/README.md index 7754a7b35..b1d8ec882 100644 --- a/apps/website/README.md +++ b/apps/website/README.md @@ -163,6 +163,13 @@ Evergreen how-to posts live in the blog collection next to release notes channel ids that match no playlist `tvg-id` on purpose) into the isolated database through the settings, then right-click a channel of the M3U fixture and search the dialog. + The 0.23/0.24 feature articles use the separate `feature-guides` capture + group (`pnpm release:screenshots --group feature-guides --theme dark`). + Playback shots decode the generated local slate documented in + `tools/release/fixtures/README.md`; settings and library shots use the same + isolated demo profile. The multi-channel EPG image is the original image + attached to the v0.24.0 GitHub release, explicitly selected by the maintainer; + its provenance is recorded alongside the assets. It is not a mock capture. `tools/testing/website-guides.test.mjs` (part of `pnpm nx test website`) checks each guide for the FAQPage schema, a link to the download hub and the presence diff --git a/apps/website/public/blog/feature-guides/SOURCES.md b/apps/website/public/blog/feature-guides/SOURCES.md new file mode 100644 index 000000000..9cb1c8871 --- /dev/null +++ b/apps/website/public/blog/feature-guides/SOURCES.md @@ -0,0 +1,11 @@ +# Screenshot provenance + +- `multi-epg-v0-24.jpg`: original JPEG attached to the + [v0.24.0 release](https://github.com/4gray/iptvnator/releases/tag/v0.24.0), + reused at the maintainer's explicit request on 2026-09-27. + [Original attachment](https://github.com/user-attachments/assets/205dc53e-70cf-4c63-a49b-9b976fbeb5e5). + Downloaded without edits; 2648×1626. +- `screenshots/guide-*-dark.png`: actual Electron UI captured with isolated + fictional playlists by the `feature-guides` group in + `tools/release/screenshots.manifest.json`. Playback uses the original local + demo slate documented in `tools/release/fixtures/README.md`. diff --git a/apps/website/public/blog/feature-guides/multi-epg-v0-24.jpg b/apps/website/public/blog/feature-guides/multi-epg-v0-24.jpg new file mode 100644 index 000000000..07d137e13 Binary files /dev/null and b/apps/website/public/blog/feature-guides/multi-epg-v0-24.jpg differ diff --git a/apps/website/public/blog/feature-guides/screenshots/guide-backup-dark.png b/apps/website/public/blog/feature-guides/screenshots/guide-backup-dark.png new file mode 100644 index 000000000..b6a8417d9 Binary files /dev/null and b/apps/website/public/blog/feature-guides/screenshots/guide-backup-dark.png differ diff --git a/apps/website/public/blog/feature-guides/screenshots/guide-fullscreen-channels-dark.png b/apps/website/public/blog/feature-guides/screenshots/guide-fullscreen-channels-dark.png new file mode 100644 index 000000000..b255c5c4c Binary files /dev/null and b/apps/website/public/blog/feature-guides/screenshots/guide-fullscreen-channels-dark.png differ diff --git a/apps/website/public/blog/feature-guides/screenshots/guide-library-watched-dark.png b/apps/website/public/blog/feature-guides/screenshots/guide-library-watched-dark.png new file mode 100644 index 000000000..a9186fa46 Binary files /dev/null and b/apps/website/public/blog/feature-guides/screenshots/guide-library-watched-dark.png differ diff --git a/apps/website/public/blog/feature-guides/screenshots/guide-player-subtitles-dark.png b/apps/website/public/blog/feature-guides/screenshots/guide-player-subtitles-dark.png new file mode 100644 index 000000000..805f9cca1 Binary files /dev/null and b/apps/website/public/blog/feature-guides/screenshots/guide-player-subtitles-dark.png differ diff --git a/apps/website/public/blog/feature-guides/screenshots/guide-stream-info-dark.png b/apps/website/public/blog/feature-guides/screenshots/guide-stream-info-dark.png new file mode 100644 index 000000000..eafc156c4 Binary files /dev/null and b/apps/website/public/blog/feature-guides/screenshots/guide-stream-info-dark.png differ diff --git a/apps/website/public/blog/feature-guides/screenshots/guide-update-channel-dark.png b/apps/website/public/blog/feature-guides/screenshots/guide-update-channel-dark.png new file mode 100644 index 000000000..b179b6e46 Binary files /dev/null and b/apps/website/public/blog/feature-guides/screenshots/guide-update-channel-dark.png differ diff --git a/apps/website/src/content/blog/fullscreen-channel-episode-guide.mdx b/apps/website/src/content/blog/fullscreen-channel-episode-guide.mdx index 72ecd03ed..c2251b10f 100644 --- a/apps/website/src/content/blog/fullscreen-channel-episode-guide.mdx +++ b/apps/website/src/content/blog/fullscreen-channel-episode-guide.mdx @@ -3,6 +3,7 @@ title: Switch Channels and Episodes Without Leaving Fullscreen description: Open IPTVnator's fullscreen side panel with C or the left edge of the video. Search live channels, choose a season, and switch episodes while keeping the player fullscreen. pubDate: 2026-09-27 author: 4gray +heroImage: /iptvnator/blog/feature-guides/screenshots/guide-fullscreen-channels-dark.png tags: - guide - playback @@ -62,6 +63,10 @@ For M3U, **Page Up / Page Down** also let you step through channels without open panel. Those keys have a different job inside the separate programme Guide, where they move between days. +![Fullscreen video with the live-channel list open on the left](/iptvnator/blog/feature-guides/screenshots/guide-fullscreen-channels-dark.png) + +*Press C in player fullscreen to open the channel list. The pictured channels and video are local demo fixtures.* + ## Choose another episode Start an Xtream or Stalker series episode and open the panel the same way. Instead of a diff --git a/apps/website/src/content/blog/library-organization-guide.mdx b/apps/website/src/content/blog/library-organization-guide.mdx index 0f967a626..db4c04d76 100644 --- a/apps/website/src/content/blog/library-organization-guide.mdx +++ b/apps/website/src/content/blog/library-organization-guide.mdx @@ -3,13 +3,14 @@ title: "Keep Your Library Tidy: Sources, Favorites and Watched Titles" description: Review source availability, return from a favorite channel to its playlist, mark titles watched and simplify catalog views in IPTVnator without confusing hidden items with missing data. pubDate: 2026-09-27 author: 4gray +heroImage: /iptvnator/blog/feature-guides/screenshots/guide-library-watched-dark.png tags: - guide draft: true faq: - q: Does an unavailable source mean my channels were deleted? a: No. Availability describes a connection check. A timeout, a temporary outage or an unverified account is different from deleting a saved source or losing its channels. - - q: Does source cleanup delete entries automatically? + - q: Does library watched state delete entries automatically? a: No. The desktop cleanup dialog checks sources and lets you review the selection before deleting. Confirmed expired or disabled accounts can be preselected; uncertain results need review. - q: What happens when I delete a source? a: Its saved favorites, history and playback progress are removed with it. Downloaded files are kept. Export a playlist backup first if you want a restorable copy of supported source state. @@ -83,6 +84,7 @@ Deleting a source also removes its saved favorites, history and playback progres Downloaded files are kept. Hiding a category or collapsing a panel is a better fit when your goal is simply to see less in the current view. + ## Keep watch state in step with what you finished For Xtream and Stalker movies, open the detail page and use **Mark as Watched** when you @@ -94,6 +96,10 @@ Series already have controls for marking a season or the whole series watched. U when you are catching the library up with your viewing, rather than opening every episode just to seek to its end. +![Movie detail page with the watched toggle active beside Play and Favorites](/iptvnator/blog/feature-guides/screenshots/guide-library-watched-dark.png) + +*The checked watched button marks this fictional demo movie as finished. Click it again to mark the movie as unwatched.* + ## Fit more covers on screen Under **Settings → General**, turn off **Show titles under covers** for a denser movie diff --git a/apps/website/src/content/blog/m3u-programme-guide.mdx b/apps/website/src/content/blog/m3u-programme-guide.mdx index 2fc5d7df6..7fc799d00 100644 --- a/apps/website/src/content/blog/m3u-programme-guide.mdx +++ b/apps/website/src/content/blog/m3u-programme-guide.mdx @@ -2,7 +2,9 @@ title: Browse the TV Guide Without Leaving Your Channel description: Keep watching while you compare channel schedules in IPTVnator's M3U programme guide. Open the grid, filter by group or favorites, and switch channels without closing it. pubDate: 2026-09-27 +updatedDate: 2026-09-27 author: 4gray +heroImage: /iptvnator/blog/feature-guides/multi-epg-v0-24.jpg tags: - guide - epg @@ -47,6 +49,10 @@ Opening Guide does not restart the current channel. The same player changes size room for the schedules. Collapse the preview strip when you want more space for the grid, then expand it when you want to see the picture again. +![Multi-channel EPG with the current channel playing above the programme grid](/iptvnator/blog/feature-guides/multi-epg-v0-24.jpg) + +*The multi-channel Guide in v0.24. The highlighted grid button in the top toolbar opens this view. Screenshot from the v0.24.0 release.* + ## Choose the channels you want to compare The rows come from **your playlist**, rather than every channel present in an XMLTV file. diff --git a/apps/website/src/content/blog/player-controls-guide.mdx b/apps/website/src/content/blog/player-controls-guide.mdx index 7ab281bc3..085b2a475 100644 --- a/apps/website/src/content/blog/player-controls-guide.mdx +++ b/apps/website/src/content/blog/player-controls-guide.mdx @@ -3,6 +3,7 @@ title: "Subtitles, Quality and Picture-in-Picture: Get More from the Player" description: Use IPTVnator's unified player controls to choose stream quality, load subtitles, adjust their timing and keep video visible in picture-in-picture. Learn why some options depend on the stream or player. pubDate: 2026-09-27 author: 4gray +heroImage: /iptvnator/blog/feature-guides/screenshots/guide-player-subtitles-dark.png tags: - guide - playback @@ -73,6 +74,10 @@ supports your file rather than simply changing its filename extension. If the stream already includes subtitle tracks, you can choose one directly from the same menu. **Off** hides subtitles without removing the movie or changing the audio track. +![Subtitle menu in the unified player controls with the Load subtitle file action](/iptvnator/blog/feature-guides/screenshots/guide-player-subtitles-dark.png) + +*Open Subtitles from the player control bar to load a subtitle file. Playback here uses a local demonstration stream.* + ## Adjust timing and readability For a selected external subtitle in the web players, use **Show subtitles earlier** or diff --git a/apps/website/src/content/blog/playlist-backup-restore-guide.mdx b/apps/website/src/content/blog/playlist-backup-restore-guide.mdx index a335ca0ad..b731366a1 100644 --- a/apps/website/src/content/blog/playlist-backup-restore-guide.mdx +++ b/apps/website/src/content/blog/playlist-backup-restore-guide.mdx @@ -3,6 +3,7 @@ title: Back Up Your Playlists and Move to a New Computer description: Export and restore an IPTVnator playlist backup, understand which favorites and playback state it carries, and check what needs to be moved separately when changing computers. pubDate: 2026-09-27 author: 4gray +heroImage: /iptvnator/blog/feature-guides/screenshots/guide-backup-dark.png tags: - guide - troubleshooting @@ -45,6 +46,10 @@ Export the receiving computer's library too if it already has sources and favori care about. Restoring a snapshot can replace saved state for a matching source; preserving both backups gives you a record of each library before the import. +![Settings Backup with Import data and Export data actions](/iptvnator/blog/feature-guides/screenshots/guide-backup-dark.png) + +*Settings → Backup contains both actions: Export data creates the backup; Import data restores it.* + ## What travels in the file | Source or data | Included | Separate or rebuilt later | diff --git a/apps/website/src/content/blog/stable-nightly-updates-guide.mdx b/apps/website/src/content/blog/stable-nightly-updates-guide.mdx index c77ac1d33..37e8d4d8a 100644 --- a/apps/website/src/content/blog/stable-nightly-updates-guide.mdx +++ b/apps/website/src/content/blog/stable-nightly-updates-guide.mdx @@ -3,6 +3,7 @@ title: Stable or Nightly? How IPTVnator Updates Work description: Choose an update channel in the desktop app, understand what happens when you switch back to Stable, and find the build details that make a nightly bug report useful. pubDate: 2026-09-27 author: 4gray +heroImage: /iptvnator/blog/feature-guides/screenshots/guide-update-channel-dark.png tags: - guide draft: true @@ -41,6 +42,10 @@ Nightly can be useful when an issue you reported has been fixed but the next sta has not arrived. Include the build you tested when replying to that issue: two people using “nightly” may be running different code. +![Settings About with the Stable and Nightly update-channel choices](/iptvnator/blog/feature-guides/screenshots/guide-update-channel-dark.png) + +*The update-channel selector lives in Settings → About. Choose a channel here, then save the change.* + ## Switch to Nightly 1. Export a playlist backup from **Settings → Backup** and keep the file somewhere you can diff --git a/apps/website/src/content/blog/stream-info-diagnostics-guide.mdx b/apps/website/src/content/blog/stream-info-diagnostics-guide.mdx index c20d23aab..124183f2f 100644 --- a/apps/website/src/content/blog/stream-info-diagnostics-guide.mdx +++ b/apps/website/src/content/blog/stream-info-diagnostics-guide.mdx @@ -3,6 +3,7 @@ title: What Stream Info Can Tell You About Playback Problems description: Read resolution, frame rate, bitrate, buffer and dropped frames in IPTVnator's Stream info panel, then collect a useful playback report without guessing from a single number. pubDate: 2026-09-27 author: 4gray +heroImage: /iptvnator/blog/feature-guides/screenshots/guide-stream-info-dark.png tags: - guide - playback @@ -45,6 +46,10 @@ The panel shows only values the current engine can report. A missing audio bitra source frame rate is not a zero reading. Immediately after starting, the panel may say there is no stream data yet. +![Stream info panel showing metadata for a playing local demonstration stream](/iptvnator/blog/feature-guides/screenshots/guide-stream-info-dark.png) + +*The information button opens Stream info over the video. These values describe the local demonstration stream used for this screenshot.* + ## Read the values in context | Field | What it helps you understand | diff --git a/tools/release/capture-fixtures.ts b/tools/release/capture-fixtures.ts index f6c52760c..8017d78b0 100644 --- a/tools/release/capture-fixtures.ts +++ b/tools/release/capture-fixtures.ts @@ -89,10 +89,12 @@ export function writeM3uFixture(dataDir: string): string { ['Culture', 'Atlas Culture', 'atlas-culture'], ['Culture', 'Night Music', 'night-music'], ]; - const stream = `${XTREAM_MOCK_ORIGIN}/live/marketing/marketing/52000.m3u8`; const lines = ['#EXTM3U']; channels.forEach(([group, title, slug], index) => { + // Distinct URLs keep the player from highlighting every demo channel + // as the current one when a fullscreen list is captured. + const stream = `${XTREAM_MOCK_ORIGIN}/live/marketing/marketing/${52000 + index}.m3u8`; lines.push( `#EXTINF:-1 tvg-id="demo-${index + 1}" tvg-name="${title}" tvg-logo="${XTREAM_MOCK_ORIGIN}/assets/marketing/logo/${slug}.svg?size=256x256" group-title="${group}",${title}`, stream diff --git a/tools/release/capture-navigation-feature-actions.ts b/tools/release/capture-navigation-feature-actions.ts new file mode 100644 index 000000000..ff83082f4 --- /dev/null +++ b/tools/release/capture-navigation-feature-actions.ts @@ -0,0 +1,94 @@ +/** Real UI captures for the 0.23/0.24 feature guides, using local demo media. */ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import type { Page } from '@playwright/test'; +import { type CaptureAction, openXtreamSection } from './capture-navigation-helpers'; +import { openM3uGroups } from './capture-navigation-portal-actions'; +import { openSettings } from './capture-navigation-setup-actions'; + +async function openSettingsSection(page: Page, section: string): Promise { + await openSettings(page); + await page.locator(`[data-test-id="settings-section-${section}"]`).click(); + await page.locator(`#${section}`).waitFor({ state: 'visible' }); +} + +async function openUpdateChannel(page: Page): Promise { + await openSettingsSection(page, 'about'); + await page.locator('[data-test-id="select-update-channel"]').click(); + await page.locator('[data-test-id="update-channel-nightly"]').waitFor(); +} + +async function openBackup(page: Page): Promise { + await openSettingsSection(page, 'backup'); +} + +async function openLibraryWatched(page: Page): Promise { + await openXtreamSection(page, 'vod', 'Action & Mystery'); + const watched = page.locator('[data-testid="vod-watched-toggle"]'); + await watched.waitFor(); + if ((await watched.getAttribute('aria-label')) === 'Mark as Watched') { + await watched.click(); + } + await page.getByRole('button', { name: 'Mark as Unwatched', exact: true }).waitFor(); +} + +async function openDemoPlayback(page: Page): Promise { + await openSettingsSection(page, 'playback'); + const player = page.locator('[data-test-id="select-video-player"]'); + if (!(await player.innerText()).includes('HTML5')) { + await player.click(); + await page.locator('[data-test-id="html5"]').click(); + await page.locator('[data-test-id="save-settings"]').click(); + await page.locator('[data-test-id="save-settings"]').waitFor({ state: 'hidden' }); + } + // A local generated H.264 slate, never a provider stream. Use MPEG-TS + // segments so the normal M3U/HLS player path decodes it without overrides. + const fixture = path.resolve('tools/release/fixtures/guide-demo.mpegts'); + await page.route('**/live/marketing/marketing/*.m3u8', (route) => + route.fulfill({ + contentType: 'application/vnd.apple.mpegurl', + body: '#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:20\n#EXT-X-MEDIA-SEQUENCE:0\n#EXTINF:20.0,\nhttp://localhost:3211/demo/guide-demo.ts\n#EXT-X-ENDLIST\n', + }) + ); + await page.route('**/demo/guide-demo.ts', (route) => + route.fulfill({ contentType: 'video/mp2t', body: readFileSync(fixture) }) + ); + await openM3uGroups(page); + await page.locator('[data-test-id="channel-item"]').first().click(); + await page.waitForFunction(() => { + const video = document.querySelector('video'); + return video && video.readyState >= 2 && video.videoWidth === 1280; + }, undefined, { timeout: 25_000 }); + // Freeze decoded media, not the interface, for a repeatable illustration. + await page.locator('video').evaluate((video: HTMLVideoElement) => video.pause()); + await page.locator('video').hover(); +} + +async function openFullscreenChannels(page: Page): Promise { + await openDemoPlayback(page); + await page.getByRole('button', { name: 'Enter fullscreen', exact: true }).click(); + await page.keyboard.press('c'); + await page.locator('[data-test-id="fullscreen-channel-panel"][aria-hidden="false"]').waitFor(); + await page.locator('[data-test-id="m3u-fullscreen-view-all"]').click(); +} + +async function openSubtitles(page: Page): Promise { + await openDemoPlayback(page); + await page.getByRole('button', { name: 'Subtitles', exact: true }).click(); + await page.locator('[data-test-id="player-controls-load-subtitle"]').waitFor(); +} + +async function openStreamInfo(page: Page): Promise { + await openDemoPlayback(page); + await page.locator('[data-test-id="player-controls-stream-info-button"]').click(); + await page.locator('[data-test-id="player-controls-stream-info-panel"]').waitFor(); +} + +export const FEATURE_ACTIONS: Readonly> = { + 'open-update-channel': openUpdateChannel, + 'open-backup': openBackup, + 'open-library-watched': openLibraryWatched, + 'open-fullscreen-channels': openFullscreenChannels, + 'open-player-subtitles': openSubtitles, + 'open-stream-info': openStreamInfo, +}; diff --git a/tools/release/capture-navigation.ts b/tools/release/capture-navigation.ts index 6c9e0e8d4..0e9c5e291 100644 --- a/tools/release/capture-navigation.ts +++ b/tools/release/capture-navigation.ts @@ -25,6 +25,7 @@ import { DOWNLOAD_ACTIONS } from './capture-navigation-download-actions'; import { EPG_ACTIONS } from './capture-navigation-epg-actions'; import { PORTAL_ACTIONS } from './capture-navigation-portal-actions'; import { SETUP_ACTIONS } from './capture-navigation-setup-actions'; +import { FEATURE_ACTIONS } from './capture-navigation-feature-actions'; export { clickDialogOption, @@ -41,6 +42,7 @@ const ACTIONS: Readonly> = { ...PORTAL_ACTIONS, ...DOWNLOAD_ACTIONS, ...EPG_ACTIONS, + ...FEATURE_ACTIONS, }; /* ------------------------------------------------------------------ */ @@ -93,6 +95,12 @@ export async function runAction( // dialog open or a dirty settings form. Clear whatever the previous step // left behind before this one starts navigating: a dialog backdrop // swallows every click, and unsaved settings raise a leave prompt. + await page.evaluate(async () => { + if (document.fullscreenElement) await document.exitFullscreen(); + }); + if (await page.locator('.cdk-overlay-backdrop-showing').count()) { + await page.keyboard.press('Escape'); + } await dismissDialogs(page); await discardUnsavedSettings(page); await run(page, param); diff --git a/tools/release/fixtures/README.md b/tools/release/fixtures/README.md new file mode 100644 index 000000000..62274da5f --- /dev/null +++ b/tools/release/fixtures/README.md @@ -0,0 +1,27 @@ +# Guide playback fixture + +`guide-demo.mpegts` is an original, synthetic 20-second H.264 video slate at +1280×720 / 25 fps, with no audio or third-party footage. Feature-guide captures +serve it through a local HLS manifest, decode it with the real HTML5 player, +and pause the decoded frame for repeatability. No player state or statistics +are fabricated. Subtitle menus use the app's normal file-selection flow. + +To regenerate on macOS with FFmpeg (Helvetica is a system font): + +```sh +ffmpeg -f lavfi -i 'gradients=s=1280x720:r=25:c0=0x14243e:c1=0x507f9f:n=2:d=20:speed=0.01:seed=24' \ + -vf "drawtext=fontfile=/System/Library/Fonts/Helvetica.ttc:text='IPTVnator':fontcolor=white:fontsize=52:x=(w-text_w)/2:y=(h-text_h)/2-30,drawtext=fontfile=/System/Library/Fonts/Helvetica.ttc:text='DEMO STREAM':fontcolor=white@0.6:fontsize=20:x=(w-text_w)/2:y=(h-text_h)/2+35" \ + -c:v libx264 -preset fast -crf 28 -pix_fmt yuv420p -g 50 -an \ + -f mpegts tools/release/fixtures/guide-demo.mpegts +``` + +Capture the six feature-guide screens with: + +```sh +pnpm nx run electron-backend:build-e2e +pnpm release:screenshots --group feature-guides --theme dark +``` + +The EPG article uses the original GitHub release image selected by the +maintainer, not this fixture. Its source is documented in +`apps/website/public/blog/feature-guides/SOURCES.md`. diff --git a/tools/release/fixtures/guide-demo.mpegts b/tools/release/fixtures/guide-demo.mpegts new file mode 100644 index 000000000..3e324895d Binary files /dev/null and b/tools/release/fixtures/guide-demo.mpegts differ diff --git a/tools/release/screenshot-guards.mjs b/tools/release/screenshot-guards.mjs index f92d0576a..9d7641010 100644 --- a/tools/release/screenshot-guards.mjs +++ b/tools/release/screenshot-guards.mjs @@ -40,6 +40,12 @@ export const HOST_RESOLVER_RULES = /** Setup actions the driver implements. Manifest steps must match. */ export const KNOWN_ACTIONS = [ + 'open-update-channel', + 'open-backup', + 'open-library-watched', + 'open-fullscreen-channels', + 'open-player-subtitles', + 'open-stream-info', 'open-dashboard', 'open-settings', 'open-xtream-vod', diff --git a/tools/release/screenshots.manifest.json b/tools/release/screenshots.manifest.json index acb7e70d8..d9dc53582 100644 --- a/tools/release/screenshots.manifest.json +++ b/tools/release/screenshots.manifest.json @@ -203,6 +203,54 @@ "setup": [ "open-settings-tmdb" ] + }, + { + "slug": "guide-update-channel", + "group": "feature-guides", + "description": "update channel", + "setup": [ + "open-update-channel" + ] + }, + { + "slug": "guide-backup", + "group": "feature-guides", + "description": "backup", + "setup": [ + "open-backup" + ] + }, + { + "slug": "guide-library-watched", + "group": "feature-guides", + "description": "library watched state", + "setup": [ + "open-library-watched" + ] + }, + { + "slug": "guide-player-subtitles", + "group": "feature-guides", + "description": "player subtitles", + "setup": [ + "open-player-subtitles" + ] + }, + { + "slug": "guide-stream-info", + "group": "feature-guides", + "description": "stream info", + "setup": [ + "open-stream-info" + ] + }, + { + "slug": "guide-fullscreen-channels", + "group": "feature-guides", + "description": "fullscreen channels", + "setup": [ + "open-fullscreen-channels" + ] } ] } diff --git a/tools/testing/website-guides.test.mjs b/tools/testing/website-guides.test.mjs index dbcf4e0de..fb35f0a28 100644 --- a/tools/testing/website-guides.test.mjs +++ b/tools/testing/website-guides.test.mjs @@ -17,7 +17,7 @@ const SITE = 'https://4gray.github.io/iptvnator'; const GUIDES = [ { slug: 'm3u-programme-guide', - screenshots: [], + screenshots: ['blog/feature-guides/multi-epg-v0-24.jpg'], }, { slug: 'xtream-codes-setup-guide', @@ -80,6 +80,31 @@ const GUIDES = [ const readDist = (relativePath) => readFile(new URL(relativePath, distRoot), 'utf8'); +const FEATURE_GUIDE_SHOTS = { + 'stable-nightly-updates-guide': 'guide-update-channel', + 'playlist-backup-restore-guide': 'guide-backup', + 'library-organization-guide': 'guide-library-watched', + 'player-controls-guide': 'guide-player-subtitles', + 'stream-info-diagnostics-guide': 'guide-stream-info', + 'fullscreen-channel-episode-guide': 'guide-fullscreen-channels', +}; + +if (process.env.PUBLIC_INCLUDE_DRAFTS === 'true') { + for (const [slug, shot] of Object.entries(FEATURE_GUIDE_SHOTS)) { + GUIDES.push({ slug, screenshots: [`blog/feature-guides/screenshots/${shot}-dark.png`] }); + } +} + +test('feature guide drafts reference screenshots that ship with the site', async () => { + for (const [slug, shot] of Object.entries(FEATURE_GUIDE_SHOTS)) { + const source = await readFile(new URL(`../../apps/website/src/content/blog/${slug}.mdx`, import.meta.url), 'utf8'); + const screenshot = `blog/feature-guides/screenshots/${shot}-dark.png`; + assert.ok(source.includes(`](/iptvnator/${screenshot})`), `${slug}: inline screenshot`); + assert.ok(source.includes(`heroImage: /iptvnator/${screenshot}`), `${slug}: card image`); + await access(new URL(screenshot, distRoot)); + } +}); + const HUB_GROUPS = ['getting-started', 'live-tv-epg', 'playback', 'library', 'updates']; const DRAFT_GUIDES = [ 'm3u-programme-guide', 'stable-nightly-updates-guide', 'fullscreen-channel-episode-guide',