Files
serato-doctor/serato_doctor/duplicates.py
T
2026-06-30 18:52:16 -07:00

42 lines
1.5 KiB
Python

from collections import defaultdict
from typing import DefaultDict, Iterable, List, Tuple
from serato_doctor.matching import cloud_conflict_name, normalize
from serato_doctor.models.duplicate import DuplicateGroup, DuplicateKind
from serato_doctor.models.track import DiskTrack
def find_duplicate_groups(
tracks: Iterable[DiskTrack],
) -> Tuple[DuplicateGroup, ...]:
"""Find exact-name duplicates and suspected numeric conflict copies."""
track_tuple = tuple(tracks)
by_name: DefaultDict[str, List[DiskTrack]] = defaultdict(list)
by_conflict_name: DefaultDict[str, List[DiskTrack]] = defaultdict(list)
for track in track_tuple:
by_name[normalize(track.filename)].append(track)
by_conflict_name[cloud_conflict_name(track.filename)].append(track)
groups = []
for key, matches in by_name.items():
if len(matches) > 1:
groups.append(_group(DuplicateKind.EXACT_NAME, key, matches))
for key, matches in by_conflict_name.items():
distinct_names = {normalize(track.filename) for track in matches}
if len(distinct_names) > 1:
groups.append(_group(DuplicateKind.CLOUD_CONFLICT, key, matches))
return tuple(
sorted(groups, key=lambda group: (group.kind.value, group.comparison_key))
)
def _group(
kind: DuplicateKind, key: str, tracks: Iterable[DiskTrack]
) -> DuplicateGroup:
ordered = tuple(sorted(tracks, key=lambda track: str(track.path)))
return DuplicateGroup(kind, key, ordered)