mirror of
https://github.com/MrSleeps/jellyfin-scene-search.git
synced 2026-11-14 22:11:27 +00:00
166 lines
5.3 KiB
Python
166 lines
5.3 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
|
|
from scene_search.cli import make_parser
|
|
from scene_search.database import connect, init_db
|
|
from scene_search.jellyfin_index import (
|
|
_reconcile_library,
|
|
resolve_libraries,
|
|
resolve_library_ids,
|
|
)
|
|
|
|
|
|
class FakeJellyfinClient:
|
|
def media_folders(self) -> list[dict]:
|
|
return [
|
|
{"Id": "lib-movies", "Name": "Movies"},
|
|
{"Id": "lib-tv", "Name": "TV"},
|
|
{"Id": "lib-home", "Name": "Home Videos"},
|
|
]
|
|
|
|
|
|
class MultiLibrarySelectionTests(unittest.TestCase):
|
|
def test_all_libraries_can_exclude_named_library(self) -> None:
|
|
client = FakeJellyfinClient()
|
|
|
|
with patch(
|
|
"scene_search.jellyfin_index.settings",
|
|
SimpleNamespace(
|
|
jellyfin_exclude_libraries=(),
|
|
jellyfin_libraries=(),
|
|
jellyfin_library="",
|
|
),
|
|
):
|
|
libraries = resolve_libraries(
|
|
client, # type: ignore[arg-type]
|
|
None,
|
|
all_libraries=True,
|
|
excluded=["TV"],
|
|
)
|
|
|
|
self.assertEqual(
|
|
[library["Name"] for library in libraries],
|
|
["Home Videos", "Movies"],
|
|
)
|
|
|
|
def test_repeated_library_arguments_select_multiple(self) -> None:
|
|
parser = make_parser()
|
|
args = parser.parse_args(
|
|
[
|
|
"jellyfin-index",
|
|
"--library",
|
|
"Movies",
|
|
"--library",
|
|
"TV",
|
|
]
|
|
)
|
|
|
|
self.assertEqual(args.library, ["Movies", "TV"])
|
|
self.assertFalse(args.all_libraries)
|
|
|
|
def test_plugin_library_ids_resolve_to_jellyfin_libraries(self) -> None:
|
|
client = FakeJellyfinClient()
|
|
|
|
libraries = resolve_library_ids(
|
|
client, # type: ignore[arg-type]
|
|
["lib-tv", "lib-movies"],
|
|
)
|
|
|
|
self.assertEqual(
|
|
[library["Name"] for library in libraries],
|
|
["Movies", "TV"],
|
|
)
|
|
|
|
def test_requested_library_names_are_case_insensitive(self) -> None:
|
|
client = FakeJellyfinClient()
|
|
|
|
with patch(
|
|
"scene_search.jellyfin_index.settings",
|
|
SimpleNamespace(
|
|
jellyfin_exclude_libraries=(),
|
|
jellyfin_libraries=(),
|
|
jellyfin_library="",
|
|
),
|
|
):
|
|
libraries = resolve_libraries(
|
|
client, # type: ignore[arg-type]
|
|
["movies", "HOME VIDEOS"],
|
|
)
|
|
|
|
self.assertEqual(
|
|
[library["Name"] for library in libraries],
|
|
["Home Videos", "Movies"],
|
|
)
|
|
|
|
|
|
class LibraryReconciliationTests(unittest.TestCase):
|
|
def test_reconcile_only_prunes_stale_items_from_target_library(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
db_path = root / "scenes.sqlite3"
|
|
thumbs_dir = root / "thumbs"
|
|
init_db(db_path)
|
|
|
|
rows = [
|
|
("keep", "Keep", "jf-keep", "lib-a"),
|
|
("stale", "Stale", "jf-stale", "lib-a"),
|
|
("other", "Other", "jf-other", "lib-b"),
|
|
]
|
|
with connect(db_path) as con:
|
|
for media_id, title, jellyfin_id, library_id in rows:
|
|
con.execute(
|
|
"""
|
|
INSERT INTO media (
|
|
id, source_path, title, content_sha256, size_bytes,
|
|
mtime_ns, interval_ms, model_id, embedding_dim,
|
|
source_kind, jellyfin_item_id, jellyfin_library_id
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
(
|
|
media_id,
|
|
f"jellyfin://{jellyfin_id}",
|
|
title,
|
|
"hash",
|
|
0,
|
|
0,
|
|
10000,
|
|
"model",
|
|
2,
|
|
"jellyfin",
|
|
jellyfin_id,
|
|
library_id,
|
|
),
|
|
)
|
|
folder = thumbs_dir / media_id
|
|
folder.mkdir(parents=True)
|
|
(folder / "frame.jpg").write_bytes(b"x")
|
|
|
|
fake_settings = SimpleNamespace(
|
|
db_path=db_path,
|
|
thumbs_dir=thumbs_dir,
|
|
)
|
|
with patch("scene_search.jellyfin_index.settings", fake_settings):
|
|
pruned = _reconcile_library("lib-a", {"jf-keep"})
|
|
|
|
self.assertEqual(pruned, 1)
|
|
with connect(db_path) as con:
|
|
remaining = {
|
|
row["id"]
|
|
for row in con.execute("SELECT id FROM media").fetchall()
|
|
}
|
|
|
|
self.assertEqual(remaining, {"keep", "other"})
|
|
self.assertFalse((thumbs_dir / "stale").exists())
|
|
self.assertTrue((thumbs_dir / "keep").exists())
|
|
self.assertTrue((thumbs_dir / "other").exists())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|