App-DrivePlayer

 view release on metacpan or  search on metacpan

lib/App/DrivePlayer/GUI/MetadataFetch.pm  view on Meta::CPAN

                my $net_meta = eval { $fetcher->fetch(
                    title  => $track->{title},
                    artist => $track->{artist},
                    album  => $track->{album},
                ) };
                if ($net_meta) {
                    $meta   = $meta ? { %$net_meta, %$meta } : $net_meta;
                    $source = $source ? "$source + text search" : 'text search';
                }
            }

            # 3. Fingerprint as last resort
            if (!$tags_complete && !$meta && $use_fp) {
                print {$writer} encode_json({
                    status => 'fingerprinting', n => $n, total => $total,
                    title  => $track->{title},
                }) . "\n";
                $meta = eval { $fetcher->fetch_by_fingerprint(drive_id => $track->{drive_id}) };
                $source = 'fingerprint' if $meta;
            }

            $source //= 'none';

            # 4. Get duration: prefer embedded tags, then ffprobe
            my $duration_ms = $meta ? delete $meta->{duration_ms} : undef;
            my $dur_source;
            if ($duration_ms) {
                $dur_source = 'embedded tags';
            } elsif (!$track->{duration_ms}
                    && App::DrivePlayer::MetadataFetcher::ffprobe_available()) {
                $duration_ms = eval {
                    App::DrivePlayer::MetadataFetcher::probe_duration_ms(
                        undef, $track->{drive_id}, $token,
                    )
                };
                $dur_source = 'ffprobe' if $duration_ms;
            }

            print {$writer} encode_json({
                result      => 1,
                track_id    => $track->{id},
                track       => $track,
                meta        => $meta,
                duration_ms => $duration_ms,
                source      => $source,
                dur_source  => $dur_source,
            }) . "\n";
        }

        print {$writer} encode_json({ done => 1 }) . "\n";
        close $writer;
        POSIX::_exit(0);
    }

    # ---- parent: reads results without blocking ----
    close $writer;
    $self->_meta_pid($pid);
    $self->_meta_reader($reader);
    $self->_meta_buf(q{});

    my $updated = 0;

    my $finish = sub {
        if (my $wid = $self->_meta_watch_id) {
            $self->_meta_watch_id(undef);
            Glib::Source->remove($wid);
        }
        $self->_meta_pid(undef);
        $self->_meta_fetch_item->set_label('Fetch All Metadata');
        close $reader;
        $self->_meta_reader(undef);
        waitpid($pid, 0);
    };

    my $process_msg = sub {
        my ($msg) = @_;
        if ($msg->{status}) {
            $self->_set_status(
                "$msg->{status} $msg->{n}/$msg->{total} ($updated updated): $msg->{title}"
            );
        }
        elsif ($msg->{result}) {
            $updated += $self->_apply_meta_result($msg);
        }
        return;
    };

    my $watch_id = Glib::IO->add_watch(fileno($reader), ['in', 'hup'], sub {
        my (undef, $cond) = @_;

        my $chunk = q{};
        my $bytes = sysread($reader, $chunk, 65536);

        if (!defined $bytes || $bytes == 0) {
            $finish->();
            $self->_set_status("Metadata fetch done — $updated of $total updated.");
            $self->_load_library();
            $self->_auto_sync_to_sheet() if $updated;
            return FALSE;
        }

        my $buf = $self->_meta_buf . $chunk;
        while ($buf =~ s/\A([^\n]+)\n//) {
            my $msg = eval { decode_json($1) } or next;
            $process_msg->($msg);
        }
        $self->_meta_buf($buf);

        return TRUE;
    });

    $self->_meta_watch_id($watch_id);
    $self->_meta_fetch_item->set_label('Stop Metadata Fetch');
    $self->_set_status("Fetching metadata for $total tracks in background…");
    return;
}

sub _stop_metadata_fetch {
    my ($self) = @_;
    return unless $self->_meta_watch_id;
    Glib::Source->remove($self->_meta_watch_id);
    $self->_meta_watch_id(undef);
    $self->_meta_fetch_item->set_label('Fetch All Metadata');
    if (my $pid = $self->_meta_pid) {
        kill 'TERM', $pid;
        waitpid($pid, 0);
        $self->_meta_pid(undef);
    }
    # Drain any result messages the child had already written before dying
    if (my $reader = $self->_meta_reader) {
        my $buf = $self->_meta_buf;
        my $chunk = q{};
        while (sysread($reader, $chunk, 65536)) {
            $buf .= $chunk;
        }
        while ($buf =~ s/\A([^\n]+)\n//) {
            my $msg = eval { decode_json($1) } or next;
            next unless $msg->{result};
            $self->_apply_meta_result($msg);
        }
        close $reader;
        $self->_meta_reader(undef);
        $self->_meta_buf(q{});
    }
    $self->_set_status('Metadata fetch stopped. Progress saved — will resume here next time.');
    return;
}

sub _reset_metadata_fetch {
    my ($self) = @_;
    if ($self->_meta_watch_id) {
        $self->_show_error('Cannot reset while a fetch is in progress.');
        return;
    }
    $self->db->reset_metadata_fetched();
    $self->_set_status('Metadata fetch progress reset — all tracks will be retried.');
    return;
}



( run in 0.522 second using v1.01-cache-2.11-cpan-39bf76dae61 )