-
Notifications
You must be signed in to change notification settings - Fork 34
/
group_audio_files.pl
executable file
·128 lines (93 loc) · 2.87 KB
/
group_audio_files.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/perl
# Daniel "Trizen" Șuteu
# License: GPLv3
# Date: 11 August 2019
# https://github.com/trizen
# Group MP3 files in directories based on their artist name.
# Example:
# Foo - abc.mp3
# Foo - xyz.mp3
# Both files will be moved in a new directory named "Foo".
# The directory "Foo" is created in the current working directory from which the script is executed.
use 5.016;
use strict;
use warnings;
binmode(STDOUT, ':utf8');
use Encode qw(decode_utf8);
use Text::Unidecode qw(unidecode);
use File::Find qw(find);
use File::Copy qw(move);
use File::Basename qw(basename);
use File::Spec::Functions qw(catdir catfile curdir);
use List::Util qw(sum);
use List::UtilsBy qw(max_by);
my $file_formats = qr{\.(?:mp3|mp4|webm|mkv|opus|ogg|oga)\z}i; # file formats
my (@files) = grep { -e $_ } @ARGV;
if (not @files) {
die "usage: $0 [dir]\n";
}
my @audio_files;
find(\&wanted_files, @files);
sub wanted_files {
my $file = $File::Find::name;
push @audio_files, $file if ($file =~ $file_formats);
}
if (@audio_files) {
say ":: Found ", scalar(@audio_files), " audio files...";
}
else {
say ":: No file found...";
}
my %groups;
foreach my $filename (@audio_files) {
my $basename = decode_utf8(basename($filename));
my $artist;
if ($basename =~ /^[\d\s.\-–]*(.+?) -/) {
$artist = $1;
}
elsif ($basename =~ /^[\d\s.\-–]*(.+?)-/) {
$artist = $1;
}
else {
next;
}
# Remove extra whitespace
$artist = join(' ', split(' ', $artist));
# Unidecode key and remove whitespace
my $key = join('', split(' ', unidecode(CORE::fc($artist))));
$key =~ s/[[:punct:]]+//g; # remove any punctuation characters
$key =~ s/\d+//g; # remove any digits
if ($key eq '' or $artist eq '') {
next;
}
push @{$groups{$key}{files}},
{
filepath => $filename,
basename => $basename,
};
++$groups{$key}{artists}{$artist};
}
while (my ($key, $group) = each %groups) {
my $files = $group->{files};
my $artists = $group->{artists};
sum(values %$artists) > 1 or next; # ignore single files
my $common_name = max_by { $artists->{$_} } sort { $a cmp $b } keys %$artists;
foreach my $file (@{$files}) {
my $group_dir = catdir(curdir(), $common_name);
if (not -e $group_dir) {
mkdir($group_dir) || do {
warn "[!] Can't create directory `$group_dir`: $!\n";
next;
};
}
if (not -d $group_dir) {
warn "[!] Not a directory: $group_dir\n";
next;
}
my $target = catfile($group_dir, $file->{basename});
if (not -e $target) {
say "[*] Moving file `$file->{basename}` into `$common_name` directory...";
move($file->{filepath}, $target) || warn "[!] Failed to move: $!\n";
}
}
}