Files
musicdl-catalog-sync-suite/Music_Server/docs/superpowers/plans/2026-04-19-music-cloud-snapshot-origin-implementation.md
T

33 KiB

Music_Cloud Snapshot and Private Origin Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add snapshot export on the Music_Cloud side so the public service can consume a read-only catalog mirror, and add the signed private-origin primitives needed for NAS-only fallback streaming.

Architecture: Keep all authoritative writes in Music_Cloud, export a compact read model into catalog_read.db, publish it with a manifest.json, and add signed-token helpers that the live NAS web stack can use for private-origin playback without exposing filesystem paths.

Tech Stack: Python 3, sqlite3, dataclasses, json, hashlib, hmac, base64, unittest


Repository root: D:\source\musicdl-catalog-sync-worktrees\catalog-sync

File Structure

  • Create: musicdl/catalogsync/export/__init__.py
  • Create: musicdl/catalogsync/export/models.py
  • Create: musicdl/catalogsync/export/service.py
  • Create: musicdl/catalogsync/private_origin/__init__.py
  • Create: musicdl/catalogsync/private_origin/tokens.py
  • Create: tests/catalogsync/test_snapshot_models.py
  • Create: tests/catalogsync/test_snapshot_export.py
  • Create: tests/catalogsync/test_private_origin_tokens.py
  • Modify: musicdl/catalogsync/cli.py
  • Create: scripts/catalogsync/export_snapshot.ps1

Task 1: Define the snapshot manifest contract

Files:

  • Create: musicdl/catalogsync/export/__init__.py

  • Create: musicdl/catalogsync/export/models.py

  • Test: tests/catalogsync/test_snapshot_models.py

  • Step 1: Write the failing test

import unittest

from musicdl.catalogsync.export.models import SnapshotManifest


class SnapshotManifestTests(unittest.TestCase):
    def test_round_trip_manifest_dict(self):
        manifest = SnapshotManifest(
            snapshot_id="snap-001",
            generated_at="2026-04-19T08:00:00Z",
            schema_version=1,
            playlist_count=12,
            track_count=34,
            file_count=56,
            cover_count=7,
        )

        payload = manifest.to_dict()
        restored = SnapshotManifest.from_dict(payload)

        self.assertEqual("snap-001", restored.snapshot_id)
        self.assertEqual(34, restored.track_count)


if __name__ == "__main__":
    unittest.main()
  • Step 2: Run test to verify it fails

Run: python -m unittest tests.catalogsync.test_snapshot_models -v
Expected: ERROR with ModuleNotFoundError: No module named 'musicdl.catalogsync.export.models'

  • Step 3: Write minimal implementation

musicdl/catalogsync/export/__init__.py

from .models import SnapshotManifest

__all__ = ["SnapshotManifest"]

musicdl/catalogsync/export/models.py

from dataclasses import asdict, dataclass


@dataclass(frozen=True)
class SnapshotManifest:
    snapshot_id: str
    generated_at: str
    schema_version: int
    playlist_count: int
    track_count: int
    file_count: int
    cover_count: int

    def to_dict(self) -> dict:
        return asdict(self)

    @classmethod
    def from_dict(cls, payload: dict) -> "SnapshotManifest":
        return cls(
            snapshot_id=str(payload["snapshot_id"]),
            generated_at=str(payload["generated_at"]),
            schema_version=int(payload["schema_version"]),
            playlist_count=int(payload["playlist_count"]),
            track_count=int(payload["track_count"]),
            file_count=int(payload["file_count"]),
            cover_count=int(payload["cover_count"]),
        )
  • Step 4: Run test to verify it passes

Run: python -m unittest tests.catalogsync.test_snapshot_models -v
Expected: OK

  • Step 5: Commit
git add tests/catalogsync/test_snapshot_models.py musicdl/catalogsync/export/__init__.py musicdl/catalogsync/export/models.py
git commit -m "feat: add snapshot manifest model"

Task 2: Export a read-only catalog snapshot database

Files:

  • Create: musicdl/catalogsync/export/service.py

  • Test: tests/catalogsync/test_snapshot_export.py

  • Step 1: Write the failing test

import json
import sqlite3
import tempfile
import unittest
from pathlib import Path

