Skip to main content

MIGRATION_001

Constant MIGRATION_001 

Source
const MIGRATION_001: &str = r#"
-- Jellyfin servers the user has connected to
CREATE TABLE IF NOT EXISTS servers (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    url TEXT NOT NULL UNIQUE,
    version TEXT,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    last_connected_at TEXT
);

-- User accounts on Jellyfin servers
CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
    username TEXT NOT NULL,
    access_token TEXT,
    is_active INTEGER DEFAULT 0,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    last_login_at TEXT,
    UNIQUE(server_id, username)
);

-- Libraries/views from Jellyfin
CREATE TABLE IF NOT EXISTS libraries (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    collection_type TEXT,
    image_tag TEXT,
    sort_order INTEGER DEFAULT 0,
    synced_at TEXT,
    UNIQUE(server_id, id)
);

-- Media items (movies, shows, episodes, albums, songs, artists)
CREATE TABLE IF NOT EXISTS items (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
    library_id TEXT REFERENCES libraries(id) ON DELETE SET NULL,
    parent_id TEXT REFERENCES items(id) ON DELETE CASCADE,

    -- Core metadata
    name TEXT NOT NULL,
    sort_name TEXT,
    original_title TEXT,
    item_type TEXT NOT NULL,  -- Movie, Series, Episode, MusicAlbum, Audio, MusicArtist, etc.

    -- Media info
    overview TEXT,
    tagline TEXT,
    genres TEXT,  -- JSON array
    tags TEXT,    -- JSON array
    studios TEXT, -- JSON array

    -- For episodes
    series_id TEXT,
    series_name TEXT,
    season_id TEXT,
    season_name TEXT,
    index_number INTEGER,        -- Episode number
    parent_index_number INTEGER, -- Season number

    -- For music
    album_id TEXT,
    album_name TEXT,
    album_artist TEXT,
    artists TEXT,  -- JSON array

    -- Dates
    premiere_date TEXT,
    production_year INTEGER,
    date_created TEXT,

    -- Runtime (ticks)
    runtime_ticks INTEGER,

    -- Images
    primary_image_tag TEXT,
    backdrop_image_tags TEXT,  -- JSON array

    -- Ratings
    community_rating REAL,
    official_rating TEXT,

    -- Sync metadata
    synced_at TEXT,
    etag TEXT,

    UNIQUE(server_id, id)
);

-- Full-text search index for items
CREATE VIRTUAL TABLE IF NOT EXISTS items_fts USING fts5(
    name,
    overview,
    album_name,
    album_artist,
    artists,
    series_name,
    content='items',
    content_rowid='rowid'
);

