# Historisk implementationsprompt för filnamnsmatchning ## Status efter projektuppdelningen Detta dokument är den ursprungliga implementationsprompten från tiden före projektuppdelningen och bevaras endast som historiskt källmaterial. Gamla sökvägar och framtidsformuleringar nedan beskriver dåvarande repositorystruktur och är inte aktuella instruktioner. Den implementerade filnamnsmatchningen finns nu i `/storage/disk1/WCX-collection/scripts/match_filenames.py`, och dess framtida tester hör hemma under `/storage/disk1/WCX-collection/tests/`. WCX-collection är en separat extern konsument som öppnar facitdatabasen strikt read-only med SQLite `mode=ro`; facitprojektet är fortsatt ensam skrivare. ## Ursprunglig prompt Jag vill implementera en första, skrivskyddad version av funktionaliteten som beskrivs i: ```text docs/filename-matching-requirements.md ``` Läs hela kravspecifikationen innan du föreslår eller skriver kod. ## Projektets nuvarande struktur Projektrot: ```text /storage/disk1/WCX ``` SQLite-databas: ```text /storage/disk1/WCX/database/wcx.db ``` Databasschema: ```text /storage/disk1/WCX/scripts/schema.sql ``` Nya script ska placeras i: ```text /storage/disk1/WCX/scripts ``` Databasen innehåller tabellen `movie` med bland annat: ```text id name duration_seconds ``` Den innehåller även `movie_history` med historiska snapshots och bland annat: ```text movie_id name duration_seconds archived_at change_summary ``` Fältet `movie.name` är filmens kanoniska namn från siten. Det består alltid av ett eller flera personnamn. Flera personer separeras med `+`. Exempel: ```text Alyn Borav Ilona Angelique Lapiedra + Scarlett Lapiedra ``` Lokala videofiler kan däremot ha kraftigt nedsmutsade filnamn, exempelvis: ```text Alyn_Borav_AlynBorav.mp4 AngeliqueLapiedra_ScarlettLapiedra.mp4 BrendaBoop_Interwiev_Fisting.mp4 Busty_Slovakian_Ilona_On_Bed.mp4 Eden_Ivy_EdenIvy_DP.mp4 Version_AngelikaFyres_DP.mp4 ``` ## Uppgift Implementera den första versionen av: ```text scripts/match_filenames.py ``` Scriptet ska analysera lokala videofiler och försöka matcha dem mot poster i `movie`. Det ska vara konservativt. Korrekthet är viktigare än hur många filer som matchas automatiskt. Resultatet ska klassificeras som: ```text matched ambiguous unmatched ``` Osäkra filer ska lämnas för manuell inspektion. ## Omfattning för första versionen Första versionen ska: 1. Läsa videofiler från en angiven katalog. 2. Ha stöd för rekursiv sökning. 3. Läsa `id`, `name` och `duration_seconds` från `movie`. 4. Läsa historiska längder från `movie_history`. 5. Normalisera filnamn och databasnamn. 6. Dela flerpersonsnamn på `+`. 7. Hitta exakta normaliserade namn i nedsmutsade filnamn. 8. Hantera namn som skrivits utan separatorer. 9. Hantera upprepade namn i filnamnet. 10. Prioritera den mest specifika flerpersonsposten. 11. Identifiera när filnamnet innehåller flera kända personer. 12. Läsa videons faktiska längd med `ffprobe`. 13. Använda längd som en sekundär signal. 14. Jämföra längden mot både aktuell och historisk sitelängd. 15. Rangordna kandidater med en förklarbar poängmodell. 16. Kräva både en tillräckligt hög poäng och en tydlig marginal till kandidat nummer två för `matched`. 17. Klassificera osäkra resultat som `ambiguous`. 18. Skriva rapporter för `matched`, `ambiguous` och `unmatched`. 19. Inte ändra databasen eller några videofiler. ## Viktiga säkerhetsregler Scriptet får inte: * ändra databasen * flytta filer * döpa om filer * radera filer * registrera alias * permanent koppla en fil till en databaspost * automatcha en fil enbart på grund av liknande längd * automatcha en fil enbart på grund av fuzzy matching Fuzzy matching får gärna förberedas eller användas för kandidatförslag, men ett resultat som är beroende av fuzzy matching ska i första versionen klassificeras som `ambiguous`. En felaktig automatisk match är värre än en omatchad fil. ## Namnnormalisering Normaliseringen ska minst: * ta bort filändelsen * göra texten gemen * normalisera diakritiska tecken * ta bort mellanslag * ta bort `_` * ta bort `-` * ta bort `+` * ta bort punkter och annan interpunktion * behålla bokstäver och siffror Exempel: ```text Alyn_Borav_AlynBorav.mp4 → alynboravalynborav Alyn Borav → alynborav ``` Exakta normaliserade delsträngsträffar ska väga tungt. Extra ord i filnamnet behöver inte tas bort om databasnamnet ändå kan identifieras som en exakt normaliserad delsträng. ## Flerpersonsfilmer För: ```text Angelique Lapiedra + Scarlett Lapiedra ``` ska personerna behandlas separat: ```text Angelique Lapiedra Scarlett Lapiedra ``` Båda dessa filnamn ska kunna matcha posten: ```text AngeliqueLapiedra_ScarlettLapiedra.mp4 ScarlettLapiedra_AngeliqueLapiedra.mp4 ``` Ordningen ska alltså inte vara avgörande. Om databasen innehåller både: ```text Scarlett Knight Scarlett Knight + Anya Shidlerova ``` och filen heter: ```text ScarlettKnight_AnyaShidlerova.mp4 ``` ska flerpersonsposten rankas högre. ## Videolängd Använd `ffprobe` för att läsa faktisk längd utan omkodning. Längd ska endast användas för att stärka eller diskriminera mellan redan rimliga namnkandidater. Beräkna både: ```text absolut skillnad i sekunder procentuell skillnad ``` En första längdmodell kan ungefär vara: ```text ≤ 15 sekunder eller ≤ 1 % mycket stark signal ≤ 60 sekunder eller ≤ 2 % stark signal ≤ 3 minuter eller ≤ 5 % rimlig signal ≤ 10 minuter eller ≤ 10 % svag signal > 10 minuter och > 10 % negativ signal ``` Dessa värden ska definieras som tydliga konstanter så att de senare kan justeras. En större längdskillnad ska inte automatiskt ge `unmatched`, eftersom filer kan vara klippta, förlängda eller motsvara en äldre version. Historiska längder från `movie_history` ska kunna ge klassificeringen: ```text historical_version_match ``` Andra möjliga versionsklassificeringar: ```text current_version_match historical_version_match possible_truncated_version possible_extended_version duration_mismatch unknown_version ``` ## Automatisk matchning `matched` ska kräva: 1. en tillräckligt stark namnmatchning 2. en totalpoäng över ett tydligt tröskelvärde 3. en tydlig marginal till kandidat nummer två 4. inga starka motstridiga namnsignaler 5. att fuzzy matching inte är den enda avgörande signalen Alla tröskelvärden ska ligga som namngivna konstanter nära början av scriptet. Poängen ska vara förklarbar. Varje kandidat ska innehålla en lista över skäl, exempelvis: ```text exact normalized full-name match all participants matched exact participant set current duration differs by 5 seconds historical duration differs by 8 seconds extra known participant found fuzzy match required ``` ## Kommandoradsgränssnitt Scriptet ska minst stödja: ```bash scripts/match_filenames.py /path/to/videos ``` Följande argument ska finnas: ```text --database --recursive --limit --output-directory --debug --no-ffprobe ``` Standardsökväg för databasen: ```text /storage/disk1/WCX/database/wcx.db ``` `--no-ffprobe` ska göra det möjligt att testa enbart namnmatchningen. ## Rapportering Skapa följande CSV-filer i output-katalogen: ```text matched.csv ambiguous.csv unmatched.csv ``` Rapporterna ska vara UTF-8-kodade och ha rubrikrad. `matched.csv` ska minst innehålla: ```text path filename file_duration_seconds movie_id movie_name site_duration_seconds matched_duration_seconds version_classification score score_margin match_reason ``` `ambiguous.csv` ska minst innehålla: ```text path filename file_duration_seconds candidate_1_id candidate_1_name candidate_1_score candidate_2_id candidate_2_name candidate_2_score reason ``` `unmatched.csv` ska minst innehålla: ```text path filename file_duration_seconds best_candidate_id best_candidate_name best_score reason ``` Skriv även en sammanfattning till konsolen: ```text Files analyzed Matched Ambiguous Unmatched ffprobe failures ``` I `--debug` ska de bästa kandidaterna och deras poängkomponenter visas per fil. ## Tekniska krav Använd Python 3.12 och endast standardbiblioteket i första versionen. `ffprobe` får anropas via `subprocess`. Använd: * `argparse` * `csv` * `dataclasses` * `pathlib` * `sqlite3` * `subprocess` * `unicodedata` * tydliga type hints Använd inte externa Python-paket utan att först motivera varför de behövs. Fel i en enskild videofil eller ett `ffprobe`-anrop får inte avbryta hela körningen. Scriptet ska ge tydliga felmeddelanden och avsluta med felkod endast vid övergripande fel, exempelvis: * databasen saknas * databasschemat är inkompatibelt * inputkatalogen saknas * output-katalogen kan inte skapas ## Tester Skapa även: ```text tests/test_match_filenames.py ``` Använd `unittest` från standardbiblioteket. Tester ska minst täcka: 1. enkel normalisering 2. namn med understreck 3. sammanfogade namn 4. upprepade namn 5. extra skräpord 6. flerpersonsnamn 7. personer i omvänd ordning 8. prioritering av flerpersonspost 9. korta namn 10. längdpoäng 11. historisk längdmatchning 12. otillräcklig marginal mellan kandidater 13. fuzzy-beroende resultat blir inte `matched` 14. fil utan kandidat blir `unmatched` Använd temporär SQLite-databas och temporära kataloger i testerna. Testerna får inte använda produktionsdatabasen eller verkliga videofiler. ## Arbetssätt Arbeta stegvis. Börja med att: 1. läsa den befintliga kravspecifikationen 2. inspektera relevant databasschema 3. föreslå en kort implementationstruktur 4. implementera normalisering och kandidatmodell 5. implementera namnmatchning 6. implementera längdmatchning 7. implementera klassificering 8. implementera CSV-rapportering 9. lägga till tester 10. uppdatera README med användning och begränsningar Gör inga förändringar utanför den här funktionaliteten. När du levererar kod ska du ge hela innehållet för nya eller ändrade filer, inte små kodfragment som kräver besvärlig manuell infogning. När något är osäkert ska du välja den konservativa matchningen och tydligt dokumentera antagandet.