App-DrivePlayer

 view release on metacpan or  search on metacpan

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

    my $existing = $self->_rs('Track')->find({ drive_id => $t{drive_id} });

    if ($existing) {
        # Always refresh structural fields from the Drive scan.
        # Preserve metadata fields that are already populated; only fill in
        # nulls from the scan's filename-derived values.  This ensures that
        # metadata loaded from the sheet (or set via AcoustID) is never
        # silently overwritten by a re-scan.
        my %update = map { $_ => _trunc($t{$_}, $_) } @STRUCTURAL_FIELDS;
        for my $f (@METADATA_FIELDS) {
            my $cur = $existing->get_column($f);
            # Treat the drive_id placeholder as absent for the title field.
            my $absent = !defined $cur || $cur eq ''
                      || ($f eq 'title' && $cur eq $existing->drive_id);
            $update{$f} = _trunc($t{$f}, $f) if $absent;
        }
        $existing->update(\%update);
    } else {
        $self->_rs('Track')->create({
            drive_id      => $t{drive_id},
            title         => _trunc($t{title},         'title'),
            artist        => _trunc($t{artist},        'artist'),
            album         => _trunc($t{album},         'album'),
            track_number  => $t{track_number},
            year          => $t{year},
            duration_ms   => $t{duration_ms},
            size          => $t{size},
            mime_type     => _trunc($t{mime_type},     'mime_type'),
            modified_time => _trunc($t{modified_time}, 'modified_time'),
            folder_id     => $t{folder_id},
            folder_path   => _trunc($t{folder_path},   'folder_path'),
            genre         => _trunc($t{genre},         'genre'),
            comment       => _trunc($t{comment},       'comment'),
        });
    }
}

sub update_track_metadata {
    my ($self, $id, %fields) = @_;
    my $row = $self->_rs('Track')->find($id) or return;
    my %allowed = map { $_ => 1 }
        qw( title artist album track_number year duration_ms genre comment );
    my %update = map { $_ => $fields{$_} }
        grep { $allowed{$_} } keys %fields;
    $row->update(\%update) if %update;
}

# Count tracks whose $field exactly equals $value, optionally excluding one
# track id. Used to decide whether an edit should offer to propagate a
# rename to siblings (e.g. cleaning up mojibake artist/album/genre values
# without having to revisit every track).
sub count_tracks_with_field {
    my ($self, $field, $value, $exclude_id) = @_;
    return 0 unless defined $value && length $value;
    my %where = ($field => $value);
    $where{id} = { '!=' => $exclude_id } if defined $exclude_id;
    return $self->_rs('Track')->search(\%where)->count;
}

# Bulk-rename: set $field = $new for every track where $field = $old.
# Returns the number of rows updated.
sub rename_field {
    my ($self, $field, $old, $new) = @_;
    return 0 unless defined $old && length $old;
    return $self->_rs('Track')->search({ $field => $old })
                              ->update({ $field => $new });
}

# Track ids whose $field exactly equals $value. Used to drive a targeted
# sheet sync after a bulk rename so we don't rewrite the whole spreadsheet.
sub track_ids_with_field {
    my ($self, $field, $value) = @_;
    return () unless defined $value && length $value;
    return $self->_rs('Track')->search({ $field => $value },
                                       { columns => ['id'] })
                              ->get_column('id')->all;
}

# Insert or update a track using only the metadata fields available from the
# sheet (drive_id + title/artist/album etc.).  Structural fields (folder_id,
# mime_type, size, …) are left null or at their placeholder values so that a
# subsequent Drive scan can fill them in via upsert_track.
sub upsert_track_from_metadata {
    my ($self, %fields) = @_;
    $self->_rs('Track')->update_or_create(
        {
            drive_id     => $fields{drive_id},
            title        => _trunc($fields{title} || $fields{drive_id}, 'title'),
            mime_type    => _trunc($fields{mime_type} || 'audio/', 'mime_type'),
            folder_id    => $fields{folder_id}    // undef,
            artist       => _trunc($fields{artist},    'artist'),
            album        => _trunc($fields{album},     'album'),
            track_number => $fields{track_number} // undef,
            year         => $fields{year}         // undef,
            duration_ms  => $fields{duration_ms}  // undef,
            genre        => _trunc($fields{genre},     'genre'),
            comment      => _trunc($fields{comment},   'comment'),
        },
        { key => 'unique_drive_id' },
    );
}

sub get_track {
    my ($self, $id) = @_;
    return _row_to_hash( $self->_rs('Track')->find($id) );
}

# Walk Track → Folder → ScanFolder and return the scan folder hashref.
# Used by targeted single-row sheet sync so it knows which tab to update.
sub scan_folder_for_track {
    my ($self, $track_id) = @_;
    my $track  = $self->_rs('Track')->find($track_id) or return;
    my $folder = $track->folder                       or return;
    my $sf     = $folder->scan_folder                 or return;
    return _row_to_hash($sf);
}

sub get_track_by_drive_id {
    my ($self, $drive_id) = @_;
    return _row_to_hash(
        $self->_rs('Track')->find({ drive_id => $drive_id })



( run in 1.250 second using v1.01-cache-2.11-cpan-bbb979687b5 )