Download by URL, another unicode fix for windows, add track genre and number (albums only), possible PAR issue fixed
This commit is contained in:
234
src/ya.pl
234
src/ya.pl
@@ -9,13 +9,12 @@ use POSIX qw/strftime/;
|
|||||||
use constant IS_WIN => $^O eq 'MSWin32';
|
use constant IS_WIN => $^O eq 'MSWin32';
|
||||||
use constant
|
use constant
|
||||||
{
|
{
|
||||||
NL => IS_WIN ? "\015\012" : "\012",
|
NL => IS_WIN ? "\015" : "\012",
|
||||||
TARGET_ENC => IS_WIN ? 'cp1251' : 'utf8',
|
|
||||||
TIMEOUT => 5,
|
TIMEOUT => 5,
|
||||||
AGENT => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0',
|
AGENT => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0',
|
||||||
YANDEX_BASE => 'https://music.yandex.ru',
|
YANDEX_BASE => 'https://music.yandex.ru',
|
||||||
MD5_SALT => 'XGRlBW9FXlekgbPrRHuSiA',
|
MD5_SALT => 'XGRlBW9FXlekgbPrRHuSiA',
|
||||||
MUSIC_INFO_REGEX => qr/var\s+Mu\s+=\s+(.+?);\s+<\/script>/is,
|
MUSIC_INFO_REGEX => qr{var\s+Mu\s+=\s+(.+?);\s+</script>}is,
|
||||||
DOWNLOAD_INFO_MASK => '/api/v1.5/handlers/api-jsonp.jsx?requestId=2&nc=%d&action=getTrackSrc&p=download-info/%s/2.mp3',
|
DOWNLOAD_INFO_MASK => '/api/v1.5/handlers/api-jsonp.jsx?requestId=2&nc=%d&action=getTrackSrc&p=download-info/%s/2.mp3',
|
||||||
DOWNLOAD_PATH_MASK => 'http://%s/get-mp3/%s/%s?track-id=%s&from=service-10-track&similarities-experiment=default',
|
DOWNLOAD_PATH_MASK => 'http://%s/get-mp3/%s/%s?track-id=%s&from=service-10-track&similarities-experiment=default',
|
||||||
PLAYLIST_INFO_MASK => '/users/%s/playlists/%d',
|
PLAYLIST_INFO_MASK => '/users/%s/playlists/%d',
|
||||||
@@ -27,6 +26,9 @@ use constant
|
|||||||
COVER_RESOLUTION => '400x400',
|
COVER_RESOLUTION => '400x400',
|
||||||
GENERIC_COLLECTION => "\x{441}\x{431}\x{43e}\x{440}\x{43d}\x{438}\x{43a}",
|
GENERIC_COLLECTION => "\x{441}\x{431}\x{43e}\x{440}\x{43d}\x{438}\x{43a}",
|
||||||
GENERIC_TITLE => 'Various Artists',
|
GENERIC_TITLE => 'Various Artists',
|
||||||
|
URL_ALBUM_REGEX => qr{music\.yandex\.ru/album/(\d+)}is,
|
||||||
|
URL_TRACK_REGEX => qr{music\.yandex\.ru/album/(\d+)/track/(\d+)}is,
|
||||||
|
URL_PLAYLIST_REGEX => qr{music\.yandex\.ru/users/(.+?)/playlists/(\d+)}is
|
||||||
};
|
};
|
||||||
use constant
|
use constant
|
||||||
{
|
{
|
||||||
@@ -35,29 +37,54 @@ use constant
|
|||||||
INFO => 'INFO',
|
INFO => 'INFO',
|
||||||
OK => 'OK'
|
OK => 'OK'
|
||||||
};
|
};
|
||||||
|
use constant
|
||||||
|
{
|
||||||
|
WIN_UTF8_CODEPAGE => 65001,
|
||||||
|
STD_OUTPUT_HANDLE => 0xFFFFFFF5,
|
||||||
|
FG_BLUE => 1,
|
||||||
|
FG_GREEN => 2,
|
||||||
|
FG_RED => 4,
|
||||||
|
BG_WHITE => 112
|
||||||
|
};
|
||||||
|
|
||||||
my %log_colors =
|
my %log_colors =
|
||||||
(
|
(
|
||||||
&DEBUG => 'red on_white',
|
&DEBUG =>
|
||||||
&ERROR => 'red',
|
{
|
||||||
&INFO => 'blue on_white',
|
nix => 'red on_white',
|
||||||
&OK => 'green on_white'
|
win => FG_RED | BG_WHITE
|
||||||
|
},
|
||||||
|
&ERROR =>
|
||||||
|
{
|
||||||
|
nix => 'red',
|
||||||
|
win => FG_RED
|
||||||
|
},
|
||||||
|
&INFO =>
|
||||||
|
{
|
||||||
|
nix => 'blue on_white',
|
||||||
|
win => FG_BLUE | BG_WHITE
|
||||||
|
},
|
||||||
|
&OK =>
|
||||||
|
{
|
||||||
|
nix => 'green on_white',
|
||||||
|
win => FG_GREEN | BG_WHITE
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
my %req_modules =
|
my %req_modules =
|
||||||
(
|
(
|
||||||
NIX => [],
|
NIX => [],
|
||||||
WIN => [ qw/Win32::Console::ANSI Win32API::File/ ],
|
WIN => [ qw/Win32API::File Win32::Console/ ],
|
||||||
ALL => [ qw/Digest::MD5 File::Copy File::Temp MP3::Tag JSON::PP Getopt::Long::Descriptive Term::ANSIColor LWP::UserAgent HTTP::Cookies HTML::Entities/ ]
|
ALL => [ qw/Digest::MD5 File::Copy File::Temp MP3::Tag JSON::PP Getopt::Long::Descriptive Term::ANSIColor LWP::UserAgent HTTP::Cookies HTML::Entities/ ]
|
||||||
);
|
);
|
||||||
|
|
||||||
$\ = NL;
|
$\ = NL;
|
||||||
binmode STDOUT, ':encoding('.TARGET_ENC.')';
|
|
||||||
|
|
||||||
my @missing_modules;
|
my @missing_modules;
|
||||||
for(@{$req_modules{ALL}}, IS_WIN ? @{$req_modules{WIN}} : @{$req_modules{NIX}})
|
for(@{$req_modules{ALL}}, IS_WIN ? @{$req_modules{WIN}} : @{$req_modules{NIX}})
|
||||||
{
|
{
|
||||||
eval "require $_";
|
# Suppress MP3::Tag deprecated regex and other warnings
|
||||||
|
eval "local \$SIG{'__WARN__'} = sub {}; require $_";
|
||||||
if($@)
|
if($@)
|
||||||
{
|
{
|
||||||
($_) = $@ =~ /locate (.+?)(?:\.pm)? in \@INC/;
|
($_) = $@ =~ /locate (.+?)(?:\.pm)? in \@INC/;
|
||||||
@@ -72,6 +99,19 @@ if(@missing_modules)
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# PAR issue workaround && different win* approach for Unicode output
|
||||||
|
if(IS_WIN)
|
||||||
|
{
|
||||||
|
binmode STDOUT, ':unix:utf8';
|
||||||
|
# Unicode (UTF-8) codepage
|
||||||
|
Win32::Console::OutputCP(WIN_UTF8_CODEPAGE);
|
||||||
|
$main::console = Win32::Console->new(STD_OUTPUT_HANDLE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
binmode STDOUT, ':encoding(utf8)';
|
||||||
|
}
|
||||||
|
|
||||||
my ($opt, $usage) = Getopt::Long::Descriptive::describe_options
|
my ($opt, $usage) = Getopt::Long::Descriptive::describe_options
|
||||||
(
|
(
|
||||||
basename(__FILE__).' %o',
|
basename(__FILE__).' %o',
|
||||||
@@ -79,6 +119,7 @@ my ($opt, $usage) = Getopt::Long::Descriptive::describe_options
|
|||||||
['kind|k:s', 'playlist kind (eg. ya-playlist, music-blog, music-partners, etc.)'],
|
['kind|k:s', 'playlist kind (eg. ya-playlist, music-blog, music-partners, etc.)'],
|
||||||
['album|a:i', 'album to download'],
|
['album|a:i', 'album to download'],
|
||||||
['track|t:i', 'track to download (album id must be specified)'],
|
['track|t:i', 'track to download (album id must be specified)'],
|
||||||
|
['url|u:s', 'download by URL'],
|
||||||
['dir|d:s', 'download path (current direcotry will be used by default)', {default => '.'}],
|
['dir|d:s', 'download path (current direcotry will be used by default)', {default => '.'}],
|
||||||
['proxy=s', 'HTTP-proxy (format: 1.2.3.4:8888)'],
|
['proxy=s', 'HTTP-proxy (format: 1.2.3.4:8888)'],
|
||||||
['exclude=s', 'skip tracks specified in file'],
|
['exclude=s', 'skip tracks specified in file'],
|
||||||
@@ -92,16 +133,22 @@ my ($opt, $usage) = Getopt::Long::Descriptive::describe_options
|
|||||||
['Example: '],
|
['Example: '],
|
||||||
["\t".basename(__FILE__) . ' -p 123 -k ya-playlist'],
|
["\t".basename(__FILE__) . ' -p 123 -k ya-playlist'],
|
||||||
["\t".basename(__FILE__) . ' -a 123'],
|
["\t".basename(__FILE__) . ' -a 123'],
|
||||||
["\t".basename(__FILE__) . ' -a 123 -t 321']
|
["\t".basename(__FILE__) . ' -a 123 -t 321'],
|
||||||
|
["\t".basename(__FILE__) . ' -u https://music.yandex.ru/album/215690'],
|
||||||
|
["\t".basename(__FILE__) . ' -u https://music.yandex.ru/album/215688/track/1710808'],
|
||||||
|
["\t".basename(__FILE__) . ' -u https://music.yandex.ru/users/ya.playlist/playlists/1257']
|
||||||
);
|
);
|
||||||
|
|
||||||
if( $opt->help || ( !($opt->track && $opt->album) && !$opt->album && !($opt->playlist && $opt->kind) ) )
|
# Get a modifiable options copy
|
||||||
|
my %opt = %{$opt};
|
||||||
|
|
||||||
|
if( $opt{help} || ( !$opt{url} && !($opt{track} && $opt{album}) && !$opt{album} && !($opt{playlist} && $opt{kind}) ) )
|
||||||
{
|
{
|
||||||
print $usage->text;
|
print $usage->text;
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($opt->dir && !-d $opt->dir)
|
if($opt{dir} && !-d $opt{dir})
|
||||||
{
|
{
|
||||||
info(ERROR, 'Please, specify an existing directory');
|
info(ERROR, 'Please, specify an existing directory');
|
||||||
exit;
|
exit;
|
||||||
@@ -114,37 +161,55 @@ my $json_decoder = JSON::PP->new->utf8->pretty->allow_nonref->allow_singlequote;
|
|||||||
my @exclude = ();
|
my @exclude = ();
|
||||||
my @include = ();
|
my @include = ();
|
||||||
|
|
||||||
if($opt->proxy)
|
if($opt{proxy})
|
||||||
{
|
{
|
||||||
$ua->proxy(['http', 'https'], 'http://' . $opt->proxy . '/');
|
$ua->proxy(['http', 'https'], 'http://' . $opt{proxy} . '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
if($opt->exclude)
|
if($opt{exclude})
|
||||||
{
|
{
|
||||||
@exclude = read_file($opt->exclude);
|
@exclude = read_file($opt{exclude});
|
||||||
}
|
}
|
||||||
|
|
||||||
if($opt->include)
|
if($opt{include})
|
||||||
{
|
{
|
||||||
@include = read_file($opt->include);
|
@include = read_file($opt{include});
|
||||||
}
|
}
|
||||||
|
|
||||||
if($opt->album || ($opt->playlist && $opt->kind))
|
if($opt{url})
|
||||||
|
{
|
||||||
|
if($opt{url} =~ URL_TRACK_REGEX)
|
||||||
|
{
|
||||||
|
$opt{album} = $1;
|
||||||
|
$opt{track} = $2;
|
||||||
|
}
|
||||||
|
elsif($opt{url} =~ URL_ALBUM_REGEX)
|
||||||
|
{
|
||||||
|
$opt{album} = $1;
|
||||||
|
}
|
||||||
|
elsif($opt{url} =~ URL_PLAYLIST_REGEX)
|
||||||
|
{
|
||||||
|
$opt{kind} = $1;
|
||||||
|
$opt{playlist} = $2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($opt{album} || ($opt{playlist} && $opt{kind}))
|
||||||
{
|
{
|
||||||
my @track_list_info;
|
my @track_list_info;
|
||||||
|
|
||||||
if($opt->album)
|
if($opt{album})
|
||||||
{
|
{
|
||||||
info(INFO, 'Fetching album info: ' . $opt->album);
|
info(INFO, 'Fetching album info: ' . $opt{album});
|
||||||
|
|
||||||
@track_list_info = get_album_tracks_info($opt->album);
|
@track_list_info = get_album_tracks_info($opt{album});
|
||||||
|
|
||||||
if($opt->track)
|
if($opt{track})
|
||||||
{
|
{
|
||||||
info(INFO, 'Filtering single track: ' . $opt->track . ' [' . $opt->album . ']');
|
info(INFO, 'Filtering single track: ' . $opt{track} . ' [' . $opt{album} . ']');
|
||||||
@track_list_info = grep
|
@track_list_info = grep
|
||||||
(
|
(
|
||||||
(split(/\./, $_->{dir}))[1] eq $opt->track
|
(split(/\./, $_->{dir}))[1] eq $opt{track}
|
||||||
,
|
,
|
||||||
@track_list_info
|
@track_list_info
|
||||||
);
|
);
|
||||||
@@ -152,9 +217,9 @@ if($opt->album || ($opt->playlist && $opt->kind))
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info(INFO, 'Fetching playlist info: ' . $opt->playlist . ' [' . $opt->kind . ']');
|
info(INFO, 'Fetching playlist info: ' . $opt{playlist} . ' [' . $opt{kind} . ']');
|
||||||
|
|
||||||
@track_list_info = get_playlist_tracks_info($opt->playlist);
|
@track_list_info = get_playlist_tracks_info($opt{playlist});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -190,7 +255,7 @@ if($opt->album || ($opt->playlist && $opt->kind))
|
|||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if($skip && $opt->include)
|
if($skip && $opt{include})
|
||||||
{
|
{
|
||||||
info(INFO, 'Skipping: ' . $track_info_ref->{title});
|
info(INFO, 'Skipping: ' . $track_info_ref->{title});
|
||||||
next;
|
next;
|
||||||
@@ -214,11 +279,15 @@ if($opt->album || ($opt->playlist && $opt->kind))
|
|||||||
info(OK, 'Done!');
|
info(OK, 'Done!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(IS_WIN)
|
||||||
|
{
|
||||||
|
$main::console->Free();
|
||||||
|
}
|
||||||
|
|
||||||
sub fetch_track
|
sub fetch_track
|
||||||
{
|
{
|
||||||
my $track_info_ref = shift;
|
my $track_info_ref = shift;
|
||||||
|
|
||||||
#fix_encoding(\$track_info_ref->{title});
|
|
||||||
$track_info_ref->{title} =~ s/\s+$//;
|
$track_info_ref->{title} =~ s/\s+$//;
|
||||||
$track_info_ref->{title} =~ s/[\\\/:"*?<>|]+/-/g;
|
$track_info_ref->{title} =~ s/[\\\/:"*?<>|]+/-/g;
|
||||||
|
|
||||||
@@ -251,7 +320,7 @@ sub fetch_track
|
|||||||
info(ERROR, 'Failed to add MP3 tags for ' . $file_path);
|
info(ERROR, 'Failed to add MP3 tags for ' . $file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $target_path = $opt->dir . '/' . $track_info_ref->{title} . FILE_SAVE_EXT;
|
my $target_path = $opt{dir} . '/' . $track_info_ref->{title} . FILE_SAVE_EXT;
|
||||||
if(rename_track($file_path, $target_path))
|
if(rename_track($file_path, $target_path))
|
||||||
{
|
{
|
||||||
info(INFO, $file_path . ' -> ' . $target_path);
|
info(INFO, $file_path . ' -> ' . $target_path);
|
||||||
@@ -284,7 +353,7 @@ sub download_track
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($file_handle, $file_path) = File::Temp::tempfile(DIR => $opt->dir);
|
my ($file_handle, $file_path) = File::Temp::tempfile(DIR => $opt{dir});
|
||||||
return unless $file_handle;
|
return unless $file_handle;
|
||||||
|
|
||||||
binmode $file_handle;
|
binmode $file_handle;
|
||||||
@@ -380,28 +449,27 @@ sub get_album_tracks_info
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#fix_encoding(\$title);
|
|
||||||
|
|
||||||
info(INFO, 'Album title: ' . $title);
|
info(INFO, 'Album title: ' . $title);
|
||||||
info(INFO, 'Tracks total: ' . $json->{pageData}->{trackCount});
|
info(INFO, 'Tracks total: ' . $json->{pageData}->{trackCount});
|
||||||
|
|
||||||
my @volumes = ();
|
my @tracks = ();
|
||||||
for my $vol(@{$json->{pageData}->{volumes}})
|
for my $vol(@{$json->{pageData}->{volumes}})
|
||||||
{
|
{
|
||||||
push @volumes, @{$vol};
|
my $track_number = 1;
|
||||||
|
for my $track(@{$vol})
|
||||||
|
{
|
||||||
|
push @tracks, create_track_entry($track, $track_number++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return map
|
return @tracks;
|
||||||
{
|
|
||||||
create_track_entry($_)
|
|
||||||
} @volumes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_playlist_tracks_info
|
sub get_playlist_tracks_info
|
||||||
{
|
{
|
||||||
my $playlist_id = shift;
|
my $playlist_id = shift;
|
||||||
|
|
||||||
my $request = $ua->get(YANDEX_BASE.sprintf(PLAYLIST_INFO_MASK, $opt->kind, $playlist_id));
|
my $request = $ua->get(YANDEX_BASE.sprintf(PLAYLIST_INFO_MASK, $opt{kind}, $playlist_id));
|
||||||
if(!$request->is_success)
|
if(!$request->is_success)
|
||||||
{
|
{
|
||||||
info(DEBUG, 'Request failed');
|
info(DEBUG, 'Request failed');
|
||||||
@@ -429,8 +497,6 @@ sub get_playlist_tracks_info
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#fix_encoding(\$title);
|
|
||||||
|
|
||||||
info(INFO, 'Playlist title: ' . $title);
|
info(INFO, 'Playlist title: ' . $title);
|
||||||
info(INFO, 'Tracks total: ' . $json->{pageData}->{playlist}->{trackCount});
|
info(INFO, 'Tracks total: ' . $json->{pageData}->{playlist}->{trackCount});
|
||||||
|
|
||||||
@@ -474,15 +540,16 @@ sub get_playlist_tracks_info
|
|||||||
push @tracks_info,
|
push @tracks_info,
|
||||||
map
|
map
|
||||||
{
|
{
|
||||||
create_track_entry($_)
|
create_track_entry($_, 0)
|
||||||
} @{ $json };
|
} @{ $json };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
@tracks_info = map
|
@tracks_info = map
|
||||||
{
|
{
|
||||||
create_track_entry($_)
|
create_track_entry($_, 0)
|
||||||
} @{ $json->{pageData}->{playlist}->{tracks} };
|
} @{ $json->{pageData}->{playlist}->{tracks} };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,8 +558,7 @@ sub get_playlist_tracks_info
|
|||||||
|
|
||||||
sub create_track_entry
|
sub create_track_entry
|
||||||
{
|
{
|
||||||
my $track_info = shift;
|
my ($track_info, $track_number) = @_;
|
||||||
|
|
||||||
|
|
||||||
# Better detection algo?
|
# Better detection algo?
|
||||||
my $is_part_of_album = scalar @{$track_info->{albums}} != 0;
|
my $is_part_of_album = scalar @{$track_info->{albums}} != 0;
|
||||||
@@ -502,40 +568,45 @@ sub create_track_entry
|
|||||||
($is_part_of_album && $track_info->{albums}->[0]->{artists}->[0]->{name} eq GENERIC_COLLECTION)
|
($is_part_of_album && $track_info->{albums}->[0]->{artists}->[0]->{name} eq GENERIC_COLLECTION)
|
||||||
;
|
;
|
||||||
|
|
||||||
my ($talb, $tpe2, $apic, $tyer, $tit2, $tpe1);
|
# TALB - album title; TPE2 - album artist;
|
||||||
|
# APIC - album picture; TYER - year;
|
||||||
|
# TIT2 - song title; TPE1 - song artist;
|
||||||
|
# TCON - track genre; TRCK - track number
|
||||||
|
my %mp3_tags = ();
|
||||||
|
|
||||||
$tpe1 = join ', ', map { $_->{name} } @{$track_info->{artists}};
|
$mp3_tags{TPE1} = join ', ', map { $_->{name} } @{$track_info->{artists}};
|
||||||
$tit2 = $track_info->{title};
|
$mp3_tags{TIT2} = $track_info->{title};
|
||||||
|
# No track number info in JSON if fetching from anything but album
|
||||||
|
if($track_number)
|
||||||
|
{
|
||||||
|
$mp3_tags{TRCK} = $track_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Append track postfix (like remix) if present
|
||||||
|
if(exists $track_info->{version})
|
||||||
|
{
|
||||||
|
$mp3_tags{TIT2} .= "\x20" . '(' . $track_info->{version} . ')';
|
||||||
|
}
|
||||||
|
|
||||||
# For deleted tracks
|
# For deleted tracks
|
||||||
if($is_part_of_album)
|
if($is_part_of_album)
|
||||||
{
|
{
|
||||||
$talb = $track_info->{albums}->[0]->{title};
|
$mp3_tags{TALB} = $track_info->{albums}->[0]->{title};
|
||||||
$tpe2 = $is_various ? GENERIC_TITLE : $track_info->{albums}->[0]->{artists}->[0]->{name};
|
$mp3_tags{TPE2} = $is_various ? GENERIC_TITLE : $track_info->{albums}->[0]->{artists}->[0]->{name};
|
||||||
# 'Dummy' cover for post-process
|
# 'Dummy' cover for post-process
|
||||||
$apic = $track_info->{albums}->[0]->{coverUri};
|
$mp3_tags{APIC} = $track_info->{albums}->[0]->{coverUri};
|
||||||
$tyer = $track_info->{albums}->[0]->{year};
|
$mp3_tags{TYER} = $track_info->{albums}->[0]->{year};
|
||||||
|
$mp3_tags{TCON} = $track_info->{albums}->[0]->{genre};
|
||||||
}
|
}
|
||||||
|
|
||||||
# TALB - album title; TPE2 - album artist;
|
|
||||||
# APIC - album picture; TYER - year;
|
|
||||||
# TIT2 - song title; TPE1 - song artist
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
# Download path part
|
# Download path part
|
||||||
dir => $_->{storageDir},
|
dir => $track_info->{storageDir},
|
||||||
# MP3 tags
|
# MP3 tags
|
||||||
mp3tags =>
|
mp3tags => \%mp3_tags,
|
||||||
{
|
|
||||||
TALB => $talb,
|
|
||||||
TPE2 => $tpe2,
|
|
||||||
APIC => $apic,
|
|
||||||
TYER => $tyer,
|
|
||||||
TIT2 => $tit2,
|
|
||||||
TPE1 => $tpe1
|
|
||||||
},
|
|
||||||
# Save As file name
|
# Save As file name
|
||||||
title => $tpe1 . ARTIST_TITLE_DELIM . $tit2
|
title => $mp3_tags{TPE1} . ARTIST_TITLE_DELIM . $mp3_tags{TIT2}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,24 +765,33 @@ sub create_json
|
|||||||
return $json;
|
return $json;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub fix_encoding
|
|
||||||
{
|
|
||||||
my $ref = shift;
|
|
||||||
from_to($$ref, 'unicode', TARGET_ENC);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub info
|
sub info
|
||||||
{
|
{
|
||||||
my ($type, $msg) = @_;
|
my ($type, $msg) = @_;
|
||||||
|
|
||||||
if($type eq DEBUG)
|
if($type eq DEBUG)
|
||||||
{
|
{
|
||||||
return if !$opt->debug;
|
return if !$opt{debug};
|
||||||
# Func, line, msg
|
# Func, line, msg
|
||||||
$msg = (caller(1))[3] . "(" . (caller(0))[2] . "): " . $msg;
|
$msg = (caller(1))[3] . "(" . (caller(0))[2] . "): " . $msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg = Term::ANSIColor::colored('['.$type.']', $log_colors{$type}) . ' ' . $msg;
|
if(IS_WIN)
|
||||||
|
{
|
||||||
|
local $\ = undef;
|
||||||
|
|
||||||
|
my $attr = $main::console->Attr();
|
||||||
|
$main::console->Attr($log_colors{$type}->{win});
|
||||||
|
|
||||||
|
print '['.$type.']';
|
||||||
|
|
||||||
|
$main::console->Attr($attr);
|
||||||
|
$msg = ' ' . $msg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$msg = Term::ANSIColor::colored('['.$type.']', $log_colors{$type}->{nix}) . ' ' . $msg;
|
||||||
|
}
|
||||||
# Actual terminal width detection?
|
# Actual terminal width detection?
|
||||||
$msg = sprintf('%-80s', $msg);
|
$msg = sprintf('%-80s', $msg);
|
||||||
|
|
||||||
@@ -763,7 +843,7 @@ sub read_file
|
|||||||
return @lines;
|
return @lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
info(ERROR, 'Failed to open file ' . $opt->ignore);
|
info(ERROR, 'Failed to open file ' . $opt{ignore});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user