App-DrivePlayer

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Revision history for App::DrivePlayer.

0.2.9 2026-04-30
        - Fix: player IPC no longer drops mpv events when a recv()
          slices an event mid-line.  _drain_events and
          _send_command_sync now share _pending_buf so partial
          events get reassembled across reads.  Symptom: roughly
          every 15-20 tracks playback would stick on a finished
          track because end-file was lost and on_track_end never
          fired.
        - Settings: saving with changed Google credentials now
          tears down rest_api / drive / player and rebuilds them
          against the new config, so a refreshed token takes
          effect without restarting the app.
        - Errors: HTTP 4xx / OAuth-grant failures from Google now
          pop the Settings dialog automatically after the error
          is dismissed, routing the user straight to the
          credentials.  Re-entry from errors fired inside the
          Settings dialog itself (e.g. Find-or-Create) is
          suppressed.
        - Tracklist: Artist, Album and Genre cells are now click
          targets that switch the sidebar to that group; the
          existing cursor-changed signal repopulates the
          tracklist.  A hand cursor on hover indicates the cell
          is clickable.
        - Edit metadata: when an artist / album / genre value
          changes and other tracks share the previous value, a
          single confirmation dialog offers to apply the rename

README.md  view on Meta::CPAN

```

Or install everything in one step:

```
make install
```

## Setup

### 1. Create Google Cloud credentials

1. Go to [Google Cloud Console](https://console.cloud.google.com/) and create a project
2. Enable the **Google Drive API**: APIs & Services → Enable APIs → search "Drive API"
3. Create OAuth credentials: APIs & Services → Credentials → Create Credentials → OAuth client ID → **Desktop app**
4. Note the **Client ID** and **Client Secret**

### 2. Configure the application

Launch the app, open **File → Settings**, and paste the Client ID and Client Secret. Click Save.

The config file is written to `~/.config/drive_player/config.yaml`.

### 3. Authorise access to Google Drive

README.md  view on Meta::CPAN

DrivePlayer can store your library metadata in a Google Spreadsheet so it
survives across devices or a fresh install:

- **File → Settings**: enter or create a Spreadsheet ID
- Metadata is pushed to the sheet automatically after each sync
- On a new device, the sheet is pulled automatically when the app starts with
  an empty database, restoring your library without a full rescan

## Files

- `~/.config/drive_player/config.yaml` — OAuth credentials, log level, folder list
- `~/.config/drive_player/token.dat` — OAuth2 token cache
- `~/.local/share/drive_player/music.db` — SQLite track library
- `~/.local/share/drive_player/drive_player.log` — Application log

## License

MIT — see [LICENSE](LICENSE)

bin/drive_player  view on Meta::CPAN


=head1 SETUP

=head2 1. Install dependencies

  make install

This installs system packages (Gtk3, Glib, SQLite, mpv) and all required
CPAN modules.

=head2 2. Create a Google Cloud project and OAuth credentials

=over 4

=item 1.

Go to L<https://console.cloud.google.com/> and create a new project (or
select an existing one).

=item 2.

Enable the B<Google Drive API>: APIs & Services > Enable APIs and Services >
search for "Drive API" > Enable.

=item 3.

Create OAuth credentials: APIs & Services > Credentials > Create Credentials
> OAuth client ID.  Choose B<Desktop app> as the application type.

=item 4.

Download or note the B<Client ID> and B<Client Secret>.

=back

=head2 3. Configure the application

bin/drive_player  view on Meta::CPAN

Launch the app and choose B<File E<gt> Add Music Folder>.  Enter the Google
Drive folder ID (the last path component of the folder's Drive URL) and a
display name.  The app will scan the folder and add tracks to the library.

=head1 FILES

=over 4

=item F<~/.config/drive_player/config.yaml>

Main configuration file (OAuth credentials, log level, folder list).

=item F<~/.config/drive_player/token.dat>

OAuth2 token cache.  Created by the token creator; refreshed automatically.

=item F<~/.local/share/drive_player/music.db>

SQLite database storing the scanned track library.

=item F<~/.local/share/drive_player/drive_player.log>

config.yaml.example  view on Meta::CPAN

# Drive Player configuration example
# Copy this to ~/.config/drive_player/config.yaml and fill in your credentials.

# Google OAuth2 credentials
# Create an OAuth2 Client ID at: https://console.cloud.google.com/
# Enable the Google Drive API for your project.
auth:
  class: OAuth2Client
  client_id: "YOUR_CLIENT_ID.apps.googleusercontent.com"
  client_secret: "YOUR_CLIENT_SECRET"
  token_file: "~/.config/drive_player/token.dat"
  scope:
    - "https://www.googleapis.com/auth/drive.readonly"

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

    }
}

sub _init_api {
    my ($self) = @_;
    return $self->rest_api if $self->rest_api;

    my $auth_cfg = $self->config->auth_config();
    unless ($auth_cfg->{client_id} && $auth_cfg->{client_secret}) {
        $self->_show_error(
            "Google API credentials not configured.\n\n" .
            "Open File > Settings and enter your OAuth Client ID and Secret.\n\n" .
            "You can obtain these from the Google Cloud Console under\n" .
            "APIs & Services > Credentials (OAuth 2.0 Client ID, Desktop app type)."
        );
        return;
    }
    unless (-f ($auth_cfg->{token_file} // '')) {
        $self->_show_error("OAuth token file not found: $auth_cfg->{token_file}\n\n" .
            "Run the token creator from p5-google-restapi:\n" .
            "  bin/google_restapi_oauth_token_creator");

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

        'Settings', $self->win,
        [qw/ modal destroy-with-parent /],
        'Save',   'ok',
        'Cancel', 'cancel',
    );
    $dlg->set_default_size(520, -1);

    my $vbox = $dlg->get_content_area();
    $vbox->set_spacing(0);

    # ---- Google API credentials ----
    my $auth_frame = Gtk3::Frame->new('Google API Credentials');
    $auth_frame->set_border_width(8);
    my $grid = Gtk3::Grid->new();
    $grid->set_row_spacing(8);
    $grid->set_column_spacing(8);
    $grid->set_border_width(8);
    $auth_frame->add($grid);
    $vbox->pack_start($auth_frame, FALSE, FALSE, 0);

    my $row = 0;

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

        $e->set_tooltip_text($tip);
        $grid->attach($e, 1, $row, 1, 1);
        $entries{$key} = $e;
        $row++;
    }

    my $auth_note = Gtk3::Label->new();
    $auth_note->set_markup(
        '<span size="small" foreground="#555555">'
        . 'Create a Desktop-app OAuth client at '
        . '<a href="https://console.cloud.google.com/apis/credentials">'
        . 'Google Cloud Console → Credentials</a>, then enable the '
        . '<a href="https://console.cloud.google.com/apis/library/drive.googleapis.com">'
        . 'Drive API</a>.  Generate the token file by running '
        . '<tt>google_restapi_oauth_token_creator</tt> in a terminal.'
        . '</span>'
    );
    $auth_note->set_xalign(0.0);
    $auth_note->set_line_wrap(TRUE);
    $auth_note->set_max_width_chars(60);
    $grid->attach($auth_note, 1, $row, 1, 1);

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

    );
    $dlg->run();
    $dlg->destroy();

    if (_is_auth_error($msg) && !$self->_settings_open) {
        $self->_settings_dialog();
    }
}

# Heuristic: does an error message look like a Google API auth/connection
# failure that the user can fix in the Settings dialog (bad credentials,
# expired/revoked token, etc.)?
sub _is_auth_error {
    my ($msg) = @_;
    return 0 unless defined $msg && length $msg;

    return 1 if $msg =~ m{
        \b (?: 401 | 403 ) \b           # unauthorized / forbidden
        | \b invalid _ grant \b
        | \b invalid _ token \b
        | \b invalid _ client \b

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

L<App::DrivePlayer::Player> on first use, so start-up is fast even when network
access is unavailable.

=item *

Running folder scans (via L<App::DrivePlayer::Scanner>) in a background thread
with live progress reporting.

=item *

Persisting configuration changes (music folder list, OAuth2 credentials)
through L<App::DrivePlayer::Config>.

=back

Requires the GTK3 system libraries and the L<Gtk3> and L<Glib> Perl
modules.  Not covered by the unit test suite.

=head1 METHODS

=head2 new

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

    ));
    $self->_load_library();
    return;
}

sub _auto_sync_from_sheet_on_new_db {
    my ($self) = @_;

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

    # Skip silently if OAuth credentials aren't configured on this device yet
    my $auth = $self->config->auth_config();
    return unless $auth->{client_id} && $auth->{client_secret};
    return unless -f ($auth->{token_file} // '');

    my $api = $self->_init_api() or return;

    my $sheet_id = $self->config->sheet_id();

    unless ($sheet_id) {
        # Search Drive for the spreadsheet by name



( run in 0.920 second using v1.01-cache-2.11-cpan-140bd7fdf52 )