61 lines
2.2 KiB
Python
Executable File
61 lines
2.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""Detector A1 — albumartist hijacked from a random ROOT file of the folder.
|
||
|
||
Rule (Alex): a track's albumartist = its OWN artist, unless the album is a genuine
|
||
compilation. A value taken from one stray file in the root must NOT be propagated.
|
||
|
||
Finds folders where the folder-level albumartist equals the `artist` of a MINORITY
|
||
file, but is neither the top artist nor the folder name.
|
||
|
||
Benign hits to review manually (NOT corruption):
|
||
- eponymous compilation (ДДТ/2008 L'Echoppe — files are a duet credit)
|
||
- letter sub-folder of a set (Сборка Песни для детей/L)
|
||
- category root (z Другое, Enigma, Ленинград)
|
||
- any folder the user explicitly decided (Re Sfaction, S Is Here)
|
||
|
||
Usage:
|
||
python3 find_aa_hijack.py [путь_к_APPLY-PLAN.csv]
|
||
"""
|
||
import sys
|
||
|
||
import csv
|
||
import collections
|
||
import os
|
||
|
||
P = sys.argv[1] if len(sys.argv) > 1 else '/Users/admin/Downloads/music-tags/APPLY-PLAN.csv'
|
||
rows = list(csv.DictReader(open(P, encoding='utf-8-sig', newline='')))
|
||
|
||
|
||
def s(v):
|
||
return (v or '').strip()
|
||
|
||
|
||
by_folder = collections.defaultdict(list)
|
||
for r in rows:
|
||
by_folder[r['folder']].append(r)
|
||
|
||
suspects = []
|
||
for folder, rs in sorted(by_folder.items()):
|
||
aa_vals = collections.Counter(s(r['new_albumartist']) for r in rs if s(r['new_albumartist']))
|
||
if len(aa_vals) != 1:
|
||
continue # mixed folder — separate case
|
||
aa = next(iter(aa_vals))
|
||
art = collections.Counter(s(r['new_artist']) for r in rs if s(r['new_artist']))
|
||
if not art:
|
||
continue
|
||
top_artist, top_n = art.most_common(1)[0]
|
||
folder_name = os.path.basename(folder)
|
||
# albumartist matches a MINORITY artist, not the top one, not the folder name
|
||
if aa != top_artist and aa != folder_name and aa in art:
|
||
minority = art[aa]
|
||
if minority < top_n:
|
||
suspects.append((folder, aa, top_artist, top_n, minority, len(rs)))
|
||
|
||
print(f"ПОДОЗРЕВАЕМЫХ ПАПОК: {len(suspects)}")
|
||
print()
|
||
for folder, aa, top, tn, mn, tot in suspects:
|
||
print(f" albumartist={aa!r} (у {mn} из {tot})")
|
||
print(f" должен быть {top!r} (у {tn})")
|
||
print(f" {folder}")
|