NAME
App::DrivePlayer::SheetDB - Sync the DrivePlayer library to/from a Google Sheet
SYNOPSIS
use App::DrivePlayer::SheetDB;
my $sheet = App::DrivePlayer::SheetDB->new(
api => $google_rest_api,
spreadsheet_id => $id, # omit when calling create()
);
my $id = $sheet->create(); # create spreadsheet, returns ID
my $summary = $sheet->sync_with_db($db, drive_exists => \&check);
my $counts = $sheet->push_to_sheet($db); # merge-push only
my $counts = $sheet->pull_from_sheet($db); # new-device restore
DESCRIPTION
Maintains a Google Spreadsheet with one worksheet per scan folder, plus a folders index tab:
- folders
-
drive_idandnamefor every top-level folder in the library. - One tab per folder (named after the folder)
-
Track metadata columns:
drive_id title artist album track_number year duration_ms genre comment. Structural fields (folder_id, etc.) are re-derived from Drive scanning and are not stored in the sheet.
The local SQLite database remains the working store for all runtime queries. The Sheet is a portable sync target accessible from any device with Drive access.
NEW DEVICE WORKFLOW
On first launch the app detects a fresh local DB and automatically runs pull_from_sheet to seed scan folders and track metadata from the sheet. The user then runs Library -> Sync to discover audio files on Drive and reconcile two-way.
METHODS
new(%args)
api (Google::RestApi instance) is required. spreadsheet_id is optional (omit before calling create()).
create()
Creates a new "DrivePlayer Library" spreadsheet with a folders tab. Returns and stores the new spreadsheet ID.
sync_with_db($db, drive_exists => \&cb)
Two-way reconciliation keyed on drive_id:
Fields present on both sides: DB wins, and any DB blanks are filled from the sheet. A blank (
undefor empty string) is treated as "missing" on either side, so cleared fields cannot be propagated.drive_idin the DB but not the sheet: row added to the sheet.drive_idon the sheet but not in the DB:drive_exists->($id)is called. A truthy result adds the track to the DB; a falsy result deletes the row from the sheet. If the callback throws (API failure), the row is preserved on both sides --sync never destroys data on error.Scan-folder list: union only; folders are never auto-deleted because the list is user-owned configuration.
Returns a summary hashref.
push_to_sheet($db)
Merge-push only: local non-blank values overwrite the sheet, local blanks preserve whatever is on the sheet, and drive_ids only on the sheet are kept intact. Used by auto-push after metadata edits.
pull_from_sheet($db)
Upserts scan folders into SQLite and applies track metadata to any tracks already present (keyed by drive_id). Used once on first launch when the local DB is brand new.