462 lines
10 KiB
Markdown
462 lines
10 KiB
Markdown
# 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.
|