diff options
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/daemon.py')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/daemon.py | 38 |
1 files changed, 33 insertions, 5 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/daemon.py b/packages/meshbay-node/src/meshbay_node/daemon.py index 779ad91..72813b6 100644 --- a/packages/meshbay-node/src/meshbay_node/daemon.py +++ b/packages/meshbay-node/src/meshbay_node/daemon.py @@ -448,6 +448,7 @@ class NodeDaemon: # enricher (Pillow, not ffmpeg/mutagen). No credential, no # third-party client to construct: EXIF is read locally. self._photo_enricher = PhotoEnricher(self._media_cache) + self._state["media_cache"] = self._media_cache log.info("Media cache opened: %s", media_cache_db) # 5. Denylist @@ -1218,6 +1219,14 @@ class NodeDaemon: if old is None or (old.name == entry.name and old.path == entry.path): continue self._enriched_attempted.discard((indexer.group_id, entry.id)) + # The rename re-derives the title (the whole point of this + # method), which can change the correct TMDB match — but + # file_tmdb is keyed by content hash, unchanged by a rename, so + # nothing else would ever dislodge the old name's match. A + # manual "Fix match" correction is kept (clear_file_tmdb skips + # anything in media_cache.tmdb_override). + if self._media_cache is not None: + await self._media_cache.clear_file_tmdb(entry.id) await self._enrich_new_video_entries(indexer, updates) async def _enrich_new_audio_entries(self, indexer: DirectoryIndexer, entries: list) -> None: @@ -1576,21 +1585,22 @@ def main() -> None: parser.add_argument("command", nargs="?", choices=["init", "reset", "status", "ui", "gek-init", "gek", "operator", "member", "group", "file", - "denylist", "reload", "restart-daemon", + "video", "denylist", "reload", "restart-daemon", "calibrate-argon2"], help="init: provision config + keystore | reset: erase all " "node state | status: node state and keys " "| ui: print the admin UI URL | operator pair: pair a " "browser with this node | member list|invite|revoke|unpin " "| group list|add|remove | gek init|rotate | file list|rm " - "| denylist show|clear | reload: re-read node.toml " + "| video rematch: re-resolve TMDB matches for a group's " + "videos | denylist show|clear | reload: re-read node.toml " "(systemctl --user reload) | restart-daemon: restart " "the systemd unit (systemctl --user restart) " "| calibrate-argon2: benchmark") parser.add_argument("subcommand", nargs="?", help="'pair' for operator; list|invite|revoke|unpin for " "member; list|add|remove for group; init|rotate for gek; " - "list|rm for file; show|clear for denylist") + "list|rm for file; rematch for video; show|clear for denylist") parser.add_argument("target", nargs="?", help="username for member invite|revoke|unpin; group name " "for group add; file id for file rm; identifier for " @@ -1615,8 +1625,8 @@ def main() -> None: # Query commands print a report; library logging would interleave with it. quiet = args.command in ("status", "ui", "gek-init", "gek", "operator", - "member", "group", "file", "denylist", "reload", - "restart-daemon", "reset") + "member", "group", "file", "video", "denylist", + "reload", "restart-daemon", "reset") logging.basicConfig( level=logging.ERROR if quiet else getattr(logging, args.log_level), format="%(asctime)s %(levelname)-8s %(name)s: %(message)s", @@ -2107,6 +2117,24 @@ def main() -> None: print("usage: meshbay-node file list|rm <id> [--group NAME] [--yes]") sys.exit(1) + if args.command == "video": + cfg = load_config(args.config or DEFAULT_CONFIG_PATH) + if (args.subcommand or "") != "rematch": + print("usage: meshbay-node video rematch [--group NAME] [--yes]") + sys.exit(1) + group_id = _resolve_group(cfg, args.group) + if not args.yes: + print("Re-resolve every automatic TMDB match for this group's videos?") + print("Manual 'Fix match' corrections are kept. Re-resolution is lazy —") + print("each poster re-queries TMDB the next time it is opened.") + if input("proceed? [y/N] ").strip().lower() not in ("y", "yes"): + print("cancelled") + return + out = _daemon_api(cfg, f"/api/groups/{group_id}/video/rematch", method="POST") + print(f"cleared {out.get('removed', 0)} automatic match(es) " + f"across {out.get('videos', 0)} video file(s)") + return + if args.command == "group": if args.subcommand in (None, "list"): # Milestone 14.2. |