Quiblo wiki

Code reference · Core

:core:database

Room: entities, DAOs and eleven migrations.

dev.quiblo.core.database

Destructive migration is deliberately not enabled — dropping a user's sources on a schema change would be data loss. The schema JSON is exported and committed, and Room validates the live database against it at open time, so a migration that does not produce exactly what the entities declare fails loudly at launch.

abstract classQuibloDatabase

The single local database. There is no remote counterpart and never will be.

data classChannelEntity

One playable item belonging to a source. Replaced wholesale on refresh.

Note what is not here: a favourite flag. It would be destroyed by the next refresh. Indices matter here more than anywhere else in the project — see ChannelDao.observeBrowse.

data classProfileEntity

Who is watching. The parent that favourites and resume points cascade from.

Guest is a row rather than a flag in preferences, and that is the design. Deleting the row takes its favourites and its resume points with it by foreign key, atomically — so the promise that guest data does not outlive its session is kept by the database rather than by every screen remembering to help.

data classFavoriteEntity

A favourite, keyed by profile plus provider identity — never by row id.

Deliberately not joined to ChannelEntity by primary key. Surviving a refresh in which the stream URL changed and every row was reinserted is the entire point.

profileId is part of the primary key, not merely a column, so two people can hold the same favourite independently.

data classResumePositionEntity

Where a viewer stopped, plus enough about the item to list it as history.

The descriptive columns are denormalised rather than joined, because for an episode there is nothing to join to. A history list that joined would show films and silently drop every episode — which is most of what anyone actually resumes.

Keyed by profile and stable key, so two people can stop at different points in the same film. Rows written before the descriptive columns existed keep resuming correctly and are excluded from the history list by their empty title.

data classProgrammeEntity

One guide entry, keyed to a channel by stable identity.

data classTitleMetadataEntity

Cached title information, keyed by cleaned title and kind.

Keyed by title rather than channel id, because a refresh reassigns every id and the cache would be thrown away for nothing. Kind is half the key because "Fargo" is a film and a series, and with the title alone whichever tab was opened first would answer for the other.

data classCategoryOverrideEntity

A local rename or hide, keyed by the provider's own title.

data classChannelLogoEntity

A logo from the reference list. A cache of a public catalogue, not user data.

interfaceChannelDao

The browse query, the category counts, and the refresh transaction.

observeBrowse is one query with optional predicates rather than four queries, so the combinations cannot drift apart, and it filters in SQL rather than in composition.

It needs the composite index on source, kind and sort order. Without it SQLite matches every row for the source, tests the kind one row at a time, then builds a temporary B-tree to sort — on every emission.

observeBrowse(...)The single browse query. Joins the favourite flag in SQL so a large list is not re-mapped on every toggle.
observeCategoriesByKind(...)Categories in the provider's own order, not alphabetically.
replaceForSource(...)One transaction, chunked inserts. A refresh that fails midway cannot leave half a list.

interfaceResumePositionDao / FavoriteDao / ProgrammeDao / TitleMetadataDao / ChannelLogoDao / CategoryOverrideDao / SourceDao

The remaining tables. Each exposes flows for screens and suspend calls for writes.

data classChannelWithFavorite / CategoryCount

Query projections — a row plus its favourite flag, and a category plus its size.