Created
September 16, 2012 23:18
-
-
Save oylenshpeegul/3734797 to your computer and use it in GitHub Desktop.
Perl script to download a playlist from tunes.io
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
# Download all the files in a daily playlist from tunes.io | |
# | |
# https://gist.github.com/3734797 | |
$ENV{MOJO_MAX_MESSAGE_SIZE} = 30_000_000; | |
use v5.16; | |
use warnings; | |
use CLI::Startup; | |
use Data::Printer; | |
use DateTime; | |
use IO::All; | |
use JSON; | |
use Mojo::IOLoop; | |
use Mojo::UserAgent; | |
use Time::HiRes qw(time); | |
our $VERSION = '0.0.1'; | |
my $app = CLI::Startup->new({ | |
options => { | |
'basedir=s' => 'Output directory [default: ~/tunesio]', | |
'date=s' => 'Date yyyy-mm-dd [default: today]', | |
'debug!' => 'Debug output', | |
'download=i' => 'Download files [default: 1 (yes)]', | |
'mono=i' => 'Bounce files down to mono [default: 0 (no)]', | |
'normalize=i' => 'Normalize files [default: 0 (no)]', | |
'verbose!' => 'Verbose output', | |
}, | |
default_settings => { | |
basedir => "$ENV{HOME}/tunesio", | |
date => DateTime->today->strftime('%F'), | |
download => 1, | |
mono => 0, | |
normalize => 0, | |
}, | |
}); | |
$app->init; | |
my $opt = $app->get_options; | |
say $opt->{date} if $opt->{debug}; | |
my $outdir = "$opt->{basedir}/$opt->{date}"; | |
say $outdir if $opt->{debug}; | |
io($outdir)->mkpath; | |
# Get the playlist from the website. | |
my $playlist = get_playlist($outdir, $opt); | |
p $playlist if $opt->{debug}; | |
chdir $outdir or die "$outdir: $!"; | |
# Download all the files concurrently. | |
get_songs($playlist, $opt) if $opt->{download}; | |
# Bounce the files down to mono. | |
if ($opt->{mono}) { | |
foreach my $file (glob "*.mp3") { | |
my $out = $file =~ s/\.mp3$/.mono.mp3/r; | |
my @cmd = ('sox', '-G', $file, '-c', '1', $out); | |
say "@cmd" if $opt->{debug}; | |
system(@cmd) == 0 or die "Cannot run '@cmd'"; | |
rename $out, $file; | |
} | |
} | |
# Normalize all the files as a set. | |
if ($opt->{normalize}) { | |
my $cmd = "normalize-audio --mix *.mp3"; | |
system($cmd) == 0 or warn "Cannot run '$cmd'"; | |
} | |
# If the playlist JSON file exists locally, just read it in and decode | |
# it. Otherwise, download the playlist from the website (and encode | |
# and save it for next time). | |
sub get_playlist { | |
my($outdir, $opt) = @_; | |
my $file = "$outdir/playlist"; | |
say $file if $opt->{debug}; | |
if ( -e $file ) { | |
return decode_json(io($file)->all); | |
} | |
else { | |
my $url = "http://tunes.io/playlist.jsp?date=$opt->{date}"; | |
say $url if $opt->{debug}; | |
my $dom = Mojo::UserAgent->new->get($url)->res->dom; | |
my @songs = $dom->find('ul.playlist') | |
->map(sub{$_->children->each}) # li | |
->map(sub{$_->children->each}) # a | |
->each; | |
my $playlist = []; | |
for my $song (@songs) { | |
my $title = $song->text; | |
$title =~ s/\s/_/g; | |
$title =~ s{/}{.}g; | |
push $playlist, {title => $title, url => $song->{href}}; | |
} | |
io($file)->print(encode_json($playlist)); | |
return $playlist; | |
} | |
} | |
# Download all of the songs in the given playlist. | |
sub get_songs { | |
my($playlist, $opt) = @_; | |
my $ua = Mojo::UserAgent->new(max_redirects => 3); | |
my $iol = Mojo::IOLoop->delay(sub { warn "done"; }); | |
my $start = time; | |
say 'Downloading ', scalar @$playlist, ' songs asynchronously' | |
if $opt->{verbose}; | |
for my $song (@$playlist) { | |
say "getting '$song->{title}' from\n$song->{url}" | |
if $opt->{verbose}; | |
my $now = time; | |
$iol->begin; | |
# Use the song title plus the file extension from the URL as | |
# the filename. | |
my $file = $song->{title} . substr($song->{url}, -4); | |
$ua->get($song->{url} => sub { | |
my($ua, $tx) = @_; | |
my $u = sprintf "%-25s", $song->{url}; | |
my $content = $tx->res->body; | |
io($file)->print($content); | |
my $retval = "$u has "; | |
if ($tx->success) { | |
my $len = sprintf "%8d", length($content); | |
my $et = sprintf "%5.3f", time - $now; | |
$retval .= "length $len and loaded in $et ms"; | |
} | |
else { | |
my($message, $code) = $tx->error; | |
$code = "??" unless defined $code; | |
$retval .= "error ($code) $message"; | |
} | |
warn "\n$retval\n" if $opt->{verbose}; | |
$iol->end($retval); | |
}); | |
} | |
say for $iol->wait; | |
printf "Total elapsed time: %5.3f s\n", time - $start; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment