#!/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}")