Files
atm-curs-dl/check_duplicates.py
Claude Agent f0b8f28fa9 Detectare duplicate: Q&A 18.08.2026 este acelasi video cu 11.08.2026
audio_fingerprint.py - amprenta pe anvelopa de energie, fara transcriere.
md5 nu prinde duplicatele (urcari encodate separat, durate la 0.08s distanta).
Validat: 1.000 pe perechea duplicat, 0.038 pe sesiuni diferite.
check_duplicates.py - acelasi lucru pe transcrieri, cu --selftest.
Scanat tot modulul Q&A: un singur duplicat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
2026-09-12 17:40:18 +00:00

63 lines
2.5 KiB
Python

"""
Găsește sesiuni Q&A duplicate (același videoclip urcat sub două date).
Compară transcrierile la nivel de cuvinte normalizate (fără diacritice, fără
punctuație), pentru că whisper produce text ușor diferit la două rulări pe
aceeași sursă — un md5 pe audio nu le prinde.
Prag: >60% cuvinte potrivite ȘI un bloc comun de >=40 de cuvinte consecutive.
Blocul lung e cel care contează: două sesiuni diferite pe aceleași subiecte
împart multe cuvinte, dar niciodată 40 la rând.
"""
import difflib
import re
import sys
import unicodedata
from itertools import combinations
from pathlib import Path
def words(path: Path) -> list[str]:
t = unicodedata.normalize("NFKD", path.read_text(encoding="utf-8").lower())
return re.findall(r"[a-z]+", t.encode("ascii", "ignore").decode())
def is_duplicate(a: list[str], b: list[str]) -> tuple[bool, float, int]:
if not a or not b or min(len(a), len(b)) / max(len(a), len(b)) < 0.7:
return False, 0.0, 0 # lungimi prea diferite ca să fie același video
blocks = difflib.SequenceMatcher(None, a, b, autojunk=False).get_matching_blocks()
pct = 200 * sum(bl.size for bl in blocks) / (len(a) + len(b))
longest = max(bl.size for bl in blocks)
return pct > 60 and longest >= 40, pct, longest
def selftest():
base = "cand pretul ajunge in suport major si apare un buy doji intri cu toata pozitia".split() * 12
same = base[:-3] + "intri cu jumatate".split() # mici diferente de transcriere
other = "volumul arata cine domina piata in fiecare moment al zilei".split() * 12
assert is_duplicate(base, same)[0], "acelasi video nu a fost detectat"
assert not is_duplicate(base, other)[0], "sesiuni diferite marcate ca duplicat"
assert not is_duplicate(base, [])[0], "transcriere goala (inca se scrie) trebuie ignorata"
assert not is_duplicate(base, base[:20])[0], "lungimi foarte diferite trebuie ignorate"
print("selftest ok")
def main():
if "--selftest" in sys.argv:
selftest()
return 0
d = Path(sys.argv[1] if len(sys.argv) > 1 else "transcripts/09_Q_and_A")
docs = {p.stem: words(p) for p in sorted(d.glob("*.txt"))}
found = []
for x, y in combinations(docs, 2):
dup, pct, longest = is_duplicate(docs[x], docs[y])
if dup:
found.append((x, y))
print(f"DUPLICAT: {x} == {y} ({pct:.1f}% potrivite, bloc comun {longest} cuvinte)")
if not found:
print(f"Niciun duplicat între cele {len(docs)} transcrieri.")
return 0
if __name__ == "__main__":
sys.exit(main())