mirror of
https://github.com/MrSleeps/jellyfin-scene-search.git
synced 2026-11-14 22:11:27 +00:00
Use reusable Meilisearch rebuild service
This commit is contained in:
+12
-67
@@ -30,6 +30,7 @@ from .maintenance import list_media_by_source, remove_media_by_source
|
||||
from .meili import Meili
|
||||
from .routing import ExplicitContentRouter, apply_context_routing
|
||||
from .search_service import SearchService
|
||||
from .sync import rebuild_meili
|
||||
|
||||
|
||||
def format_ts(ms: int) -> str:
|
||||
@@ -141,74 +142,18 @@ def cmd_index_file(args: argparse.Namespace) -> None:
|
||||
|
||||
|
||||
def cmd_sync_meili(_: argparse.Namespace) -> None:
|
||||
init_db(settings.db_path)
|
||||
meili = Meili(settings.meili_url, settings.meili_master_key, settings.meili_index)
|
||||
def report(update: dict) -> None:
|
||||
if update.get("stage") == "sync":
|
||||
print(
|
||||
f"Queued {int(update.get('documents_processed') or 0)} "
|
||||
"documents into Meilisearch"
|
||||
)
|
||||
|
||||
with connect(settings.db_path) as con:
|
||||
dims = [r[0] for r in con.execute("SELECT DISTINCT embedding_dim FROM media")]
|
||||
if not dims:
|
||||
raise SystemExit("Nothing indexed yet")
|
||||
if len(dims) != 1:
|
||||
raise SystemExit(f"Multiple embedding dimensions found: {dims}. Use separate indexes per model.")
|
||||
dim = int(dims[0])
|
||||
meili.configure_user_vectors(dim)
|
||||
meili.clear_documents()
|
||||
|
||||
cursor = con.execute(
|
||||
"""
|
||||
SELECT f.id, f.media_id, f.timestamp_ms, f.thumb_path, f.embedding_f16,
|
||||
f.caption, f.caption_model, f.captioned_at,
|
||||
f.caption_route, f.caption_route_reason, f.route_score,
|
||||
f.route_model, f.route_policy, f.routed_at,
|
||||
m.title, m.source_path, m.embedding_dim, m.model_id,
|
||||
m.source_kind, m.jellyfin_item_id, m.jellyfin_media_source_id,
|
||||
m.jellyfin_library_id, m.jellyfin_item_type, m.jellyfin_tags,
|
||||
m.trickplay_width
|
||||
FROM frames f
|
||||
JOIN media m ON m.id = f.media_id
|
||||
ORDER BY f.id
|
||||
"""
|
||||
)
|
||||
|
||||
total = 0
|
||||
while True:
|
||||
rows = cursor.fetchmany(100)
|
||||
if not rows:
|
||||
break
|
||||
docs = []
|
||||
for row in rows:
|
||||
vec = unpack_vector(row["embedding_f16"], row["embedding_dim"])
|
||||
docs.append({
|
||||
"id": row["id"],
|
||||
"media_id": row["media_id"],
|
||||
"title": row["title"],
|
||||
"source_path": row["source_path"],
|
||||
"timestamp_ms": row["timestamp_ms"],
|
||||
"thumb_path": row["thumb_path"],
|
||||
"model_id": row["model_id"],
|
||||
"caption": row["caption"],
|
||||
"caption_model": row["caption_model"],
|
||||
"captioned_at": row["captioned_at"],
|
||||
"caption_route": row["caption_route"],
|
||||
"caption_route_reason": row["caption_route_reason"],
|
||||
"route_score": row["route_score"],
|
||||
"route_model": row["route_model"],
|
||||
"route_policy": row["route_policy"],
|
||||
"routed_at": row["routed_at"],
|
||||
"source_kind": row["source_kind"],
|
||||
"jellyfin_item_id": row["jellyfin_item_id"],
|
||||
"jellyfin_media_source_id": row["jellyfin_media_source_id"],
|
||||
"jellyfin_library_id": row["jellyfin_library_id"],
|
||||
"jellyfin_item_type": row["jellyfin_item_type"],
|
||||
"jellyfin_tags": tags_from_json(row["jellyfin_tags"]),
|
||||
"trickplay_width": row["trickplay_width"],
|
||||
"_vectors": {"siglip2": vec.tolist()},
|
||||
})
|
||||
meili.add_documents(docs)
|
||||
total += len(docs)
|
||||
print(f"Queued {total} documents into Meilisearch")
|
||||
|
||||
print("Meilisearch rebuild complete (tasks may continue briefly inside Meili).")
|
||||
total = rebuild_meili(progress=report)
|
||||
if total:
|
||||
print("Meilisearch rebuild complete (tasks may continue briefly inside Meili).")
|
||||
else:
|
||||
print("Meilisearch cleared; no canonical media remain.")
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user