""" A video in a photo folder (a phone's clips, backed up beside its photos, docs/MESHBAY_DESIGN.md ยง9.12) is probed and thumbnailed by the video enricher, so the Photos app can show it in its album; one outside every photo folder and every video folder is left alone, as before. """ import asyncio import pytest from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from meshbay_common.crypto import generate_gek from meshbay_node.config import Config, GroupConfig, HubConfig, KeystoreConfig, NodeConfig from meshbay_node.daemon import NodeDaemon from meshbay_node.indexer import DirectoryIndexer from meshbay_node.indexer.enrich import Enricher from meshbay_node.indexer.enrich_photo import PhotoEnricher from meshbay_node.media_cache import MediaCache from meshbay_node.roster import Roster from conftest import one_root pytestmark = pytest.mark.asyncio def _free_port() -> int: import socket with socket.socket() as s: s.bind(("127.0.0.1", 0)) return s.getsockname()[1] async def _make_daemon(tmp_path, shared, group_id): config = Config( hub=HubConfig(url="http://localhost:9999", username="testuser"), node=NodeConfig(quic_port=_free_port(), ui_port=_free_port()), groups=[GroupConfig( id=group_id, name="test-group", shared_dir=str(shared), visibility="private", quic_port=29015, )], keystore=KeystoreConfig(path=tmp_path / "keystore.enc"), data_dir=tmp_path / "data", ) daemon = NodeDaemon(config) daemon._broadcast_coalesce_secs = 0.01 daemon._media_cache = MediaCache(db_path=tmp_path / "media_cache.db") await daemon._media_cache.open() daemon._enricher = Enricher(daemon._media_cache) daemon._photo_enricher = PhotoEnricher(daemon._media_cache) daemon._roster = Roster(db_path=tmp_path / "roster.db") await daemon._roster.open() return daemon async def test_a_video_in_a_photo_folder_goes_to_the_video_enricher(tmp_path): group_id = "b" * 32 shared = tmp_path / "shared" (shared / "Photos" / "bob-photos").mkdir(parents=True) (shared / "Photos" / "bob-photos" / "clip.mp4").write_bytes(b"a clip") (shared / "Photos" / "bob-photos" / "shot.jpg").write_bytes(b"a photo") (shared / "elsewhere.mp4").write_bytes(b"not in any app folder") daemon = await _make_daemon(tmp_path, shared, group_id) video_spawned, photo_spawned = [], [] daemon._enricher.spawn = lambda entry, path, on_done: video_spawned.append(entry.name) daemon._photo_enricher.spawn = lambda entry, path, on_done: photo_spawned.append(entry.name) try: await daemon._roster.set_app_directories(group_id, "photo", ["shared/Photos"], set_by="op") indexer = DirectoryIndexer( roots=one_root(shared), group_id=group_id, sk_node=Ed25519PrivateKey.generate(), gek=generate_gek()) await indexer.initial_scan() await daemon._on_index_change(indexer) await asyncio.sleep(0.05) assert video_spawned == ["clip.mp4"] assert photo_spawned == ["shot.jpg"] finally: await daemon._media_cache.close() await daemon._roster.close()