from musicdl.catalogsync.export.service import export_snapshot


class SnapshotExportTests(unittest.TestCase):
    def test_export_snapshot_writes_read_model_and_manifest(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            tmpdir_path = Path(tmpdir)
            source_db = tmpdir_path / "catalogsync.db"
            target_dir = tmpdir_path / "snapshot"
            target_db = target_dir / "catalog_read.db"
            target_manifest = target_dir / "manifest.json"

            conn = sqlite3.connect(source_db)
            conn.executescript(
                '''
                create table playlists (
                    id integer primary key,
                    platform text not null,
                    remote_playlist_id text not null,
                    name text not null,
                    description text,
                    cover_url text,
                    play_count integer default 0,
                    collected_song_count integer default 0
                );
                create table songs (
                    id integer primary key,
                    platform text not null,
                    remote_song_id text not null,
                    name text not null,
                    singers text,
                    ext text,
                    file_size_bytes integer,
                    metadata_json text
                );
                create table playlist_songs (
                    playlist_id integer not null,
                    song_id integer not null,
                    position integer not null
                );
                insert into playlists values (1, 'netease', '18165', '娴嬭瘯姝屽崟', 'desc', 'https://img/1.jpg', 99, 1);
                insert into songs values (10, 'netease', '65800', '娴峰笨浣?, '椹篃 / Crabbit', 'flac', 123456, '{"duration_ms": 0}');
                insert into playlist_songs values (1, 10, 1);
                '''
            )
            conn.commit()
            conn.close()

            manifest = export_snapshot(source_db=source_db, target_dir=target_dir)

            self.assertTrue(target_db.exists())
            self.assertTrue(target_manifest.exists())
            self.assertEqual(1, manifest.playlist_count)
            self.assertEqual(1, manifest.track_count)

            read_conn = sqlite3.connect(target_db)
            row = read_conn.execute(
                "select playlist_id, name, play_count, song_count from catalog_playlists"
            ).fetchone()
            read_conn.close()

            self.assertEqual((1, "娴嬭瘯姝屽崟", 99, 1), row)

            payload = json.loads(target_manifest.read_text(encoding="utf-8"))
            self.assertEqual("snap-playlists-1-tracks-1", payload["snapshot_id"])


if __name__ == "__main__":
    unittest.main()
  • Step 2: Run test to verify it fails

Run: python -m unittest tests.catalogsync.test_snapshot_export -v
Expected: ERROR with ImportError because export_snapshot does not exist yet

  • Step 3: Write minimal implementation

musicdl/catalogsync/export/service.py

import json
import sqlite3
from datetime import datetime, timezone
from pathlib import Path

from .models import SnapshotManifest


def export_snapshot(source_db: Path, target_dir: Path) -> SnapshotManifest:
    source_db = Path(source_db)
    target_dir = Path(target_dir)
    target_dir.mkdir(parents=True, exist_ok=True)
    target_db = target_dir / "catalog_read.db"
    target_manifest = target_dir / "manifest.json"

    if target_db.exists():
        target_db.unlink()

    source_conn = sqlite3.connect(source_db)
    source_conn.row_factory = sqlite3.Row
    target_conn = sqlite3.connect(target_db)

    target_conn.executescript(
        """
        create table catalog_playlists (
            playlist_id integer primary key,
            platform text not null,
            remote_playlist_id text not null,
            name text not null,
            description text,
            cover_url text,
            play_count integer not null default 0,
            song_count integer not null default 0
        );
        create table catalog_tracks (
            song_id integer primary key,
            platform text not null,
            remote_song_id text not null,
            name text not null,
            singers text,
            ext text,
            file_size_bytes integer,
            metadata_json text
        );
        create table catalog_playlist_tracks (
            playlist_id integer not null,
            song_id integer not null,
            position integer not null
        );
        """
    )

    playlists = source_conn.execute(
        """
        select
            p.id as playlist_id,
            p.platform,
            p.remote_playlist_id,
            p.name,
            p.description,
            p.cover_url,
            coalesce(p.play_count, 0) as play_count,
            coalesce(p.collected_song_count, 0) as song_count
        from playlists p
        """
    ).fetchall()

    tracks = source_conn.execute(
        """
        select
            s.id as song_id,
            s.platform,
            s.remote_song_id,
            s.name,
            s.singers,
            s.ext,
            s.file_size_bytes,
            s.metadata_json
        from songs s
        """
    ).fetchall()

    playlist_tracks = source_conn.execute(
        """
        select playlist_id, song_id, position
        from playlist_songs
        order by playlist_id, position
        """
    ).fetchall()

    target_conn.executemany(
        """
        insert into catalog_playlists (
            playlist_id, platform, remote_playlist_id, name, description, cover_url, play_count, song_count
        ) values (
            :playlist_id, :platform, :remote_playlist_id, :name, :description, :cover_url, :play_count, :song_count
        )
        """,
        playlists,
    )
    target_conn.executemany(
        """
        insert into catalog_tracks (
            song_id, platform, remote_song_id, name, singers, ext, file_size_bytes, metadata_json
        ) values (
            :song_id, :platform, :remote_song_id, :name, :singers, :ext, :file_size_bytes, :metadata_json
        )
        """,
        tracks,
    )
    target_conn.executemany(
        """
        insert into catalog_playlist_tracks (playlist_id, song_id, position)
        values (:playlist_id, :song_id, :position)
        """,
        playlist_tracks,
    )
    target_conn.commit()
    target_conn.close()
    source_conn.close()

    manifest = SnapshotManifest(
        snapshot_id=f"snap-playlists-{len(playlists)}-tracks-{len(tracks)}",
        generated_at=datetime.now(timezone.utc).isoformat(),
        schema_version=1,
        playlist_count=len(playlists),
        track_count=len(tracks),
        file_count=0,
        cover_count=sum(1 for row in playlists if row["cover_url"]),
    )
    target_manifest.write_text(
        json.dumps(manifest.to_dict(), ensure_ascii=False, indent=2),
        encoding="utf-8",
    )
    return manifest
  • Step 4: Run test to verify it passes

Run: python -m unittest tests.catalogsync.test_snapshot_export -v
Expected: OK

  • Step 5: Commit
git add tests/catalogsync/test_snapshot_export.py musicdl/catalogsync/export/service.py
git commit -m "feat: export read-only catalog snapshot"

Task 3: Add a CLI entrypoint and operator script for snapshot export

Files:

  • Modify: musicdl/catalogsync/cli.py

  • Create: scripts/catalogsync/export_snapshot.ps1

  • Test: tests/catalogsync/test_snapshot_cli.py

  • Step 1: Write the failing test

import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from musicdl.catalogsync.cli import main


class SnapshotCliTests(unittest.TestCase):
    def test_export_snapshot_command_dispatches_to_service(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            source_db = Path(tmpdir) / "catalogsync.db"
            source_db.touch()
            target_dir = Path(tmpdir) / "snapshot"

            with patch("musicdl.catalogsync.cli.export_snapshot") as export_mock:
                exit_code = main(
                    [
                        "export-snapshot",
                        "--db",
                        str(source_db),
                        "--target-dir",
                        str(target_dir),
                    ]
                )

            self.assertEqual(0, exit_code)
            export_mock.assert_called_once_with(source_db=source_db, target_dir=target_dir)


if __name__ == "__main__":
    unittest.main()
  • Step 2: Run test to verify it fails

Run: python -m unittest tests.catalogsync.test_snapshot_cli -v
Expected: ERROR because the parser does not know export-snapshot

  • Step 3: Write minimal implementation

musicdl/catalogsync/cli.py

from pathlib import Path
import argparse

from musicdl.catalogsync.export.service import export_snapshot


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="musicdl-catalogsync")
    subparsers = parser.add_subparsers(dest="command", required=True)

    export_parser = subparsers.add_parser("export-snapshot")
    export_parser.add_argument("--db", required=True)
    export_parser.add_argument("--target-dir", required=True)

    return parser


def main(argv=None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    if args.command == "export-snapshot":
        export_snapshot(source_db=Path(args.db), target_dir=Path(args.target_dir))
        return 0

    parser.error(f"unsupported command: {args.command}")

scripts/catalogsync/export_snapshot.ps1

param(
    [Parameter(Mandatory = $true)]
    [string]$DbPath,
    [Parameter(Mandatory = $true)]
    [string]$TargetDir
)

$ErrorActionPreference = "Stop"
python -m musicdl.catalogsync.cli export-snapshot --db $DbPath --target-dir $TargetDir
if ($LASTEXITCODE -ne 0) {
    throw "export-snapshot failed with exit code $LASTEXITCODE"
}
Write-Host "Snapshot exported to $TargetDir"
  • Step 4: Run test to verify it passes

Run: python -m unittest tests.catalogsync.test_snapshot_cli -v
Expected: OK

  • Step 5: Commit
git add tests/catalogsync/test_snapshot_cli.py musicdl/catalogsync/cli.py scripts/catalogsync/export_snapshot.ps1
git commit -m "feat: add snapshot export command"

Task 4: Add signed private-origin token helpers for NAS fallback playback

Files:

  • Create: musicdl/catalogsync/private_origin/__init__.py

  • Create: musicdl/catalogsync/private_origin/tokens.py

  • Test: tests/catalogsync/test_private_origin_tokens.py

  • Step 1: Write the failing test

import time
import unittest

from musicdl.catalogsync.private_origin.tokens import create_origin_token, verify_origin_token


class PrivateOriginTokenTests(unittest.TestCase):
    def test_create_and_verify_origin_token(self):
        now = int(time.time())
        token = create_origin_token(
            secret="test-secret",
            locator="netease/椹篃/娴峰笨浣?flac",
            expires_at=now + 300,
        )

        payload = verify_origin_token(
            secret="test-secret",
            token=token,
            now=now,
        )

        self.assertEqual("netease/椹篃/娴峰笨浣?flac", payload["locator"])


if __name__ == "__main__":
    unittest.main()
  • Step 2: Run test to verify it fails

Run: python -m unittest tests.catalogsync.test_private_origin_tokens -v
Expected: ERROR with ModuleNotFoundError

  • Step 3: Write minimal implementation

musicdl/catalogsync/private_origin/__init__.py

from .tokens import create_origin_token, verify_origin_token

__all__ = ["create_origin_token", "verify_origin_token"]

musicdl/catalogsync/private_origin/tokens.py

import base64
import hashlib
import hmac
import json


def _sign(secret: str, payload_json: str) -> str:
    digest = hmac.new(
        secret.encode("utf-8"),
        payload_json.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return digest


def create_origin_token(secret: str, locator: str, expires_at: int) -> str:
    payload = {"locator": locator, "expires_at": int(expires_at)}
    payload_json = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
    signature = _sign(secret=secret, payload_json=payload_json)
    envelope = {"payload": payload, "sig": signature}
    return base64.urlsafe_b64encode(
        json.dumps(envelope, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
    ).decode("ascii")


def verify_origin_token(secret: str, token: str, now: int) -> dict:
    envelope_json = base64.urlsafe_b64decode(token.encode("ascii")).decode("utf-8")
    envelope = json.loads(envelope_json)
    payload = envelope["payload"]
    payload_json = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
    expected_sig = _sign(secret=secret, payload_json=payload_json)
    if not hmac.compare_digest(expected_sig, envelope["sig"]):
        raise ValueError("invalid signature")
    if int(payload["expires_at"]) < int(now):
        raise ValueError("token expired")
    return payload
  • Step 4: Run test to verify it passes

Run: python -m unittest tests.catalogsync.test_private_origin_tokens -v
Expected: OK

  • Step 5: Commit
git add tests/catalogsync/test_private_origin_tokens.py musicdl/catalogsync/private_origin/__init__.py musicdl/catalogsync/private_origin/tokens.py
git commit -m "feat: add private origin token helpers"

Task 5: Expand snapshot export to file availability and toplist read models

Files:

  • Modify: musicdl/catalogsync/export/service.py

  • Test: tests/catalogsync/test_snapshot_export.py

  • Step 1: Write the failing test

import sqlite3
import tempfile
import unittest
from pathlib import Path

from musicdl.catalogsync.export.service import export_snapshot


class SnapshotExportExtendedTests(unittest.TestCase):
    def test_export_snapshot_writes_track_files_and_toplists(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            tmpdir_path = Path(tmpdir)
            source_db = tmpdir_path / "catalogsync.db"
            target_dir = tmpdir_path / "snapshot"

            conn = sqlite3.connect(source_db)
            conn.executescript(
                '''
                create table playlists (
                    id integer primary key,
                    platform text not null,
                    remote_playlist_id text not null,
                    name text not null,
                    description text,
                    cover_url text,
                    play_count integer default 0,
                    collected_song_count integer default 0
                );
                create table songs (
                    id integer primary key,
                    platform text not null,
                    remote_song_id text not null,
                    name text not null,
                    singers text,
                    ext text,
                    file_size_bytes integer,
                    metadata_json text
                );
                create table playlist_songs (playlist_id integer not null, song_id integer not null, position integer not null);
                create table playlist_pools (
                    id integer primary key,
                    platform text not null,
                    pool_kind text not null,
                    external_id text not null,
                    name text not null,
                    url text,
                    metadata_json text
                );
                create table pool_playlists (pool_id integer not null, playlist_id integer not null);
                create table storage_backends (
                    id integer primary key,
                    name text not null,
                    backend_type text not null,
                    base_path text,
                    config_json text
                );
                create table file_assets (
                    id integer primary key,
                    song_id integer not null,
                    quality_label text,
                    ext text,
                    file_size_bytes integer
                );
                create table file_locations (
                    id integer primary key,
                    file_asset_id integer not null,
                    backend_id integer not null,
                    locator text not null,
                    status text not null,
                    is_primary integer not null
                );
                insert into playlists values (1, 'kuwo', '16', '酷我飙升榜', 'desc', 'https://img/top.jpg', 1000, 1);
                insert into songs values (10, 'netease', '65800', '海屿你', '马也 / Crabbit', 'flac', 123456, '{"duration_ms": 0}');
                insert into playlist_songs values (1, 10, 1);
                insert into playlist_pools values (5, 'kuwo', 'toplist', 'kuwo_top_16', '酷我榜单', null, '{}');
                insert into pool_playlists values (5, 1);
                insert into storage_backends values (2, 'main-s3', 'object_storage', null, '{"public_base_url": "https://cdn.example"}');
                insert into file_assets values (7, 10, 'super', 'flac', 123456);
                insert into file_locations values (9, 7, 2, 'music/netease/test.flac', 'active', 1);
                '''
            )
            conn.commit()
            conn.close()

            export_snapshot(source_db=source_db, target_dir=target_dir)

            read_conn = sqlite3.connect(target_dir / "catalog_read.db")
            track_file_row = read_conn.execute(
                "select song_id, backend_type, backend_name, locator from catalog_track_files"
            ).fetchone()
            toplist_row = read_conn.execute(
                "select toplist_id, group_name, song_count from catalog_toplists"
            ).fetchone()
            read_conn.close()

            self.assertEqual((10, "object_storage", "main-s3", "music/netease/test.flac"), track_file_row)
            self.assertEqual(("kuwo_top_16", "酷我榜单", 1), toplist_row)


if __name__ == "__main__":
    unittest.main()
  • Step 2: Run test to verify it fails

Run: python -m unittest tests.catalogsync.test_snapshot_export.SnapshotExportExtendedTests -v
Expected: FAIL with sqlite3.OperationalError: no such table: catalog_track_files

  • Step 3: Write minimal implementation

musicdl/catalogsync/export/service.py

import json
import sqlite3
from datetime import datetime, timezone
from pathlib import Path

from .models import SnapshotManifest


def export_snapshot(source_db: Path, target_dir: Path) -> SnapshotManifest:
    source_db = Path(source_db)
    target_dir = Path(target_dir)
    target_dir.mkdir(parents=True, exist_ok=True)
    target_db = target_dir / "catalog_read.db"
    target_manifest = target_dir / "manifest.json"

    if target_db.exists():
        target_db.unlink()

    source_conn = sqlite3.connect(source_db)
    source_conn.row_factory = sqlite3.Row
    target_conn = sqlite3.connect(target_db)

    target_conn.executescript(
        """
        create table catalog_playlists (
            playlist_id integer primary key,
            platform text not null,
            remote_playlist_id text not null,
            name text not null,
            description text,
            cover_url text,
            play_count integer not null default 0,
            song_count integer not null default 0
        );
        create table catalog_tracks (
            song_id integer primary key,
            platform text not null,
            remote_song_id text not null,
            name text not null,
            singers text,
            ext text,
            file_size_bytes integer,
            metadata_json text
        );
        create table catalog_playlist_tracks (
            playlist_id integer not null,
            song_id integer not null,
            position integer not null
        );
        create table catalog_track_files (
            song_id integer not null,
            quality_label text,
            ext text,
            file_size_bytes integer,
            backend_type text,
            backend_name text,
            locator text,
            public_url text,
            status text,
            is_primary integer
        );
        create table catalog_toplists (
            toplist_id text primary key,
            platform text not null,
            name text not null,
            description text,
            cover_url text,
            play_count integer not null,
            song_count integer not null,
            group_name text not null
        );
        """
    )

    playlists = source_conn.execute(
        """
        select
            p.id as playlist_id,
            p.platform,
            p.remote_playlist_id,
            p.name,
            p.description,
            p.cover_url,
            coalesce(p.play_count, 0) as play_count,
            coalesce(p.collected_song_count, 0) as song_count
        from playlists p
        """
    ).fetchall()
    tracks = source_conn.execute(
        """
        select
            s.id as song_id,
            s.platform,
            s.remote_song_id,
            s.name,
            s.singers,
            s.ext,
            s.file_size_bytes,
            s.metadata_json
        from songs s
        """
    ).fetchall()
    playlist_tracks = source_conn.execute(
        """
        select playlist_id, song_id, position
        from playlist_songs
        order by playlist_id, position
        """
    ).fetchall()
    track_files = source_conn.execute(
        """
        select
            fa.song_id,
            fa.quality_label,
            fa.ext,
            fa.file_size_bytes,
            sb.backend_type,
            sb.name as backend_name,
            fl.locator,
            case
                when sb.backend_type = 'object_storage'
                then coalesce(json_extract(sb.config_json, '$.public_base_url'), '') || '/' || fl.locator
                else null
            end as public_url,
            fl.status,
            fl.is_primary
        from file_locations fl
        join file_assets fa on fa.id = fl.file_asset_id
        join storage_backends sb on sb.id = fl.backend_id
        where fl.status = 'active'
        """
    ).fetchall()
    toplists = source_conn.execute(
        """
        select
            pp.external_id as toplist_id,
            p.platform,
            p.name,
            p.description,
            p.cover_url,
            coalesce(p.play_count, 0) as play_count,
            coalesce(p.collected_song_count, 0) as song_count,
            pp.name as group_name
        from playlists p
        join pool_playlists rel on rel.playlist_id = p.id
        join playlist_pools pp on pp.id = rel.pool_id
        where pp.pool_kind = 'toplist'
        """
    ).fetchall()

    target_conn.executemany(
        """
        insert into catalog_playlists (
            playlist_id, platform, remote_playlist_id, name, description, cover_url, play_count, song_count
        ) values (
            :playlist_id, :platform, :remote_playlist_id, :name, :description, :cover_url, :play_count, :song_count
        )
        """,
        playlists,
    )
    target_conn.executemany(
        """
        insert into catalog_tracks (
            song_id, platform, remote_song_id, name, singers, ext, file_size_bytes, metadata_json
        ) values (
            :song_id, :platform, :remote_song_id, :name, :singers, :ext, :file_size_bytes, :metadata_json
        )
        """,
        tracks,
    )
    target_conn.executemany(
        """
        insert into catalog_playlist_tracks (playlist_id, song_id, position)
        values (:playlist_id, :song_id, :position)
        """,
        playlist_tracks,
    )
    target_conn.executemany(
        """
        insert into catalog_track_files (
            song_id, quality_label, ext, file_size_bytes, backend_type, backend_name, locator, public_url, status, is_primary
        ) values (
            :song_id, :quality_label, :ext, :file_size_bytes, :backend_type, :backend_name, :locator, :public_url, :status, :is_primary
        )
        """,
        track_files,
    )
    target_conn.executemany(
        """
        insert into catalog_toplists (
            toplist_id, platform, name, description, cover_url, play_count, song_count, group_name
        ) values (
            :toplist_id, :platform, :name, :description, :cover_url, :play_count, :song_count, :group_name
        )
        """,
        toplists,
    )
    target_conn.commit()
    target_conn.close()
    source_conn.close()

    manifest = SnapshotManifest(
        snapshot_id=f"snap-playlists-{len(playlists)}-tracks-{len(tracks)}",
        generated_at=datetime.now(timezone.utc).isoformat(),
        schema_version=1,
        playlist_count=len(playlists),
        track_count=len(tracks),
        file_count=len(track_files),
        cover_count=sum(1 for row in playlists if row["cover_url"]),
    )
    target_manifest.write_text(json.dumps(manifest.to_dict(), ensure_ascii=False, indent=2), encoding="utf-8")
    return manifest
  • Step 4: Run test to verify it passes

Run: python -m unittest tests.catalogsync.test_snapshot_export.SnapshotExportExtendedTests -v
Expected: OK

  • Step 5: Commit
git add tests/catalogsync/test_snapshot_export.py musicdl/catalogsync/export/service.py
git commit -m "feat: export track files and toplists in snapshot"

Task 6: Add the NAS private-origin streaming route to the ops web app

Files:

  • Create: musicdl/catalogsync/private_origin/service.py

  • Modify: musicdl/catalogsync/ops/web.py

  • Test: tests/catalogsync/test_private_origin_web.py

  • Step 1: Write the failing test

import tempfile
import unittest
from pathlib import Path

from fastapi.testclient import TestClient

from musicdl.catalogsync.ops.web import create_app
from musicdl.catalogsync.private_origin.tokens import create_origin_token


class PrivateOriginWebTests(unittest.TestCase):
    def test_private_origin_stream_serves_local_file_when_token_is_valid(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            music_file = Path(tmpdir) / "test.flac"
            music_file.write_bytes(b"test-audio")
            token = create_origin_token(
                secret="secret-123",
                locator=str(music_file),
                expires_at=4102444800,
            )

            app = create_app(
                db_path=Path(tmpdir) / "catalogsync.db",
                env_file=Path(tmpdir) / "catalogsync.env",
            )
            app.state.private_origin_secret = "secret-123"
            client = TestClient(app)
            response = client.get(f"/api/private-origin/stream/{token}")

            self.assertEqual(200, response.status_code)
            self.assertEqual(b"test-audio", response.content)


if __name__ == "__main__":
    unittest.main()
  • Step 2: Run test to verify it fails

Run: python -m unittest tests.catalogsync.test_private_origin_web -v
Expected: FAIL with 404 Not Found for /api/private-origin/stream/{token}

  • Step 3: Write minimal implementation

musicdl/catalogsync/private_origin/service.py

from pathlib import Path

from fastapi import HTTPException

from .tokens import verify_origin_token


def resolve_private_origin_file(secret: str, token: str, now: int) -> Path:
    payload = verify_origin_token(secret=secret, token=token, now=now)
    file_path = Path(payload["locator"]).resolve()
    if not file_path.exists() or not file_path.is_file():
        raise HTTPException(status_code=404, detail="private origin file not found")
    return file_path

musicdl/catalogsync/ops/web.py

import time
from pathlib import Path

from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse

from musicdl.catalogsync.private_origin.service import resolve_private_origin_file


def create_app(db_path: Path, env_file: Path) -> FastAPI:
    app = FastAPI(title="Catalogsync Operations Console")

    @app.get("/api/private-origin/stream/{token}")
    def private_origin_stream(token: str):
        secret = getattr(app.state, "private_origin_secret", "")
        if not secret:
            raise HTTPException(status_code=503, detail="private origin secret not configured")
        file_path = resolve_private_origin_file(secret=secret, token=token, now=int(time.time()))
        return FileResponse(path=file_path, filename=file_path.name)

    return app
  • Step 4: Run test to verify it passes

Run: python -m unittest tests.catalogsync.test_private_origin_web -v
Expected: OK

  • Step 5: Commit
git add tests/catalogsync/test_private_origin_web.py musicdl/catalogsync/private_origin/service.py musicdl/catalogsync/ops/web.py
git commit -m "feat: add nas private origin stream route"