The MIT notice carried "Copyright 2020-2021" with no holder, which is the one
line the licence needs to be attributable: the permission grant refers back to
"the above copyright notice", and a notice naming nobody is weak to point at
when the project is republished without attribution.
Uses the handle the project already publishes as its author in package.json.
The range now starts at 2019, the year of the first commit, and runs to the
current year. The canonical "MIT License" title line is added so automated
licence detection matches the file exactly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every page pulled its three typefaces from fonts.googleapis.com and
fonts.gstatic.com, each blog post fetched the author avatar from
githubusercontent.com, and the giscus client script ran on page load. That is
four outside origins contacted before a reader does anything, each costing a
DNS lookup and a TLS handshake on the critical path.
Fonts now come from the @fontsource packages the app already uses and are
emitted as .woff2 beside the site; the avatar is a 3 KB file in public/; and
the giscus embed is created by a "Show comments" button that carries the
configuration as data attributes, so the script is only built when a reader
asks for it.
A delivered page now makes no third-party request at all, verified across the
whole build. The variable Bricolage package names itself "Bricolage Grotesque
Variable", so that exact name leads the display stack in the Tailwind config.
The giscus test now checks the button configuration and asserts the client
script is absent from the delivered HTML.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"TiviMate for PC" is the query behind most of this page's traffic, so the
page now names the apps people mean instead of describing a category. What
it says about them is what could actually be verified: TiviMate's Google
Play listing (Android and Android TV, reads M3U, Xtream Codes and Stalker,
"a media player that provides no content"), and IPTV Smarters publishing
Windows and macOS builds — which is why the page does not claim TV apps
have no desktop version.
The useful half is the part no affiliate site writes: these names are
heavily abused, the stores carry copycat listings, and much of the search
result for any of them is someone selling "subscriptions" under the app's
brand. The developers are player authors who do not sell channels, which is
exactly what IPTVnator says about itself — the page links to our own
unofficial-websites post for the same reason.
Three FAQ entries answer the query directly, and the page joins the
NAMES_THIRD_PARTY_SOFTWARE set so the test requires its dated disclaimer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Third page in the set, and the one that answers the question people ask
before installing anything: where should IPTV actually run. It compares the
two places rather than two products — everything that needs a keyboard
(adding a provider, attaching a guide, mapping a channel, working out why a
stream fails) against everything that needs a couch — and states plainly
that IPTVnator has no TV build, with the phone remote as the answer for a
laptop wired to the television.
No third-party software is named, so the page carries no ThirdPartyNote: the
rows describe the ordinary experience of each kind of device and say so in
the caption.
The compare hub's lead no longer claims every page is a choice inside the
app, and the three newest entries get distinct icons.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Second page under the third-party rules. Kodi is a media center that reaches
IPTV through a PVR add-on, so the comparison is about shape rather than
score: where the source comes in, what each program is built for, and the
platforms. The tables say plainly what Kodi does that IPTVnator does not —
a local library with scraped metadata, add-ons and skins, a remote-friendly
interface and builds for TV boxes and a Raspberry Pi — and the verdict tells
a reader already running Kodi on a TV to stay there.
Kodi's IPTV capabilities come from whichever PVR client is installed, so
rows that depend on one say "Depends on the add-on" instead of claiming
something this page cannot verify.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
First comparison page that names other software. It answers the question
every "IPTV on a computer" guide raises — VLC opens the playlist, so why a
dedicated player — with three tables (what each can connect to, what a
playlist file cannot carry, and the practical differences) and eight FAQ
entries, then ends where the honest answer is: IPTVnator hands VLC the
stream, with the playlist's headers and the resume position, and reads the
position back.
Naming another project brings rules, now recorded in the registry doc
comment and a new README section: a dated ThirdPartyNote stating the other
project is independent and endorses nothing, only stable publicly documented
facts, no claim of a missing feature that was not checked, and no logos,
brand styling or links to the other project. The compare test enforces the
note and the date for every page in its NAMES_THIRD_PARTY_SOFTWARE set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
New post at /blog/epg-wrong-program-fix/: a table separating the three
symptoms (an empty row, a channel showing another station's schedule, and
every programme shifted by a fixed amount), how the tvg-id → tvg-name → name
lookup chain produces the first two, the manual "Map EPG channel" flow, and
the display-only EPG time offset with the sign to use. Seven FAQ entries.
The three screenshots are mock-backed. The Xtream mock gains /demo/guide.xml,
an XMLTV guide for its marketing live channels whose ids deliberately match no
playlist tvg-id, which is what makes the manual mapping worth showing; the
capture imports it through the settings, accepting the private-network
confirmation a loopback source raises, then right-clicks a channel of the M3U
fixture and searches the dialog. The new actions live in
capture-navigation-epg-actions.ts, alongside the setup, portal and download
modules, and borrow one entry point from each of its two neighbours instead of
duplicating their navigation.
Also fixes assertMockServerIdentity: it checked both expected categories
against get_vod_categories, but "Urban Drama" is a series category, so the
reuse path rejected every already-running mock and a capture could only run
when the port happened to be free. Each category is now checked against its
own endpoint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
New guide at /blog/remote-control-guide/: enabling the remote in Settings,
opening it on a phone from the QR code, what each control does and which
list it navigates, a checklist for a page that does not load, and why the
remote must stay on the local network. Eight FAQ entries. The remote-control
feature page now links to it instead of the M3U guide.
Both screenshots are mock-backed. The phone view is the first "browser" shot:
a manifest entry names a loopback URL and a mobile viewport, and the capture
frames it in a separate Chromium page behind the same network and content
guards, with the manifest validator accepting loopback origins only. The
setup saves the remote-control setting so the app's own server answers, then
selects a live channel; the Xtream mock's marketing scenario now serves live
stream URLs from local bytes so that selection never leaves the machine.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
New guide at /blog/alternative-sources-guide/: where the Sources chip comes
from (a local lookup across the reader's own Xtream playlists, never a search
outside them), how to read fact tags versus ~guesses, check availability,
switch playlists mid-film without losing the timecode, pin a preferred copy
per movie, and what the opt-in auto-switch does and on which players. Eight
FAQ entries, opening with the general ContentDisclaimer.
The two screenshots are mock-backed: the Xtream mock gains a marketing2
scenario that serves the identical marketing catalog under a second
credential pair (with a spec proving the catalogs match), the capture seeds it
as a "Fictional Xtream Backup" source only for shots that walk into it, opens
its category so the movies reach the local content cache that discovery reads,
and two new setup actions open the chip and the checked popover.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
New guide at /blog/offline-downloads-guide/: what the desktop download manager
can save, choosing the folder, downloading a movie, episodes and seasons,
following the queue (pause, resume, automatic reconnects), the offline
library, and the Needs attention states, with a nine-question FAQ. The prose
frames the feature as offline viewing of content the reader already streams
and defers legality to the provider's terms and local law.
ContentDisclaimer.astro carries that notice in a general and an offline
variant so later guides reuse it instead of rewording it.
The three screenshots are mock-backed captures. The Xtream mock's marketing
scenario now serves movie and episode stream URLs from generated local bytes
(downloadStreamFixture: 'local-media'), because the capture's network gate
rejects the public HLS stub every other scenario redirects to; the capture
stubs Electron's folder dialog so "Change Folder" authorizes a folder inside
the isolated data dir rather than the real OS Downloads folder; two new setup
actions queue a movie and two episodes and open the manager and the offline
detail.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tags were decorative: a plain span on every card and post header, with no
page, filter or search behind them, and a vocabulary of 32 slugs across 16
posts where 22 slugs appeared once. They now form a closed vocabulary of ten
topics (src/lib/blog-tags.ts) that the content collection schema enforces, so
an unknown slug fails the build instead of minting a new tag.
Every used tag gets a hub at /blog/tag/<tag>/ with CollectionPage and
BreadcrumbList structured data; the blog index and the hubs show a "Topics"
rail with post counts; and every chip links to its hub. The cards become
<article> elements with the title link stretched over the card, because chip
links cannot nest inside a card that is one big <a>.
Posts are retagged to the new vocabulary. A structural test covers the rail,
each hub, the chip targets, the sitemap and the absence of nested anchors.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The blog card kept the date and every tag in one non-wrapping flex row, so a
post with five or six tags overflowed the card frame and the date broke across
three lines inside the three-column grid. The date now sits on its own
non-wrapping line, tags wrap below it, and only the first three render as chips
while the rest collapse into a "+N" chip whose tooltip lists them. The featured
card still shows every tag.
The homepage rendered two cards under the featured post and left the third grid
column empty; it now shows three.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Publish /download/docker/ so "Self-host it with Docker" no longer sends
visitors off-site to the developer README. The page covers what the
image contains, a four-step quick start with the repository compose
command and an image-only compose snippet, the environment variables
and port that matter, the published tag patterns with the update
command, what the browser version leaves out, and a seven-question FAQ.
It links to docker/README.md for the full reference, to the desktop vs
browser comparison and to the setup guides; the README links back.
The download hub gains a fourth card, the homepage and hub links point
at the page, the platform switcher shows a Docker card on the OS pages,
and the comparison page links both the page and the README. The page
emits SoftwareApplication / FAQPage / BreadcrumbList JSON-LD and is
covered by website-download-pages.test.mjs.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Publish /compare/ with three pages answering the choices the app asks
users to make: M3U vs Xtream Codes vs Stalker portal, the built-in web
players vs embedded MPV vs external MPV/VLC, and the desktop app vs the
self-hosted browser version. Each opens with a one-paragraph verdict,
carries a feature matrix whose cells are yes, no or a qualifier, and
emits WebPage / FAQPage / BreadcrumbList JSON-LD — not
SoftwareApplication, because guidance is not a product listing.
These compare IPTVnator's own options against each other rather than
naming other products, so every cell is checkable against this
repository. Pages that name competitors remain phase 3's open half; the
plan now records what has to be decided first.
A registry in src/lib/comparisons.ts drives the hub, the switcher and
the tests. The header gains a Compare entry and the features hub links
across. tools/testing/website-compare-pages.test.mjs checks canonical
URLs, the verdict block, the table, schema, cross-links and sitemap
entries.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Publish /features/ with one landing page per feature people search for:
M3U playlist player, Xtream Codes player, Stalker portal player, TV
guide (EPG) and phone remote control. Each page composes a feature hero
(download and setup-guide calls to action) with the download-page
sections, carries SoftwareApplication (featureList) / FAQPage /
BreadcrumbList structured data, links to the other feature pages and
the matching guides, and uses only mock-backed screenshots.
A registry in src/lib/features.ts drives the hub, the per-page
switcher, the homepage feature cards (now links) and the header
Features entry, so a new page is one registry entry and one .astro
file. tools/testing/website-feature-pages.test.mjs checks canonical
URLs, schema, cross-links, hub coverage and sitemap entries.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`Build on macos arm64` started failing on master with
security set-key-partition-list -S apple-tool:,apple: -s -k *** <tmp>.keychain
SecKeychainUnlock: The user name or passphrase you entered is not correct.
Every failure ran the `macos-26-arm64` runner image 20260831.0337; the same
job on image 20260728.0273 kept passing, as did `macos-15-intel`. The image
exposed an electron-builder bug: `app-builder-lib` hands the certificate's
`.p12` import password to `set-key-partition-list -k`, which authenticates
against the temporary keychain and therefore needs the keychain's own
generated password. Older macOS builds accepted the wrong password once the
keychain was unlocked; the new one verifies it.
Upstream fixed this in electron-userland/electron-builder#10101 (master,
v27 alpha) and backported it in #10172 to release/v26 on 2026-09-03, but no
26.x containing it is published (26.16.0 predates the backport, #10167).
Apply the backport to the installed 26.15.7 as a pnpm patch, guarded by
`tools/dependencies/app-builder-lib-keychain-password.test.mjs`: it checks
that the patched version is the installed one, that the compiled source
passes `keychainPassword`, and — with `security` recorded — that
`createKeychain` unlocks the partition list with the password it gave
`create-keychain`, not the import password. The test fails 2/3 on the
unpatched code. CI runs it next to the Vite patch guard; CLAUDE.md and
AGENTS.md document when to retire the patch.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Publish "How to Load an M3U Playlist and Add an EPG in IPTVnator": the
three import methods plus drag-and-drop and OS file opening, the
playlist views, refresh and startup auto-update, attaching an XMLTV
guide through Settings or a url-tvg header, the tvg-id / tvg-name /
name matching order with manual mapping, catch-up attributes,
troubleshooting and a seven-question FAQ. The three guides now link to
each other, the download pages point at all three, and llms.txt lists
the new one.
Three guide shots join the manifest: the M3U URL dialog, the Groups
view (reusing open-m3u-groups under the guides group) and the EPG
settings section with a staged source row. A settings shot leaves the
form dirty, which arms the app's close guard and blocked app.close()
indefinitely; the capture now discards unsaved settings before every
action and before teardown, and bounds every locator wait.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Publish "How to Connect a Stalker or Ministra Portal to IPTVnator": the
portal URL shapes discovery accepts, MAC normalization, the optional
serial/device-ID/signature fields and the pinning rules behind the
"generate device IDs" toggle, what endpoint discovery does on Add, the
sections a portal source gets, Account info, and a troubleshooting list
built from the app's own refusal messages, plus a seven-question FAQ.
The guide is cross-linked from the download pages and llms.txt.
Guide screenshots come from the capture script. Shots that walk into a
Stalker portal start the stalker-mock-server and seed its marketing-demo
portal for that run only, so release shots never gain a third source
card. The frame guard allowlists exactly that scenario's MAC and keeps
rejecting every other MAC-shaped string.
To keep the live-TV frame free of third-party images, the fictional live
channel list and the channel-logo SVG renderer move into
@iptvnator/shared/marketing-fixtures; both mocks now serve
/assets/marketing/logo/<slug>.svg, the Stalker marketing-demo scenario
builds its ITV categories, channels and schedule from those fixtures
instead of faker names with picsum logos, and the mock resolves asset
URLs on get_all_channels too, which is the response the app renders
the channel list from.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`website:lint` failed on master since the per-OS download pages landed:
`src/lib/downloads.ts` imported the workspace root `package.json` through a
relative path, which `@nx/enforce-module-boundaries` rejects ("external
resources cannot be imported using a relative or absolute path").
The value is only needed as a build-time constant for the offline fallback,
so `astro.config.mjs` now reads the root manifest through the file system
and injects `__IPTVNATOR_VERSION__` via Vite `define`; `downloads.ts` uses
that constant and `src/env.d.ts` declares its type. Verified with
`WEBSITE_SKIP_RELEASE_FETCH=1`: the built download pages link the
`iptvnator-<version>` assets and no define literal leaks into the output.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Publish "How to Add an Xtream Codes Account to IPTVnator" as the first
evergreen guide: what the server URL, username and password are, the
Add playlist flow with the connection test and its four verdicts, the
Auto-detect method for pasted provider messages, what the import syncs,
Account info, refresh, troubleshooting and a seven-question FAQ. The
guide is cross-linked from the three download pages and llms.txt.
Blog posts gain an optional `faq` frontmatter list: BlogPost.astro
renders it as an accordion after the body and emits FAQPage JSON-LD
next to the BlogPosting entry. LinkCards and PostButton keep internal
links in the same tab.
Guide screenshots come from the release capture script: manifest shots
may carry a `group`, `--group guides` captures only those into
apps/website/public/blog/guides/screenshots/, and a release run skips
them. New setup actions open the Add playlist dialog with the mock's
fictional Xtream credentials (connection test shown), the Auto-detect
method with a labeled hand-out, and the Xtream Live TV view. Dialog
helpers and fixture identities move into shared modules so the driver
and the navigation actions cannot import each other cyclically.
tools/testing/website-guides.test.mjs checks the FAQPage schema, the
download-hub link and the shipped screenshots of every guide;
screenshot-guards.test.mjs covers group validation and output routing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`release:notes:blog` used to emit the notes as a type-grouped inventory with
area prefixes and the highlight sections buried after the feature list; the
v0.23 post shipped in exactly that form and had to be restructured after
publication. The scaffold now starts from the shape the posts end up in:
a "What changed" table with one row per highlight, one `##` section per
highlight ahead of everything else, breaking changes on their own, the
remaining features folded into reader-facing themed sections instead of
conventional-commit scopes, Performance, every remaining fix under a Spoiler
grouped by theme, and the before-updating alert, Thanks and Download cards
(including the compare link to the previous version). Only the components a
post uses are imported.
The blog renderer moves to `release-notes-blog.mjs`; `release-notes-render.mjs`
keeps the GitHub/CHANGELOG renderers and exports the shared text helpers.
Editorial work stays editorial and is marked with TODOs: which fixes deserve
promotion out of the spoiler, one-line bullets, lead-ins, intro and thanks.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add /download/ with Windows, macOS and Linux landing pages: OS-specific
install steps, requirements, feature list, FAQ, related posts and a
platform switcher, plus SoftwareApplication / FAQPage / BreadcrumbList
JSON-LD on every page.
Direct asset links resolve the latest published release from the GitHub
Releases API at build time (asset names, sizes, publish date) and fall
back to the root package.json version when the API is unreachable;
WEBSITE_SKIP_RELEASE_FETCH=1 forces the fallback. The deploy workflow
passes GITHUB_TOKEN to the build. The homepage download cards and the
header Download link now point at the new pages, the homepage schema
reads the resolved version instead of a hard-coded 0.20.0, and the
locale count is corrected to 19.
tools/testing/website-download-pages.test.mjs checks titles, canonicals,
direct asset links, structured data, cross-links and sitemap entries of
the built output; nx test website runs it beside the Giscus test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The published post was the raw release-notes scaffold: 42 feature and 76 fix
bullets in area-prefixed inventory form, with the four highlight sections
buried after the feature list. Rework it into the v0.22 shape: a "What
changed" table, the four highlights first and expanded, themed
improved/fixed sections with one-line entries, and the long tail of fixes
under a Spoiler. Every PR/issue reference from the original is preserved;
open text drops from 6.7k to 3.4k words.
Spoiler.astro sits inside `not-prose`, so lists rendered without markers
and links in body colour; give its content list, link, strong and code
styling so grouped lists inside it read like the rest of the post.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Swap the hero image to screenshots/screenshot-player.webp (channel list,
live player, EPG). The screenshot already includes the app's own window
controls, so remove the decorative title bar (traffic lights + label);
keep the rounded frame, glow, and scanline. Delete the now-unused
hero-dark.png.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Revert to the shields Codecov badge for visual consistency with the
other for-the-badge badges (native badge has no matching style). Pin
branch=master, add the Codecov logo, and keep the canonical
app.codecov.io link. Note: the shields Codecov proxy is occasionally
slow and may briefly render 'unknown'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The shields.io Codecov proxy intermittently rendered 'unknown' (extra
API hop, cached by GitHub's camo). Switch to Codecov's native master
branch badge and point the link at app.codecov.io directly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The single donation button used a 'Buy Me A Coffee' image that actually
linked to GitHub Sponsors. Split it into two correctly labeled badges:
GitHub Sponsors and Ko-fi (ko-fi.com/4gray, same as the blog).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The workflow status badge pointed at a non-existent build-and-test.yaml.
Point it at the actual CI workflow, link it to the workflow page, and
label it CI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the flat feature list with seven themed groups, drop granular
release-note-level items, and surface recent capabilities (TMDB
enrichment, embedded MPV, download manager, global search, dashboard,
auto-updater, remote control) with desktop-only markers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add blog post warning about scam sites misusing the IPTVnator name to
sell IPTV services or push suspicious downloads (official sources + safety)
- Add "official sources only" callout to README linking to the post
- Replace enumerated language list with a count (18) linking to i18n files
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clarify that embedded MPV on Linux is x64-only, needs system mpv on PATH
under X11/Xwayland, works on .deb/pacman/RPM/AppImage, and is not offered
on Flatpak/Snap sandboxes. Links to the README support matrix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Announce the v0.22 release: opt-in TMDB metadata enrichment, redesigned
two-state VOD/series detail pages, embedded MPV on Windows and Linux, a
built-in desktop auto-updater, global search, and the reworked live EPG
timeline. Includes compressed hero/slider imagery and a disclaimer that
IPTVnator ships no content and is a pure player.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rewrite architecture sections against the current filesystem: post-reorg
lib paths (libs/portal/*, libs/playlist/*, libs/workspace/*), /workspace
route tree, canonical libs/shared/database schema (10 tables,
better-sqlite3), renamed channel-list views, EPG timeline ribbon,
18 i18n locales, import flows in libs/playlist/import/feature, and drop
the unused @ngrx/component-store recommendation.
Add a rule to Documentation After Changes (mirrored in AGENTS.md):
agents must keep CLAUDE.md/AGENTS.md up to date when changes touch
anything they describe, and fix stale claims they notice in passing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>