App-DrivePlayer

 view release on metacpan or  search on metacpan

lib/App/DrivePlayer/MetadataFetcher.pm  view on Meta::CPAN

package App::DrivePlayer::MetadataFetcher;

use App::DrivePlayer::Setup;
use File::Temp   qw( tempfile );
use HTTP::Tiny;
use JSON::PP     qw( decode_json );
use URI::Escape  qw( uri_escape_utf8 );
use Time::HiRes  qw( sleep time usleep );

my $log = do { eval { require Log::Log4perl; Log::Log4perl->get_logger(__PACKAGE__) } };

Readonly my $USER_AGENT  => 'DrivePlayer/1.0 (https://github.com/mvsjes2/drive_player)';
Readonly my $ITUNES_BASE => 'https://itunes.apple.com/search';
Readonly my $MB_BASE     => 'https://musicbrainz.org/ws/2';
Readonly my $AID_BASE    => 'https://api.acoustid.org/v2/lookup';
Readonly my $DRIVE_URL   => 'https://www.googleapis.com/drive/v3/files/%s?alt=media';
Readonly my $MB_MIN_GAP  => 1.1;

lib/App/DrivePlayer/MetadataFetcher.pm  view on Meta::CPAN

}

# ------------------------------------------------------------------
# iTunes
# ------------------------------------------------------------------

sub _fetch_itunes {
    my ($self, $title, $artist, $album) = @_;

    my $term = join(' ', grep { length } $artist, $title);
    my $url  = $ITUNES_BASE . '?term=' . uri_escape_utf8($term)
             . '&entity=song&media=music&limit=5';

    my $data    = $self->_get_plain($url) or return;
    my $results = $data->{results}        or return;
    return unless @$results;

    my $best = _best_itunes_match($results, $title, $artist, $album);
    return unless $best;

    my %meta;

lib/App/DrivePlayer/MetadataFetcher.pm  view on Meta::CPAN


    my @attempts;
    push @attempts, 'recording:' . _mb_q($title) . '~ AND artist:' . _mb_q($artist)
                  . '~ AND release:' . _mb_q($album) . '~'
        if $artist && $album;
    push @attempts, 'recording:' . _mb_q($title) . '~ AND artist:' . _mb_q($artist) . '~'
        if $artist;
    push @attempts, 'recording:' . _mb_q($title) . '~';

    for my $query (@attempts) {
        my $url = "$MB_BASE/recording?query=" . uri_escape_utf8($query)
                . '&fmt=json&limit=5&inc=releases+artist-credits+tags+genres';
        my $data = $self->_get_mb($url) or next;
        my $recs = $data->{recordings}  or next;
        next unless @$recs;
        my $meta = $self->_parse_mb_with_release($recs->[0]);
        return $meta if $meta && %$meta;
    }
    return;
}

lib/App/DrivePlayer/MetadataFetcher.pm  view on Meta::CPAN

sub _fpcalc_available { fpcalc_available() }

sub _download_partial {
    my ($self, $drive_id, $max_bytes) = @_;
    my $token = $self->token_fn->();
    unless ($token) {
        $log->warn("Fingerprint: no bearer token available") if $log;
        return;
    }

    my $url     = sprintf $DRIVE_URL, uri_escape_utf8($drive_id);
    my $max     = ($max_bytes // ($DOWNLOAD_MB * 1024 * 1024)) - 1;
    my $ua      = HTTP::Tiny->new(agent => $USER_AGENT, timeout => 30);
    my ($fh, $tmpfile) = tempfile(SUFFIX => '.audio', UNLINK => 0);
    binmode $fh;

    my $res = $ua->request('GET', $url, {
        headers       => {
            Authorization => $token,
            Range         => "bytes=0-$max",
        },

lib/App/DrivePlayer/MetadataFetcher.pm  view on Meta::CPAN

    my $json   = qx($fpcalc -json -length 120 \Q$tmpfile\E 2>/dev/null);
    return unless $json;
    my $data = eval { decode_json($json) } or return;
    return unless $data->{fingerprint} && $data->{duration};
    return { fingerprint => $data->{fingerprint}, duration => int($data->{duration}) };
}

sub _query_acoustid {
    my ($self, $fp) = @_;
    my $url = $AID_BASE
            . '?client='      . uri_escape_utf8($self->acoustid_key)
            . '&meta=recordings+compress'
            . '&duration='    . $fp->{duration}
            . '&fingerprint=' . uri_escape_utf8($fp->{fingerprint});

    my $data    = $self->_get_plain($url) or return;
    my $results = $data->{results}        or return;
    return unless @$results;

    # Pick the result with the highest score
    my ($best) = sort { $b->{score} <=> $a->{score} } @$results;
    return unless $best->{score} && $best->{score} > 0.5;

    my $recordings = $best->{recordings} or return;



( run in 1.577 second using v1.01-cache-2.11-cpan-df04353d9ac )