Use reusable Meilisearch rebuild service

This commit is contained in:
Sleeps
2026-10-06 17:55:12 +01:00
parent 21f092b072
commit 21beb355f4
+12 -67
View File
@@ -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.")