-- Triggers to keep FTS index in sync
CREATE TRIGGER IF NOT EXISTS items_ai AFTER INSERT ON items BEGIN
    INSERT INTO items_fts(rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES (new.rowid, new.name, new.overview, new.album_name, new.album_artist, new.artists, new.series_name);
END;

CREATE TRIGGER IF NOT EXISTS items_ad AFTER DELETE ON items BEGIN
    INSERT INTO items_fts(items_fts, rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES('delete', old.rowid, old.name, old.overview, old.album_name, old.album_artist, old.artists, old.series_name);
END;

CREATE TRIGGER IF NOT EXISTS items_au AFTER UPDATE ON items BEGIN
    INSERT INTO items_fts(items_fts, rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES('delete', old.rowid, old.name, old.overview, old.album_name, old.album_artist, old.artists, old.series_name);
    INSERT INTO items_fts(rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES (new.rowid, new.name, new.overview, new.album_name, new.album_artist, new.artists, new.series_name);
END;

-- Media streams (audio/subtitle tracks)
CREATE TABLE IF NOT EXISTS media_streams (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    stream_index INTEGER NOT NULL,
    stream_type TEXT NOT NULL,  -- Audio, Subtitle, Video
    codec TEXT,
    language TEXT,
    display_title TEXT,
    is_default INTEGER DEFAULT 0,
    is_forced INTEGER DEFAULT 0,
    is_external INTEGER DEFAULT 0,
    path TEXT,  -- For external subtitles
    UNIQUE(item_id, stream_index)
);

-- User-specific data (watch progress, favorites)
CREATE TABLE IF NOT EXISTS user_data (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,

    -- Playback state
    playback_position_ticks INTEGER DEFAULT 0,
    play_count INTEGER DEFAULT 0,
    is_played INTEGER DEFAULT 0,
    is_favorite INTEGER DEFAULT 0,

    -- Timestamps
    last_played_at TEXT,

    -- Sync status
    synced_at TEXT,
    pending_sync INTEGER DEFAULT 0,  -- 1 if local changes need sync

    UNIQUE(user_id, item_id)
);

-- Downloaded media files
CREATE TABLE IF NOT EXISTS downloads (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,

    -- File info
    file_path TEXT NOT NULL,
    file_size INTEGER,
    mime_type TEXT,

    -- Download state
    status TEXT DEFAULT 'pending',  -- pending, downloading, completed, failed, paused
    progress REAL DEFAULT 0,        -- 0.0 to 1.0

    -- Transcoding options used
    bitrate INTEGER,
    container TEXT,

    -- Timestamps
    queued_at TEXT DEFAULT CURRENT_TIMESTAMP,
    started_at TEXT,
    completed_at TEXT,

    -- Error tracking
    error_message TEXT,
    retry_count INTEGER DEFAULT 0,

    UNIQUE(item_id, user_id)
);

-- Offline mutation queue (changes to sync back to server)
CREATE TABLE IF NOT EXISTS sync_queue (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,

    -- Operation details
    operation TEXT NOT NULL,  -- mark_played, mark_favorite, update_progress, etc.
    item_id TEXT,
    payload TEXT,  -- JSON data for the operation

    -- Queue state
    status TEXT DEFAULT 'pending',  -- pending, processing, completed, failed
    retry_count INTEGER DEFAULT 0,

    -- Timestamps
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    processed_at TEXT,

    -- Error tracking
    error_message TEXT
);

-- Cached thumbnails
CREATE TABLE IF NOT EXISTS thumbnails (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    image_type TEXT NOT NULL,  -- Primary, Backdrop, Thumb, Logo, etc.
    image_tag TEXT NOT NULL,
    file_path TEXT NOT NULL,
    width INTEGER,
    height INTEGER,
    cached_at TEXT DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(item_id, image_type, image_tag)
);

-- User playlists (local + synced)
CREATE TABLE IF NOT EXISTS playlists (
    id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    is_local INTEGER DEFAULT 0,  -- 1 for local-only playlists
    jellyfin_id TEXT,            -- NULL for local-only
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    updated_at TEXT
);

-- Playlist items
CREATE TABLE IF NOT EXISTS playlist_items (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    playlist_id TEXT NOT NULL REFERENCES playlists(id) ON DELETE CASCADE,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    sort_order INTEGER NOT NULL,
    added_at TEXT DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(playlist_id, item_id)
);

-- Indexes for common queries
CREATE INDEX IF NOT EXISTS idx_items_server ON items(server_id);
CREATE INDEX IF NOT EXISTS idx_items_library ON items(library_id);
CREATE INDEX IF NOT EXISTS idx_items_parent ON items(parent_id);
CREATE INDEX IF NOT EXISTS idx_items_type ON items(item_type);
CREATE INDEX IF NOT EXISTS idx_items_album ON items(album_id);
CREATE INDEX IF NOT EXISTS idx_items_series ON items(series_id);
CREATE INDEX IF NOT EXISTS idx_items_season ON items(season_id);
CREATE INDEX IF NOT EXISTS idx_user_data_user ON user_data(user_id);
CREATE INDEX IF NOT EXISTS idx_user_data_item ON user_data(item_id);
CREATE INDEX IF NOT EXISTS idx_downloads_status ON downloads(status);
CREATE INDEX IF NOT EXISTS idx_sync_queue_status ON sync_queue(status);
CREATE INDEX IF NOT EXISTS idx_thumbnails_item ON thumbnails(item_id);
"#;
Expand description

Initial schema migration