0.2.82
This commit is contained in:
@@ -108,6 +108,12 @@ CREATE INDEX IF NOT EXISTS library_items_genres_idx ON library_items USING GIN (
|
||||
CREATE INDEX IF NOT EXISTS library_items_type_created_idx ON library_items (type, date_created DESC);
|
||||
CREATE INDEX IF NOT EXISTS library_items_synced_idx ON library_items (synced_at);
|
||||
|
||||
-- Every episode of one series, which is what a viewer's Next Up walks and what a series
|
||||
-- card's watched count is computed from. Both run on the tail of an ordinary request, and
|
||||
-- without this each is a scan of every episode in the library.
|
||||
CREATE INDEX IF NOT EXISTS library_items_series_episodes_idx
|
||||
ON library_items (series_id) WHERE type = 'Episode';
|
||||
|
||||
-- Durable raw MDBList responses. Source selection and display formatting happen at read
|
||||
-- time, so changing the visible sources does not require another external API request.
|
||||
CREATE TABLE IF NOT EXISTS external_media_ratings (
|
||||
@@ -903,3 +909,76 @@ CREATE INDEX IF NOT EXISTS notification_log_user_idx
|
||||
ON notification_log (emby_user_id, occurred_at DESC) WHERE emby_user_id <> '';
|
||||
CREATE INDEX IF NOT EXISTS notification_log_status_idx ON notification_log (status, occurred_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS notification_log_kind_idx ON notification_log (kind, occurred_at DESC);
|
||||
|
||||
-- Viewers: the people using one Memby account.
|
||||
--
|
||||
-- A Memby account is the household's relationship with an Emby user; a viewer is one
|
||||
-- person under it. Every account has exactly one MAIN viewer, whose state is Emby's and
|
||||
-- which behaves exactly as the account did before viewers existed, and any number of
|
||||
-- SHADOW viewers whose state is Memby's alone.
|
||||
--
|
||||
-- The main viewer's id IS the Emby user id, and that is the whole of why this feature
|
||||
-- needed no migration. Every table in this schema keys a person by a bare emby_user_id
|
||||
-- with no foreign key behind it, so substituting a viewer id for it leaves an existing
|
||||
-- household's preferences, notifications, followed shows, search history and row stats
|
||||
-- exactly where they were. A shadow id is prefixed 'v' and is therefore distinguishable
|
||||
-- from Emby's 32-hex GUIDs by inspection, which is what makes that substitution safe.
|
||||
CREATE TABLE IF NOT EXISTS viewers (
|
||||
id TEXT PRIMARY KEY,
|
||||
emby_user_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
short_name TEXT NOT NULL DEFAULT '',
|
||||
colour TEXT NOT NULL DEFAULT '',
|
||||
kind TEXT NOT NULL, -- main | shadow
|
||||
pin_hash BYTEA,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS viewers_account_idx ON viewers (emby_user_id, created_at);
|
||||
|
||||
-- One main viewer per account, enforced rather than assumed: the main viewer is what a
|
||||
-- request falls back to, so an account with two of them would resolve differently
|
||||
-- depending on which row a query happened to return first.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS viewers_account_main_idx
|
||||
ON viewers (emby_user_id) WHERE kind = 'main';
|
||||
|
||||
-- A shadow viewer's own viewing state, in the shape of the Emby UserData block it stands
|
||||
-- in for. Only the fields Memby actually renders are here: the Emby item id is the common
|
||||
-- identifier, so no library metadata is duplicated and nothing here needs invalidating
|
||||
-- when the catalogue changes.
|
||||
--
|
||||
-- There is deliberately no row for a main viewer. Their state lives in Emby, and a copy
|
||||
-- of it here would be a second answer free to disagree with the one the household's other
|
||||
-- Emby clients see.
|
||||
CREATE TABLE IF NOT EXISTS viewer_playback_state (
|
||||
viewer_id TEXT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
series_id TEXT NOT NULL DEFAULT '',
|
||||
season_id TEXT NOT NULL DEFAULT '',
|
||||
position_ticks BIGINT NOT NULL DEFAULT 0,
|
||||
runtime_ticks BIGINT NOT NULL DEFAULT 0,
|
||||
played BOOLEAN NOT NULL DEFAULT false,
|
||||
play_count INT NOT NULL DEFAULT 0,
|
||||
favourite BOOLEAN NOT NULL DEFAULT false,
|
||||
hidden_from_resume BOOLEAN NOT NULL DEFAULT false,
|
||||
last_played_at TIMESTAMPTZ,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (viewer_id, item_id)
|
||||
);
|
||||
|
||||
-- Continue Watching for a shadow viewer is this index: what they are part-way through,
|
||||
-- most recent first. The partial predicate keeps it to the rows that row can draw from
|
||||
-- rather than to everything they have ever pressed Play on.
|
||||
CREATE INDEX IF NOT EXISTS viewer_playback_resume_idx
|
||||
ON viewer_playback_state (viewer_id, last_played_at DESC)
|
||||
WHERE position_ticks > 0 AND NOT played AND NOT hidden_from_resume;
|
||||
|
||||
-- Next Up walks a series' episodes for the newest completion; favourites are their own
|
||||
-- row, and both are asked for per viewer.
|
||||
CREATE INDEX IF NOT EXISTS viewer_playback_series_idx
|
||||
ON viewer_playback_state (viewer_id, series_id, last_played_at DESC)
|
||||
WHERE series_id <> '';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS viewer_playback_favourite_idx
|
||||
ON viewer_playback_state (viewer_id, updated_at DESC) WHERE favourite;
|
||||
|
||||
Reference in New Issue
Block a user