Feature: hierarchical storage support with pattern naming (not tested on Windows with Unicode tracks).
This commit is contained in:
14
README.md
14
README.md
@@ -35,6 +35,7 @@ Origin of the script is the following article: https://kaimi.io/2013/11/yandex-m
|
|||||||
* File::Copy
|
* File::Copy
|
||||||
* File::Spec
|
* File::Spec
|
||||||
* File::Temp
|
* File::Temp
|
||||||
|
* File::Util
|
||||||
* Getopt::Long::Descriptive
|
* Getopt::Long::Descriptive
|
||||||
* HTML::Entities
|
* HTML::Entities
|
||||||
* HTTP::Cookies
|
* HTTP::Cookies
|
||||||
@@ -70,7 +71,7 @@ perl ya.pl -h
|
|||||||
```bash
|
```bash
|
||||||
brew update
|
brew update
|
||||||
brew install perl cpanminus git
|
brew install perl cpanminus git
|
||||||
cpanm Digest::MD5 File::Copy File::Spec File::Temp Getopt::Long::Descriptive HTML::Entities HTTP::Cookies JSON::PP LWP::Protocol::https LWP::UserAgent MP3::Tag Term::ANSIColor Mozilla::CA
|
cpanm Digest::MD5 File::Copy File::Spec File::Temp File::Util Getopt::Long::Descriptive HTML::Entities HTTP::Cookies JSON::PP LWP::Protocol::https LWP::UserAgent MP3::Tag Term::ANSIColor Mozilla::CA
|
||||||
|
|
||||||
git clone https://github.com/kaimi-io/yandex-music-download.git
|
git clone https://github.com/kaimi-io/yandex-music-download.git
|
||||||
cd yandex-music-download/src
|
cd yandex-music-download/src
|
||||||
@@ -86,7 +87,7 @@ Otherwise:
|
|||||||
|
|
||||||
4. Install required modules (it can be done via PPM if you're using ActiveState Perl):
|
4. Install required modules (it can be done via PPM if you're using ActiveState Perl):
|
||||||
```bash
|
```bash
|
||||||
cpan install Digest::MD5 File::Copy File::Spec File::Temp Getopt::Long::Descriptive HTML::Entities HTTP::Cookies JSON::PP LWP::Protocol::https LWP::UserAgent MP3::Tag Term::ANSIColor Mozilla::CA Win32::API Win32::Console Win32API::File
|
cpan install Digest::MD5 File::Copy File::Spec File::Temp File::Util Getopt::Long::Descriptive HTML::Entities HTTP::Cookies JSON::PP LWP::Protocol::https LWP::UserAgent MP3::Tag Term::ANSIColor Mozilla::CA Win32::API Win32::Console Win32API::File
|
||||||
```
|
```
|
||||||
5. Download and unpack Yandex Music Downloader (https://github.com/kaimi-io/yandex-music-download/archive/master.zip).
|
5. Download and unpack Yandex Music Downloader (https://github.com/kaimi-io/yandex-music-download/archive/master.zip).
|
||||||
6. Run:
|
6. Run:
|
||||||
@@ -130,8 +131,15 @@ ya.pl [-adhklpstu] [long options...]
|
|||||||
(Session_id=...)
|
(Session_id=...)
|
||||||
--bitrate INT bitrate (eg. 64, 128, 192, 320)
|
--bitrate INT bitrate (eg. 64, 128, 192, 320)
|
||||||
--pattern STR track naming pattern
|
--pattern STR track naming pattern
|
||||||
|
--path STR path pattern
|
||||||
|
|
||||||
Available placeholders: #number, #artist,
|
Available placeholders: #number, #artist,
|
||||||
#title
|
#title, #album, #year
|
||||||
|
|
||||||
|
Path pattern will be used in addition to
|
||||||
|
the download path directory
|
||||||
|
|
||||||
|
Example path pattern: #artist/#album-#year
|
||||||
|
|
||||||
-l --link do not fetch, only print links to the
|
-l --link do not fetch, only print links to the
|
||||||
tracks
|
tracks
|
||||||
|
|||||||
42
src/ya.pl
42
src/ya.pl
@@ -18,7 +18,7 @@ use constant
|
|||||||
YANDEX_BASE => 'https://music.yandex.ru',
|
YANDEX_BASE => 'https://music.yandex.ru',
|
||||||
MOBILE_YANDEX_BASE => 'https://api.music.yandex.net',
|
MOBILE_YANDEX_BASE => 'https://api.music.yandex.net',
|
||||||
MD5_SALT => 'XGRlBW9FXlekgbPrRHuSiA',
|
MD5_SALT => 'XGRlBW9FXlekgbPrRHuSiA',
|
||||||
DOWNLOAD_INFO_MASK => '/api/v2.1/handlers/track/%d:%d/web-album-track-track-main/download/m?external-domain=music.yandex.ru&overembed=no&__t=%d&hq=%d',
|
DOWNLOAD_INFO_MASK => '/api/v2.1/handlers/track/%d:%d/web-album_track-track-track-main/download/m?external-domain=music.yandex.ru&overembed=no&__t=%d&hq=%d',
|
||||||
MOBILE_DOWNLOAD_INFO_MASK => '/tracks/%d/download-info',
|
MOBILE_DOWNLOAD_INFO_MASK => '/tracks/%d/download-info',
|
||||||
DOWNLOAD_PATH_MASK => 'https://%s/get-mp3/%s/%s?track-id=%s&from=service-10-track&similarities-experiment=default',
|
DOWNLOAD_PATH_MASK => 'https://%s/get-mp3/%s/%s?track-id=%s&from=service-10-track&similarities-experiment=default',
|
||||||
PLAYLIST_INFO_MASK => '/handlers/playlist.jsx?owner=%s&kinds=%d&light=true&madeFor=&withLikesCount=true&lang=ru&external-domain=music.yandex.ru&overembed=false&ncrnd=',
|
PLAYLIST_INFO_MASK => '/handlers/playlist.jsx?owner=%s&kinds=%d&light=true&madeFor=&withLikesCount=true&lang=ru&external-domain=music.yandex.ru&overembed=false&ncrnd=',
|
||||||
@@ -28,12 +28,15 @@ use constant
|
|||||||
ALBUM_INFO_MASK => '/api/v2.1/handlers/album/%d?external-domain=music.yandex.ru&overembed=no&__t=%d',
|
ALBUM_INFO_MASK => '/api/v2.1/handlers/album/%d?external-domain=music.yandex.ru&overembed=no&__t=%d',
|
||||||
MOBILE_ALBUM_INFO_MASK => '/albums/%d/with-tracks',
|
MOBILE_ALBUM_INFO_MASK => '/albums/%d/with-tracks',
|
||||||
FILE_NAME_PATTERN => '#artist - #title',
|
FILE_NAME_PATTERN => '#artist - #title',
|
||||||
|
DEFAULT_PERMISSIONS => 755,
|
||||||
# For more details refer to 'create_track_entry' function
|
# For more details refer to 'create_track_entry' function
|
||||||
PATTERN_MP3TAGS_RELS =>
|
PATTERN_MP3TAGS_RELS =>
|
||||||
{
|
{
|
||||||
'number' => 'TRCK',
|
'number' => 'TRCK',
|
||||||
'artist' => 'TPE1',
|
'artist' => 'TPE1',
|
||||||
'title' => 'TIT2'
|
'title' => 'TIT2',
|
||||||
|
'album' => 'TALB',
|
||||||
|
'year' => 'TYER',
|
||||||
},
|
},
|
||||||
FILE_SAVE_EXT => '.mp3',
|
FILE_SAVE_EXT => '.mp3',
|
||||||
COVER_RESOLUTION => '400x400',
|
COVER_RESOLUTION => '400x400',
|
||||||
@@ -48,6 +51,7 @@ use constant
|
|||||||
AUTH_TOKEN_PREFIX => 'OAuth ',
|
AUTH_TOKEN_PREFIX => 'OAuth ',
|
||||||
COOKIE_PREFIX => 'Session_id=',
|
COOKIE_PREFIX => 'Session_id=',
|
||||||
HQ_BITRATE => '320',
|
HQ_BITRATE => '320',
|
||||||
|
DEFAULT_CODEC => 'mp3',
|
||||||
PODCAST_TYPE => 'podcast',
|
PODCAST_TYPE => 'podcast',
|
||||||
VERSION => '1.2',
|
VERSION => '1.2',
|
||||||
COPYRIGHT => '© 2013-2021 by Kaimi (https://kaimi.io)',
|
COPYRIGHT => '© 2013-2021 by Kaimi (https://kaimi.io)',
|
||||||
@@ -107,7 +111,7 @@ my %req_modules =
|
|||||||
(
|
(
|
||||||
NIX => [],
|
NIX => [],
|
||||||
WIN => [ qw/Win32::API Win32API::File Win32::Console/ ],
|
WIN => [ qw/Win32::API Win32API::File Win32::Console/ ],
|
||||||
ALL => [ qw/Mozilla::CA Digest::MD5 File::Copy File::Spec File::Temp MP3::Tag JSON::PP Getopt::Long::Descriptive Term::ANSIColor LWP::UserAgent LWP::Protocol::https HTTP::Cookies HTML::Entities/ ]
|
ALL => [ qw/Mozilla::CA Digest::MD5 File::Copy File::Spec File::Temp File::Util MP3::Tag JSON::PP Getopt::Long::Descriptive Term::ANSIColor LWP::UserAgent LWP::Protocol::https HTTP::Cookies HTML::Entities/ ]
|
||||||
);
|
);
|
||||||
|
|
||||||
$\ = NL;
|
$\ = NL;
|
||||||
@@ -202,7 +206,12 @@ my ($opt, $usage) = Getopt::Long::Descriptive::describe_options
|
|||||||
['cookie=s', 'authorization cookie for web version (Session_id=...)'],
|
['cookie=s', 'authorization cookie for web version (Session_id=...)'],
|
||||||
['bitrate=i', 'bitrate (eg. 64, 128, 192, 320)'],
|
['bitrate=i', 'bitrate (eg. 64, 128, 192, 320)'],
|
||||||
['pattern=s', 'track naming pattern', {default => FILE_NAME_PATTERN}],
|
['pattern=s', 'track naming pattern', {default => FILE_NAME_PATTERN}],
|
||||||
['Available placeholders: #number, #artist, #title'],
|
[],
|
||||||
|
['Available placeholders: #number, #artist, #title, #album, #year'],
|
||||||
|
[],
|
||||||
|
['Path pattern will be used in addition to the download path directory'],
|
||||||
|
[],
|
||||||
|
['Example path pattern: #artist/#album-#year'],
|
||||||
[],
|
[],
|
||||||
['link|l', 'do not fetch, only print links to the tracks'],
|
['link|l', 'do not fetch, only print links to the tracks'],
|
||||||
['silent|s', 'do not print informational messages'],
|
['silent|s', 'do not print informational messages'],
|
||||||
@@ -493,7 +502,20 @@ 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 = File::Spec->catfile($opt{dir}, $track_info_ref->{title} . FILE_SAVE_EXT);
|
my $target_path = $opt{dir};
|
||||||
|
if($opt{path})
|
||||||
|
{
|
||||||
|
$target_path = File::Spec->catdir($target_path, $track_info_ref->{storage_path});
|
||||||
|
}
|
||||||
|
|
||||||
|
my $file_util = File::Util->new();
|
||||||
|
if(!-d $file_util->make_dir($target_path => oct DEFAULT_PERMISSIONS => {if_not_exists => 1}))
|
||||||
|
{
|
||||||
|
info(ERROR, 'Failed to create: ' . $target_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target_path = File::Spec->catfile($target_path, $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);
|
||||||
@@ -599,7 +621,7 @@ sub get_track_url
|
|||||||
my ($idx, $target_idx) = (0, -1);
|
my ($idx, $target_idx) = (0, -1);
|
||||||
for my $track_info(@{$json->{result}})
|
for my $track_info(@{$json->{result}})
|
||||||
{
|
{
|
||||||
if($track_info->{codec} eq 'mp3')
|
if($track_info->{codec} eq DEFAULT_CODEC)
|
||||||
{
|
{
|
||||||
if($opt{bitrate} && $track_info->{bitrateInKbps} == $opt{bitrate})
|
if($opt{bitrate} && $track_info->{bitrateInKbps} == $opt{bitrate})
|
||||||
{
|
{
|
||||||
@@ -917,11 +939,13 @@ sub create_track_entry
|
|||||||
$mp3_tags{TCON} = $track_info->{albums}->[0]->{genre};
|
$mp3_tags{TCON} = $track_info->{albums}->[0]->{genre};
|
||||||
}
|
}
|
||||||
|
|
||||||
# Substitute placeholders within a path name
|
# Substitute placeholders within a track name and a path name
|
||||||
my $track_filename = $opt{pattern};
|
my $track_filename = $opt{pattern};
|
||||||
|
my $storage_path = $opt{path};
|
||||||
while (my ($pattern, $tag_id) = each %{&PATTERN_MP3TAGS_RELS})
|
while (my ($pattern, $tag_id) = each %{&PATTERN_MP3TAGS_RELS})
|
||||||
{
|
{
|
||||||
$track_filename =~ s/\#$pattern/$mp3_tags{$tag_id}/gi;
|
$track_filename =~ s/\#$pattern/$mp3_tags{$tag_id}/gi;
|
||||||
|
$storage_path =~ s/\#$pattern/$mp3_tags{$tag_id}/gi;
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -933,7 +957,9 @@ sub create_track_entry
|
|||||||
# MP3 tags
|
# MP3 tags
|
||||||
mp3tags => \%mp3_tags,
|
mp3tags => \%mp3_tags,
|
||||||
# 'Save As' file name
|
# 'Save As' file name
|
||||||
title => $track_filename
|
title => $track_filename,
|
||||||
|
# 'Save As' directory
|
||||||
|
storage_path => $storage_path,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user