Skip to content

feat(platform_playlist): CV-017 -- persistent canonical channel identity (Drift/SQLite)#921

Merged
ucguy4u merged 2 commits into
mainfrom
cv-017-persistence
Jul 19, 2026
Merged

feat(platform_playlist): CV-017 -- persistent canonical channel identity (Drift/SQLite)#921
ucguy4u merged 2 commits into
mainfrom
cv-017-persistence

Conversation

@ucguy4u

@ucguy4u ucguy4u commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

⚠️ First-of-its-kind dependency -- please review before merging

This is the first Drift/SQLite usage anywhere in packages/. Two
things worth a second set of eyes:

  1. New dependency footprint: drift, drift_dev, build_runner,
    sqlite3, sqlite3_flutter_libs, path, path_provider -- binary
    size and build time impact on TV specifically should get a look
    (chief-performance-officer) alongside the usual license/maintenance
    checks (chief-open-source-officer).
  2. Generated code committed against the grain: *.g.dart is
    force-added despite the repo-wide .gitignore rule, because no CI
    workflow currently runs dart run build_runner build for any
    package under packages/ -- only app/ has that step. Without
    checking in the generated file, this package fails to compile
    anywhere that doesn't run codegen first. The real fix is wiring a
    per-package (or matrix) codegen step into CI; committing the
    artifact is a stopgap, not a long-term policy for future schema
    changes here.

Summary

Part of #821 (CV-017, P0). Adds the last piece of the issue's
framework-owned scope: real, queryable persistence for canonical
channel identities and provider aliases.

Why Drift/SQLite over the existing SharedPreferences pattern

Every other store in this repo (HiddenGroupsStorage,
FavoriteChannelsStorage, etc.) uses flat JSON in a single
SharedPreferences key. That doesn't hold up at the scale this issue
describes ("tens of thousands of channels") -- no indexing, no
querying, and this codebase has already hit SharedPreferences' per-
value size ceiling in other stores' tests. A real SQL engine with
indexes on canonical_channel_id and tvg_id is the right tool once
alias counts get large.

Schema (matches the issue's suggested data contract)

  • CanonicalChannels: canonicalChannelId, displayName,
    normalizedName, language, country, category,
    logoFingerprint, createdAt, updatedAt.
  • ProviderChannelAliases: sourceId + providerChannelId (composite
    PK), canonicalChannelId (nullable FK), providerName,
    normalizedProviderName, tvgId, groupTitle,
    streamUrlFingerprint, resolution, isVod/isRadio/isAdult,
    matchConfidence.

CanonicalChannelRepository wraps the generated Drift database:
upsert/get/list for canonical channels, upsert/lookup for aliases (by
composite key, by canonical id, by tvg-id -- the last mirrors
CanonicalChannelMatcher's own high-confidence signal).

Not in this slice

  • Wiring FavoriteChannelRemapper/CanonicalChannelMatcher to
    actually populate this database during a real playlist import --
    this PR only adds the storage layer.
  • Migration strategy beyond schemaVersion 1 (no prior schema exists
    yet to migrate from).

Test plan

  • New tests: canonical upsert/get round-trip and overwrite-on-
    conflict, alias upsert/lookup by composite key, aliases-by-
    canonical-id, aliases-by-tvg-id across providers, not-found cases
    for both.
  • Full platform_playlist suite green (111 tests), using an
    in-memory Drift database (NativeDatabase.memory()) -- no disk
    I/O in tests.
  • flutter analyze clean on changed files (one pre-existing,
    unrelated lint on the barrel file).
  • dart format --set-exit-if-changed clean on hand-written files
    (generated .g.dart left as the generator produced it).

…ity (Drift/SQLite)

Adds the last remaining piece of #821's framework scope: real,
queryable persistence for canonical channel identities and provider
aliases, backed by Drift/SQLite instead of the SharedPreferences-JSON
pattern every other store in this repo uses.

Schema (matches the issue's suggested data contract):
- CanonicalChannels: canonicalChannelId, displayName, normalizedName,
  language, country, category, logoFingerprint, createdAt, updatedAt.
- ProviderChannelAliases: sourceId + providerChannelId (composite PK),
  canonicalChannelId (nullable FK), providerName,
  normalizedProviderName, tvgId, groupTitle, streamUrlFingerprint,
  resolution, isVod/isRadio/isAdult, matchConfidence.

CanonicalChannelRepository wraps the generated Drift database:
upsert/get/list for canonical channels, upsert/lookup for aliases (by
composite key, by canonical id, by tvg-id -- the last one mirrors
CanonicalChannelMatcher's own high-confidence signal).

## Why Drift/SQLite over the existing SharedPreferences pattern

Flat JSON-in-a-single-preference-key doesn't hold up at the scale this
issue describes ("tens of thousands of channels") -- no indexing, no
querying, and SharedPreferences has a hard per-value size ceiling this
codebase has already hit in other stores' tests. A real SQL engine with
indexes on canonical_channel_id and tvg_id is the right tool once alias
counts get large.

## First-of-its-kind dependency -- flags for review

This is the first Drift/SQLite usage anywhere in packages/. Two things
a reviewer should weigh in on:

1. New dependency footprint: drift, drift_dev, build_runner, sqlite3,
   sqlite3_flutter_libs, path, path_provider -- binary size and build
   time impact on TV specifically should get a look (chief-performance-
   officer) alongside the usual license/maintenance checks (chief-open-
   source-officer).
2. Generated code (*.g.dart) is committed despite the repo-wide
   .gitignore rule (force-added) because no CI workflow currently runs
   `dart run build_runner build` for any package under packages/ --
   only app/ has that step. Without checking in the generated file,
   this package would fail to compile anywhere that doesn't run
   codegen first. The real fix is wiring a per-package (or matrix)
   codegen step into CI; committing the artifact is a stopgap, not a
   long-term policy for future Drift schema changes in this package.

## Not in this slice
- Wiring FavoriteChannelRemapper/CanonicalChannelMatcher to actually
  populate this database during a real playlist import -- this PR only
  adds the storage layer.
- Migration strategy beyond schemaVersion 1 (no prior schema to migrate
  from yet).
@github-actions

Copy link
Copy Markdown
Contributor

Plugin Module Size Gate

Policy: modules over 3 MB must be delivered as plugins; plugin modules over 5 MB must document cache management.

Module Size Type Status
packages/platform_playlist 0.21 MB bundled OK

@github-actions

Copy link
Copy Markdown
Contributor

🚀 PR Quick Check Summary

Check Status Description
PR Validation ✅ success Title format, docs, bundled model guardrail
Code Quality ❌ failure Analyze, formatting
Core Tests ✅ success Core package unit tests

💡 Note: Full app tests, coverage reports, and security scans run on merge to main.

View Details

@ucguy4u
ucguy4u merged commit 27d0116 into main Jul 19, 2026
9 of 14 checks passed
@ucguy4u
ucguy4u deleted the cv-017-persistence branch July 19, 2026 02:43
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

🚀 PR Quick Check Summary

Check Status Description
PR Validation ✅ success Title format, docs, bundled model guardrail
Code Quality ❌ failure Analyze, formatting
Core Tests ✅ success Core package unit tests

💡 Note: Full app tests, coverage reports, and security scans run on merge to main.

View Details

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant