Something went wrong. Try again.
Something went wrong. Try again.
mediatracker TODO.md
8.4 kB · 39 lines
Markdown
at main
TODO #
Open tasks and decisions. The skills in .claude/skills/ describe how things are; what is still open goes here, and an entry is deleted when it is done (then the skill is updated).
Watch button: a new episode during a rewatch #
- Where:
nextToWatchinTvShowDetailPage,useMarkEpisodeWatched; skillmediatracker-detail-pages("Watch button on the detail pages"). - Problem: the button points at the episode after the latest logged watch, so a newly released episode doesn't show up in the middle of a rewatch - it is only reached at the end.
- To decide: should an unseen new episode win over the rewatch (the button then points at the first never-watched episode after the latest watch)? Does backfilling an old episode, which moves "the latest watch" back, need a rule of its own?
Anime: provider order per section and episode-number translation #
- Where: skill
mediatracker-anime("Known gaps" of "Anime enrichment (AniDB)", "Not done" of "Title resolution"). - Open: the other direction of the idea - provider order per section and translating episode numbers between sources (the season/offset from the Fribb mapping is how to do it) - and measuring the share of MAL ids the mapping can resolve on real data.
Plugin system (third-party plugins, first step a "core" plugin) #
- Goal: plugins written by third parties add sources, enrichments, review sources, jobs, controllers and settings; the frontend lists them and turns a plugin or single components on and off and configures them; install/remove is started from the frontend and takes a restart (as in Jellyfin).
- Done:
MetadataSourceis an open struct (no enum);ISourceRegistry+AddMetadataSource(source, owner)register sources and fail the start on a collision; the mock backend has a source (Acme) announced like a plugin's; checked in the frontend against the mock: the source choice of the search, its results, its detail page and the source priorities show it without a frontend change (filters not looked at). The anime mapping logic keeps namingTmdb/Tvdb/Malon purpose (it is about those sources). - Decided:
- Plugin id = GUID (like Jellyfin), written once by the developer into the manifest and as a constant in the plugin; never changes. No domain/NSID (a developer must not need one). The host checks the format (not empty, not all zeros) and refuses a second plugin with the same id; it can't tell a copied id from a new version (a catalog or signatures would be needed, later). The sample manifest must show a freshly generated id and say "do not copy" (Jellyfin's template ships a real example GUID in
build.yaml, and its README only lists tools to generate one). - Internal names come from the id, automatically: PostgreSQL schema
plugin_<32 hex>, settings pathPlugins:<id>:, cache key prefix, data directory, routes/plugins/<id>/. Logs and the UI use the manifest's name, never the GUID. The assigned names are stored at first load (table of plugin id -> schema/paths), so a later rule change or rename orphans nothing. - Source keys are not prefixed: short, chosen by the plugin in its manifest (
trakt), lowercase letters/digits (seeMetadataSource.IsValidName: raise the 32 limit toward the lexicon's 64 if needed), checked for collisions at load (built-ins reserved). They are data in users' records and so a namespace shared between instances; two plugins with one key for different services can't be told apart by the host (later: a list of known keys like the lexicon'sknownValues).
- Plugin id = GUID (like Jellyfin), written once by the developer into the manifest and as a constant in the plugin; never changes. No domain/NSID (a developer must not need one). The host checks the format (not empty, not all zeros) and refuses a second plugin with the same id; it can't tell a copied id from a new version (a catalog or signatures would be needed, later). The sample manifest must show a freshly generated id and say "do not copy" (Jellyfin's template ships a real example GUID in
- Manifest: implemented as
plugin.json(see skillmediatracker-plugins). Still open: Jellyfin'smeta.jsonand repositorymanifest.jsonschemas were not read, so a catalog format (a list of installable plugins with download address, checksum,targetAbi-like compatibility) is undecided. - Open:
- Plugin cache and database, what is left: the schema is separate by convention only (one database user). A plugin can no longer ask for another plugin's cache through the API (the cache is the handle that
AddCachereturns), but with reflection or direct SQL it still can. - Dependencies between plugins are not modeled (a plugin cannot say it needs another one).
- Install from an address or a catalog (parked, not needed yet; only worth it once someone distributes plugins): a package can only be uploaded; a list of installable plugins (download address, checksum, compatibility) and fetching it is undecided (the backend must not fetch addresses it is only told about: SSRF).
- Frontend: the pages of plugins are generic (list, a page and sidebar entry per plugin, component settings and switches); UI that needs a plugin's own code (a custom page, a widget on a detail page) is not possible: that needs frontend modules loaded at run time (module federation or dynamic ES modules, with typed texts) - only once third-party plugins exist.
- Plugin cache and database, what is left: the schema is separate by convention only (one database user). A plugin can no longer ask for another plugin's cache through the API (the cache is the handle that
- Steps: (1) done, see skill
mediatracker-plugins: contract, manifest, loader, the core plugin,/admin/plugins(on/off from the next start); (2) done as generic pages (see skillmediatracker-plugins): a page and a sidebar entry per plugin and the settings/switch of each component, all fromGET /admin/plugins; what stays open is UI that only a plugin's own code can draw (custom pages: runtime-loaded frontend modules, only once third-party plugins exist); (3) done (upload a zip, remove, take back, restart; see skillmediatracker-plugins); (4) done (cache category and database schema per plugin; see skillmediatracker-plugins); (5) catalog/signatures: parked, no need for it so far.
Mock backend, mock Studio and Storybook: what is still open #
- Where: skills
mediatracker-testing(mock hosts, fakes) andmediatracker-frontend-conventions(Storybook). - C# of the mock work: the fakes have unit tests (
Mediatracker.Testing.Tests) and both mock hosts start in tests on a real PostgreSQL (Mediatracker.Api.Mock.Tests,Mediatracker.FeedStudio.Mock.Tests: login, library with the odd titles, failing source, images, seeded feeds, publishing); still without tests of their own:MockDatabase,MockWorld,MockServicesbeyond what those host tests exercise. - Mock data: faults are per title or, with
PUT /mock/faults, for every call of the source (not per request path, not random); only TMDB (andAcme) as sources; the mock Studio has no feeds with odd names, and the embedded Studio login in the mocked Mediatracker is not set up; admin pages show real services (Tap, AniDB) as down or off. - Storybook: about 100 components of the Mediatracker app and most of the Studio's have no story; components that ask an API can have
parameters.api(done forReviewsCollapsibleandEpisodeCard), the Studio'sFeedForm,RulePanel,PublishPanelandTemplateDialogtoo; still open:MediaSection,SeasonCollapsible,FeedListSection,DatasourceEditor; most stories were only checked bynpm run check:stories(renders, overflow), not looked at; no interaction or screenshot tests,check:storiesis in no pipeline. - Frontend walk-through with the odd titles: the Mediatracker's pages (home, library, calendar, search, discover, lists, feeds, detail pages of the odd and failing titles, settings, admin) were walked at 375/768/1280 px (dark at 375) with
npm run walk:pages: no page gets wider than the window; what is left are small overflows in loading skeletons (a few px) and the id mappings page at 768 px (cards' texts 56/47, 63/47); withnpm run walk:interactionsthe collapsibles of the detail pages (seasons with the odd episodes, media, alternative titles) with their episode cards expanded and the dialogs behind the action buttons (watch history, add to list, rating, move to another entry) were opened at 375 and 1280 px: nothing overflows (the only finding is a toast's icon, a library's own element). Not done: menus, forms filled in, the Log watch / mark several dialogs on a show with episodes. The Studio's feed pages, tokens, accounts, datasources and identity were walked the same way, but not its rule panel, template dialog, datasource editor with data, nor the publish flow. - Not tried in the IDEs: the Rider and WebStorm run configurations were written as files, not opened (the ids in the
Compose allones follow the pattern of the existing multi-launch file).