From 162c68769f13607e606325c2b2fddd13d769d2b7 Mon Sep 17 00:00:00 2001 From: Caleb Allen Date: Fri, 14 Aug 2026 17:48:38 -0400 Subject: [PATCH] feat(wails): move the frontend onto v3's generated bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit frontend/wailsjs/ is deleted and frontend/bindings/ takes its place — a real TypeScript module tree nested by Go import path, generated by wails3's static analyser rather than by building the app and running it. The @go alias absorbs the constant prefix, so a call site imports '@go/library/library.js' and the codemod over all 93 sites was a specifier rewrite plus splitting @go/models' namespaces into one import per package. The 12 SetContext bindings and the fake `context` model are gone, as Phase 2's ServiceStartup port promised: 272 methods across 12 services, none of them plumbing. @runtime/runtime is now a local shim (src/wails/runtime.ts) over @wailsio/runtime, so the 22 EventsOn imports are untouched. It unwraps v3's WailsEvent into v2's callback shape, which is exact here: nothing in backend/events passes more than one data argument, and v3 only packs arguments into a slice when there is more than one. v3 tells the truth about two things v2 lied about, and that is most of the diff. A Go nil slice really does arrive as JSON null, and a Go named string type really is an enum; v2 typed them as T[] and string. utils/binding.ts states the app's actual contract — an absent list is an empty list — once, at the boundary where it is true, and also drops the CancellablePromise the app never cancels. Four test fixtures widen an enum field back to its value union. Not done, and Phase 5's to fix: frontend/test/support/wails-fake.ts still fakes window.go, which v3 does not have, so `make ui-test` is broken and harness.test.ts fails to compile on EventsEmit. That test also asserts v2 ordering that no longer holds — v3's Events.Emit calls the backend and does not notify in-page listeners at all. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01UDCbcCZQepnpSQYJ6SxxZm --- Makefile | 22 +- .../wailsapp/wails/v3/internal/eventcreate.ts | 9 + .../wailsapp/wails/v3/internal/eventdata.d.ts | 2 + .../Service.d.ts => bindings/time/index.ts} | 5 +- frontend/bindings/time/models.ts | 38 + .../backend/autotagservice/index.ts | 19 + .../backend/autotagservice/models.ts | 187 ++ .../backend/autotagservice/service.ts | 283 ++ .../yellowjacket/backend/config/config.ts | 236 ++ .../yellowjacket/backend/config/index.ts} | 8 +- .../backend/database/sql/sqlcgen/index.ts | 6 + .../backend/database/sql/sqlcgen/models.ts | 10 + .../yellowjacket/backend/download/index.ts | 38 + .../yellowjacket/backend/download/models.ts | 732 +++++ .../yellowjacket/backend/download/service.ts | 207 ++ .../yellowjacket/backend/explore/index.ts | 35 + .../yellowjacket/backend/explore/models.ts | 469 +++ .../yellowjacket/backend/explore/service.ts | 604 ++++ .../backend/frontendutil/frontendutil.ts | 47 + .../backend/frontendutil/index.ts | 7 + .../yellowjacket/backend/home/index.ts | 15 + .../yellowjacket/backend/home/models.ts | 53 + .../yellowjacket/backend/home/service.ts | 27 + .../yellowjacket/backend/jobs/index.ts | 22 + .../yellowjacket/backend/jobs/models.ts | 159 ++ .../yellowjacket/backend/jobs/service.ts | 68 + .../yellowjacket/backend/library/index.ts | 25 + .../yellowjacket/backend/library/library.ts | 454 +++ .../yellowjacket/backend/library/models.ts | 315 ++ .../backend/mediacontrols/index.ts | 6 + .../backend/mediacontrols/models.ts | 7 + .../yellowjacket/backend/player/index.ts | 16 + .../yellowjacket/backend/player/models.ts | 61 + .../yellowjacket/backend/player/player.ts | 182 ++ .../yellowjacket/backend/playlist/index.ts | 19 + .../yellowjacket/backend/playlist/models.ts | 103 + .../yellowjacket/backend/playlist/service.ts | 336 +++ .../yellowjacket/backend/queue/index.ts | 19 + .../yellowjacket/backend/queue/models.ts | 72 + .../yellowjacket/backend/queue/queue.ts | 219 ++ .../yellowjacket/backend/tagwriter/index.ts | 13 + .../yellowjacket/backend/tagwriter/models.ts | 28 + .../backend/tagwriter/tagwriter.ts | 80 + .../yellowjacket/backend/tracklist/index.ts | 10 + .../yellowjacket/backend/tracklist/models.ts | 41 + frontend/index.ts | 6 +- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 8 + .../artist-details/artist-details.ts | 4 +- .../components/artists-view/artists-view.ts | 26 +- .../components/autotag-view/autotag-view.ts | 49 +- .../src/components/config-page/config-page.ts | 30 +- .../config-page/download-clients.ts | 32 +- .../components/cover-grid/album-dropdown.ts | 2 +- .../components/cover-grid/album-selection.ts | 15 +- .../components/cover-grid/cover-grid-types.ts | 2 +- .../src/components/cover-grid/cover-grid.ts | 19 +- .../components/cover-grid/scroll-manager.ts | 2 +- .../download-picker/download-picker.ts | 2 +- .../downloads-view/downloads-view.ts | 2 +- .../duplicate-tracks-dialog.ts | 2 +- .../explore-album-details.ts | 23 +- .../explore-artist-details.ts | 23 +- .../components/explore-view/explore-view.ts | 30 +- .../first-run-wizard/first-run-wizard.ts | 4 +- .../components/genre-details/genre-details.ts | 21 +- .../src/components/genres-view/genres-view.ts | 10 +- .../src/components/home-view/home-view.ts | 9 +- frontend/src/components/jobs/jobs-view.ts | 4 +- .../library-filter/library-filter.ts | 2 +- .../phantom-resolver/phantom-resolver.ts | 15 +- .../playlist-details/playlist-details.ts | 13 +- .../playlist-picker/playlist-picker.ts | 7 +- .../components/playlist-view/playlist-view.ts | 6 +- .../src/components/queue-panel/queue-panel.ts | 2 +- .../smart-playlist-details.ts | 9 +- .../smart-playlist-editor.ts | 4 +- .../top-results-row/top-results-row.ts | 4 +- .../components/track-details/track-details.ts | 10 +- frontend/src/components/track-list/columns.ts | 2 +- .../components/track-list/search-ranking.ts | 2 +- .../src/components/track-list/track-list.ts | 4 +- .../src/services/keyboard-shortcut-service.ts | 2 +- .../store/controllers/library-controller.ts | 2 +- .../store/controllers/playlist-controller.ts | 2 +- frontend/src/store/download-store.ts | 4 +- frontend/src/store/favorites-store.ts | 4 +- frontend/src/store/job-store.ts | 4 +- frontend/src/store/library-store.ts | 30 +- frontend/src/store/player-store.ts | 2 +- frontend/src/store/playlist-store.ts | 4 +- frontend/src/store/queue-store.ts | 2 +- frontend/src/store/shortcuts-store.ts | 5 +- frontend/src/store/theme-store.ts | 2 +- frontend/src/store/tracklist-store.ts | 20 +- frontend/src/utils/binding.ts | 88 + frontend/src/utils/library-status.ts | 2 +- frontend/src/utils/track-index.ts | 2 +- frontend/src/wails/runtime.ts | 33 + .../test/components/library-status.test.ts | 9 +- frontend/test/harness.test.ts | 4 +- frontend/test/stores/download-store.test.ts | 16 +- frontend/test/stores/job-store.test.ts | 13 +- frontend/tsconfig.json | 9 +- frontend/vite.config.mts | 8 +- .../wailsjs/go/autotagservice/Service.d.ts | 49 - frontend/wailsjs/go/autotagservice/Service.js | 91 - frontend/wailsjs/go/config/Config.d.ts | 65 - frontend/wailsjs/go/config/Config.js | 123 - frontend/wailsjs/go/download/Service.d.ts | 50 - frontend/wailsjs/go/download/Service.js | 95 - frontend/wailsjs/go/explore/Service.d.ts | 120 - frontend/wailsjs/go/explore/Service.js | 231 -- .../wailsjs/go/frontendutil/FrontendUtil.d.ts | 13 - .../wailsjs/go/frontendutil/FrontendUtil.js | 23 - frontend/wailsjs/go/jobs/Service.d.ts | 17 - frontend/wailsjs/go/jobs/Service.js | 31 - frontend/wailsjs/go/library/Library.d.ts | 104 - frontend/wailsjs/go/library/Library.js | 199 -- frontend/wailsjs/go/models.ts | 2534 ----------------- frontend/wailsjs/go/player/Player.d.ts | 47 - frontend/wailsjs/go/player/Player.js | 87 - frontend/wailsjs/go/playlist/Service.d.ts | 77 - frontend/wailsjs/go/playlist/Service.js | 147 - frontend/wailsjs/go/queue/Queue.d.ts | 54 - frontend/wailsjs/go/queue/Queue.js | 103 - frontend/wailsjs/go/tagwriter/TagWriter.d.ts | 16 - frontend/wailsjs/go/tagwriter/TagWriter.js | 27 - frontend/wailsjs/runtime/package.json | 24 - frontend/wailsjs/runtime/runtime.d.ts | 249 -- frontend/wailsjs/runtime/runtime.js | 238 -- lefthook.yml | 7 +- scripts/bindings-check.sh | 41 +- 133 files changed, 5755 insertions(+), 5094 deletions(-) create mode 100644 frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.ts create mode 100644 frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts rename frontend/{wailsjs/go/home/Service.d.ts => bindings/time/index.ts} (56%) mode change 100755 => 100644 create mode 100644 frontend/bindings/time/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/autotagservice/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/autotagservice/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/autotagservice/service.ts create mode 100644 frontend/bindings/yellowjacket/backend/config/config.ts rename frontend/{wailsjs/go/home/Service.js => bindings/yellowjacket/backend/config/index.ts} (52%) mode change 100755 => 100644 create mode 100644 frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/download/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/download/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/download/service.ts create mode 100644 frontend/bindings/yellowjacket/backend/explore/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/explore/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/explore/service.ts create mode 100644 frontend/bindings/yellowjacket/backend/frontendutil/frontendutil.ts create mode 100644 frontend/bindings/yellowjacket/backend/frontendutil/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/home/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/home/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/home/service.ts create mode 100644 frontend/bindings/yellowjacket/backend/jobs/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/jobs/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/jobs/service.ts create mode 100644 frontend/bindings/yellowjacket/backend/library/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/library/library.ts create mode 100644 frontend/bindings/yellowjacket/backend/library/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/mediacontrols/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/mediacontrols/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/player/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/player/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/player/player.ts create mode 100644 frontend/bindings/yellowjacket/backend/playlist/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/playlist/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/playlist/service.ts create mode 100644 frontend/bindings/yellowjacket/backend/queue/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/queue/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/queue/queue.ts create mode 100644 frontend/bindings/yellowjacket/backend/tagwriter/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/tagwriter/models.ts create mode 100644 frontend/bindings/yellowjacket/backend/tagwriter/tagwriter.ts create mode 100644 frontend/bindings/yellowjacket/backend/tracklist/index.ts create mode 100644 frontend/bindings/yellowjacket/backend/tracklist/models.ts create mode 100644 frontend/src/utils/binding.ts create mode 100644 frontend/src/wails/runtime.ts delete mode 100755 frontend/wailsjs/go/autotagservice/Service.d.ts delete mode 100755 frontend/wailsjs/go/autotagservice/Service.js delete mode 100755 frontend/wailsjs/go/config/Config.d.ts delete mode 100755 frontend/wailsjs/go/config/Config.js delete mode 100755 frontend/wailsjs/go/download/Service.d.ts delete mode 100755 frontend/wailsjs/go/download/Service.js delete mode 100755 frontend/wailsjs/go/explore/Service.d.ts delete mode 100755 frontend/wailsjs/go/explore/Service.js delete mode 100755 frontend/wailsjs/go/frontendutil/FrontendUtil.d.ts delete mode 100755 frontend/wailsjs/go/frontendutil/FrontendUtil.js delete mode 100755 frontend/wailsjs/go/jobs/Service.d.ts delete mode 100755 frontend/wailsjs/go/jobs/Service.js delete mode 100755 frontend/wailsjs/go/library/Library.d.ts delete mode 100755 frontend/wailsjs/go/library/Library.js delete mode 100755 frontend/wailsjs/go/models.ts delete mode 100755 frontend/wailsjs/go/player/Player.d.ts delete mode 100755 frontend/wailsjs/go/player/Player.js delete mode 100755 frontend/wailsjs/go/playlist/Service.d.ts delete mode 100755 frontend/wailsjs/go/playlist/Service.js delete mode 100755 frontend/wailsjs/go/queue/Queue.d.ts delete mode 100755 frontend/wailsjs/go/queue/Queue.js delete mode 100755 frontend/wailsjs/go/tagwriter/TagWriter.d.ts delete mode 100755 frontend/wailsjs/go/tagwriter/TagWriter.js delete mode 100644 frontend/wailsjs/runtime/package.json delete mode 100644 frontend/wailsjs/runtime/runtime.d.ts delete mode 100644 frontend/wailsjs/runtime/runtime.js diff --git a/Makefile b/Makefile index bd0063e..5e9f8e2 100644 --- a/Makefile +++ b/Makefile @@ -100,14 +100,9 @@ ui-visual-update: ## Re-record the screenshot baselines ui-setup: ## Install the Vitest browser provider's own Chromium (once) @cd frontend && pnpm install && npx playwright install chromium -# Bindings are generated by `wails`, NOT by `go generate`, so the +# Bindings are generated by `wails3`, NOT by `go generate`, so the # pre-commit codegen check does not cover them: a renamed Go struct -# field currently surfaces at runtime, in a window. -# -# NOTE (plan 009 phase 4): this still checks v2's frontend/wailsjs, -# which nothing regenerates now that the v2 CLI is gone. It is -# excluded from the pre-commit hook until the frontend moves to -# frontend/bindings. +# field would otherwise surface at runtime, inside a window. bindings-check: ## Fail if the generated bindings are stale @./scripts/bindings-check.sh @@ -130,14 +125,13 @@ commit-check: ## Fail if a commit subject is not a Conventional Commit @./scripts/commit-check.sh $(if $(RANGE),--range $(RANGE)) # v3 generates TypeScript into frontend/bindings/, nested by Go import -# path, rather than v2's frontend/wailsjs/. The 93 frontend import -# sites still point at the old tree, so this writes the new one beside -# it until plan 009 phase 4 moves them; the chmod dance that used to -# follow is gone with v2's generator, which wrote its runtime files 755. +# path, rather than v2's frontend/wailsjs/. The `@go` alias absorbs the +# constant prefix, so a call site imports '@go/library/library.js'. # -# The tag set is pinned deliberately: the generator is a static -# analyser, so it only sees the configuration it is told about, and the -# one that matters is the one users run. +# No -f flag: the tag set is the default one, deliberately, because the +# generator is a static analyser that sees only the configuration it is +# told about and the one that matters is the one users run. See +# scripts/bindings-check.sh for why the other two do not apply. bindings: ## Regenerate frontend/bindings from the bound Go services go tool wails3 generate bindings -clean=true -ts -i diff --git a/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.ts b/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.ts new file mode 100644 index 0000000..1ea1058 --- /dev/null +++ b/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.ts @@ -0,0 +1,9 @@ +//@ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Create as $Create } from "@wailsio/runtime"; + +Object.freeze($Create.Events); diff --git a/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts b/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts new file mode 100644 index 0000000..3dd1807 --- /dev/null +++ b/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts @@ -0,0 +1,2 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT diff --git a/frontend/wailsjs/go/home/Service.d.ts b/frontend/bindings/time/index.ts old mode 100755 new mode 100644 similarity index 56% rename from frontend/wailsjs/go/home/Service.d.ts rename to frontend/bindings/time/index.ts index 43c908e..ffd69de --- a/frontend/wailsjs/go/home/Service.d.ts +++ b/frontend/bindings/time/index.ts @@ -1,5 +1,6 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -import {home} from '../models'; -export function GetShelves():Promise>; +export { + Duration +} from "./models.js"; diff --git a/frontend/bindings/time/models.ts b/frontend/bindings/time/models.ts new file mode 100644 index 0000000..6e14050 --- /dev/null +++ b/frontend/bindings/time/models.ts @@ -0,0 +1,38 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * A Duration represents the elapsed time between two instants + * as an int64 nanosecond count. The representation limits the + * largest representable duration to approximately 290 years. + */ +export enum Duration { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = 0, + + minDuration = -9223372036854775808, + maxDuration = 9223372036854775807, + + /** + * Common durations. There is no definition for units of Day or larger + * to avoid confusion across daylight savings time zone transitions. + * + * To count the number of units in a [Duration], divide: + * + * second := time.Second + * fmt.Print(int64(second/time.Millisecond)) // prints 1000 + * + * To convert an integer number of units to a Duration, multiply: + * + * seconds := 10 + * fmt.Print(time.Duration(seconds)*time.Second) // prints 10s + */ + Nanosecond = 1, + Microsecond = 1000, + Millisecond = 1000000, + Second = 1000000000, + Minute = 60000000000, + Hour = 3600000000000, +}; diff --git a/frontend/bindings/yellowjacket/backend/autotagservice/index.ts b/frontend/bindings/yellowjacket/backend/autotagservice/index.ts new file mode 100644 index 0000000..2d8d648 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/autotagservice/index.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export type { + AlignmentView, + ApplyResultView, + CandidateView, + FailureView, + LocalTrackView, + PendingItem, + ScoreBreakdownView, + ScoreView, + SearchHitView +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/autotagservice/models.ts b/frontend/bindings/yellowjacket/backend/autotagservice/models.ts new file mode 100644 index 0000000..2c4ae9d --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/autotagservice/models.ts @@ -0,0 +1,187 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * AlignmentView mirrors autotag.TrackAlignment. LocalIndex of -1 + * means "candidate has this track, folder doesn't" (status=missing). + */ +export interface AlignmentView { + "localIndex": number; + "localTitle": string; + "localLengthMillis": number; + "candidatePosition": number; + "candidateDiscNumber": number; + "candidateTitle": string; + "candidateMbid": string; + "candidateLength": number; + "titleScore": number; + "lengthDeltaMs": number; + "trackNumberOk": boolean; + "status": string; +} + +/** + * ApplyResultView mirrors autotag.ApplyResult for the frontend. + */ +export interface ApplyResultView { + "groupKey": string; + "succeeded": number; + "failed": number; + "failures": FailureView[] | null; +} + +/** + * CandidateView mirrors autotag.Candidate + its alignments. The + * CoverArtURL is populated eagerly only for the top-ranked + * candidate (eager network fetch); the frontend calls + * GetCandidateCoverArt to lazy-load art for the others when the + * user selects them. Date is this release's own date (re-issue + * year for remasters); OriginalDate is the release-group's + * first-release-date (the original album year). + */ +export interface CandidateView { + "releaseMbid": string; + "releaseGroupMbid": string; + "title": string; + "artistCredit": string; + "date": string; + "originalDate": string; + "country": string; + "status": string; + "primaryType": string; + "trackCount": number; + "score": number; + "breakdown": ScoreBreakdownView; + "source": string; + "provenance": string; + "coverArtUrl": string; + "alignments": AlignmentView[] | null; +} + +/** + * FailureView carries one failed track for the frontend's toast. + */ +export interface FailureView { + "filePath": string; + "error": string; +} + +/** + * LocalTrackView mirrors autotag.LocalTrack. + */ +export interface LocalTrackView { + "audioFileId": number; + "filePath": string; + "title": string; + "artist": string; + "trackNumber": number; + "discNumber": number; + "lengthMillis": number; + "recordingMbid": string; +} + +/** + * PendingItem is a projection of tagging_items that's safe to hand + * to the frontend. Score is dereferenced to 0 when NULL so TS sees + * a plain number. + */ +export interface PendingItem { + "groupKey": string; + "libraryId": number; + "libraryName": string; + + /** + * FolderSubPath is the album folder's path relative to its + * library root (e.g. "Beatles/Abbey Road"). Empty when the + * path can't be derived (no audio files yet, missing library + * row, etc.). Populated by ListPendingFolders and + * GetPendingFolder; not present on rows returned by other + * list/get queries that haven't been extended. + */ + "folderSubPath": string; + "trackCount": number; + "albumName": string; + "albumArtist": string; + "discNumber": number; + "bestMatchReleaseMbid": string; + "score": number; + "status": string; + + /** + * Synthetic marks a group SplitMixedFolder carved out of a + * bigger folder by matching tags rather than a directory — the + * review UI labels these distinctly since several may share the + * same FolderSubPath. + */ + "synthetic": boolean; + + /** + * LikelyMixedBag is a cheap SQL-side approximation of autotag. + * IsMixedBag, computed for the whole library in one pass by + * ListPendingFolders (see ListLikelyMixedBagGroupKeys) rather + * than hydrating every group's tracks in Go. It's a badge hint, + * not a guarantee — ScoreView.MixedBag (computed from the real + * track list when a folder is opened) is the authoritative check + * that gates the SplitMixedFolder action itself. + */ + "likelyMixedBag": boolean; +} + +/** + * ScoreBreakdownView mirrors autotag.ScoreBreakdown for display. + * Each field is in [0, 1]. + */ +export interface ScoreBreakdownView { + "titleAvg": number; + "lengthAvg": number; + "artistFit": number; + "albumFit": number; + "trackCountFit": number; + "releaseMeta": number; + "evidence": number; +} + +/** + * ScoreView is the Wails-friendly projection of autotag.GroupScore. + */ +export interface ScoreView { + "groupKey": string; + "localTracks": LocalTrackView[] | null; + "candidates": CandidateView[] | null; + + /** + * Recommendation is the qualitative confidence tier for the + * ranked list: "none", "low", "medium", or "strong". Unlike the + * raw score it accounts for ambiguity (a rival release group + * scoring nearly as high) and alignment defects. + */ + "recommendation": string; + + /** + * MixedBag is true when this group's tracks look like an + * unrelated pile rather than one release (autotag.IsMixedBag) — + * the review UI offers SplitMixedFolder when set. Always false + * for a group that's already Synthetic; a split group doesn't + * get split again. + */ + "mixedBag": boolean; + + /** + * Synthetic mirrors PendingItem.Synthetic for the currently + * open group. + */ + "synthetic": boolean; +} + +/** + * SearchHitView is one in-app MusicBrainz search result surfaced to + * the review UI. Kind is "releasegroup" or "recording" so the + * frontend knows which resolve path SelectSearchCandidate must take. + */ +export interface SearchHitView { + "mbid": string; + "kind": string; + "title": string; + "artist": string; + "detail": string; +} diff --git a/frontend/bindings/yellowjacket/backend/autotagservice/service.ts b/frontend/bindings/yellowjacket/backend/autotagservice/service.ts new file mode 100644 index 0000000..8b0159a --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/autotagservice/service.ts @@ -0,0 +1,283 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service is the Wails-bound surface for the review UI. All + * exported methods become TypeScript stubs that the frontend calls. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as jobs$0 from "../jobs/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * AckLibraryWarning records that the user has seen the first- + * time-apply irreversibility warning for this library. + */ +export function AckLibraryWarning(libraryID: number): $CancellablePromise { + return $Call.ByID(1523853598, libraryID); +} + +/** + * Apply runs the apply pipeline for the given group + chosen + * release MBID. Uses the cached candidate list from the last + * GetCandidates / GetCandidatesForPasteURL call so the user + * always applies the exact release they saw in the UI. + * Empty releaseMBID picks the top-scored candidate. + */ +export function Apply(groupKey: string, releaseMBID: string): $CancellablePromise<$models.ApplyResultView | null> { + return $Call.ByID(2015906074, groupKey, releaseMBID); +} + +/** + * ApplyAsync dispatches an Apply for the given group in a + * background goroutine and returns immediately. Progress is + * reported to the frontend via three Wails events: + * + * - AutotagApplyStarted {groupKey, total} + * - AutotagApplyProgress {groupKey, current, total, succeeded, failed} + * - AutotagApplyFinished {groupKey, succeeded, failed, error} + * + * The candidate list and locals are snapshot synchronously before + * returning so a concurrent GetCandidates can't shuffle the + * selected release out from under the running job. Returns + * ErrApplyInFlight when a job for the same groupKey is already + * running; otherwise nil. Job-level errors land in the Finished + * event payload, not the return value. + */ +export function ApplyAsync(groupKey: string, releaseMBID: string): $CancellablePromise { + return $Call.ByID(2246879134, groupKey, releaseMBID); +} + +/** + * ClearCompletedEntries marks every confirmed item in the given + * library (or all libraries when libraryID = 0) as cleared. The + * rows stay in the table — so a re-scan of the same folder + * won't bounce them back into 'pending' — but they no longer + * appear in the review queue. Returns the number of rows + * affected so the frontend can show a quick toast. + */ +export function ClearCompletedEntries(libraryID: number): $CancellablePromise { + return $Call.ByID(1606786204, libraryID); +} + +/** + * GetCandidateCoverArt is the lazy-load entrypoint for cover art + * when the user picks a non-top candidate. Goes through the + * CAA-only chain (disk cache → network) — never the local + * library-by-name index, since that index conflates embedded ID3 + * art with externally-fetched art and the embedded path produces + * stale or wrong-version covers for autotag review. + */ +export function GetCandidateCoverArt(releaseMBID: string, releaseGroupMBID: string): $CancellablePromise { + return $Call.ByID(1410264131, releaseMBID, releaseGroupMBID); +} + +/** + * GetCandidates runs the scorer for a group and returns the full + * candidate list (ordered best-first) along with the local tracks. + * Caches the scored candidate list so Apply can operate on the + * exact release the user saw — even if a concurrent rescore would + * have shuffled the ranking. Also writes the top candidate's + * score back to the tagging_items row so the sidebar's match-% + * pill and the score-desc list sort reflect the freshly-computed + * ranking instead of the scanner's pre-review estimate. + */ +export function GetCandidates(groupKey: string): $CancellablePromise<$models.ScoreView | null> { + return $Call.ByID(873001524, groupKey); +} + +/** + * GetCandidatesForPasteURL resolves a MusicBrainz release-or- + * release-group URL into a ScoreView by fetching the pasted entity + * from MB, running track alignment against the local group, and + * prepending a fully-scored candidate to the existing list. The + * pasted candidate's provenance is "paste" so the UI can label it. + */ +export function GetCandidatesForPasteURL(groupKey: string, mbReleaseURL: string): $CancellablePromise<$models.ScoreView | null> { + return $Call.ByID(2470223543, groupKey, mbReleaseURL); +} + +/** + * GetLocalCoverArt returns a data-URI for the artwork associated + * with the group's local files, so the review UI can show "what the + * folder already looks like" beside the fetched candidate art. + * It first checks each file for embedded ID3/Vorbis pictures, then + * falls back to a sidecar cover image (cover.jpg, folder.png, …) + * sitting in the album directory. Untagged folders carry one or the + * other far more often than they have a DB release-group cover, so we + * read the files directly. Returns "" when nothing is found. + */ +export function GetLocalCoverArt(groupKey: string): $CancellablePromise { + return $Call.ByID(2603982821, groupKey); +} + +/** + * GetNextPending returns the next pending tagging item after the + * internal cursor, or nil when the queue is empty. Advances the + * cursor on success. + */ +export function GetNextPending(): $CancellablePromise<$models.PendingItem | null> { + return $Call.ByID(1751929138); +} + +/** + * GetPendingFolder returns the tagging item for a specific group + * key — used by the sidebar after the user clicks an entry, so + * the header can render even when the cursor-based queue iteration + * hasn't visited that key yet. + */ +export function GetPendingFolder(groupKey: string): $CancellablePromise<$models.PendingItem | null> { + return $Call.ByID(3455545217, groupKey); +} + +/** + * LeaveAsIs is the "local tags are correct, don't rescore" + * acknowledgement — bumps status to 'confirmed' without touching + * any files. + */ +export function LeaveAsIs(groupKey: string): $CancellablePromise { + return $Call.ByID(3897348801, groupKey); +} + +/** + * ListPendingFolders returns every tagging item in the given + * library (or all libraries when libraryID = 0) that has not been + * explicitly cleared. Includes items in all review states — + * pending, skipped, and confirmed — so the sidebar can group + * them into sections (the user wanted skipped/completed to stay + * visible at the bottom rather than vanish). Sorted by score + * descending so the UI surfaces the most confident matches first + * (NULL scores fall to the bottom); the frontend re-groups by + * status for display. + */ +export function ListPendingFolders(libraryID: number): $CancellablePromise<$models.PendingItem[] | null> { + return $Call.ByID(617511590, libraryID); +} + +/** + * RetagGroup flips a group back to 'pending' so the user can + * re-review after an apply or skip. Drops the durably-cached + * candidate list too, so the next open recomputes against fresh + * MusicBrainz data rather than reusing the stale stored result. + */ +export function RetagGroup(groupKey: string): $CancellablePromise { + return $Call.ByID(2552507548, groupKey); +} + +/** + * SearchCandidates runs an in-app MusicBrainz search — the "suggest a + * new candidate" escape hatch when the automatic cascade misses. + * kind is "recording" (title + artist, for singletons) or anything + * else (album + artist release-group search). Returns lightweight + * hits; SelectSearchCandidate resolves the picked one into a scored + * candidate. + */ +export function SearchCandidates(kind: string, query: string, artist: string): $CancellablePromise<$models.SearchHitView[] | null> { + return $Call.ByID(4062823000, kind, query, artist); +} + +/** + * SelectSearchCandidate resolves a picked search hit into a fully- + * scored candidate and splices it to the top of the group's candidate + * list — the same shape as the paste-URL path, so Apply works + * unchanged. A "recording" hit is resolved to a representative + * release first. + */ +export function SelectSearchCandidate(groupKey: string, kind: string, mbid: string): $CancellablePromise<$models.ScoreView | null> { + return $Call.ByID(2540832563, groupKey, kind, mbid); +} + +/** + * SetJobRegistry wires the background job registry so an apply reports + * progress and offers a cancel like every other long-running operation. + * + * Before this, apply was a bare goroutine whose progress lived in a + * component field that navigation discarded, with no cancel and no + * record of where it stopped (errors.C3). Everything routed through the + * registry gets progress, cancel and the global indicator for free; the + * three subsystems that lacked them were the three that were not + * registered. + */ +export function SetJobRegistry(reg: jobs$0.Registry | null): $CancellablePromise { + return $Call.ByID(3755409662, reg); +} + +/** + * Skip marks the current group as skipped — it stays in the + * queue but renders in the "Skipped" section at the bottom of + * the sidebar, separate from pending items, so the user can + * revisit it later if they change their mind. + */ +export function Skip(groupKey: string): $CancellablePromise { + return $Call.ByID(1248497081, groupKey); +} + +/** + * SplitMixedFolder is the "this folder is a pile of unrelated + * tracks" escape hatch: it partitions the group's local tracks via + * autotag.SplitPlan — tag-matched sub-albums (see + * autotag.ClusterByAlbumArtist) plus a one-track cluster for every + * track that didn't share an (album, album-artist) pair with + * anything else — and carves each piece out into its own synthetic + * tagging group, reassigning just those audio_files rows (no files + * move on disk). Every track leaves the original group; nothing is + * left behind to be scored as "extra tracks" of whichever piece + * happens to match first. The synthetic groups are scored with + * relaxed missing-track handling (rank.go, recommend.go), since + * they're expected to be an incomplete subset of whatever release + * they belong to. + * + * Returns the resulting PendingItems — the leftover original group + * first (if anything remains in it), then the new synthetic groups + * — so the frontend can splice them into the sidebar without a full + * reload. Errors with errNothingToSplit when the folder is already + * one coherent unit (SplitPlan produces a single cluster covering + * every track); callers should treat that as "nothing to show", not + * a failure. + */ +export function SplitMixedFolder(groupKey: string): $CancellablePromise<$models.PendingItem[] | null> { + return $Call.ByID(3640008069, groupKey); +} + +/** + * StartAutotagQueue resets the cursor for a new review session + * and kicks off the background prefetch worker so sidebar pills + * populate while the user reviews the first folder. Cancels any + * previous prefetch (library filter may have changed). Pass + * libraryID=0 to cover all libraries. + */ +export function StartAutotagQueue(libraryID: number): $CancellablePromise { + return $Call.ByID(568310002, libraryID); +} + +/** + * StartBackgroundPrefetch kicks off (or restarts) the prefetch + * worker over all libraries. Called from app startup hooks so + * unscored pending items get their match-% backfilled while the + * user does other things — by the time they navigate to the + * autotag page, the sidebar pills are already populated. + * Idempotent: items with an existing score are skipped, so it's + * safe to call after every library scan and on every app launch. + */ +export function StartBackgroundPrefetch(): $CancellablePromise { + return $Call.ByID(428378477); +} + +/** + * WritesInFlight reports whether an apply is currently rewriting tags + * on disk. Quitting mid-apply leaves a folder half-retagged, so the app + * asks before closing (errors.p4). + */ +export function WritesInFlight(): $CancellablePromise { + return $Call.ByID(1561223347); +} diff --git a/frontend/bindings/yellowjacket/backend/config/config.ts b/frontend/bindings/yellowjacket/backend/config/config.ts new file mode 100644 index 0000000..75f97fa --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/config/config.ts @@ -0,0 +1,236 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Config represents the application configuration. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as download$0 from "../download/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as tracklist$0 from "../tracklist/models.js"; + +/** + * GetDefaultPage returns the view the app opens to on launch. + */ +export function GetDefaultPage(): $CancellablePromise { + return $Call.ByID(3209119019); +} + +/** + * GetDownloadPreferences returns the configured auto-download + * guardrails. + */ +export function GetDownloadPreferences(): $CancellablePromise { + return $Call.ByID(4104118319); +} + +/** + * GetFavoritesIconStyle returns the configured icon style. + */ +export function GetFavoritesIconStyle(): $CancellablePromise { + return $Call.ByID(3222927230); +} + +/** + * GetFavoritesPlaylistID returns the configured default playlist ID. + */ +export function GetFavoritesPlaylistID(): $CancellablePromise { + return $Call.ByID(4116868495); +} + +/** + * GetLibraryDirectory returns the currently configured library directory path. + */ +export function GetLibraryDirectory(): $CancellablePromise { + return $Call.ByID(2123565657); +} + +/** + * GetPinDefaultPlaylist returns whether the default playlist + * is pinned to the top of the playlist view. + */ +export function GetPinDefaultPlaylist(): $CancellablePromise { + return $Call.ByID(3818283301); +} + +/** + * GetQueueFallback returns what plays, if anything, once the queue + * runs out. + */ +export function GetQueueFallback(): $CancellablePromise { + return $Call.ByID(1504773860); +} + +/** + * GetScanConcurrency returns the configured scan concurrency mode. + */ +export function GetScanConcurrency(): $CancellablePromise { + return $Call.ByID(3233312385); +} + +/** + * GetShortcuts returns the current shortcut bindings map. + */ +export function GetShortcuts(): $CancellablePromise<{ [_ in string]?: string } | null> { + return $Call.ByID(1616717634); +} + +/** + * GetThemeAccentColor returns the configured accent colour. + */ +export function GetThemeAccentColor(): $CancellablePromise { + return $Call.ByID(4264374057); +} + +/** + * GetThemeBackgroundShade returns the configured background shade. + */ +export function GetThemeBackgroundShade(): $CancellablePromise { + return $Call.ByID(578772653); +} + +/** + * GetTrackListColumns returns the configured track-list columns. + */ +export function GetTrackListColumns(): $CancellablePromise { + return $Call.ByID(3426289065); +} + +/** + * Load reads and parses the config file from disk. + */ +export function Load(): $CancellablePromise { + return $Call.ByID(2408891113); +} + +/** + * ResetShortcuts resets all shortcuts to defaults. + */ +export function ResetShortcuts(): $CancellablePromise { + return $Call.ByID(3197898083); +} + +/** + * Save writes the config to disk. Refuses to write if the config + * was never successfully loaded — prevents overwriting user config + * with defaults during abnormal startup/shutdown sequences. + */ +export function Save(): $CancellablePromise { + return $Call.ByID(1988945736); +} + +/** + * SetDefaultPage validates and saves a new launch page. + */ +export function SetDefaultPage(page: string): $CancellablePromise { + return $Call.ByID(2714957423, page); +} + +/** + * SetDownloadPreferences saves new auto-download guardrails. This only + * persists them; the download package cannot depend on config (config + * already depends on download for UserConfig), so making the change + * live without a restart is the caller's job — the frontend settings + * save calls this and download.Service.SetPreferences in the same + * action, and app.go's initDownloadRuntime applies the saved value to + * the running Manager at startup. + */ +export function SetDownloadPreferences(prefs: download$0.AutoDownloadPrefs): $CancellablePromise { + return $Call.ByID(2655203755, prefs); +} + +/** + * SetFavoritesIconStyle validates and saves a new icon style. + */ +export function SetFavoritesIconStyle(style: string): $CancellablePromise { + return $Call.ByID(812590378, style); +} + +/** + * SetFavoritesPlaylistID saves a new default playlist ID. + */ +export function SetFavoritesPlaylistID(id: number): $CancellablePromise { + return $Call.ByID(2117079163, id); +} + +/** + * SetLibraryDirectory validates and saves a new library directory, + * then emits the LibraryConfigChanged event so listeners (e.g. the + * Library scanner) can react. + */ +export function SetLibraryDirectory(dir: string): $CancellablePromise { + return $Call.ByID(1352505021, dir); +} + +/** + * SetPinDefaultPlaylist saves whether the default playlist + * should be pinned to the top of the playlist view. + */ +export function SetPinDefaultPlaylist(pin: boolean): $CancellablePromise { + return $Call.ByID(372446849, pin); +} + +/** + * SetQueueFallback validates and saves a new queue-fallback mode. + */ +export function SetQueueFallback(mode: string): $CancellablePromise { + return $Call.ByID(2824429312, mode); +} + +/** + * SetScanConcurrency validates and saves a new scan concurrency + * mode. The change takes effect on the next scan. + */ +export function SetScanConcurrency(mode: string): $CancellablePromise { + return $Call.ByID(742893445, mode); +} + +/** + * SetShortcut saves a single shortcut binding. + */ +export function SetShortcut(action: string, key: string): $CancellablePromise { + return $Call.ByID(275130705, action, key); +} + +/** + * SetShortcuts saves the entire shortcut bindings map. + */ +export function SetShortcuts(bindings: { [_ in string]?: string } | null): $CancellablePromise { + return $Call.ByID(4073898118, bindings); +} + +/** + * SetThemeAccentColor validates and saves a new accent colour. + */ +export function SetThemeAccentColor(color: string): $CancellablePromise { + return $Call.ByID(3536665861, color); +} + +/** + * SetThemeBackgroundShade validates and saves a new background shade. + */ +export function SetThemeBackgroundShade(shade: string): $CancellablePromise { + return $Call.ByID(2709290065, shade); +} + +/** + * SetTrackListColumns validates and saves a new column layout. + */ +export function SetTrackListColumns(columns: tracklist$0.Column[] | null): $CancellablePromise { + return $Call.ByID(4226159685, columns); +} + +/** + * Validate returns errors if there is a breaking issue with the config. + */ +export function Validate(): $CancellablePromise { + return $Call.ByID(3087982155); +} diff --git a/frontend/wailsjs/go/home/Service.js b/frontend/bindings/yellowjacket/backend/config/index.ts old mode 100755 new mode 100644 similarity index 52% rename from frontend/wailsjs/go/home/Service.js rename to frontend/bindings/yellowjacket/backend/config/index.ts index 0c41620..ec844f7 --- a/frontend/wailsjs/go/home/Service.js +++ b/frontend/bindings/yellowjacket/backend/config/index.ts @@ -1,7 +1,7 @@ -// @ts-check // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -export function GetShelves() { - return window['go']['home']['Service']['GetShelves'](); -} +import * as Config from "./config.js"; +export { + Config +}; diff --git a/frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/index.ts b/frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/index.ts new file mode 100644 index 0000000..ce842bb --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Library +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/models.ts b/frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/models.ts new file mode 100644 index 0000000..eee58ac --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/database/sql/sqlcgen/models.ts @@ -0,0 +1,10 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export interface Library { + "ID": number; + "Name": string; + "Path": string; + "CreatedAt": string; + "AutotagWarningAcked": number; +} diff --git a/frontend/bindings/yellowjacket/backend/download/index.ts b/frontend/bindings/yellowjacket/backend/download/index.ts new file mode 100644 index 0000000..9cbc1ec --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/download/index.ts @@ -0,0 +1,38 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Entity, + Format, + Kind, + Protocol, + RequestScope, + RequestState, + State +} from "./models.js"; + +export type { + AutoDownloadPrefs, + Candidate, + CandidateFile, + Caps, + Config, + Descriptor, + DownloadItem, + DownloadView, + ExpectedTrack, + Field, + MatchScore, + QualityScore, + Reconciler, + Request, + RequestInput, + SearchRequest, + StartResult, + Summary +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/download/models.ts b/frontend/bindings/yellowjacket/backend/download/models.ts new file mode 100644 index 0000000..7092151 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/download/models.ts @@ -0,0 +1,732 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * AutoDownloadPrefs gates and scores what AutoPickable may choose + * without asking. Zero values are permissive: no size window and no + * format restriction. + */ +export interface AutoDownloadPrefs { + /** + * MinSizeMB and MaxSizeMB bound what auto-pick will grab. Zero + * means no bound on that side. A candidate outside the window is + * filtered out of auto-pick entirely, not merely scored down — a + * tiny "sampler" torrent or a boxset ten times the expected size is + * usually the wrong thing entirely, not a worse copy of the right + * thing. + */ + "minSizeMb": number; + "maxSizeMb": number; + + /** + * PreferredSizeMB nudges the score toward a target size within the + * min/max window (a lossless rip and a heavily-padded lossless rip + * can both pass the window). Zero disables the nudge; sizeFit then + * returns a neutral value that does not affect ranking. + */ + "preferredSizeMb": number; + + /** + * AllowedFormats restricts auto-pick to candidates whose audio + * files are all in one of these formats. Empty means no + * restriction. + */ + "allowedFormats": Format[] | null; +} + +/** + * Candidate is one acquirable thing a provider found: a Soulseek user's + * folder, a torrent, a YouTube playlist. Providers fill the descriptive + * fields; the ranker fills Match, Quality and Score. + */ +export interface Candidate { + /** + * ID is unique within the provider that produced it, and is what + * gets handed back to Grab. + */ + "id": string; + "providerId": number; + "kind": Kind; + + /** + * Protocol determines which transport can fetch this. + */ + "protocol": Protocol; + + /** + * Descriptive. + */ + "title": string; + "artist"?: string; + + /** + * peer username, indexer name, channel + */ + "origin"?: string; + "files": CandidateFile[] | null; + "totalSize": number; + + /** + * Health is the provider's own availability signal, normalized to + * 0..1: seeder count for torrents, free upload slots and queue + * length for Soulseek. 0.5 when the provider has no signal. + */ + "health": number; + + /** + * Scores, filled by the ranker. + */ + "match": MatchScore; + "quality": QualityScore; + "score": number; +} + +/** + * CandidateFile is one file inside a candidate. Soulseek and torrent + * results give paths and sizes but no tags, so Format and duration are + * inferred from the path and size where possible. + */ +export interface CandidateFile { + "path": string; + "size": number; + "format": Format; + + /** + * kbps, 0 when unknown + */ + "bitrate"?: number; + "isAudio": boolean; + + /** + * expected track position + */ + "matchedTo"?: number; +} + +/** + * Caps declares which roles a provider fills and which optional + * behaviours it supports. The frontend renders controls from this + * rather than switching on Kind, so a provider that gains resume + * support later needs no frontend change. + */ +export interface Caps { + /** + * Roles. + */ + "canSearch": boolean; + "canTransport": boolean; + "canDelegate": boolean; + + /** + * CanList marks a provider that keeps a persistent wanted list of + * its own, which the reconciler mirrors this app's list into. + */ + "canList": boolean; + + /** + * Optional behaviours. + */ + "canResume": boolean; + "canCancel": boolean; + "reportsSize": boolean; + + /** + * Protocols this provider can transport. Empty for providers that + * only fetch their own search results. + */ + "transports": Protocol[] | null; +} + +/** + * Config is a provider's stored settings. Secret values are not held + * here — they live in the secrets store keyed by provider ID, so a + * config blob can be logged or shown in the UI without redaction. + */ +export interface Config { + "id": number; + "kind": Kind; + "name": string; + "enabled": boolean; + "priority": number; + "settings": { [_ in string]?: string } | null; + + /** + * SetSecrets names which of the descriptor's secret fields already + * have a stored value, without exposing it. Populated only when a + * Config is built for the frontend (see Service.withSecretFlags); + * empty when read from or written to the store. + */ + "setSecrets"?: { [_ in string]?: boolean } | null; +} + +/** + * Descriptor is the static, instance-independent description of a + * provider kind: what it is called, what it can do, and which settings + * it needs. The settings page renders its form from this, so a new + * provider gets a config UI without any frontend work. + */ +export interface Descriptor { + "kind": Kind; + "name": string; + + /** + * Summary is one line explaining what connecting this gets you. + */ + "summary": string; + + /** + * Caps are the kind's inherent capabilities, before configuration. + */ + "caps": Caps; + + /** + * Fields are the settings the user must supply. + */ + "fields": Field[] | null; + + /** + * RequiresExternal names the software the user must run themselves + * (a slskd daemon, a Lidarr instance), or is empty for providers + * that need nothing but a binary on PATH. + */ + "requiresExternal"?: string; +} + +/** + * DownloadItem is one grab attempt, as stored. + * + * deliberate: distinguishes it from download.Download (the attempt) and + * download.Request (the durable record) at every call site, which a bare + * "Item" would not. + */ +export interface DownloadItem { + "id": string; + "downloadId": string; + "providerId": number; + "transportId"?: number; + "externalId"?: string; + "candidate": Candidate; + "state": State; + "bytesDone": number; + "bytesTotal": number; + "error"?: string; + "createdAt": string; + "updatedAt": string; +} + +/** + * DownloadView is one row of the downloads list. + * + * would stop reading as "one row of the Downloads list" the moment this + * package also has a Requests list — see RequestInput/Request nearby. + */ +export interface DownloadView { + "id": string; + + /** + * Anchors. Any may be empty; all empty means free-text. + */ + "releaseMbid"?: string; + "releaseGroupMbid"?: string; + + /** + * RecordingMBID anchors a single-track download. Its Expected holds + * exactly that one track, which is what lets a track download be + * scored — and therefore auto-picked — on the same footing as an + * album. + */ + "recordingMbid"?: string; + + /** + * RequestID links back to the durable Request row this download was + * raised for or attached to, or 0 for a free-text download with + * nothing stable to attach to. The reconciler and manual anchored + * downloads both write the outcome back through it. + */ + "requestId"?: number; + + /** + * Source records where the download came from, for the downloads + * list. Empty means "manual". + */ + "source"?: string; + + /** + * Display and query text. Artist/Album are what searches are built + * from; Query overrides them when the user typed something raw. + */ + "artist": string; + "album": string; + "query"?: string; + + /** + * Expected is the tracklist the anchor resolves to, used for + * completeness scoring and for the autotag match at import. Empty + * for free-text downloads. + */ + "expected"?: ExpectedTrack[] | null; + + /** + * LibraryID is the library imported files belong to. + */ + "libraryId": number; + "createdAt": string; + "state": State; + "error"?: string; + "items": DownloadItem[] | null; +} + +/** + * Entity says what a request's MBID names, and is the only type + * distinction the durable request list makes. + */ +export enum Entity { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Request entity types. + * + * EntityArtist is a subscription rather than a thing to fetch: it + * is never satisfied, and each reconcile expands the artist's + * discography into child requests. + */ + EntityArtist = "artist", + + /** + * EntityReleaseGroup is an album in the abstract — any release of + * it satisfies the request, which is what a user means by "I want + * this album". + */ + EntityReleaseGroup = "release-group", + + /** + * EntityRelease is one specific edition, used when the user picked + * a particular pressing. + */ + EntityRelease = "release", + + /** + * EntityRecording is a single track. + */ + EntityRecording = "recording", +}; + +/** + * ExpectedTrack is one track of the release the user asked for. + */ +export interface ExpectedTrack { + "position": number; + "discNumber": number; + "title": string; + "artist": string; + "lengthMillis": number; +} + +/** + * Field describes one provider setting for the settings form. + */ +export interface Field { + "key": string; + "label": string; + "placeholder"?: string; + "help"?: string; + + /** + * Secret marks a value stored in the secrets store rather than the + * provider config row, and rendered as a password input. + */ + "secret": boolean; + + /** + * Path marks a value that names a local filesystem directory, so + * the settings form can offer a native folder picker beside the + * text input rather than making the user type or paste it. + */ + "path": boolean; + "required": boolean; + "default"?: string; +} + +/** + * Format is a normalized audio container/codec name. + */ +export enum Format { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Audio formats, ordered by the quality ranking in formatRank. + */ + FormatUnknown = "", + FormatFLAC = "flac", + FormatALAC = "alac", + FormatWAV = "wav", + FormatMP3 = "mp3", + FormatAAC = "aac", + FormatOGG = "ogg", + FormatOpus = "opus", + FormatWMA = "wma", +}; + +/** + * Kind identifies a provider implementation. It is stored in the + * database and used to look up the constructor in the registry, so + * values are stable strings and never renamed. + */ +export enum Kind { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Provider kinds. + */ + KindSlskd = "slskd", + KindYtDlp = "yt-dlp", + KindLidarr = "lidarr", + KindProwlarr = "prowlarr", + KindQBittorrent = "qbittorrent", + KindSABnzbd = "sabnzbd", + + /** + * KindFake is an in-memory provider used by tests. It is never + * offered in the UI. + */ + KindFake = "fake", +}; + +/** + * MatchScore answers "is this the release the user asked for?" It is + * deliberately separate from QualityScore: a perfect match at 128kbps + * and a mediocre match in FLAC are different failures, and collapsing + * them into one number makes the ranking impossible to explain. + */ +export interface MatchScore { + /** + * Overall is 0..1. + */ + "overall": number; + + /** + * filenames vs expected titles + */ + "titleFit": number; + + /** + * path/origin vs expected artist + */ + "artistFit": number; + + /** + * folder name vs album title + */ + "albumFit": number; + + /** + * audio files vs expected count + */ + "completeness": number; + + /** + * Anchored records whether an MBID drove this score. Unanchored + * matches are capped, because there is nothing to be right about. + */ + "anchored": boolean; +} + +/** + * Protocol is how a candidate's bytes are moved. Search-only providers + * report it so the pipeline can pick a compatible transport; providers + * that transport their own results use ProtocolDirect. + */ +export enum Protocol { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Transport protocols. + * + * ProtocolDirect means the finding provider also does the fetch. + */ + ProtocolDirect = "direct", + ProtocolTorrent = "torrent", + ProtocolUsenet = "usenet", +}; + +/** + * QualityScore answers "is this a good copy?". + */ +export interface QualityScore { + /** + * Overall is 0..1. + */ + "overall": number; + + /** + * FLAC > V0 > 320 > lower + */ + "formatRank": number; + "bitrate": number; + + /** + * seeders, free slots + */ + "health": number; + + /** + * user's per-provider preference + */ + "priority": number; + + /** + * closeness to the preferred download size + */ + "sizeFit": number; + + /** + * Mixed marks a candidate whose files are not all the same format, + * which usually means a hand-assembled folder rather than a rip. + */ + "mixed": boolean; +} + +/** + * Reconciler works the request list. + */ +export interface Reconciler { +} + +/** + * Request is one row of the durable request list. + */ +export interface Request { + "id": number; + "mbid": string; + "entity": Entity; + "libraryId": number; + + /** + * Artist and Title are display cache only. Matching always uses + * the MBID. + */ + "artist": string; + "title": string; + "scope": RequestScope; + + /** + * Secondary includes compilations, live albums and remixes in an + * artist request's expansion. + */ + "secondary": boolean; + "state": RequestState; + + /** + * ParentID is set on requests the reconciler derived from an artist + * subscription. A request the user pinned directly has none, so + * removing the artist leaves it alone. + */ + "parentId"?: number; + "attempts": number; + "lastError"?: string; + "lastTriedAt"?: string; + "nextTryAt"?: string; + + /** + * ExternalIDs maps provider row ID (as a string, because JSON + * object keys are strings) to that provider's own identifier for + * this request. Only set for providers that keep a persistent + * list of their own. + */ + "externalIds"?: { [_ in string]?: string } | null; + "createdAt": string; + "updatedAt": string; +} + +/** + * RequestInput is what the frontend submits to request something. It + * is one MBID and the type of thing it names, because that is + * genuinely all a durable request is. + */ +export interface RequestInput { + "mbid": string; + "entity": string; + "libraryId": number; + + /** + * Artist and Title are display text only, and optional: the + * reconciler fills them in from the catalog when the caller has + * nothing but an MBID. + */ + "artist": string; + "title": string; + + /** + * Scope and Secondary apply to artist requests. + */ + "scope": string; + "secondary": boolean; +} + +/** + * RequestScope applies to artist requests only. + */ +export enum RequestScope { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Artist request scopes. + * + * ScopeFuture takes only releases first published after the artist + * was added. Default, because subscribing to an artist should not + * silently queue their entire back catalogue. + */ + ScopeFuture = "future", + + /** + * ScopeAll backfills the whole discography as well. + */ + ScopeAll = "all", +}; + +/** + * RequestState is where a request sits. There is deliberately no + * "failed": an attempt can fail, a request cannot. A request that has + * tried and not found anything is still wanted, with attempts and + * last_error recording why it is taking a while. + */ +export enum RequestState { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Request states. + * + * RequestStateWanted is the active state: due for another attempt + * when its backoff elapses. + */ + RequestStateWanted = "wanted", + + /** + * RequestStateSatisfied means the library owns it. How it got + * there — downloaded here, ripped, bought elsewhere — does not + * matter. + */ + RequestStateSatisfied = "satisfied", + + /** + * RequestStatePaused is the user saying "keep this on the list but + * stop trying". + */ + RequestStatePaused = "paused", +}; + +/** + * SearchRequest is what the frontend submits to start a download. + */ +export interface SearchRequest { + "libraryId": number; + "releaseMbid": string; + "releaseGroupMbid": string; + "artist": string; + "album": string; + "query": string; + "expected": ExpectedTrack[] | null; +} + +/** + * StartResult is what the picker needs after a search. + */ +export interface StartResult { + "downloadId": string; + "candidates": Candidate[] | null; + + /** + * AutoPicked reports that the pipeline already chose and is + * downloading, so the picker should show progress rather than a + * list of choices. + */ + "autoPicked": boolean; +} + +/** + * State is the lifecycle position of a download item. + */ +export enum State { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Download item states. Searching through Importing are live; + * Complete, Cancelled and Failed are terminal. + */ + StateSearching = "searching", + StateFound = "found", + StateQueued = "queued", + StateGrabbing = "grabbing", + StateVerifying = "verifying", + StateTagging = "tagging", + StateImporting = "importing", + StateComplete = "complete", + StateCancelled = "cancelled", + StateFailed = "failed", +}; + +/** + * Summary reports what a pass did, for logging and for the UI. + */ +export interface Summary { + /** + * Expanded is how many child requests artist subscriptions produced. + */ + "expanded": number; + + /** + * Satisfied is how many requests the library turned out to own. + */ + "satisfied": number; + + /** + * Attempted is how many requests were searched for. + */ + "attempted": number; + + /** + * Started is how many of those found a clear enough winner to + * download unattended. + */ + "started": number; + + /** + * Synced is how many requests were pushed to an external list. + */ + "synced": number; + + /** + * Waiting is how many requests are on the list and still being + * looked for. A pass that did nothing is the normal case, and the + * UI can only say so honestly if it knows the list was not empty. + */ + "waiting": number; + + /** + * NoProviders reports that nothing could be searched because no + * download client is enabled — the one "nothing happened" the user + * can actually fix. + */ + "noProviders": boolean; +} diff --git a/frontend/bindings/yellowjacket/backend/download/service.ts b/frontend/bindings/yellowjacket/backend/download/service.ts new file mode 100644 index 0000000..41aa704 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/download/service.ts @@ -0,0 +1,207 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service is the frontend-facing surface of the download subsystem. + * Its methods are bound into Wails and called from TypeScript, so + * signatures use plain types and return errors the UI can render. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * AddProvider creates a provider and stores any secret settings + * separately. Secrets arrive in the same map as ordinary settings + * because that is what the form submits; they are split out here and + * never written to the provider row. + */ +export function AddProvider(kind: string, name: string, settings: { [_ in string]?: string } | null): $CancellablePromise { + return $Call.ByID(2479360532, kind, name, settings); +} + +/** + * AddRequest puts something on the request list and asks for a + * reconcile pass, so the user sees something happen rather than + * waiting six hours for the next scheduled one. + */ +export function AddRequest(req: $models.RequestInput): $CancellablePromise { + return $Call.ByID(2621441882, req); +} + +/** + * Cancel aborts a live download. + */ +export function Cancel(downloadID: string): $CancellablePromise { + return $Call.ByID(1726338100, downloadID); +} + +/** + * Candidates returns the ranked candidates of a live download, so the + * picker can be reopened without searching again. + */ +export function Candidates(downloadID: string): $CancellablePromise<$models.Candidate[] | null> { + return $Call.ByID(2436454548, downloadID); +} + +/** + * ClearFinished removes terminal downloads from the list. + */ +export function ClearFinished(): $CancellablePromise { + return $Call.ByID(3757713449); +} + +/** + * ClearSatisfiedRequests drops everything already owned. + */ +export function ClearSatisfiedRequests(): $CancellablePromise { + return $Call.ByID(2681765363); +} + +/** + * DeleteProvider removes a provider and its credentials. + */ +export function DeleteProvider(id: number): $CancellablePromise { + return $Call.ByID(495153424, id); +} + +/** + * ImportExternalRequests adopts a provider's own list — "import the + * artists Lidarr is already monitoring". + */ +export function ImportExternalRequests(providerID: number, libraryID: number): $CancellablePromise { + return $Call.ByID(4055326022, providerID, libraryID); +} + +/** + * IsRequested answers the Explore pages' question — should this album + * show "want" or "wanted?" — without making them load the whole list. + */ +export function IsRequested(mbid: string, libraryID: number): $CancellablePromise { + return $Call.ByID(3695628158, mbid, libraryID); +} + +/** + * ListDownloads returns recent downloads, newest first. + */ +export function ListDownloads(limit: number): $CancellablePromise<$models.DownloadView[] | null> { + return $Call.ByID(2505337995, limit); +} + +/** + * ListProviders returns the user's configured download clients. + */ +export function ListProviders(): $CancellablePromise<$models.Config[] | null> { + return $Call.ByID(913508028); +} + +/** + * ListRequests returns the whole durable request list. + */ +export function ListRequests(): $CancellablePromise<$models.Request[] | null> { + return $Call.ByID(3593452892); +} + +/** + * PauseRequest stops attempts without forgetting the request. + */ +export function PauseRequest(id: number, paused: boolean): $CancellablePromise { + return $Call.ByID(2652842089, id, paused); +} + +/** + * Pick starts the transfer for the candidate the user chose. + */ +export function Pick(downloadID: string, candidateID: string): $CancellablePromise { + return $Call.ByID(40308223, downloadID, candidateID); +} + +/** + * ProviderKinds returns every provider type that can be added, with the + * settings each one needs. The settings page renders its forms from + * this, so a new adapter needs no frontend change. + */ +export function ProviderKinds(): $CancellablePromise<$models.Descriptor[] | null> { + return $Call.ByID(2902376358); +} + +/** + * ReconcileRequests runs a pass now and reports what it did. This + * backs the "check now" button, so it runs synchronously: the user + * pressed it and is waiting for an answer. + */ +export function ReconcileRequests(): $CancellablePromise<$models.Summary> { + return $Call.ByID(593439868); +} + +/** + * RemoveRequest takes something off the list. Removing an artist takes + * its derived albums with it, by cascade; an album the user pinned + * themselves has no parent and survives. + */ +export function RemoveRequest(id: number): $CancellablePromise { + return $Call.ByID(2423963479, id); +} + +/** + * SetPreferences pushes the auto-download guardrails straight into the + * running Manager, without persisting them. Persistence is + * config.Config's job (GetDownloadPreferences/SetDownloadPreferences); + * this package cannot depend on config, since config already depends on + * download for UserConfig. The frontend settings save is expected to + * call the config setter and this method in the same action, the way + * UpdateProvider already achieves "live without a restart" by touching + * storage and the running Manager together. + */ +export function SetPreferences(prefs: $models.AutoDownloadPrefs): $CancellablePromise { + return $Call.ByID(3789717356, prefs); +} + +/** + * SetReconciler wires the request-list loop. Optional: without it the + * request list still stores and lists requests, it just never acts on + * them. + */ +export function SetReconciler(r: $models.Reconciler | null): $CancellablePromise { + return $Call.ByID(2390832784, r); +} + +/** + * StartDownload searches for a release and either auto-picks a clear + * winner or returns ranked candidates for the user to choose from. + * + * When the search carries a MusicBrainz anchor, it also resolves or + * creates the durable Request the anchor names and attaches it, via + * ensureRequest — so a manual download that fails or finds nothing + * right now leaves a durable record behind instead of just vanishing, + * and the reconciler picks it up on its normal schedule exactly as if + * the user had explicitly added it to the request list. + */ +export function StartDownload(req: $models.SearchRequest): $CancellablePromise<$models.StartResult> { + return $Call.ByID(1191306198, req); +} + +/** + * TestProvider backs the "test connection" button. It builds the + * provider from its stored config and asks it to check itself, so the + * result reflects exactly what a real search would use. + */ +export function TestProvider(id: number): $CancellablePromise { + return $Call.ByID(2688601475, id); +} + +/** + * UpdateProvider saves changes to a provider. A secret field left + * blank keeps its stored value rather than clearing it — the form does + * not echo secrets back, so an empty box means "unchanged", not + * "delete". + */ +export function UpdateProvider(id: number, name: string, enabled: boolean, priority: number, settings: { [_ in string]?: string } | null): $CancellablePromise { + return $Call.ByID(42359606, id, name, enabled, priority, settings); +} diff --git a/frontend/bindings/yellowjacket/backend/explore/index.ts b/frontend/bindings/yellowjacket/backend/explore/index.ts new file mode 100644 index 0000000..ebbacfc --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/explore/index.ts @@ -0,0 +1,35 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + ShelfKind +} from "./models.js"; + +export type { + AlbumCompleteFunc, + IndexStatus, + LBSimilarArtist, + LBTopRecording, + LBTopReleaseGroup, + LyricsResult, + MBArtist, + MBRecording, + MBRelease, + MBReleaseGroup, + MBSearchResult, + MBTrack, + MusicBrainzClient, + RateLimiter, + Shelf, + ShelfPage, + ThumbnailRequest, + TierStatus, + TopResult, + TrackLyrics, + TrackThumbnailRequest +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/explore/models.ts b/frontend/bindings/yellowjacket/backend/explore/models.ts new file mode 100644 index 0000000..74d22ef --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/explore/models.ts @@ -0,0 +1,469 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * AlbumCompleteFunc answers "is the local album with this id complete", + * i.e. do its files declare a track total the library actually has. + */ +export type AlbumCompleteFunc = any; + +/** + * IndexStatus is the full index build status, exposed to the frontend. + */ +export interface IndexStatus { + "building": boolean; + "ready": boolean; + + /** + * RFC3339 timestamp of last complete build + */ + "lastBuilt"?: string; + "tiers": TierStatus[] | null; + "artists": number; + "recordings": number; + "releaseGroups": number; + "totalRows": number; +} + +/** + * LBSimilarArtist represents a similar artist from the + * ListenBrainz labs API. + */ +export interface LBSimilarArtist { + "artistMbid": string; + "name": string; + "score": number; +} + +/** + * LBTopRecording represents a popular recording from the + * ListenBrainz popularity API. + * + * JSON tags use camelCase for Wails→frontend serialization. + * The API response uses snake_case, so we unmarshal into + * lbTopRecordingWire first, then convert. + */ +export interface LBTopRecording { + "recordingMbid": string; + "artistName": string; + "trackName": string; + "totalListenCount": number; + "caaReleaseMbid": string; + + /** + * ReleaseGroupMBID is resolved from CAAReleaseMBID so a top-track + * row can link to its album page with the track highlighted. + */ + "releaseGroupMbid"?: string; + "releaseName": string; + + /** + * milliseconds (from LB API) + */ + "length": number; + "inLibrary": boolean; + "localId"?: number; +} + +/** + * LBTopReleaseGroup represents a popular release group from the + * ListenBrainz popularity API. + */ +export interface LBTopReleaseGroup { + "releaseGroupMbid": string; + "title": string; + "artistName": string; + "type": string; + "date": string; + "totalListenCount": number; + "caaReleaseMbid": string; + "inLibrary": boolean; + "localId"?: number; +} + +/** + * LyricsResult is a single lyric-search hit, mapped from the DB layer + * into the camelCase shape the frontend consumes. + */ +export interface LyricsResult { + "recordingId": number; + "filePath": string; + "lengthMs": number; + "title": string; + "artist": string; + "album": string; +} + +/** + * MBArtist is a Wails-friendly projection of a MusicBrainz artist. + */ +export interface MBArtist { + "mbid": string; + "name": string; + "sortName": string; + "englishName"?: string; + "type": string; + "country": string; + "disambiguation": string; + "score": number; + + /** + * raw LB listen count (0 if unknown) + */ + "popularity": number; + "listenerCount": number; + + /** + * true if the user owns music by this artist + */ + "inLibrary": boolean; + + /** + * local artist row ID for navigation + */ + "localId"?: number; +} + +/** + * MBRecording is a Wails-friendly projection of a MusicBrainz + * recording. + */ +export interface MBRecording { + "mbid": string; + "title": string; + "length": number; + "artistCredit": string; + + /** + * for linking the artist to its detail page + */ + "artistMbid"?: string; + "score": number; + + /** + * raw LB listen count (0 if unknown) + */ + "popularity": number; + "listenerCount": number; + + /** + * parent release, for album navigation + */ + "caaReleaseMbid"?: string; + + /** + * ReleaseGroupMBID is resolved from CAAReleaseMBID so a track can + * link to its album page with the track highlighted, matching how + * tracks behave everywhere else. + */ + "releaseGroupMbid"?: string; + + /** + * album title + */ + "releaseName"?: string; + + /** + * true if the user owns this recording + */ + "inLibrary": boolean; + + /** + * local recording row ID + */ + "localId"?: number; +} + +/** + * MBRelease is a Wails-friendly projection of a MusicBrainz release. + */ +export interface MBRelease { + "mbid": string; + "title": string; + "date": string; + "country": string; + "status": string; + "artistCredit"?: string; + "tracks"?: MBTrack[] | null; + + /** + * ReleaseGroupMBID is the parent release group's MBID. Empty unless + * the lookup requested the "release-groups" include (LookupRelease + * does); used to resolve a release-level MBID (what many taggers + * write) back to the release-group MBID everything else on the + * album page is keyed by. + */ + "releaseGroupMbid"?: string; +} + +/** + * MBReleaseGroup is a Wails-friendly projection of a MusicBrainz + * release group. + */ +export interface MBReleaseGroup { + "mbid": string; + "title": string; + "primaryType": string; + "secondaryTypes"?: string[] | null; + "firstReleaseDate": string; + "artistCredit": string; + + /** + * for linking the artist to its detail page + */ + "artistMbid"?: string; + + /** + * raw LB listen count (0 if unknown) + */ + "popularity": number; + "listenerCount": number; + + /** + * true if the user owns this album + */ + "inLibrary": boolean; + + /** + * local release_group row ID + */ + "localId"?: number; +} + +/** + * MBSearchResult aggregates the three searchable entity types + * returned by the MusicBrainz search API. + */ +export interface MBSearchResult { + "artists"?: MBArtist[] | null; + "releaseGroups"?: MBReleaseGroup[] | null; + "recordings"?: MBRecording[] | null; + "topResults"?: TopResult[] | null; +} + +/** + * MBTrack is a Wails-friendly projection of a MusicBrainz track. + */ +export interface MBTrack { + "position": number; + "discNumber": number; + "title": string; + "length": number; + "mbid": string; + "inLibrary": boolean; + "localId"?: number; +} + +/** + * MusicBrainzClient wraps the musicbrainzws2 library with a local + * response cache. Every API call checks the cache first and stores + * successful responses for future hits. + * + * A proactive rate limiter gates all outgoing requests at 1 req/sec + * to avoid triggering MusicBrainz 429 responses. The underlying + * musicbrainzws2.Client still retries on 429 as a safety net, but + * the limiter should prevent most rate-limit hits. + */ +export interface MusicBrainzClient { +} + +/** + * RateLimiter enforces a maximum request rate using a token bucket. + * MusicBrainz requires ≤1 request per second and rejects ALL + * requests (not just excess) when the rate is exceeded, so callers + * block proactively via Wait rather than retrying reactively. + * + * A limiter may carry a second, slower **background lane** (see + * WithBackgroundLane). A caller marked by WithBackgroundPriority is + * paced by that lane *and* yields to interactive callers: while any + * interactive Wait is outstanding, background waits do not take a + * token at all. This is what keeps a multi-thousand-request backfill + * from putting the album page the user is looking at right now behind + * hours of queued work. + * + * RateLimiter is safe for concurrent use. + */ +export interface RateLimiter { +} + +/** + * Shelf is one horizontal row on the Explore page. + * + * A shelf carries albums or artists, never both: they route to + * different pages and render as different cards, and a row that is + * sometimes one and sometimes the other is two components pretending to + * be one. + */ +export interface Shelf { + "id": string; + "kind": ShelfKind; + + /** + * Title is the row heading. + */ + "title": string; + + /** + * Subtitle says why these are here. As on Home it is not + * decoration: without it a shelf is indistinguishable from a + * random grid. + */ + "subtitle": string; + "albums"?: MBReleaseGroup[] | null; + "artists"?: MBArtist[] | null; +} + +/** + * ShelfKind identifies what a shelf is built from, so the frontend can + * pick an icon and a spec can assert on a shelf without matching + * display copy. + */ +export enum ShelfKind { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Shelf kinds. + */ + ShelfPopularAlbums = "popular-albums", + ShelfPopularArtists = "popular-artists", + ShelfMoreFromOwned = "more-from-owned", +}; + +/** + * ShelfPage is what Explore renders before a query. + * + * State exists because "no shelves" has three different causes here and + * the page must not present them identically — a blank panel is the bug + * this whole feature is fixing, and a blank panel that says nothing + * about why is the same bug with more code behind it. + */ +export interface ShelfPage { + "shelves": Shelf[] | null; + + /** + * State is one of: + * + * "ready" — the catalog is here and the shelves are below. + * "building" — it is being fetched or built right now. + * "no-index" — there is no catalog. Search still works over + * whatever is in the index, which may be nothing; + * the page says so and points at Settings. + */ + "state": string; +} + +/** + * ThumbnailRequest is a single item in a batch thumbnail request. + */ +export interface ThumbnailRequest { + "mbid": string; + "albumName": string; + "artistName": string; +} + +/** + * TierStatus represents the state of a single index tier. + */ +export interface TierStatus { + "name": string; + + /** + * "pending", "running", "complete", "error", "skipped" + */ + "state": string; + "total": number; + "completed": number; + "error"?: string; + + /** + * Detail is a human-readable progress line for stages whose raw + * completed/total numbers say little on their own — the listens + * stream reports "42.3 / 205.1 GB · 18 MB/s · ~3h20m left" here. + */ + "detail"?: string; +} + +/** + * TopResult represents a single top-result card shown above the + * categorized search lists. Computed by intent scoring after all + * reranking is complete. + */ +export interface TopResult { + /** + * "artist", "release_group", "recording" + */ + "entityType": string; + "mbid": string; + "name": string; + + /** + * for tracks/albums + */ + "artistCredit"?: string; + + /** + * for linking the artist subtitle + */ + "artistMbid"?: string; + "intentScore": number; + + /** + * Artist-specific + * "Group", "Person" + */ + "artistType"?: string; + "country"?: string; + + /** + * Album-specific + */ + "primaryType"?: string; + "year"?: string; + + /** + * Track-specific. ReleaseGroupMBID is resolved (from CAAReleaseMBID) + * so a track click can open its album page with the track highlighted, + * matching how tracks behave everywhere else. ReleaseName is the album + * title used for the album page header. + */ + "length"?: number; + "caaReleaseMbid"?: string; + "releaseGroupMbid"?: string; + "releaseName"?: string; + + /** + * Library status — populated from index cross-reference columns. + */ + "inLibrary": boolean; +} + +/** + * TrackLyrics is the stored or freshly-fetched lyrics for one track. + * Source is "embedded" (from the file's tags / library DB), "lrclib" + * (fetched on demand), or "" when none are available. + */ +export interface TrackLyrics { + "plain": string; + "synced": string; + "instrumental": boolean; + "source": string; +} + +/** + * TrackThumbnailRequest is a single item in a batch track thumbnail + * request. Either ReleaseMBID or ReleaseGroupMBID may be empty; + * the proxy tries whichever is present. + */ +export interface TrackThumbnailRequest { + /** + * stable key used in the returned map + */ + "key": string; + "releaseMbid": string; + "releaseGroupMbid": string; + "albumName": string; + "artistName": string; +} diff --git a/frontend/bindings/yellowjacket/backend/explore/service.ts b/frontend/bindings/yellowjacket/backend/explore/service.ts new file mode 100644 index 0000000..9dd4aab --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/explore/service.ts @@ -0,0 +1,604 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service is the Wails-bound service for the explore feature. + * It owns the lifecycle of all explore-related components: the + * MusicBrainz client, ListenBrainz client, rate limiter, and + * response cache. Its exported methods form the binding surface + * that the frontend calls via generated TypeScript stubs. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as time$0 from "../../../time/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as jobs$0 from "../jobs/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * AdoptPausedIndexBuild re-registers a build paused in a previous + * session so it appears in the jobs panel, still paused. + */ +export function AdoptPausedIndexBuild(): $CancellablePromise { + return $Call.ByID(3754928399); +} + +/** + * BackfillLibraryDiscographies enriches owned artists that have not had + * their discography fetched yet, in the background. Idempotent and + * bounded — the query only returns unenriched artists and each is marked + * discog_fetched on success, so this is cheap (an empty query) once every + * owned artist is covered and safe to call on every scan and launch. + */ +export function BackfillLibraryDiscographies(): $CancellablePromise { + return $Call.ByID(3678857155); +} + +/** + * BackfillLibraryLyrics fetches lyrics from LRCLIB for library tracks + * that don't have them, in the background. Idempotent and bounded — + * each recording is tried once (a miss is cached), and a run stops + * after a fixed number of passes, resuming on the next launch. + */ +export function BackfillLibraryLyrics(): $CancellablePromise { + return $Call.ByID(4204427482); +} + +/** + * BackfillReleaseGroupMBIDs resolves release groups whose scan only + * found a release-level MBID (MUSICBRAINZ_ALBUMID — many taggers write + * this instead of, or in addition to, MUSICBRAINZ_RELEASEGROUPID) into + * the release-group MBID everything else on the album page is keyed + * by. Bounded and resumable, in the background: a scan can't afford a + * live MusicBrainz call, so `library.updateMBIDs` stashes the release + * MBID in `pending_release_mbid` instead, and this is what resolves it + * — the same "defer the network call out of the scan path" shape as + * BackfillLibraryDiscographies. + */ +export function BackfillReleaseGroupMBIDs(): $CancellablePromise { + return $Call.ByID(2594807082); +} + +/** + * BrowseReleaseGroups fetches release groups for a given artist MBID. + * Checks the local index first for instant results, then fetches from + * MusicBrainz for complete data (secondary types, precise dates). + * Also adds results to the search index (Tier 5: organic growth). + */ +export function BrowseReleaseGroups(artistMBID: string): $CancellablePromise<$models.MBReleaseGroup[] | null> { + return $Call.ByID(404562912, artistMBID); +} + +/** + * BrowseReleases fetches releases for a given release group MBID. + * + * Local-first, non-blocking: a warm response cache is served instantly; + * on a miss the request does NOT block on a live MusicBrainz browse + * (which pulls every version's full tracklist and can take seconds). + * Instead it kicks off a background fetch and returns empty — the + * AlbumReleasesReady event signals the caller to re-fetch once the cache + * is warm. + */ +export function BrowseReleases(releaseGroupMBID: string): $CancellablePromise<$models.MBRelease[] | null> { + return $Call.ByID(2551207897, releaseGroupMBID); +} + +/** + * CAALimiter returns the shared Cover Art Archive rate limiter. + * Consumers must respect it for any fresh CAA HTTP GETs. + */ +export function CAALimiter(): $CancellablePromise<$models.RateLimiter | null> { + return $Call.ByID(1239092428); +} + +/** + * CheckLibraryMBIDs returns which of the given MBIDs exist in the + * local music library. Returns a map of MBID → entity type + * ("artist", "release_group", "recording"). + * + * It has no frontend caller — `downloadcatalog.go` is the one consumer, + * asking about a single MBID at a time. + */ +export function CheckLibraryMBIDs(mbids: string[] | null): $CancellablePromise<{ [_ in string]?: string } | null> { + return $Call.ByID(3168338597, mbids); +} + +/** + * CoreCatalogImported reports whether a prebuilt catalog artifact has + * been merged into this index. + */ +export function CoreCatalogImported(): $CancellablePromise { + return $Call.ByID(186364153); +} + +/** + * CoverArtGroupURL returns the Cover Art Archive URL for a release + * group's front cover at the default 250px size. This is the + * correct endpoint for search results, which return release group + * MBIDs rather than individual release MBIDs. + */ +export function CoverArtGroupURL(releaseGroupMBID: string): $CancellablePromise { + return $Call.ByID(2102228641, releaseGroupMBID); +} + +/** + * CoverArtURL returns the Cover Art Archive URL for a release's + * front cover at the default 250px size. + */ +export function CoverArtURL(releaseMBID: string): $CancellablePromise { + return $Call.ByID(3231496554, releaseMBID); +} + +/** + * GenerateMix returns the next batch of tracks for a dynamic-mix queue + * fallback, built by expanding the seed's artists to their similar + * artists (weighted by how often each appears in the seed, sharpened + * by shared genre tags) and restricting candidates to what is actually + * in the library — a queue can only play files that exist. + * + * continuing extends the current mix session — regenerating from its + * original seed rather than seedPaths — instead of starting a fresh + * one. Pass false whenever the queue that just exhausted was not + * itself a mix batch (a real selection just ran out); pass true when + * it was (the mix keeps going indefinitely). label names the batch + * after its most-represented seed artist, for the "Playing from" UI. + */ +export function GenerateMix(seedPaths: string[] | null, continuing: boolean): $CancellablePromise<[string[] | null, string]> { + return $Call.ByID(4293623744, seedPaths, continuing); +} + +/** + * GetArtistImageCached returns a base64 data URL for the artist's + * photo ONLY if it's already on disk — no MB/Wikidata resolution + * or Wikimedia fetch. Returns "" if not cached. + */ +export function GetArtistImageCached(artistMBID: string): $CancellablePromise { + return $Call.ByID(670428351, artistMBID); +} + +/** + * GetArtistImageCachedPath returns the asset-handler URL path for + * the artist's cached medium thumbnail, e.g. + * "/artist-images/b1/b10bbbfc-.../primary_md.jpg". No base64, no + * network calls — just a disk existence check. Returns "" if no + * image is cached. + */ +export function GetArtistImageCachedPath(artistMBID: string): $CancellablePromise { + return $Call.ByID(589683480, artistMBID); +} + +/** + * GetArtistImageURL returns a base64 data URL for the artist's + * photo. Cached on disk — first call resolves via MB/Wikidata and + * fetches from Wikimedia Commons, subsequent calls are instant. + * Returns "" if no image is available. + */ +export function GetArtistImageURL(artistMBID: string): $CancellablePromise { + return $Call.ByID(1780431074, artistMBID); +} + +/** + * GetArtistImagesCachedPaths resolves artist portraits for many MBIDs + * in one call, returning MBID → asset-handler path for the medium + * thumbnail. Disk existence checks only: no MusicBrainz, no Wikidata, + * no Wikimedia, no network of any kind. MBIDs with no cached portrait + * are omitted rather than returned empty. + * + * It exists because the resolving entry point (GetArtistImageURL) was + * being used where a cache check belonged: a page rendering a dozen + * search results paid a full MB → Wikidata → Wikipedia → Wikimedia + * resolution for every artist whose portrait was already on disk. Its + * predecessor could not serve that — it keyed on artist *name* through + * the library's own MBID map, so it only ever answered for artists the + * user already owned, which on a catalog search is nearly none of them. + * + * Paths rather than base64: a portrait is ~128 kB as a data URL, and + * the asset handler serves the same bytes without crossing the IPC + * boundary or being retained by a JS string. + */ +export function GetArtistImagesCachedPaths(mbids: string[] | null): $CancellablePromise<{ [_ in string]?: string } | null> { + return $Call.ByID(2104788604, mbids); +} + +/** + * GetArtistMBID returns the MusicBrainz ID for a local library + * artist by name, or "" if not found or no MBID tagged. + */ +export function GetArtistMBID(artistName: string): $CancellablePromise { + return $Call.ByID(944736774, artistName); +} + +/** + * GetArtistPlayCount returns the total LB listen count for an artist. + * Returns 0 if unknown. + */ +export function GetArtistPlayCount(artistMBID: string): $CancellablePromise { + return $Call.ByID(143535277, artistMBID); +} + +/** + * GetCandidateThumbnail returns CAA-only cover art for an autotag + * candidate, skipping the library-by-name index so embedded ID3 + * art on the user's existing files doesn't pollute the candidate + * preview. Disk cache → network on RG → network on release. + */ +export function GetCandidateThumbnail(releaseMBID: string, releaseGroupMBID: string): $CancellablePromise { + return $Call.ByID(1946932424, releaseMBID, releaseGroupMBID); +} + +/** + * GetExploreShelves builds the page Explore shows before a query. + * + * One call rather than one per shelf, for `home`'s reason: the shelves + * share nothing expensive, but the page has nothing useful to render + * until it knows which rows exist, and rows that pop in one at a time + * reflow under the cursor. + */ +export function GetExploreShelves(): $CancellablePromise<$models.ShelfPage> { + return $Call.ByID(144329614); +} + +/** + * GetIndexStatus returns the current search index build status. + */ +export function GetIndexStatus(): $CancellablePromise<$models.IndexStatus> { + return $Call.ByID(3481063523); +} + +/** + * GetLibrarySimilarArtists returns similar artists to the given + * MBID that are also in the user's local library. Uses the + * pre-computed similar_artist_map table (populated during Tier 4 + * index build) joined with the artists table. No API calls. + * + * The artists table allows multiple rows with the same MBID + * (different artist credits like "A feat. B" that resolve to the + * same MB artist), so we use EXISTS instead of JOIN to avoid + * duplicating similar_artist_map rows. + */ +export function GetLibrarySimilarArtists(artistMBID: string): $CancellablePromise<$models.LBSimilarArtist[] | null> { + return $Call.ByID(104313157, artistMBID); +} + +/** + * GetThumbnail returns a base64 data URL for the release group's + * cover art. Checks local library art first (by album+artist + * name), then disk cache, then Cover Art Archive. + * Returns "" if no cover art is available. + */ +export function GetThumbnail(releaseGroupMBID: string, albumName: string, artistName: string): $CancellablePromise { + return $Call.ByID(4046192225, releaseGroupMBID, albumName, artistName); +} + +/** + * GetThumbnails fetches multiple thumbnails in one call and returns + * a map of MBID → base64 data URL. Entries with no art are omitted. + * GetThumbnails returns ONLY cached/local art instantly — no network + * fetches. For items missing from the cache, the frontend should + * call GetThumbnail() individually so results stream in rather than + * blocking on a batch. + */ +export function GetThumbnails(requests: $models.ThumbnailRequest[] | null): $CancellablePromise<{ [_ in string]?: string } | null> { + return $Call.ByID(3124819542, requests); +} + +/** + * GetTrackLyrics returns lyrics for a recording. If the library + * already has them (from embedded tags) they're returned as-is; + * otherwise it fetches from LRCLIB, persists them (updating the FTS + * index), and returns them. Never returns an error to the frontend — + * a miss just yields an empty result. + */ +export function GetTrackLyrics(recordingID: number): $CancellablePromise<$models.TrackLyrics> { + return $Call.ByID(1131284622, recordingID); +} + +/** + * GetTrackThumbnail returns cover art for a track. Accepts both + * the track's CAA release MBID and the resolved parent release + * group MBID (either may be empty). Tries the RG first to reuse + * discography cache; falls back to the release-level CAA endpoint + * when the RG isn't known — useful when the track's preferred CAA + * release doesn't belong to any RG currently in the index. + */ +export function GetTrackThumbnail(releaseMBID: string, releaseGroupMBID: string, albumName: string, artistName: string): $CancellablePromise { + return $Call.ByID(4191599666, releaseMBID, releaseGroupMBID, albumName, artistName); +} + +/** + * GetTrackThumbnails returns ONLY cached/local art for track + * requests, keyed by the caller-provided Key so callers can map + * results back to rows in their UI. + */ +export function GetTrackThumbnails(requests: $models.TrackThumbnailRequest[] | null): $CancellablePromise<{ [_ in string]?: string } | null> { + return $Call.ByID(2383043155, requests); +} + +/** + * IndexBaselineSeries returns the incremental listens series the index's + * popularity is caught up to. A change across a refresh means new data + * was folded in. + */ +export function IndexBaselineSeries(): $CancellablePromise { + return $Call.ByID(3317566669); +} + +/** + * IndexImportComplete reports whether the dump import has finished all + * of its stages. Distinct from IsIndexReady, which only means the index + * holds enough rows to answer queries — a partially imported index is + * ready but not complete. Used by the headless builder to decide + * whether another run is needed. + */ +export function IndexImportComplete(): $CancellablePromise { + return $Call.ByID(3872948181); +} + +/** + * IndexLastImported returns when the dump import last completed, or the + * zero time if it never has. + */ +export function IndexLastImported(): $CancellablePromise { + return $Call.ByID(1889359471); +} + +/** + * InvalidateIndexDiscographies clears the discography build + * timestamp so the next index build re-runs Tiers 2-4. Call + * after a library rescan that may have populated new MBIDs. + */ +export function InvalidateIndexDiscographies(): $CancellablePromise { + return $Call.ByID(2573191521); +} + +/** + * InvalidateLibrarySync clears the "ready" markers guarding the gated + * library-sync steps so they re-run on the next launch. Call after a + * mutation that changes owned content outside a scan (e.g. removing a + * library), which would otherwise leave stale in_library flags and + * orphaned lyric-index rows. + */ +export function InvalidateLibrarySync(): $CancellablePromise { + return $Call.ByID(2446309672); +} + +/** + * IsIndexReady returns true once the index has been populated. + */ +export function IsIndexReady(): $CancellablePromise { + return $Call.ByID(1366461294); +} + +/** + * LookupArtist fetches a single MusicBrainz artist by MBID. + * Checks the local index first — has name, type, country, + * disambiguation, sort_name for indexed artists. Falls back to + * MB API for unknown artists and backfills the index for next time. + */ +export function LookupArtist(mbid: string): $CancellablePromise<$models.MBArtist | null> { + return $Call.ByID(3986196952, mbid); +} + +/** + * LookupReleaseGroup fetches a single MusicBrainz release group by MBID. + */ +export function LookupReleaseGroup(mbid: string): $CancellablePromise<$models.MBReleaseGroup | null> { + return $Call.ByID(2946174711, mbid); +} + +/** + * MusicBrainz returns the shared cached MB client so other services + * (e.g. autotag) can reuse it without spinning up a second limiter. + */ +export function MusicBrainz(): $CancellablePromise<$models.MusicBrainzClient | null> { + return $Call.ByID(3453528034); +} + +/** + * PopulateLocalCrossReferences updates the local_*_id columns on + * explore_index after a library scan. + */ +export function PopulateLocalCrossReferences(): $CancellablePromise { + return $Call.ByID(1058376024); +} + +/** + * PopulateLocalCrossReferencesIfNeeded runs the library→index sync only + * when it has not run since the last library change. Use it on the + * unchanged-library launch path so the write-heavy re-sync is skipped in + * steady state; the scan-completion path calls the unconditional form. + */ +export function PopulateLocalCrossReferencesIfNeeded(): $CancellablePromise { + return $Call.ByID(814782146); +} + +/** + * PrefetchReleases warms the local response cache for a set of release + * groups in the background so opening any of them is instant. Called from + * the artist page once its top-releases / discography render — album + * navigation almost always originates there. Already-cached groups are + * skipped; a cap bounds how many live fetches a single artist view can + * trigger so the MusicBrainz rate limiter isn't flooded. + * A release group the user owns *completely* is skipped outright: since + * tag-derived completeness landed, such an album opens with no catalog + * call at all — identity from its MBID, tracklist from its own files — + * so warming the most expensive request in the app on its behalf buys + * nothing. The skip is not merely an optimisation; those slots go to + * albums that will actually need the browse. + */ +export function PrefetchReleases(releaseGroupMBIDs: string[] | null): $CancellablePromise { + return $Call.ByID(306872972, releaseGroupMBIDs); +} + +/** + * PrepareIndexRebuild clears the completion marker so the next build + * re-imports from the newest published dump. + */ +export function PrepareIndexRebuild(): $CancellablePromise { + return $Call.ByID(2772457219); +} + +/** + * RebuildLyricsIndex rebuilds the FTS lyrics index from the current + * library. Cheap; safe to call after every scan. + */ +export function RebuildLyricsIndex(): $CancellablePromise { + return $Call.ByID(300335776); +} + +/** + * RebuildLyricsIndexIfNeeded rebuilds the lyrics FTS only when it has not + * been built since the last library change. The backfill keeps the index + * in sync incrementally thereafter, so on an unchanged library the full + * rebuild is redundant; the scan-completion path calls the unconditional + * form. + */ +export function RebuildLyricsIndexIfNeeded(): $CancellablePromise { + return $Call.ByID(1447291626); +} + +/** + * RecordSearchClick records that the user clicked a search result. + * Called from the frontend when any search result is clicked. + */ +export function RecordSearchClick(query: string, mbid: string, entityType: string): $CancellablePromise { + return $Call.ByID(824485756, query, mbid, entityType); +} + +/** + * RefreshIndexNow folds newly published incremental listens dumps into + * the index synchronously. Pass 0 to bypass the cadence gate. + */ +export function RefreshIndexNow(minInterval: time$0.Duration): $CancellablePromise { + return $Call.ByID(4165220556, minInterval); +} + +/** + * RefreshListenCounts folds any newly-published incremental listen dumps + * into the index's popularity numbers, in the background. No-op when + * offline, when a full build is running, when there is no baseline + * import, or when the last refresh was within the weekly cadence. Fully + * local — downloads the small daily dumps but makes no ListenBrainz API + * calls. + */ +export function RefreshListenCounts(): $CancellablePromise { + return $Call.ByID(3813092323); +} + +/** + * ResolveReleaseGroupMBIDs takes a list of CAA release MBIDs (from + * recording metadata) and returns a map of release MBID → release + * group MBID. The frontend uses this to fetch track cover art via + * the parent release group, reusing whatever cache exists for the + * album already. + */ +export function ResolveReleaseGroupMBIDs(caaReleaseMBIDs: string[] | null): $CancellablePromise<{ [_ in string]?: string } | null> { + return $Call.ByID(325093206, caaReleaseMBIDs); +} + +/** + * SearchLocal queries only the local FTS5 index and returns fully + * ranked results instantly with no network calls. This is the + * primary interactive search path: now that the index is populated + * from the MetaBrainz dumps it covers essentially every popular + * entity, so the frontend drives search entirely from here. The + * old MusicBrainz network pipeline (Search) is retained for a future + * opt-in "search online" affordance but is no longer called on the + * hot path. + * + * Returns nil if the index has no hits for the query, so the caller + * can fall back to whatever owned-library matches it already has. + */ +export function SearchLocal(query: string): $CancellablePromise<$models.MBSearchResult | null> { + return $Call.ByID(189423736, query); +} + +/** + * SearchLyrics finds library tracks whose lyrics contain the given + * fragment, ranked by relevance. Pure local FTS — no network. + */ +export function SearchLyrics(query: string): $CancellablePromise<$models.LyricsResult[] | null> { + return $Call.ByID(4153164053, query); +} + +/** + * SetAlbumComplete injects the completeness check. It is injected + * rather than imported because `library` and `explore` do not depend on + * each other in either direction today, and one prefetch heuristic is + * not a reason to introduce that edge — the alternative, re-deriving + * "complete" from SQL here, would be a second definition of it. + */ +export function SetAlbumComplete(fn: $models.AlbumCompleteFunc): $CancellablePromise { + return $Call.ByID(942474493, fn); +} + +/** + * SetJobRegistry wires the background job registry into the search + * index so its build reports progress and controls to the frontend. + */ +export function SetJobRegistry(reg: jobs$0.Registry | null): $CancellablePromise { + return $Call.ByID(4291900709, reg); +} + +/** + * SimilarArtists returns artists similar to the given artist MBID. + */ +export function SimilarArtists(artistMBID: string): $CancellablePromise<$models.LBSimilarArtist[] | null> { + return $Call.ByID(2048211426, artistMBID); +} + +/** + * StartIndexBuild kicks off the background search index build. + * Call this after the library scan completes so the indexer doesn't + * starve the scan for DB access. + */ +export function StartIndexBuild(): $CancellablePromise { + return $Call.ByID(1444396415); +} + +/** + * StopIndexBuild cancels the background search index build. + * Call before a full rescan to free the DB for the scan. + */ +export function StopIndexBuild(): $CancellablePromise { + return $Call.ByID(3056245285); +} + +/** + * TopRecordingsForArtist returns the most-listened recordings for an + * artist. Serves instantly from the local index when available; when the + * artist isn't indexed yet it returns empty immediately and fetches the + * discography in the background, emitting ArtistDiscographyReady so the + * caller can re-fetch — the request never blocks on a live fetch. + */ +export function TopRecordingsForArtist(artistMBID: string): $CancellablePromise<$models.LBTopRecording[] | null> { + return $Call.ByID(2077579960, artistMBID); +} + +/** + * TopReleaseGroupsForArtist returns the most-listened release groups for + * an artist. Same non-blocking contract as TopRecordingsForArtist: index + * hit is instant, a miss kicks off a background discography fetch and + * returns empty, and ArtistDiscographyReady signals when to re-fetch. + */ +export function TopReleaseGroupsForArtist(artistMBID: string): $CancellablePromise<$models.LBTopReleaseGroup[] | null> { + return $Call.ByID(602197643, artistMBID); +} + +/** + * WaitForIndexIdle blocks until no index build or artist indexing + * goroutine is running. Does not cancel a running build. + */ +export function WaitForIndexIdle(): $CancellablePromise { + return $Call.ByID(3200929511); +} diff --git a/frontend/bindings/yellowjacket/backend/frontendutil/frontendutil.ts b/frontend/bindings/yellowjacket/backend/frontendutil/frontendutil.ts new file mode 100644 index 0000000..3915b07 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/frontendutil/frontendutil.ts @@ -0,0 +1,47 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * FrontendUtil provides frontend-bound Go functions. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +/** + * DirectoryPicker opens a directory selection dialog. + * + * v3 has no separate directory dialog: it is the file dialog told to + * choose directories and not files. + */ +export function DirectoryPicker(): $CancellablePromise { + return $Call.ByID(3245034282); +} + +/** + * ImageFilePicker opens a file selection dialog filtered to image + * files (JPEG, PNG). Returns the selected file path, or empty + * string if the user cancelled. + */ +export function ImageFilePicker(): $CancellablePromise { + return $Call.ByID(3408786006); +} + +/** + * PlaylistFilePicker opens a file selection dialog filtered + * to M3U/M3U8 playlist files. Multiple files may be selected. + */ +export function PlaylistFilePicker(): $CancellablePromise { + return $Call.ByID(2858498591); +} + +/** + * ReadFile reads a file from disk and returns its contents. + * Used by the frontend to read cover art image files selected + * via ImageFilePicker. + */ +export function ReadFile(path: string): $CancellablePromise { + return $Call.ByID(3350710867, path); +} diff --git a/frontend/bindings/yellowjacket/backend/frontendutil/index.ts b/frontend/bindings/yellowjacket/backend/frontendutil/index.ts new file mode 100644 index 0000000..5c57627 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/frontendutil/index.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as FrontendUtil from "./frontendutil.js"; +export { + FrontendUtil +}; diff --git a/frontend/bindings/yellowjacket/backend/home/index.ts b/frontend/bindings/yellowjacket/backend/home/index.ts new file mode 100644 index 0000000..c35651f --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/home/index.ts @@ -0,0 +1,15 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Kind +} from "./models.js"; + +export type { + Shelf +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/home/models.ts b/frontend/bindings/yellowjacket/backend/home/models.ts new file mode 100644 index 0000000..f6a5059 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/home/models.ts @@ -0,0 +1,53 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as library$0 from "../library/models.js"; + +/** + * Kind identifies what a shelf is built from, so the frontend can pick + * an icon and the e2e suite can assert on a shelf without matching + * display copy. + */ +export enum Kind { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Shelf kinds. + */ + KindRecentlyPlayed = "recently-played", + KindRecentlyAdded = "recently-added", + KindMostPlayed = "most-played", + KindUnplayed = "unplayed", + KindStale = "stale", + KindArtist = "artist", + KindGenre = "genre", + KindRandom = "random", +}; + +/** + * Shelf is one horizontal row on the home page. + */ +export interface Shelf { + /** + * ID is stable within a response, for list keying. + */ + "id": string; + "kind": Kind; + + /** + * Title is the row heading. + */ + "title": string; + + /** + * Subtitle says why these albums are here. It is not decoration: + * without it a shelf is indistinguishable from a random grid. + */ + "subtitle": string; + "albums": library$0.Album[] | null; +} diff --git a/frontend/bindings/yellowjacket/backend/home/service.ts b/frontend/bindings/yellowjacket/backend/home/service.ts new file mode 100644 index 0000000..6176bec --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/home/service.ts @@ -0,0 +1,27 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service builds the home page's shelves. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * GetShelves returns the home page's rows, in display order. + * + * It is a single call rather than one per shelf because the shelves + * share an album lookup and because the page has nothing useful to + * render until it knows which rows exist — a page that pops rows in one + * at a time reflows under the user's cursor. + */ +export function GetShelves(): $CancellablePromise<$models.Shelf[] | null> { + return $Call.ByID(2822423495); +} diff --git a/frontend/bindings/yellowjacket/backend/jobs/index.ts b/frontend/bindings/yellowjacket/backend/jobs/index.ts new file mode 100644 index 0000000..667a2e0 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/jobs/index.ts @@ -0,0 +1,22 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export { + Kind, + Level, + State +} from "./models.js"; + +export type { + Caps, + Job, + LogEntry, + Registry, + Stage, + Stat +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/jobs/models.ts b/frontend/bindings/yellowjacket/backend/jobs/models.ts new file mode 100644 index 0000000..6bbbb7f --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/jobs/models.ts @@ -0,0 +1,159 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Caps describes which controls a job supports. The frontend renders + * buttons from these rather than switching on Kind, so a job that gains + * pause support later needs no frontend change. + */ +export interface Caps { + "pausable": boolean; + "cancellable": boolean; +} + +/** + * Job is the frontend-facing snapshot of a single background job. + */ +export interface Job { + "id": string; + "kind": Kind; + "title": string; + "subtitle"?: string; + "state": State; + "phase"?: string; + + /** + * Current/Total drive the progress bar. Total == 0 means the job + * is indeterminate and the frontend should render a spinner. + */ + "current": number; + "total": number; + "caps": Caps; + "stages": Stage[] | null; + "stats": Stat[] | null; + "error"?: string; + + /** + * unix milliseconds + */ + "startedAt": number; + "updatedAt": number; + "endedAt"?: number; + "logCount": number; + "warnCount": number; + "errorCount": number; +} + +/** + * Kind identifies the subsystem that owns a job. The frontend uses it + * to pick an icon and to route "view details" to the right panel. + */ +export enum Kind { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Job kinds. + */ + KindLibraryScan = "library-scan", + KindIndexBuild = "index-build", + KindDownload = "download", + KindAutotagApply = "autotag-apply", + + /** + * KindCatalogEnrich is background catalog work for content the user + * already owns — the discography backfills. It is distinct from + * KindIndexBuild because the two differ in what cancelling costs: + * an index build discards hours of downloading and the frontend + * confirms before stopping one, where a backfill is resumable per + * artist and stopping it is free. + */ + KindCatalogEnrich = "catalog-enrich", +}; + +/** + * Level is the severity of a job log entry. + */ +export enum Level { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Log levels. + */ + LevelInfo = "info", + LevelWarn = "warn", + LevelError = "error", +}; + +/** + * LogEntry is one line of a job's output log. + */ +export interface LogEntry { + /** + * unix milliseconds + */ + "time": number; + "level": Level; + "message": string; + "detail"?: string; +} + +/** + * Registry owns every known job and pushes coalesced snapshots to the + * frontend. It is safe for concurrent use. + */ +export interface Registry { +} + +/** + * Stage is one named sub-step of a multi-stage job, such as an index + * build tier. Jobs with a single linear phase leave Stages empty. + */ +export interface Stage { + "name": string; + + /** + * pending, running, complete, error, skipped + */ + "state": string; + "current": number; + "total": number; + "error"?: string; +} + +/** + * Stat is a display-only key/value pair shown in the job detail panel + * (e.g. "Added" / "1,204"). + */ +export interface Stat { + "label": string; + "value": string; +} + +/** + * State is the lifecycle position of a job. + */ +export enum State { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Job states. Queued, Running, Paused and Pausing are live; Complete, + * Cancelled and Error are terminal. + */ + StateQueued = "queued", + StateRunning = "running", + StatePausing = "pausing", + StatePaused = "paused", + StateCancelling = "cancelling", + StateComplete = "complete", + StateCancelled = "cancelled", + StateError = "error", +}; diff --git a/frontend/bindings/yellowjacket/backend/jobs/service.ts b/frontend/bindings/yellowjacket/backend/jobs/service.ts new file mode 100644 index 0000000..f1295bb --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/jobs/service.ts @@ -0,0 +1,68 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service is the Wails-bound facade over the registry. It deliberately + * exposes only the read and control surface — producers get a *Handle + * through the registry instead, so the frontend cannot invent jobs. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * CancelJob abandons a job. + */ +export function CancelJob(id: string): $CancellablePromise { + return $Call.ByID(680141523, id); +} + +/** + * ClearFinishedJobs removes every terminal job from the list. + */ +export function ClearFinishedJobs(): $CancellablePromise { + return $Call.ByID(4219744941); +} + +/** + * DismissJob removes a single finished job from the list. + */ +export function DismissJob(id: string): $CancellablePromise { + return $Call.ByID(1904307161, id); +} + +/** + * GetJobLog returns the retained log tail for one job. + */ +export function GetJobLog(id: string): $CancellablePromise<$models.LogEntry[] | null> { + return $Call.ByID(441057229, id); +} + +/** + * GetJobs returns every known job — active first by registration order, + * including recently finished ones so the panel can show outcomes. + */ +export function GetJobs(): $CancellablePromise<$models.Job[] | null> { + return $Call.ByID(3979035366); +} + +/** + * PauseJob asks the owning subsystem to pause a job. Returns + * immediately; the job reports "paused" once it actually stops. + */ +export function PauseJob(id: string): $CancellablePromise { + return $Call.ByID(4094353111, id); +} + +/** + * ResumeJob continues a paused job. + */ +export function ResumeJob(id: string): $CancellablePromise { + return $Call.ByID(3937701508, id); +} diff --git a/frontend/bindings/yellowjacket/backend/library/index.ts b/frontend/bindings/yellowjacket/backend/library/index.ts new file mode 100644 index 0000000..04d14f2 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/library/index.ts @@ -0,0 +1,25 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Library from "./library.js"; +export { + Library +}; + +export type { + Album, + AlbumCompleteness, + Artist, + GenreWithCount, + Info, + RemovalHooks, + RemovalImpact, + RemovalResult, + RemovalSummary, + RescanHooks, + ScanHooks, + ScanMetrics, + ScanWarning, + Track, + TrackMBIDs +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/library/library.ts b/frontend/bindings/yellowjacket/backend/library/library.ts new file mode 100644 index 0000000..f5157ca --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/library/library.ts @@ -0,0 +1,454 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Library manages scanning and querying the music collection. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as sqlcgen$0 from "../database/sql/sqlcgen/models.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as jobs$0 from "../jobs/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * AcquirePipelineLock acquires the pipeline mutex for a tag write + * operation. The caller must call ReleasePipelineLock when done. + * If a scan is currently in progress, AcquirePipelineLock blocks + * until it completes (and vice versa). + */ +export function AcquirePipelineLock(): $CancellablePromise { + return $Call.ByID(2056761494); +} + +/** + * AddLibrary creates a new library from a directory path, emits a + * LibraryAdded event, and starts an asynchronous scan. + */ +export function AddLibrary(path: string): $CancellablePromise { + return $Call.ByID(4258719739, path); +} + +/** + * CancelAllScans cancels the current scan and clears the entire + * queue so no further libraries are scanned. + */ +export function CancelAllScans(): $CancellablePromise { + return $Call.ByID(2826471660); +} + +/** + * CancelCurrentScan cancels only the currently scanning library. + * The next queued library (if any) starts automatically when the + * current scan's goroutine completes. + */ +export function CancelCurrentScan(): $CancellablePromise { + return $Call.ByID(2190880081); +} + +/** + * CancelScan cancels an in-progress scan. Returns immediately; + * scan goroutines stop at their next checkpoint. + * + * Deprecated: Use CancelCurrentScan or CancelAllScans for + * queue-aware cancellation. + */ +export function CancelScan(): $CancellablePromise { + return $Call.ByID(4160132270); +} + +/** + * FullRescan clears the queue and player, wipes all library data + * (database records and cover art files), and performs a fresh + * scan of every library in the database. The returned ScanMetrics + * reflects the last library scanned; clear-phase durations are + * folded into its totals. + */ +export function FullRescan(): $CancellablePromise<$models.ScanMetrics | null> { + return $Call.ByID(378891236); +} + +/** + * GetAlbumCompleteness answers "do I have all of this album" from the + * tags read at scan time, with no network. + * + * The album page used to ask MusicBrainz, because the only track total + * it had was the length of whatever tracklist it was already showing — + * which for a library copy is a tautology. The denominator in a file's + * "5/12" is a real answer and it is already on disk; this is where it + * gets read. + * + * Complete is deliberately >= rather than ==: bonus and hidden tracks + * routinely put a folder over its declared total, and that is a + * complete album, not a broken one. + */ +export function GetAlbumCompleteness(albumID: number): $CancellablePromise<$models.AlbumCompleteness> { + return $Call.ByID(2895787150, albumID); +} + +/** + * GetAlbumTracks returns all tracks for a given album (release group), ordered by disc and track number. + */ +export function GetAlbumTracks(albumID: number): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(300451334, albumID); +} + +/** + * GetAlbumTracksByLibrary returns tracks for the given album, + * scoped to the given library. + */ +export function GetAlbumTracksByLibrary(albumID: number, libraryID: number): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(3304485554, albumID, libraryID); +} + +/** + * GetAlbumsByArtist returns all albums where the given artist is the album artist. + */ +export function GetAlbumsByArtist(artistID: number): $CancellablePromise<$models.Album[] | null> { + return $Call.ByID(1456840721, artistID); +} + +/** + * GetAlbumsByArtistByLibrary returns albums for the given artist + * that have tracks in the given library. + */ +export function GetAlbumsByArtistByLibrary(artistID: number, libraryID: number): $CancellablePromise<$models.Album[] | null> { + return $Call.ByID(2809291, artistID, libraryID); +} + +/** + * GetAllAlbums returns all albums with cover art and artist info for the cover grid. + */ +export function GetAllAlbums(): $CancellablePromise<$models.Album[] | null> { + return $Call.ByID(2015458954); +} + +/** + * GetAllAlbumsByLibrary returns albums that have tracks in the given library. + */ +export function GetAllAlbumsByLibrary(libraryID: number): $CancellablePromise<$models.Album[] | null> { + return $Call.ByID(4023050470, libraryID); +} + +/** + * GetAllArtists returns artists that are credited as album artists, ordered by name. + */ +export function GetAllArtists(): $CancellablePromise<$models.Artist[] | null> { + return $Call.ByID(2529088294); +} + +/** + * GetAllArtistsByLibrary returns artists that have albums with tracks + * in the given library. + */ +export function GetAllArtistsByLibrary(libraryID: number): $CancellablePromise<$models.Artist[] | null> { + return $Call.ByID(1170594642, libraryID); +} + +/** + * GetAllGenresWithCounts returns all genres with their track counts. + */ +export function GetAllGenresWithCounts(): $CancellablePromise<$models.GenreWithCount[] | null> { + return $Call.ByID(602231298); +} + +/** + * GetAllGenresWithCountsByLibrary returns genres with track counts + * scoped to the given library. + */ +export function GetAllGenresWithCountsByLibrary(libraryID: number): $CancellablePromise<$models.GenreWithCount[] | null> { + return $Call.ByID(772684334, libraryID); +} + +/** + * GetAllLibrariesWithTrackCounts returns all libraries with their + * audio file counts. Typically 1-5 libraries so the loop is trivial. + */ +export function GetAllLibrariesWithTrackCounts(): $CancellablePromise<$models.Info[] | null> { + return $Call.ByID(3420301148); +} + +/** + * GetAllTracks returns an array of track structs of every file in the library. + */ +export function GetAllTracks(): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(2991050010); +} + +/** + * GetAllTracksByLibrary returns tracks scoped to a specific library. + */ +export function GetAllTracksByLibrary(libraryID: number): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(3882999766, libraryID); +} + +/** + * GetFilePathsByAlbums returns the file paths of every track in the + * given albums, grouped by album id. + * + * "Play this artist", "play these albums" and the album drag cache each + * resolved paths with one binding call per album, sequentially, and each + * asked for whole track rows to read one field off them (perf.m2). This + * is that question asked once. The result is grouped rather than + * flattened because the caller owns the ordering — an album list is + * sorted by name, not by id — and because the drag cache stores it per + * album. + * + * A library id of 0 means "every library", matching the caller's + * selected-library filter being unset. + */ +export function GetFilePathsByAlbums(albumIDs: number[] | null, libraryID: number): $CancellablePromise<{ [_ in `${number}`]?: string[] | null } | null> { + return $Call.ByID(1692993358, albumIDs, libraryID); +} + +/** + * GetFilePathsByGenres returns the file paths of every track tagged with + * the given genres, grouped by genre name. See GetFilePathsByAlbums — + * same finding, same shape, and the caller still owns the de-duplication + * across genres because it owns the order. + */ +export function GetFilePathsByGenres(genreNames: string[] | null, libraryID: number): $CancellablePromise<{ [_ in string]?: string[] | null } | null> { + return $Call.ByID(1180707302, genreNames, libraryID); +} + +/** + * GetFilePathsByRecordingMBIDs returns the file paths of every track + * whose recording MBID is in mbids, grouped by MBID. + * + * This is the catalog side of GetFilePathsByAlbums. An Explore album + * page knows what the user owns as a set of recording MBIDs and nothing + * else: that is exactly how the backend decides a track's InLibrary + * flag (markReleasesInLibrary → CheckMBIDs), and MBTrack.LocalID is a + * declared field that nothing writes, so there is no id to ask by. + * + * Grouped rather than flattened for the same two reasons as its + * siblings — the caller owns the order (the tracklist's, not the + * database's), and one recording can have more than one file, which is + * what this app's duplicate detection exists for. + * + * A library id of 0 means "every library". + */ +export function GetFilePathsByRecordingMBIDs(mbids: string[] | null, libraryID: number): $CancellablePromise<{ [_ in string]?: string[] | null } | null> { + return $Call.ByID(2789061644, mbids, libraryID); +} + +/** + * GetRemovalImpact returns pre-removal counts for the confirmation + * dialog. All queries are read-only. + */ +export function GetRemovalImpact(libraryID: number): $CancellablePromise<$models.RemovalImpact | null> { + return $Call.ByID(2715588999, libraryID); +} + +/** + * GetScanQueueLength returns the number of libraries waiting in the + * scan queue (excludes the currently scanning library). + */ +export function GetScanQueueLength(): $CancellablePromise { + return $Call.ByID(2739382973); +} + +/** + * GetTrackMBIDs returns the MusicBrainz IDs for the track at the + * given file path. Returns empty strings for entities without MBIDs. + */ +export function GetTrackMBIDs(filePath: string): $CancellablePromise<$models.TrackMBIDs> { + return $Call.ByID(56752473, filePath); +} + +/** + * GetTracksByGenre returns all tracks tagged with the given genre. + */ +export function GetTracksByGenre(genreName: string): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(1674220245, genreName); +} + +/** + * GetTracksByGenreByLibrary returns tracks tagged with the given + * genre, scoped to the given library. + */ +export function GetTracksByGenreByLibrary(genreName: string, libraryID: number): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(4240564783, genreName, libraryID); +} + +/** + * IsScanActive returns whether a scan is currently running. + */ +export function IsScanActive(): $CancellablePromise { + return $Call.ByID(1365062070); +} + +/** + * IsScanPaused returns whether the scan is currently paused. + */ +export function IsScanPaused(): $CancellablePromise { + return $Call.ByID(2930130958); +} + +/** + * LibraryPath resolves a library's root directory by id. + */ +export function LibraryPath(id: number): $CancellablePromise { + return $Call.ByID(1798172787, id); +} + +/** + * PauseScan pauses an in-progress scan. Workers block at their + * next pause checkpoint until ResumeScan is called. + */ +export function PauseScan(): $CancellablePromise { + return $Call.ByID(3522392788); +} + +/** + * QueuedLibraryNames returns the display names of libraries waiting + * in the scan queue, in FIFO order. + */ +export function QueuedLibraryNames(): $CancellablePromise { + return $Call.ByID(2036951097); +} + +/** + * ReleasePipelineLock releases the pipeline mutex after a tag write. + */ +export function ReleasePipelineLock(): $CancellablePromise { + return $Call.ByID(2053843147); +} + +/** + * RemoveFromLibrary deletes the database rows for the given file paths + * and records each path as excluded, so the next scan does not import + * it again. **It does not touch the files on disk** — that is the + * promise the confirmation dialog makes, and the reason this operation + * is safe to put one keystroke from a focused row. + * + * The exclusion is not an enhancement. Without it the next scan finds + * the file, sees no row, and imports it again — a button that undoes + * itself, which is worse than no button. + */ +export function RemoveFromLibrary(filePaths: string[] | null): $CancellablePromise<$models.RemovalResult | null> { + return $Call.ByID(825509452, filePaths); +} + +/** + * RemoveLibrary atomically removes a library and all its data, + * performing orphan cleanup, phantom metadata conversion, FTS5 + * rebuild, and queue compaction. Returns a summary of what was removed. + */ +export function RemoveLibrary(id: number): $CancellablePromise<$models.RemovalSummary | null> { + return $Call.ByID(4110443412, id); +} + +/** + * RenameLibrary validates and updates a library's display name. + */ +export function RenameLibrary(id: number, newName: string): $CancellablePromise { + return $Call.ByID(3949052986, id, newName); +} + +/** + * RestorePausedScans adopts scans that were paused when the app last + * shut down back into the job registry, still paused. Call during + * startup before SoftScanAllLibraries so the soft scan does not restart + * a library the user deliberately paused. + */ +export function RestorePausedScans(): $CancellablePromise { + return $Call.ByID(4243712755); +} + +/** + * ResumeScan unblocks a paused scan. + */ +export function ResumeScan(): $CancellablePromise { + return $Call.ByID(3079702539); +} + +/** + * ScanAllLibraries queries all libraries from the database and queues + * each one for scanning. Existing dedup logic ensures no duplicates. + */ +export function ScanAllLibraries(): $CancellablePromise { + return $Call.ByID(266098306); +} + +/** + * ScanLibrary queues a scan for the library with the given database ID. + * If no scan is active the library is scanned immediately; otherwise it + * is appended to the queue. Duplicate requests (same library already + * scanning or already queued) are silently ignored. + */ +export function ScanLibrary(id: number): $CancellablePromise { + return $Call.ByID(2994459743, id); +} + +/** + * SearchTracks performs an FTS5 full-text search and returns + * matching tracks with full metadata. + */ +export function SearchTracks(query: string): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(3848515709, query); +} + +/** + * SearchTracksByLibrary performs an FTS5 search scoped to a specific + * library and returns matching tracks with full metadata. + */ +export function SearchTracksByLibrary(query: string, libraryID: number): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(152384327, query, libraryID); +} + +/** + * SetJobRegistry wires the background job registry so scans report + * progress, logs, and pause/cancel controls to the frontend. + */ +export function SetJobRegistry(reg: jobs$0.Registry | null): $CancellablePromise { + return $Call.ByID(4271773525, reg); +} + +/** + * SetRemovalHooks provides optional hooks for cross-cutting + * orchestration during RemoveLibrary. + */ +export function SetRemovalHooks(h: $models.RemovalHooks): $CancellablePromise { + return $Call.ByID(3190933207, h); +} + +/** + * SetRescanHooks provides optional hooks for cross-cutting + * orchestration during FullRescan. + */ +export function SetRescanHooks(h: $models.RescanHooks): $CancellablePromise { + return $Call.ByID(1944064333, h); +} + +/** + * SetScanHooks provides optional hooks for cross-cutting + * orchestration after each library scan. + */ +export function SetScanHooks(h: $models.ScanHooks): $CancellablePromise { + return $Call.ByID(520513414, h); +} + +/** + * SoftScanAllLibraries performs a lightweight launch-time scan. + * First it claims any orphaned tracks (library_id=0) left over from + * the pre-multi-library schema. Then for each library it compares + * the number of audio files on disk against the track count in the + * database. Only libraries where the counts differ (files added or + * removed since last scan) are queued for a full scan. Libraries + * that are unchanged are silently skipped — no progress bar, no + * scan events. + */ +export function SoftScanAllLibraries(): $CancellablePromise { + return $Call.ByID(1401473402); +} diff --git a/frontend/bindings/yellowjacket/backend/library/models.ts b/frontend/bindings/yellowjacket/backend/library/models.ts new file mode 100644 index 0000000..3f11eca --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/library/models.ts @@ -0,0 +1,315 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as time$0 from "../../../time/models.js"; + +/** + * Album represents an album for the cover grid display. + * + * Year is the album's preferred display year — the release-group's + * original-release-date (MusicBrainz first-release-date) when known, + * falling back to the file-tag year. ReleaseYear is the file-tag + * year of the specific release in the library; for a 2010 remaster + * of a 1973 album, Year=1973 and ReleaseYear=2010. + */ +export interface Album { + "ID": number; + "Name": string; + "ArtistName": string; + "ArtistMBID": string; + "MBID": string; + "CoverArtPath": string; + "CoverArtSmall": string; + "CoverArtMedium": string; + "CoverArtLarge": string; + "Year": number; + "ReleaseYear": number; +} + +/** + * AlbumCompleteness says how much of an album is present, as the files + * themselves claim. + * + * Known is the part that matters: a tag that never declared a total is + * not the same as a total that is unmet, and rendering the two alike + * would put an "incomplete" mark on most of an untagged library. When + * Known is false, Expected means nothing and the caller must say + * nothing. + */ +export interface AlbumCompleteness { + "owned": number; + "expected": number; + "known": boolean; + "complete": boolean; +} + +/** + * Artist represents an artist in the library. + */ +export interface Artist { + "ID": number; + "Name": string; + "MBID": string; + "ImageSmall": string; + "ImageMedium": string; + "ImageLarge": string; +} + +/** + * GenreWithCount holds a genre name and its associated track count. + */ +export interface GenreWithCount { + "Name": string; + "TrackCount": number; +} + +/** + * Info contains library metadata enriched with track count + * for the frontend settings UI. + */ +export interface Info { + "id": number; + "name": string; + "path": string; + "trackCount": number; +} + +/** + * RemovalHooks contains callbacks invoked during library removal. + * These break circular dependencies between the library, player, + * and queue packages. + */ +export interface RemovalHooks { + /** + * StopPlayback stops the currently-playing track. + */ + "StopPlayback": any; + + /** + * CompactQueue reloads queue state after cascade deletes. + */ + "CompactQueue": any; + + /** + * PostRemove runs after the removal commits, for cross-cutting + * invalidation (e.g. clearing library-sync "ready" markers). + */ + "PostRemove": any; +} + +/** + * RemovalImpact contains pre-removal counts for the confirmation dialog. + */ +export interface RemovalImpact { + "trackCount": number; + "playlistsAffected": number; + "queueItemCount": number; +} + +/** + * RemovalResult reports what one RemoveFromLibrary call did. + */ +export interface RemovalResult { + /** + * TracksRemoved is how many audio_files rows were deleted. It can + * be lower than len(filePaths) if a path was already gone. + */ + "tracksRemoved": number; + + /** + * PathsExcluded is how many paths the scanner will now skip. + */ + "pathsExcluded": number; +} + +/** + * RemovalSummary contains post-removal counts for the toast notification. + */ +export interface RemovalSummary { + "tracksDeleted": number; + "artistsRemoved": number; + "albumsRemoved": number; + "genresRemoved": number; + "playlistsAffected": number; + "queueItemsRemoved": number; +} + +/** + * RescanHooks holds optional callbacks that run before and after + * the library-clear-and-scan phase of a full rescan. The app + * layer sets these to coordinate cross-cutting concerns (e.g. + * clearing the queue, restoring playlists) without the library + * needing to know about those packages. + */ +export interface RescanHooks { + /** + * PreClear runs before library data is wiped + * (e.g. clear queue and stop playback). + */ + "PreClear": any; + + /** + * PostScan runs after the scan completes + * (e.g. restore playlists from M3U8 files). + */ + "PostScan": any; +} + +/** + * ScanHooks contains callbacks invoked after a library scan + * completes. The app layer wires these so the library package + * does not depend on the playlist package directly. + */ +export interface ScanHooks { + /** + * RepopulatePlaylists re-imports tracks for playlists that + * lost their playlist_tracks rows (e.g., from a pre-fix + * FullRescan). Runs before ResolvePhantoms. + */ + "RepopulatePlaylists": any; + + /** + * ResolvePhantoms re-links phantom playlist tracks whose + * files now exist in the library after scanning. + */ + "ResolvePhantoms": any; + + /** + * OnAllScansComplete runs after ALL queued scans finish + * (queue drained). + */ + "OnAllScansComplete": any; +} + +/** + * ScanMetrics holds timing and count data collected during a library scan. + * Worker-pool fields are protected by a mutex; DB-writer fields are + * single-threaded and use plain addition. + */ +export interface ScanMetrics { + /** + * Top-level phases (wall-clock). + */ + "total": time$0.Duration; + "loadExisting": time$0.Duration; + "walkDuration": time$0.Duration; + "extractionWallClock": time$0.Duration; + "dbWritesWallClock": time$0.Duration; + "orphanCleanup": time$0.Duration; + "postScanVariants": time$0.Duration; + + /** + * Per-format extraction (cumulative across workers). + */ + "formatExtraction": { [_ in string]?: number } | null; + "formatCount": { [_ in string]?: number } | null; + + /** + * Sub-operation cumulative times (across workers). + */ + "tagExtraction": time$0.Duration; + "durationExtraction": time$0.Duration; + + /** + * DB sub-operations (cumulative, single-threaded DB writer). + */ + "batchCommits": time$0.Duration; + "coverArtSave": time$0.Duration; + + /** + * Thumbnail generation (async worker pool). + */ + "thumbnailWallClock": time$0.Duration; + "thumbnailGeneration": time$0.Duration; + "thumbnailSmall": time$0.Duration; + "thumbnailMedium": time$0.Duration; + "thumbnailLarge": time$0.Duration; + + /** + * Full-rescan-specific phases. + */ + "clearQueue": time$0.Duration; + "clearDatabase": time$0.Duration; + "clearCoverFiles": time$0.Duration; + + /** + * File counts. + */ + "added": number; + "updated": number; + "skipped": number; + "removed": number; + + /** + * Cancelled is true when the scan was stopped via CancelScan. + */ + "cancelled": boolean; + + /** + * Library identification. + * library that was scanned + */ + "libraryId": number; + + /** + * display name of scanned library + */ + "libraryName": string; + + /** + * Non-fatal issues encountered during scanning. + */ + "warnings": ScanWarning[] | null; +} + +/** + * ScanWarning represents a non-fatal issue encountered during scanning. + */ +export interface ScanWarning { + "filePath": string; + "phase": string; + "err": string; +} + +/** + * Track represents a playable audio file in the library. + */ +export interface Track { + "TrackName": string; + "ArtistName": string; + "TrackLength": string; + "FilePath": string; + "TrackNumber": number; + "DiscNumber": number; + "Album": string; + "Genre": string[] | null; + "Year": number; + "Composer": string; + "FileType": string; + "SampleRate": number; + "BitDepth": number; + "Channels": number; + "Bitrate": number; + "FileSize": number; + "PlayCount": number; + "LastPlayed": string; + "RecordingMBID": string; + "ArtistMBID": string; + "ReleaseGroupMBID": string; + "CoverArtPath": string; + "CoverArtSmall": string; + "CoverArtMedium": string; + "CoverArtLarge": string; +} + +/** + * TrackMBIDs holds MusicBrainz identifiers for a track, resolved + * from the recording, release group, and artist tables. + */ +export interface TrackMBIDs { + "recordingMbid": string; + "releaseGroupMbid": string; + "artistMbid": string; +} diff --git a/frontend/bindings/yellowjacket/backend/mediacontrols/index.ts b/frontend/bindings/yellowjacket/backend/mediacontrols/index.ts new file mode 100644 index 0000000..dd70b92 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/mediacontrols/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export type { + Handler +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/mediacontrols/models.ts b/frontend/bindings/yellowjacket/backend/mediacontrols/models.ts new file mode 100644 index 0000000..935ac3c --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/mediacontrols/models.ts @@ -0,0 +1,7 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Handler manages the OS media control integration. + */ +export type Handler = any; diff --git a/frontend/bindings/yellowjacket/backend/player/index.ts b/frontend/bindings/yellowjacket/backend/player/index.ts new file mode 100644 index 0000000..ed0898f --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/player/index.ts @@ -0,0 +1,16 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Player from "./player.js"; +export { + Player +}; + +export { + State, + UserVolume +} from "./models.js"; + +export type { + TrackInfo +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/player/models.ts b/frontend/bindings/yellowjacket/backend/player/models.ts new file mode 100644 index 0000000..e4ed6f2 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/player/models.ts @@ -0,0 +1,61 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * State represents the current playback state. + */ +export enum State { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Playback state values. + */ + Playing = "playing", + Paused = "paused", + Stopped = "stopped", +}; + +/** + * TrackInfo contains metadata and playback state for the currently + * loaded track. It is emitted as the payload of the TrackChanged + * event and serialized as camelCase JSON to match the frontend + * TrackInfo interface in player-store.ts. + */ +export interface TrackInfo { + "fileName": string; + "filePath": string; + "state": State; + "title": string; + "artist": string; + "album": string; + "coverArt": string; + "coverArtSmall": string; + "coverArtMedium": string; + "coverArtLarge": string; + "trackLength": number; + "seekPosition": number; + "trackChangeId": number; + "artistMbid": string; + "releaseGroupMbid": string; + "recordingMbid": string; +} + +/** + * UserVolume represents volume on a user-facing scale (0-100). + */ +export enum UserVolume { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = 0, + + /** + * User volume range bounds. + */ + MinUserVol = 0, + MaxUserVol = 100, + DefaultUserVol = 50, +}; diff --git a/frontend/bindings/yellowjacket/backend/player/player.ts b/frontend/bindings/yellowjacket/backend/player/player.ts new file mode 100644 index 0000000..4b96a0e --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/player/player.ts @@ -0,0 +1,182 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Player handles audio playback and state management. + * + * Lock ordering: always acquire p.mu BEFORE speaker.Lock(). + * The beep playback-finished callback dispatches to a new goroutine + * so it never holds p.mu while the speaker lock is held. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as mediacontrols$0 from "../mediacontrols/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * ChangeVolume adjusts the volume by a relative amount. + */ +export function ChangeVolume(deltaVolume: number): $CancellablePromise { + return $Call.ByID(742861629, deltaVolume); +} + +/** + * CurrentPosition returns the playback position as a percentage + * (0-100). + */ +export function CurrentPosition(): $CancellablePromise { + return $Call.ByID(2723620683); +} + +/** + * CurrentPositionSeconds returns the current playback position in + * display seconds. + */ +export function CurrentPositionSeconds(): $CancellablePromise { + return $Call.ByID(1423359008); +} + +/** + * EmitCurrentState pushes the current player state to the frontend. + * This is intended to be called after the frontend is ready to + * receive events, separately from RestoreState which does the heavy + * lifting during OnStartup. + */ +export function EmitCurrentState(): $CancellablePromise { + return $Call.ByID(2143346946); +} + +/** + * GetCurrentTrackInfo returns information about the currently + * loaded track. + */ +export function GetCurrentTrackInfo(): $CancellablePromise<$models.TrackInfo> { + return $Call.ByID(2615351053); +} + +/** + * InitSpeaker initializes the audio output device. This is + * separated from NewPlayer so the player struct can be created + * before wails.Run (for binding registration) while deferring + * hardware initialization to OnStartup. + */ +export function InitSpeaker(): $CancellablePromise { + return $Call.ByID(3317123670); +} + +/** + * IsPlaying reports whether the player is currently playing audio. + */ +export function IsPlaying(): $CancellablePromise { + return $Call.ByID(1219303535); +} + +/** + * LoadFile opens and decodes an audio file for playback. + */ +export function LoadFile(filePath: string): $CancellablePromise { + return $Call.ByID(2214941041, filePath); +} + +/** + * MuteToggle toggles the mute state. + */ +export function MuteToggle(): $CancellablePromise { + return $Call.ByID(2840614678); +} + +/** + * Muted reports whether playback is currently silenced. + */ +export function Muted(): $CancellablePromise { + return $Call.ByID(2954670878); +} + +/** + * Pause pauses the current playback. + */ +export function Pause(): $CancellablePromise { + return $Call.ByID(1402758839); +} + +/** + * Play starts or resumes audio playback. + */ +export function Play(): $CancellablePromise { + return $Call.ByID(3327176373); +} + +/** + * RestoreState loads the persisted player state from the database. + */ +export function RestoreState(): $CancellablePromise { + return $Call.ByID(318202560); +} + +/** + * SaveState persists the current player state to the database and + * waits for the write. This is called during shutdown to capture the + * final state, which is the one case that cannot be deferred. + */ +export function SaveState(): $CancellablePromise { + return $Call.ByID(3845072215); +} + +/** + * Seek jumps to a specific position in seconds. + */ +export function Seek(targetSeconds: number): $CancellablePromise { + return $Call.ByID(829056707, targetSeconds); +} + +/** + * SetMediaControls provides an OS media controls handler. When set, + * the player pushes metadata, playback state, volume, and seek + * notifications to the OS media overlay. + */ +export function SetMediaControls(h: mediacontrols$0.Handler): $CancellablePromise { + return $Call.ByID(1896286877, h); +} + +/** + * SetPlaybackFinishedHandler sets a callback invoked when a track + * finishes naturally. This allows the queue to drive auto-advance + * without circular imports. + */ +export function SetPlaybackFinishedHandler(handler: any): $CancellablePromise { + return $Call.ByID(2023377546, handler); +} + +/** + * SetVolume sets the playback volume (0-100), emits a + * VolumeChanged event, and persists the new level. + */ +export function SetVolume(desiredVolume: $models.UserVolume): $CancellablePromise { + return $Call.ByID(1375836663, desiredVolume); +} + +/** + * TrackLengthInSeconds returns the duration of the current track. + */ +export function TrackLengthInSeconds(): $CancellablePromise { + return $Call.ByID(3749851426); +} + +/** + * UnloadTrack tears down the current track, releasing the file and + * streamer chain. The player returns to the initial "no track + * loaded" state and emits events so the frontend clears its + * current-track display. + */ +export function UnloadTrack(): $CancellablePromise { + return $Call.ByID(3127425467); +} diff --git a/frontend/bindings/yellowjacket/backend/playlist/index.ts b/frontend/bindings/yellowjacket/backend/playlist/index.ts new file mode 100644 index 0000000..a91e6ce --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/playlist/index.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; + +export type { + CandidateTrack, + DuplicateCheckResult, + DuplicateTrackInfo, + FavoritesConfigProvider, + PhantomMatch, + PhantomSearchResult, + Summary, + Track, + WithTracks +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/playlist/models.ts b/frontend/bindings/yellowjacket/backend/playlist/models.ts new file mode 100644 index 0000000..b42bd03 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/playlist/models.ts @@ -0,0 +1,103 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * CandidateTrack represents a potential library match for a + * phantom track. + */ +export interface CandidateTrack { + "FilePath": string; + "Title": string; + "Artist": string; + "Album": string; + "Duration": string; + "Score": number; +} + +/** + * DuplicateCheckResult contains the outcome of checking for + * duplicate tracks in a playlist. + */ +export interface DuplicateCheckResult { + "Duplicates": DuplicateTrackInfo[] | null; + "Unique": string[] | null; +} + +/** + * DuplicateTrackInfo holds metadata for a track that already + * exists in a playlist. + */ +export interface DuplicateTrackInfo { + "FilePath": string; + "Title": string; + "Artist": string; + "Album": string; + "Duration": string; +} + +/** + * FavoritesConfigProvider is a narrow interface for reading and + * writing the default-playlist configuration. + */ +export type FavoritesConfigProvider = any; + +/** + * PhantomMatch represents a high-confidence pairing of a phantom + * track to a library track. + */ +export interface PhantomMatch { + "PhantomPath": string; + "PhantomTitle": string; + "Candidate": CandidateTrack; +} + +/** + * PhantomSearchResult contains auto-matched pairs and remaining + * unmatched phantom paths for a batch search operation. + */ +export interface PhantomSearchResult { + "AutoMatched": PhantomMatch[] | null; + "Unmatched": string[] | null; +} + +/** + * Summary is a lightweight representation of a playlist for the + * picker UI. + */ +export interface Summary { + "ID": number; + "Name": string; + "CreatedAt": string; + "UpdatedAt": string; + "IsSmart": boolean; +} + +/** + * Track represents a track within a playlist, including its + * metadata. + */ +export interface Track { + "ID": number; + "Position": number; + "FilePath": string; + "Title": string; + "Artist": string; + "Album": string; + "CoverArtPath": string; + "CoverArtSmall": string; + "CoverArtMedium": string; + "CoverArtLarge": string; + "Duration": string; + "Phantom": boolean; + "ArtistMBID": string; + "ReleaseGroupMBID": string; + "RecordingMBID": string; +} + +/** + * WithTracks contains a playlist summary and all its tracks. + */ +export interface WithTracks { + "Summary": Summary; + "Tracks": Track[] | null; +} diff --git a/frontend/bindings/yellowjacket/backend/playlist/service.ts b/frontend/bindings/yellowjacket/backend/playlist/service.ts new file mode 100644 index 0000000..cac9f7d --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/playlist/service.ts @@ -0,0 +1,336 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service manages playlist operations. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as library$0 from "../library/models.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * AddToDefaultPlaylist adds multiple tracks to the default + * playlist, skipping any that are already present. + */ +export function AddToDefaultPlaylist(filePaths: string[] | null): $CancellablePromise { + return $Call.ByID(1493850977, filePaths); +} + +/** + * AddTracksToPlaylist adds one or more tracks to an existing + * playlist. + */ +export function AddTracksToPlaylist(playlistID: number, filePaths: string[] | null): $CancellablePromise { + return $Call.ByID(2843242868, playlistID, filePaths); +} + +/** + * CreatePlaylist creates a new empty playlist with the given name. + */ +export function CreatePlaylist(name: string): $CancellablePromise<$models.Summary> { + return $Call.ByID(4080218878, name); +} + +/** + * CreatePlaylistWithTracks creates a new playlist and populates + * it with tracks. + */ +export function CreatePlaylistWithTracks(name: string, filePaths: string[] | null): $CancellablePromise<$models.Summary> { + return $Call.ByID(4079964334, name, filePaths); +} + +/** + * CreateSmartPlaylist creates a new smart playlist with the given + * name and JSON rule set. The rules are validated before storage. + */ +export function CreateSmartPlaylist(name: string, rulesJSON: string): $CancellablePromise<$models.Summary> { + return $Call.ByID(2020825359, name, rulesJSON); +} + +/** + * DeletePlaylist deletes a playlist and its M3U8 file. + * If the deleted playlist was the default, a new default + * playlist is automatically created. + */ +export function DeletePlaylist(playlistID: number): $CancellablePromise { + return $Call.ByID(1998239209, playlistID); +} + +/** + * EnsureDefaultPlaylist verifies the configured default playlist + * exists in the database. If the playlist is missing or no ID + * has been configured yet, a new playlist named "Favorites" is + * created and the config is updated. + */ +export function EnsureDefaultPlaylist(): $CancellablePromise { + return $Call.ByID(3280319649); +} + +/** + * EvaluateSmartPlaylist loads the rule set for a smart playlist + * from the database and evaluates it against the track library, + * returning the matching tracks. + */ +export function EvaluateSmartPlaylist(playlistID: number): $CancellablePromise { + return $Call.ByID(2192251014, playlistID); +} + +/** + * FindDuplicateTracksInPlaylist checks which of the given file + * paths already exist in the specified playlist. Returns metadata + * for each duplicate and a list of non-duplicate file paths. + */ +export function FindDuplicateTracksInPlaylist(playlistID: number, filePaths: string[] | null): $CancellablePromise<$models.DuplicateCheckResult> { + return $Call.ByID(57159613, playlistID, filePaths); +} + +/** + * FindPhantomMatches searches the library for matches for the + * given phantom file paths. High-confidence matches are returned + * as auto-matched pairs; the rest remain in the unmatched list. + */ +export function FindPhantomMatches(playlistID: number, phantomPaths: string[] | null): $CancellablePromise<$models.PhantomSearchResult> { + return $Call.ByID(2112903901, playlistID, phantomPaths); +} + +/** + * GetAllPlaylists returns all playlists ordered by most recently + * updated. + */ +export function GetAllPlaylists(): $CancellablePromise<$models.Summary[] | null> { + return $Call.ByID(1751004770); +} + +/** + * GetAllPlaylistsWithTracks returns all playlists with their + * tracks in a single call, merging phantom tracks from M3U8 files. + */ +export function GetAllPlaylistsWithTracks(): $CancellablePromise<$models.WithTracks[] | null> { + return $Call.ByID(1602859466); +} + +/** + * GetDefaultPlaylistInfo returns the ID and name of the default + * playlist for display in the frontend. + */ +export function GetDefaultPlaylistInfo(): $CancellablePromise<$models.Summary> { + return $Call.ByID(845855111); +} + +/** + * GetDefaultPlaylistTrackPaths returns the file paths of all + * tracks in the default playlist. + */ +export function GetDefaultPlaylistTrackPaths(): $CancellablePromise { + return $Call.ByID(135237414); +} + +/** + * GetPhantomCandidates returns scored candidate matches for a + * single phantom track. + */ +export function GetPhantomCandidates(playlistID: number, phantomPath: string): $CancellablePromise<$models.CandidateTrack[] | null> { + return $Call.ByID(904105323, playlistID, phantomPath); +} + +/** + * GetPlaylistTracks returns all tracks in a playlist with full + * metadata, merging phantom tracks from the M3U8 file. + */ +export function GetPlaylistTracks(playlistID: number): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(3997481994, playlistID); +} + +/** + * GetSmartPlaylistRules returns the raw JSON rule string for an + * existing smart playlist. This is used when the user opens the + * rule editor for an existing smart playlist. + */ +export function GetSmartPlaylistRules(playlistID: number): $CancellablePromise { + return $Call.ByID(997403286, playlistID); +} + +/** + * GetSmartPlaylistTracks returns the persisted snapshot of a smart + * playlist as regular playlist tracks (with resolved cover art and + * phantom entries), identical to a normal playlist. If the playlist + * has never been materialized — e.g. it predates snapshot support — + * it is evaluated and stored on first access. + */ +export function GetSmartPlaylistTracks(playlistID: number): $CancellablePromise<$models.Track[] | null> { + return $Call.ByID(2517005223, playlistID); +} + +/** + * ImportPlaylist imports a playlist from an external M3U/M3U8 + * file. It creates a new playlist in the DB, resolves tracks + * against the library, and saves an M3U8 file. + */ +export function ImportPlaylist(filePath: string): $CancellablePromise<$models.Summary> { + return $Call.ByID(3295216919, filePath); +} + +/** + * ImportPlaylists imports multiple playlists from external M3U/M3U8 + * files. Each file is imported sequentially using ImportPlaylist. + * Errors from individual imports are collected; partial success is + * possible. Returns the summaries of successfully imported playlists + * and the first error encountered (if any). + */ +export function ImportPlaylists(filePaths: string[] | null): $CancellablePromise<$models.Summary[] | null> { + return $Call.ByID(2505276524, filePaths); +} + +/** + * MaterializeUnmaterializedSmartPlaylists evaluates and snapshots any + * smart playlist that has never been materialized (smart_snapshot_at + * IS NULL) — e.g. playlists created before creation-time + * materialization existed. It runs once at startup and is idempotent: + * once every smart playlist has a snapshot it becomes a no-op. Errors + * on individual playlists are logged and skipped so one bad rule set + * doesn't block the rest. + */ +export function MaterializeUnmaterializedSmartPlaylists(): $CancellablePromise { + return $Call.ByID(3018316369); +} + +/** + * PreviewSmartPlaylist evaluates a rule set from raw JSON without + * requiring a saved playlist. This powers live preview in the rule + * editor — the frontend sends rules as they are being edited and + * receives matching tracks immediately. + */ +export function PreviewSmartPlaylist(rulesJSON: string): $CancellablePromise { + return $Call.ByID(3006325439, rulesJSON); +} + +/** + * RefreshSmartPlaylist re-evaluates a smart playlist's rules against + * the current library and replaces its persisted membership in + * playlist_tracks with the result. This is the only path that + * re-evaluates a smart playlist — opening one otherwise reads the + * stored snapshot. Triggered on rule save and by the manual Refresh + * button. + */ +export function RefreshSmartPlaylist(playlistID: number): $CancellablePromise { + return $Call.ByID(2225480718, playlistID); +} + +/** + * RemoveFromDefaultPlaylist removes multiple tracks from the + * default playlist. + */ +export function RemoveFromDefaultPlaylist(filePaths: string[] | null): $CancellablePromise { + return $Call.ByID(619411935, filePaths); +} + +/** + * RemovePhantomTracks removes phantom entries from a playlist's + * M3U8 file. Since phantom tracks have no DB rows, only the + * M3U8 file is modified. + */ +export function RemovePhantomTracks(playlistID: number, phantomPaths: string[] | null): $CancellablePromise { + return $Call.ByID(823798913, playlistID, phantomPaths); +} + +/** + * RemoveTracksFromPlaylist removes multiple tracks from a playlist + * by their playlist_track IDs. + */ +export function RemoveTracksFromPlaylist(playlistID: number, trackIDs: number[] | null): $CancellablePromise { + return $Call.ByID(2519855654, playlistID, trackIDs); +} + +/** + * RenamePlaylist renames a playlist and updates its M3U8 file. + */ +export function RenamePlaylist(playlistID: number, newName: string): $CancellablePromise { + return $Call.ByID(1835045264, playlistID, newName); +} + +/** + * RepopulateFromM3U re-imports tracks for playlists that have zero + * playlist_tracks rows but still have a corresponding M3U8 file. + * This recovers from a FullRescan that deleted playlist tracks + * before the ON DELETE SET NULL fix was in place. Each M3U8 entry + * is resolved against the audio_files table; unresolved entries + * become phantom tracks with metadata preserved from the M3U8. + */ +export function RepopulateFromM3U(): $CancellablePromise { + return $Call.ByID(3399290718); +} + +/** + * ResolvePhantomTracks replaces phantom entries in a playlist + * with real library tracks. The matches map keys are phantom + * absolute paths and values are resolved absolute paths. + */ +export function ResolvePhantomTracks(playlistID: number, matches: { [_ in string]?: string } | null): $CancellablePromise { + return $Call.ByID(1239473037, playlistID, matches); +} + +/** + * ResolvePhantomTracksAfterScan re-links phantom playlist tracks + * whose files now exist in the library. It iterates each playlist + * that has phantoms, reads its M3U8 file, resolves each entry + * against the current audio_files table using multi-root path + * resolution, and updates matching phantom playlist_tracks. This + * handles both pre-existing phantoms (created before migration 7, + * with NULL phantom_file_path) and new ones. + */ +export function ResolvePhantomTracksAfterScan(): $CancellablePromise { + return $Call.ByID(3836161100); +} + +/** + * RestoreAllPlaylists restores playlist tracks from M3U8 files. + * This is called after a full library rescan to repopulate + * playlist_tracks from the surviving M3U8 files. + */ +export function RestoreAllPlaylists(): $CancellablePromise { + return $Call.ByID(453794624); +} + +/** + * SearchLibrary searches the entire library by a free-text query + * for manual phantom resolution. + */ +export function SearchLibrary(query: string): $CancellablePromise<$models.CandidateTrack[] | null> { + return $Call.ByID(3912116995, query); +} + +/** + * SetFavoritesConfig sets the provider used to read and write + * the default-playlist configuration. + */ +export function SetFavoritesConfig(provider: $models.FavoritesConfigProvider): $CancellablePromise { + return $Call.ByID(2117418507, provider); +} + +/** + * ToggleDefaultPlaylistTrack adds or removes a single track + * from the default playlist. Returns true if the track is now + * in the playlist (was added), false if it was removed. + */ +export function ToggleDefaultPlaylistTrack(filePath: string): $CancellablePromise { + return $Call.ByID(450278306, filePath); +} + +/** + * UpdateSmartPlaylistRules updates the rule set for an existing + * smart playlist. Returns an error if the playlist does not exist + * or is not a smart playlist. + */ +export function UpdateSmartPlaylistRules(playlistID: number, rulesJSON: string): $CancellablePromise { + return $Call.ByID(2746792411, playlistID, rulesJSON); +} diff --git a/frontend/bindings/yellowjacket/backend/queue/index.ts b/frontend/bindings/yellowjacket/backend/queue/index.ts new file mode 100644 index 0000000..308c4e6 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/queue/index.ts @@ -0,0 +1,19 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Queue from "./queue.js"; +export { + Queue +}; + +export { + RepeatMode +} from "./models.js"; + +export type { + FallbackSource, + Source, + State, + Track, + TrackLoader +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/queue/models.ts b/frontend/bindings/yellowjacket/backend/queue/models.ts new file mode 100644 index 0000000..8c45745 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/queue/models.ts @@ -0,0 +1,72 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * FallbackSource resolves what should auto-play, if anything, once the + * queue is exhausted. Implemented outside this package (see app.go) so + * the queue does not need to know about config, playlists or + * similarity data. + */ +export type FallbackSource = any; + +/** + * RepeatMode represents the queue repeat behavior. + */ +export enum RepeatMode { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Repeat mode values. + */ + RepeatOff = "off", + RepeatAll = "all", + RepeatOne = "one", +}; + +/** + * Source describes the collection a queue was built from — an album, a + * playlist, a genre, an artist — so the frontend can offer to navigate + * back to it. An empty Type means the queue has no single source (the + * whole library, or one ad-hoc track). + */ +export interface Source { + "type": string; + "id": number; + "label": string; +} + +/** + * State is the full state emitted to the frontend. + */ +export interface State { + "tracks": Track[] | null; + "currentIndex": number; + "shuffleMode": boolean; + "repeatMode": RepeatMode; + "source": Source; +} + +/** + * Track represents a track in the queue with its metadata. + */ +export interface Track { + "id": number; + "audioFileId": number; + "filePath": string; + "position": number; + "title": string; + "artist": string; + "album": string; + "coverArtPath": string; + "artistMbid": string; + "releaseGroupMbid": string; + "recordingMbid": string; +} + +/** + * TrackLoader is the interface the queue uses to tell the player to load a file. + */ +export type TrackLoader = any; diff --git a/frontend/bindings/yellowjacket/backend/queue/queue.ts b/frontend/bindings/yellowjacket/backend/queue/queue.ts new file mode 100644 index 0000000..873ec28 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/queue/queue.ts @@ -0,0 +1,219 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Queue manages an ordered list of tracks for playback. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * AddTrack appends a track to the end of the queue. + * If the queue was empty, it loads the added track in a paused state. + */ +export function AddTrack(filePath: string): $CancellablePromise { + return $Call.ByID(3566864127, filePath); +} + +/** + * AddTracks appends multiple tracks to the end of the queue. + * If the queue was empty, it loads the first added track in a paused state. + */ +export function AddTracks(filePaths: string[] | null): $CancellablePromise { + return $Call.ByID(980962916, filePaths); +} + +/** + * Clear removes all tracks from the queue, stops playback, and + * resets the queue state. It persists the cleared state and + * notifies the frontend. + */ +export function Clear(): $CancellablePromise { + return $Call.ByID(4166243766); +} + +/** + * CompactAfterLibraryRemoval reloads queue state from the database + * after a library removal has cascade-deleted queue_tracks rows. + * It resets currentIndex to 0 (or -1 if empty), clears shuffleOrder, + * unloads the current track if it was removed, and emits QueueChanged. + */ +export function CompactAfterLibraryRemoval(): $CancellablePromise { + return $Call.ByID(3620379645); +} + +/** + * CycleRepeat cycles through repeat modes: off -> all -> one -> off. + */ +export function CycleRepeat(): $CancellablePromise { + return $Call.ByID(3510519482); +} + +/** + * EmitCurrentState emits the current queue state to the frontend. + * This is called after the frontend DOM is ready. + */ +export function EmitCurrentState(): $CancellablePromise { + return $Call.ByID(1245022280); +} + +/** + * GetState returns the current queue state for the frontend. + */ +export function GetState(): $CancellablePromise<$models.State> { + return $Call.ByID(3472178852); +} + +/** + * InsertNext inserts a track right after the currently playing track. + * If the queue was empty, it loads the inserted track in a paused state. + */ +export function InsertNext(filePath: string): $CancellablePromise { + return $Call.ByID(1498463573, filePath); +} + +/** + * InsertNextTracks inserts multiple tracks as a contiguous block after the current track. + * If the queue was empty, it loads the first inserted track in a paused state. + */ +export function InsertNextTracks(filePaths: string[] | null): $CancellablePromise { + return $Call.ByID(2824705517, filePaths); +} + +/** + * InsertTracksAt inserts multiple tracks at the given index. + * If the queue was empty, it loads the first inserted track in a paused state. + */ +export function InsertTracksAt(filePaths: string[] | null, index: number): $CancellablePromise { + return $Call.ByID(2550945669, filePaths, index); +} + +/** + * MoveQueueTracks moves tracks at the given indices to a new position + * as a contiguous block. The toIndex is the target position in the + * original (pre-move) array. + */ +export function MoveQueueTracks(fromIndices: number[] | null, toIndex: number): $CancellablePromise { + return $Call.ByID(2149753655, fromIndices, toIndex); +} + +/** + * Next advances to the next track. If the player was paused, the next + * track is loaded but not played. In RepeatOne mode, the current track + * is replayed instead of advancing. + */ +export function Next(): $CancellablePromise { + return $Call.ByID(1968784044); +} + +/** + * OnPlaybackFinished is called when a track finishes playing naturally. + * This drives the auto-advance behavior and records the play. + */ +export function OnPlaybackFinished(): $CancellablePromise { + return $Call.ByID(2184869763); +} + +/** + * Play handles a play request by either resuming the current track or + * starting playback from the beginning of the queue. When a track is + * already active (currentIndex != -1) the player is told to resume; + * otherwise playback starts from the first track (or a random one when + * shuffle is enabled). + */ +export function Play(): $CancellablePromise { + return $Call.ByID(3197379127); +} + +/** + * PlayIndex jumps to and plays the track at the given index. + */ +export function PlayIndex(index: number): $CancellablePromise { + return $Call.ByID(200440797, index); +} + +/** + * Previous goes to the previous track (or restarts current if >3s in). + * If the player was paused, the track is loaded but not played. + * In RepeatOne mode, the current track is replayed instead of navigating. + */ +export function Previous(): $CancellablePromise { + return $Call.ByID(708701476); +} + +/** + * RemoveTrack removes a track at the given position from the queue. + */ +export function RemoveTrack(position: number): $CancellablePromise { + return $Call.ByID(6811080, position); +} + +/** + * RemoveTracks removes multiple tracks at the given positions from the queue. + * Positions are deduplicated, validated, and removed in descending order so + * that indices remain stable during removal. + */ +export function RemoveTracks(positions: number[] | null): $CancellablePromise { + return $Call.ByID(1587232097, positions); +} + +/** + * RestoreState loads the queue state from the database. + */ +export function RestoreState(): $CancellablePromise { + return $Call.ByID(1652105446); +} + +/** + * SaveState persists the queue state to the database. Unlike every + * other write here it waits for the writer: its callers are shutdown + * and the tests, both of which need the row to exist on return. + */ +export function SaveState(): $CancellablePromise { + return $Call.ByID(2913531465); +} + +/** + * SetFallbackSource provides the queue with what to auto-play, if + * anything, once it runs out. A nil source (the default) leaves + * today's behavior: the queue just goes idle. + */ +export function SetFallbackSource(fs: $models.FallbackSource): $CancellablePromise { + return $Call.ByID(1344903240, fs); +} + +/** + * SetPlayer provides the queue with a reference to the player for auto-advance. + */ +export function SetPlayer(player: $models.TrackLoader): $CancellablePromise { + return $Call.ByID(2806250342, player); +} + +/** + * SetQueue replaces the entire queue with new tracks and starts playing. + * When shuffleStart is true and shuffle mode is active, a random first + * track is chosen instead of the one at startIndex. This is intended for + * "Play All" type actions where no specific track was selected. + * It uses a two-phase approach: the first batch of tracks (up to + * initialBatchSize) is resolved immediately so playback begins and the + * queue panel is populated without delay. The remaining tracks are then + * resolved in the background. A generation counter ensures stale + * background work is discarded if SetQueue is called again. + */ +export function SetQueue(filePaths: string[] | null, startIndex: number, shuffleStart: boolean, source: $models.Source): $CancellablePromise { + return $Call.ByID(1990450094, filePaths, startIndex, shuffleStart, source); +} + +/** + * ToggleShuffle toggles shuffle mode on/off. + */ +export function ToggleShuffle(): $CancellablePromise { + return $Call.ByID(612647562); +} diff --git a/frontend/bindings/yellowjacket/backend/tagwriter/index.ts b/frontend/bindings/yellowjacket/backend/tagwriter/index.ts new file mode 100644 index 0000000..8e12c8e --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/tagwriter/index.ts @@ -0,0 +1,13 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as TagWriter from "./tagwriter.js"; +export { + TagWriter +}; + +export type { + BatchFailure, + BatchResult, + TagChanges +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/tagwriter/models.ts b/frontend/bindings/yellowjacket/backend/tagwriter/models.ts new file mode 100644 index 0000000..099bbd2 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/tagwriter/models.ts @@ -0,0 +1,28 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * BatchFailure records a single track that failed during a batch write. + */ +export interface BatchFailure { + "filePath": string; + "error": string; +} + +/** + * BatchResult summarises the outcome of a batch tag write. + */ +export interface BatchResult { + "total": number; + "succeeded": number; + "failed": number; + "cancelled": boolean; + "failures": BatchFailure[] | null; +} + +/** + * TagChanges is a diff map of field name → new value. Only changed + * fields are present. Callers specify changed fields; unchanged + * fields are left as-is in the file. + */ +export type TagChanges = { [_ in string]?: any } | null; diff --git a/frontend/bindings/yellowjacket/backend/tagwriter/tagwriter.ts b/frontend/bindings/yellowjacket/backend/tagwriter/tagwriter.ts new file mode 100644 index 0000000..546d4e4 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/tagwriter/tagwriter.ts @@ -0,0 +1,80 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * TagWriter orchestrates the complete tag writing pipeline: + * file write → DB sync → event emission. + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * BatchWriteTrackTags applies the same TagChanges to every file in + * filePaths. It processes tracks sequentially, emits a + * BatchWriteProgress event after each track, and continues past + * individual failures. Returns a BatchResult summarising outcomes. + */ +export function BatchWriteTrackTags(filePaths: string[] | null, changes: $models.TagChanges): $CancellablePromise<$models.BatchResult> { + return $Call.ByID(2772510056, filePaths, changes); +} + +/** + * CancelBatchWrite signals the in-progress batch write to stop after + * the current track completes. + */ +export function CancelBatchWrite(): $CancellablePromise { + return $Call.ByID(3075727604); +} + +/** + * WriteTrackTags is the single entry point for writing metadata to + * a track's audio file and synchronising all changes to the + * database. It accepts a track ID (audio_file.id) and a diff map + * of changed fields. + * + * The pipeline: + * 1. Look up track from DB. + * 2. Detect audio format. + * 3. Acquire pipeline lock (mutual exclusion with scan). + * 4. Stop player if this file is currently playing. + * 5. Write tags to file (format-specific writer). + * 6. Sync database (entity relink, FTS5, orphan cleanup). + * 7. Emit TrackMetadataChanged event. + */ +export function WriteTrackTags(trackID: number, changes: $models.TagChanges): $CancellablePromise { + return $Call.ByID(1881385878, trackID, changes); +} + +/** + * WriteTrackTagsByPath resolves a file path to its audio_file.id and + * delegates to WriteTrackTags. This is the frontend-facing entry + * point since the frontend identifies tracks by FilePath. + */ +export function WriteTrackTagsByPath(filePath: string, changes: $models.TagChanges): $CancellablePromise { + return $Call.ByID(3526432184, filePath, changes); +} + +/** + * WriteUntrackedFileTags writes tags to a file that is not in the + * library, skipping every step of the full pipeline that assumes it is: + * no audio_file lookup, no database sync, no event. + * + * This exists for the download import path, which tags files while they + * are still in the staging directory. Tagging before the move is what + * makes the import atomic from the library's point of view — the + * scanner only ever sees a finished, correctly tagged file, instead of + * ingesting a mislabelled one and being corrected afterwards. + * + * Callers are responsible for ensuring the file is not in a library + * path; using this on a tracked file would leave the database stale. + */ +export function WriteUntrackedFileTags(filePath: string, changes: $models.TagChanges): $CancellablePromise { + return $Call.ByID(3625602876, filePath, changes); +} diff --git a/frontend/bindings/yellowjacket/backend/tracklist/index.ts b/frontend/bindings/yellowjacket/backend/tracklist/index.ts new file mode 100644 index 0000000..b7307f1 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/tracklist/index.ts @@ -0,0 +1,10 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + ColumnID +} from "./models.js"; + +export type { + Column +} from "./models.js"; diff --git a/frontend/bindings/yellowjacket/backend/tracklist/models.ts b/frontend/bindings/yellowjacket/backend/tracklist/models.ts new file mode 100644 index 0000000..2d6b0a8 --- /dev/null +++ b/frontend/bindings/yellowjacket/backend/tracklist/models.ts @@ -0,0 +1,41 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Column represents a visible column in the track list. + */ +export interface Column { + "id": ColumnID; +} + +/** + * ColumnID identifies a displayable column in the track list. + */ +export enum ColumnID { + /** + * The Go zero value for the underlying type of the enum. + */ + $zero = "", + + /** + * Valid column identifiers. + */ + ColTrackName = "trackName", + ColArtistName = "artistName", + ColTrackLength = "trackLength", + ColAlbum = "album", + ColGenre = "genre", + ColYear = "year", + ColComposer = "composer", + ColTrackNumber = "trackNumber", + ColDiscNumber = "discNumber", + ColFilePath = "filePath", + ColFileType = "fileType", + ColSampleRate = "sampleRate", + ColBitDepth = "bitDepth", + ColChannels = "channels", + ColBitrate = "bitrate", + ColFileSize = "fileSize", + ColPlayCount = "playCount", + ColAlbumArt = "albumArt", +}; diff --git a/frontend/index.ts b/frontend/index.ts index bb4ea94..095ca2b 100644 --- a/frontend/index.ts +++ b/frontend/index.ts @@ -39,9 +39,9 @@ import { setBasePath } from '@awesome.me/webawesome/dist/webawesome.js'; import { registerBundledIcons } from './src/icons'; import { queueStore } from '@store/queue-store'; import { searchStore } from '@store/search-store'; -import * as Player from '@go/player/Player'; -import * as Queue from '@go/queue/Queue'; -import { GetDefaultPage } from '@go/config/Config'; +import * as Player from '@go/player/player.js'; +import * as Queue from '@go/queue/queue.js'; +import { GetDefaultPage } from '@go/config/config.js'; // Importing the theme store triggers initialization: it fetches the saved // theme from the backend and applies CSS custom properties to :root. import '@store/theme-store'; diff --git a/frontend/package.json b/frontend/package.json index 82eacfa..69fadc0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,6 +8,7 @@ "@awesome.me/webawesome": "^3.2.1", "@lit-labs/signals": "^0.2.0", "@lit-labs/virtualizer": "^2.1.1", + "@wailsio/runtime": "3.0.0-beta.8", "lit": "^3.2.1" }, "devDependencies": { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index d22e22d..a45f412 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@lit-labs/virtualizer': specifier: ^2.1.1 version: 2.1.1 + '@wailsio/runtime': + specifier: 3.0.0-beta.8 + version: 3.0.0-beta.8 lit: specifier: ^3.2.1 version: 3.3.2 @@ -543,6 +546,9 @@ packages: '@vscode/web-custom-data@0.4.13': resolution: {integrity: sha512-2ZUIRfhofZ/npLlf872EBnPmn27Kt4M2UssmQIfnJvgGgMYZJ5fvtHEDnttBBf2hnVtBgNCqZMVHJA+wsFVqTA==} + '@wailsio/runtime@3.0.0-beta.8': + resolution: {integrity: sha512-c9PZJcOR9z1a6cxtBS2q5cygNajlxDE8Oxv/vvcTFYRbZSoOGBszq4uzlkPTOd0Bot1xkM81jnpaBgKWRpBh2g==} + ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -1734,6 +1740,8 @@ snapshots: '@vscode/web-custom-data@0.4.13': {} + '@wailsio/runtime@3.0.0-beta.8': {} + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 diff --git a/frontend/src/components/artist-details/artist-details.ts b/frontend/src/components/artist-details/artist-details.ts index e829125..0a8039f 100644 --- a/frontend/src/components/artist-details/artist-details.ts +++ b/frontend/src/components/artist-details/artist-details.ts @@ -4,13 +4,13 @@ import { property, state, } from 'lit/decorators.js'; -import { library } from '@go/models'; +import * as library from '@go/library/models.js'; import { LibraryController } from '@store/controllers/library-controller'; import { GetArtistImageURL, GetArtistImageCachedPath, GetArtistMBID, -} from '@go/explore/Service'; +} from '@go/explore/service.js'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; import '@components/cover-grid/cover-grid.js'; import { designTokens } from '../../styles/tokens.css'; diff --git a/frontend/src/components/artists-view/artists-view.ts b/frontend/src/components/artists-view/artists-view.ts index d67f918..23bc1b9 100644 --- a/frontend/src/components/artists-view/artists-view.ts +++ b/frontend/src/components/artists-view/artists-view.ts @@ -14,8 +14,8 @@ import { GetAlbumsByArtist, GetAlbumsByArtistByLibrary, GetFilePathsByAlbums, -} from '@go/library/Library'; -import { library } from '@go/models'; +} from '@go/library/library.js'; +import * as library from '@go/library/models.js'; import { LibraryController } from '@store/controllers/library-controller'; import { libraryStore } from '@store/library-store'; import { SearchController } from '@store/controllers/search-controller'; @@ -36,6 +36,7 @@ import '@awesome.me/webawesome/dist/components/popup/popup.js'; import type WaPopup from '@awesome.me/webawesome/dist/components/popup/popup.js'; import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js'; import '@components/playlist-picker/playlist-picker.js'; +import { dict, list } from '@utils/binding'; /** Pixels to change card width per scroll tick. */ const ZOOM_STEP = 16; @@ -1055,18 +1056,21 @@ export class ArtistsView const libId = this.libraryCtrl.selectedLibraryId; - const albums = libId !== null - ? await GetAlbumsByArtistByLibrary( - artist.ID, - libId, - ) - : await GetAlbumsByArtist(artist.ID); + const albums = await list( + libId !== null + ? GetAlbumsByArtistByLibrary( + artist.ID, + libId, + ) + : GetAlbumsByArtist(artist.ID), + ); - const byAlbum = - await GetFilePathsByAlbums( + const byAlbum = await dict( + GetFilePathsByAlbums( albums.map((a) => a.ID), libId ?? 0, - ); + ), + ); const allPaths: string[] = []; diff --git a/frontend/src/components/autotag-view/autotag-view.ts b/frontend/src/components/autotag-view/autotag-view.ts index 86cd4a2..2d4db90 100644 --- a/frontend/src/components/autotag-view/autotag-view.ts +++ b/frontend/src/components/autotag-view/autotag-view.ts @@ -17,8 +17,8 @@ import { ClearCompletedEntries, SearchCandidates, SelectSearchCandidate, -} from '@go/autotagservice/Service'; -import type { autotagservice } from '@go/models'; +} from '@go/autotagservice/service.js'; +import type * as autotagservice from '@go/autotagservice/models.js'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; import { inlineDiff, normalizeStrict, isCosmeticDiff } from '../../utils/text-diff'; @@ -29,6 +29,7 @@ import { nameDialogsIn } from '../../utils/name-dialog'; import { ViewLifecycleMixin } from '../../utils/view-lifecycle'; import { confirmAction } from '../confirm-dialog/confirm-dialog'; import '@awesome.me/webawesome/dist/components/dialog/dialog.js'; +import { list } from '@utils/binding'; type PendingItem = autotagservice.PendingItem; type ScoreView = autotagservice.ScoreView; @@ -1483,8 +1484,10 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { this.searchError = ''; this.searchRan = true; try { - this.searchResults = await SearchCandidates( - this.searchKind, query, this.searchArtist.trim(), + this.searchResults = await list( + SearchCandidates( + this.searchKind, query, this.searchArtist.trim(), + ), ); } catch (err) { console.error('autotag: candidate search failed', err); @@ -1755,12 +1758,12 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { } private topScore(): number { - if (!this.score || this.score.candidates.length === 0) return 0; - return this.score.candidates[0]?.score ?? 0; + if (!this.score || (this.score.candidates ?? []).length === 0) return 0; + return (this.score.candidates ?? [])[0]?.score ?? 0; } private async onApply(): Promise { - if (!this.current || !this.score || this.score.candidates.length === 0) return; + if (!this.current || !this.score || (this.score.candidates ?? []).length === 0) return; if (!this.hasLibraryWarningBeenAcked(this.current.libraryId)) { await this.confirmWarningThenApply(); @@ -1773,7 +1776,7 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { private async executeApply(): Promise { if (!this.current || !this.score) return; - const cand = this.score.candidates[this.selectedCandidateIdx]; + const cand = (this.score.candidates ?? [])[this.selectedCandidateIdx]; if (!cand) { this.errorMessage = 'No candidate selected.'; return; @@ -1781,7 +1784,7 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { const groupKey = this.current.groupKey; const mbid = cand.releaseMbid || cand.releaseGroupMbid; - const total = this.score.localTracks.length; + const total = (this.score.localTracks ?? []).length; // Pre-mark the folder as running so the sidebar icon flips // to the progress ring immediately — the Started event will @@ -1955,16 +1958,16 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { */ private currentCandidate(): CandidateView | null { if (!this.score) return null; - return this.score.candidates[this.selectedCandidateIdx] ?? null; + return (this.score.candidates ?? [])[this.selectedCandidateIdx] ?? null; } private async selectCandidateByIdx(idx: number): Promise { - if (!this.score || idx < 0 || idx >= this.score.candidates.length) return; + if (!this.score || idx < 0 || idx >= (this.score.candidates ?? []).length) return; this.selectedCandidateIdx = idx; // Lazy-load cover art for non-top candidates. The backend // populates art for the top one eagerly; everything else // arrives empty until the user picks it. - const cand = this.score.candidates[idx]!; + const cand = (this.score.candidates ?? [])[idx]!; if (!cand.coverArtUrl) { try { const url = await GetCandidateCoverArt( @@ -2252,7 +2255,7 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { if (!cluster || cluster.candidates.length <= 1) return nothing; const editions = cluster.candidates - .map((c) => ({ cand: c, idx: this.score?.candidates.indexOf(c) ?? -1 })) + .map((c) => ({ cand: c, idx: (this.score?.candidates ?? []).indexOf(c) ?? -1 })) .filter((e) => e.idx >= 0); return html` @@ -2316,11 +2319,11 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { // the score forgives, the UI mutes. const SUBTLE_LENGTH_MAX_MS = 5000; - for (const a of cand.alignments) { + for (const a of (cand.alignments ?? [])) { if (a.status === 'matched' || a.status === 'mismatched') { paired++; const local = a.localIndex >= 0 - ? this.score?.localTracks[a.localIndex] ?? null + ? (this.score?.localTracks ?? [])[a.localIndex] ?? null : null; if (local) { if (local.title !== a.candidateTitle) { @@ -2374,7 +2377,7 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { missingTitles.push(a.candidateTitle || '(untitled)'); } else if (a.status === 'unmatched') { const local = a.localIndex >= 0 - ? this.score?.localTracks[a.localIndex] ?? null + ? (this.score?.localTracks ?? [])[a.localIndex] ?? null : null; extraTitles.push(a.localTitle || local?.title || '(untitled)'); } @@ -2659,7 +2662,7 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { } private renderLowConfidenceBanner(clusters: VersionCluster[]) { - const top = this.score?.candidates[0]; + const top = (this.score?.candidates ?? [])[0]; if (!top) return nothing; if (top.score >= LOW_CONFIDENCE_THRESHOLD) return nothing; if (clusters.length < 2) return nothing; @@ -2695,18 +2698,18 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { * renderMatchDetails, so each column shows its own values plainly. */ private renderComparison(cand: CandidateView, clusters: VersionCluster[] = []) { - const locals = this.score?.localTracks ?? []; + const locals = (this.score?.localTracks ?? []) ?? []; // localIndex -> its alignment, so a folder row knows whether it // paired and (if so) how confidently. const alignByLocal = new Map(); - for (const a of cand.alignments) { + for (const a of (cand.alignments ?? [])) { if (a.localIndex >= 0) alignByLocal.set(a.localIndex, a); } // Candidate side: every alignment that has a candidate track // (paired or missing-from-folder), in candidate order. - const candRows = cand.alignments + const candRows = (cand.alignments ?? []) .filter((a) => a.status !== 'unmatched' && a.candidatePosition > 0) .slice() .sort((x, y) => @@ -2856,7 +2859,7 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { ${clusters.map((cluster) => { const best = cluster.candidates[cluster.bestIdx]!; const active = cluster.candidates.includes(activeCand); - const idx = this.score?.candidates.indexOf(best) ?? -1; + const idx = (this.score?.candidates ?? []).indexOf(best) ?? -1; return html`
@@ -2965,7 +2968,7 @@ export class AutotagView extends ViewLifecycleMixin(LitElement) { return html`
Candidate index out of range.
`; } - const clusters = this.clusterVersions(this.score.candidates); + const clusters = this.clusterVersions((this.score.candidates ?? [])); return html`
diff --git a/frontend/src/components/config-page/config-page.ts b/frontend/src/components/config-page/config-page.ts index 04427f5..986ac62 100644 --- a/frontend/src/components/config-page/config-page.ts +++ b/frontend/src/components/config-page/config-page.ts @@ -2,14 +2,14 @@ import { LitElement, html, css, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { EventsOn } from '@runtime/runtime'; -import type { explore } from '@go/models'; +import type * as explore from '@go/explore/models.js'; import { AddLibrary, RenameLibrary, RemoveLibrary, GetRemovalImpact, GetAllLibrariesWithTrackCounts, -} from '@go/library/Library'; +} from '@go/library/library.js'; import { GetScanConcurrency, SetScanConcurrency, @@ -17,17 +17,17 @@ import { SetDefaultPage, GetQueueFallback, SetQueueFallback, -} from '@go/config/Config'; -import { GetIndexStatus } from '@go/explore/Service'; -import { DirectoryPicker } from '@go/frontendutil/FrontendUtil'; +} from '@go/config/config.js'; +import { GetIndexStatus } from '@go/explore/service.js'; +import { DirectoryPicker } from '@go/frontendutil/frontendutil.js'; import { notificationStore } from '@store/notification-store'; import { describeError, explainError } from '@utils/describe-error'; -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { ThemeController } from '@store/controllers/theme-controller'; import { TrackListController } from '@store/controllers/tracklist-controller'; import { FavoritesController } from '@store/controllers/favorites-controller'; -import { GetAllPlaylists } from '@go/playlist/Service'; -import type { playlist } from '@go/models'; +import { GetAllPlaylists } from '@go/playlist/service.js'; +import type * as playlist from '@go/playlist/models.js'; import { Events } from '../../events'; import { SHORTCUT_CATEGORIES, @@ -49,6 +49,7 @@ import './shortcut-capture'; import { confirmAction } from '../confirm-dialog/confirm-dialog'; import { shortcutsStore } from '../../store/shortcuts-store'; import { ShortcutsController } from '../../store/controllers/shortcuts-controller'; +import { list } from '@utils/binding'; const SCROLL_STORAGE_KEY = 'yj-now-playing-scroll-mode'; const SCROLL_CHANGE_EVENT = 'yj-scroll-mode-changed'; @@ -1012,9 +1013,9 @@ export class ConfigPage extends ViewLifecycleMixin(LitElement) { const ok = await confirmAction({ title: 'Remove library', message: `Remove “${libName}”?`, - impact: `This deletes ${impact.trackCount} tracks, affects ` - + `${impact.playlistsAffected} playlists and removes ` - + `${impact.queueItemCount} queue items.`, + impact: `This deletes ${impact?.trackCount ?? 0} tracks, affects ` + + `${impact?.playlistsAffected ?? 0} playlists and removes ` + + `${impact?.queueItemCount ?? 0} queue items.`, confirmLabel: 'Remove', danger: true, }); @@ -1187,8 +1188,7 @@ export class ConfigPage extends ViewLifecycleMixin(LitElement) { private async loadPlaylists(): Promise { try { - this.playlists = - await GetAllPlaylists(); + this.playlists = await list(GetAllPlaylists()); } catch (err) { console.error( 'Failed to load playlists:', @@ -1456,10 +1456,10 @@ export class ConfigPage extends ViewLifecycleMixin(LitElement) { updated ${this.timeAgo(s.lastBuilt)}` : nothing}
- ${s.tiers?.length > 0 && s.tiers.some((t) => t.state === 'running' || t.state === 'pending' || t.state === 'error') + ${(s.tiers?.length ?? 0) > 0 && (s.tiers ?? []).some((t) => t.state === 'running' || t.state === 'pending' || t.state === 'error') ? html`
- ${s.tiers.map( + ${(s.tiers ?? []).map( (t) => html`
${this.tierIcon(t.state)} diff --git a/frontend/src/components/config-page/download-clients.ts b/frontend/src/components/config-page/download-clients.ts index 3581e60..cdeb150 100644 --- a/frontend/src/components/config-page/download-clients.ts +++ b/frontend/src/components/config-page/download-clients.ts @@ -14,10 +14,12 @@ import type { ProviderField, } from '@store/download-store'; import { downloadStore } from '@store/download-store'; -import { DirectoryPicker } from '@go/frontendutil/FrontendUtil'; -import { GetDownloadPreferences, SetDownloadPreferences } from '@go/config/Config'; -import { SetPreferences } from '@go/download/Service'; -import type { download } from '@go/models'; +import { DirectoryPicker } from '@go/frontendutil/frontendutil.js'; +import { GetDownloadPreferences, SetDownloadPreferences } from '@go/config/config.js'; +import { SetPreferences } from '@go/download/service.js'; +import type * as download from '@go/download/models.js'; +import { Format } from '@go/download/models.js'; +import { compact } from '@utils/binding'; import { describeError, explainError } from '@utils/describe-error'; import { confirmAction } from '@components/confirm-dialog/confirm-dialog'; import './config-section'; @@ -28,15 +30,15 @@ import './config-section'; * deliberately excluded — it names "no format detected", not a format a * user could opt into. */ -const AUTO_DOWNLOAD_FORMATS: { value: string; label: string }[] = [ - { value: 'flac', label: 'FLAC' }, - { value: 'alac', label: 'ALAC' }, - { value: 'wav', label: 'WAV' }, - { value: 'mp3', label: 'MP3' }, - { value: 'aac', label: 'AAC' }, - { value: 'ogg', label: 'OGG' }, - { value: 'opus', label: 'Opus' }, - { value: 'wma', label: 'WMA' }, +const AUTO_DOWNLOAD_FORMATS: { value: Format; label: string }[] = [ + { value: Format.FormatFLAC, label: 'FLAC' }, + { value: Format.FormatALAC, label: 'ALAC' }, + { value: Format.FormatWAV, label: 'WAV' }, + { value: Format.FormatMP3, label: 'MP3' }, + { value: Format.FormatAAC, label: 'AAC' }, + { value: Format.FormatOGG, label: 'OGG' }, + { value: Format.FormatOpus, label: 'Opus' }, + { value: Format.FormatWMA, label: 'WMA' }, ]; /** @@ -607,7 +609,7 @@ export class DownloadClients extends LitElement { // Secrets are never sent back to the frontend, so their fields // start blank; a blank secret on save means "leave it alone" // rather than "clear it". - this.draft = { ...(provider.settings ?? {}) }; + this.draft = compact(provider.settings); } private cancelEdit = () => { @@ -748,7 +750,7 @@ export class DownloadClients extends LitElement { } } - private toggleFormat(format: string, checked: boolean): void { + private toggleFormat(format: Format, checked: boolean): void { const current = this.prefs.allowedFormats ?? []; const allowedFormats = checked ? [...current, format] diff --git a/frontend/src/components/cover-grid/album-dropdown.ts b/frontend/src/components/cover-grid/album-dropdown.ts index f81df2e..274d33e 100644 --- a/frontend/src/components/cover-grid/album-dropdown.ts +++ b/frontend/src/components/cover-grid/album-dropdown.ts @@ -1,7 +1,7 @@ import { LitElement, html, svg, css } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { PlayerController } from '@store/controllers/player-controller'; import { FavoritesController } from '@store/controllers/favorites-controller'; import { formatMilliseconds } from '@utils/time'; diff --git a/frontend/src/components/cover-grid/album-selection.ts b/frontend/src/components/cover-grid/album-selection.ts index c27f233..6e6175c 100644 --- a/frontend/src/components/cover-grid/album-selection.ts +++ b/frontend/src/components/cover-grid/album-selection.ts @@ -2,9 +2,10 @@ import { GetAlbumTracks, GetAlbumTracksByLibrary, GetFilePathsByAlbums, -} from '@go/library/Library'; +} from '@go/library/library.js'; import { libraryStore } from '@store/library-store'; -import type { library } from '@go/models'; +import { dict, list } from '@utils/binding'; +import type * as library from '@go/library/models.js'; import type { CoverArtUrls } from '@components/track-details/track-details.js'; /** @@ -58,9 +59,11 @@ export class AlbumSelectionManager { const libId = libraryStore.getSelectedLibraryId(); - return libId !== null - ? GetAlbumTracksByLibrary(albumId, libId) - : GetAlbumTracks(albumId); + return list( + libId !== null + ? GetAlbumTracksByLibrary(albumId, libId) + : GetAlbumTracks(albumId), + ); } /** @@ -85,7 +88,7 @@ export class AlbumSelectionManager { const libId = libraryStore.getSelectedLibraryId(); - return GetFilePathsByAlbums(ids, libId ?? 0); + return dict(GetFilePathsByAlbums(ids, libId ?? 0)); } // ================================================================ diff --git a/frontend/src/components/cover-grid/cover-grid-types.ts b/frontend/src/components/cover-grid/cover-grid-types.ts index 068caa9..993e133 100644 --- a/frontend/src/components/cover-grid/cover-grid-types.ts +++ b/frontend/src/components/cover-grid/cover-grid-types.ts @@ -1,4 +1,4 @@ -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; /** * Discriminated context menu target so we know whether the diff --git a/frontend/src/components/cover-grid/cover-grid.ts b/frontend/src/components/cover-grid/cover-grid.ts index f1696be..4dbf4b9 100644 --- a/frontend/src/components/cover-grid/cover-grid.ts +++ b/frontend/src/components/cover-grid/cover-grid.ts @@ -14,8 +14,8 @@ import { grid } from '@lit-labs/virtualizer/layouts/grid.js'; import { GetAlbumTracks, GetAlbumTracksByLibrary, -} from '@go/library/Library'; -import { library } from '@go/models'; +} from '@go/library/library.js'; +import * as library from '@go/library/models.js'; import { LibraryController } from '@store/controllers/library-controller'; import { SearchController } from '@store/controllers/search-controller'; import { ViewLifecycleMixin } from '@utils/view-lifecycle'; @@ -74,6 +74,7 @@ import type { GridEntry, SortDirection, } from './cover-grid-types.js'; +import { list } from '@utils/binding'; @customElement('cover-grid') export class CoverGrid @@ -933,12 +934,14 @@ export class CoverGrid const libId = this.libraryCtrl.selectedLibraryId; - const tracks = libId !== null - ? await GetAlbumTracksByLibrary( - album.ID, - libId, - ) - : await GetAlbumTracks(album.ID); + const tracks = await list( + libId !== null + ? GetAlbumTracksByLibrary( + album.ID, + libId, + ) + : GetAlbumTracks(album.ID), + ); if (this.expandedAlbumId === album.ID) { this.expandedTracks = tracks; diff --git a/frontend/src/components/cover-grid/scroll-manager.ts b/frontend/src/components/cover-grid/scroll-manager.ts index ae47b0d..03bc000 100644 --- a/frontend/src/components/cover-grid/scroll-manager.ts +++ b/frontend/src/components/cover-grid/scroll-manager.ts @@ -1,6 +1,6 @@ import type { LitElement } from 'lit'; import type { LitVirtualizer } from '@lit-labs/virtualizer'; -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import type { LibraryController } from '@store/controllers/library-controller'; import type { GridEntry } from './cover-grid-types.js'; diff --git a/frontend/src/components/download-picker/download-picker.ts b/frontend/src/components/download-picker/download-picker.ts index 26de54c..917add5 100644 --- a/frontend/src/components/download-picker/download-picker.ts +++ b/frontend/src/components/download-picker/download-picker.ts @@ -7,7 +7,7 @@ import '@awesome.me/webawesome/dist/components/callout/callout.js'; import { designTokens } from '../../styles/tokens.css'; import type { DownloadCandidate } from '@store/download-store'; import { downloadStore } from '@store/download-store'; -import type { download } from '@go/models'; +import type * as download from '@go/download/models.js'; import './candidate-row'; import { explainError } from '@utils/describe-error'; import { nameDialogsIn } from '@utils/name-dialog'; diff --git a/frontend/src/components/downloads-view/downloads-view.ts b/frontend/src/components/downloads-view/downloads-view.ts index a4ae703..d774ab0 100644 --- a/frontend/src/components/downloads-view/downloads-view.ts +++ b/frontend/src/components/downloads-view/downloads-view.ts @@ -686,7 +686,7 @@ export class DownloadsView extends ViewLifecycleMixin(LitElement) { /** Provider/progress summary for a download's second line. */ private downloadDetail(view: DownloadRecord): string { const providers = [ - ...new Set(view.items.map((item) => item.candidate?.origin).filter(Boolean)), + ...new Set((view.items ?? []).map((item) => item.candidate?.origin).filter(Boolean)), ]; const parts: string[] = []; diff --git a/frontend/src/components/duplicate-tracks-dialog/duplicate-tracks-dialog.ts b/frontend/src/components/duplicate-tracks-dialog/duplicate-tracks-dialog.ts index 8a795ba..57c3a7f 100644 --- a/frontend/src/components/duplicate-tracks-dialog/duplicate-tracks-dialog.ts +++ b/frontend/src/components/duplicate-tracks-dialog/duplicate-tracks-dialog.ts @@ -3,7 +3,7 @@ import { customElement, state, query } from 'lit/decorators.js'; import '@awesome.me/webawesome/dist/components/dialog/dialog.js'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; import '@awesome.me/webawesome/dist/components/switch/switch.js'; -import { AddTracksToPlaylist } from '@go/playlist/Service'; +import { AddTracksToPlaylist } from '@go/playlist/service.js'; import { formatMilliseconds } from '@utils/time'; import { nameDialogsIn } from '@utils/name-dialog'; diff --git a/frontend/src/components/explore-album-details/explore-album-details.ts b/frontend/src/components/explore-album-details/explore-album-details.ts index 9b1e078..e5da042 100644 --- a/frontend/src/components/explore-album-details/explore-album-details.ts +++ b/frontend/src/components/explore-album-details/explore-album-details.ts @@ -6,15 +6,16 @@ import { LookupReleaseGroup, BrowseReleases, GetThumbnail, -} from '@go/explore/Service'; +} from '@go/explore/service.js'; import { GetAlbumTracks, GetAlbumCompleteness, GetFilePathsByAlbums, GetFilePathsByRecordingMBIDs, -} from '@go/library/Library'; -import { library } from '@go/models'; -import type { download, explore } from '@go/models'; +} from '@go/library/library.js'; +import * as library from '@go/library/models.js'; +import type * as download from '@go/download/models.js'; +import type * as explore from '@go/explore/models.js'; type MBReleaseGroup = explore.MBReleaseGroup; type MBRelease = explore.MBRelease; type MBTrack = explore.MBTrack; @@ -46,6 +47,7 @@ import type { ContextMenuHost } from '@utils/context-menu-controller.js'; import '@awesome.me/webawesome/dist/components/popup/popup.js'; import type WaPopup from '@awesome.me/webawesome/dist/components/popup/popup.js'; import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js'; +import { dict, dictByName } from '@utils/binding'; /** * The region the album header's own failures are rendered in. @@ -2248,9 +2250,8 @@ export class ExploreAlbumDetails extends LitElement implements ContextMenuHost { // to an empty set and the Play button silently does nothing. // That is exactly what the first version of this did. if (this.localAlbumId > 0) { - const byAlbum = await GetFilePathsByAlbums( - [this.localAlbumId], - libraryID, + const byAlbum = await dict( + GetFilePathsByAlbums([this.localAlbumId], libraryID), ); return byAlbum[this.localAlbumId] ?? []; @@ -2266,7 +2267,9 @@ export class ExploreAlbumDetails extends LitElement implements ContextMenuHost { if (mbids.length === 0) return []; - const byMBID = await GetFilePathsByRecordingMBIDs(mbids, libraryID); + const byMBID = await dictByName( + GetFilePathsByRecordingMBIDs(mbids, libraryID), + ); // Walked in tracklist order rather than flattened, because the // grouping is what lets the caller keep its own order. A @@ -2349,7 +2352,9 @@ export class ExploreAlbumDetails extends LitElement implements ContextMenuHost { if (!track.inLibrary || !track.mbid) return null; const libraryID = libraryStore.getSelectedLibraryId() ?? 0; - const byMBID = await GetFilePathsByRecordingMBIDs([track.mbid], libraryID); + const byMBID = await dictByName( + GetFilePathsByRecordingMBIDs([track.mbid], libraryID), + ); return byMBID[track.mbid]?.[0] ?? null; } diff --git a/frontend/src/components/explore-artist-details/explore-artist-details.ts b/frontend/src/components/explore-artist-details/explore-artist-details.ts index 91af887..e45522c 100644 --- a/frontend/src/components/explore-artist-details/explore-artist-details.ts +++ b/frontend/src/components/explore-artist-details/explore-artist-details.ts @@ -17,8 +17,8 @@ import { GetTrackThumbnails, ResolveReleaseGroupMBIDs, PrefetchReleases, -} from '@go/explore/Service'; -import type { explore } from '@go/models'; +} from '@go/explore/service.js'; +import type * as explore from '@go/explore/models.js'; type MBArtist = explore.MBArtist; type MBReleaseGroup = explore.MBReleaseGroup; type LBTopRecording = explore.LBTopRecording; @@ -34,7 +34,7 @@ import { GetAlbumsByArtist, GetFilePathsByAlbums, GetFilePathsByRecordingMBIDs, -} from '@go/library/Library'; +} from '@go/library/library.js'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; @@ -55,6 +55,7 @@ import type { ContextMenuHost } from '@utils/context-menu-controller.js'; import '@awesome.me/webawesome/dist/components/popup/popup.js'; import type WaPopup from '@awesome.me/webawesome/dist/components/popup/popup.js'; import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js'; +import { dict, dictByName } from '@utils/binding'; /* ── Constants ── */ @@ -1423,7 +1424,7 @@ export class ExploreArtistDetails extends LitElement implements ContextMenuHost let mapping: Record = {}; if (caaMbids.length > 0) { try { - mapping = (await ResolveReleaseGroupMBIDs(caaMbids)) || {}; + mapping = await dictByName(ResolveReleaseGroupMBIDs(caaMbids)); } catch (err) { console.warn('[explore-artist] failed to resolve release groups for tracks:', err); } @@ -1475,7 +1476,7 @@ export class ExploreArtistDetails extends LitElement implements ContextMenuHost // Phase 1: batched cached lookup. let cached: Record = {}; try { - cached = (await GetTrackThumbnails(requests)) || {}; + cached = await dictByName(GetTrackThumbnails(requests)); if (Object.keys(cached).length > 0) { const updated = new Map(this.trackThumbnails); for (const [key, url] of Object.entries(cached)) { @@ -1684,7 +1685,7 @@ export class ExploreArtistDetails extends LitElement implements ContextMenuHost artistName: i.artistName, })); - cached = (await GetThumbnails(requests)) || {}; + cached = await dictByName(GetThumbnails(requests)); if (Object.keys(cached).length > 0) { const updated = new Map(this.thumbnailURLs); for (const [mbid, url] of Object.entries(cached)) { @@ -1927,7 +1928,7 @@ export class ExploreArtistDetails extends LitElement implements ContextMenuHost if (albumIds.length === 0) return []; const libraryID = libraryStore.getSelectedLibraryId() ?? 0; - const byAlbum = await GetFilePathsByAlbums(albumIds, libraryID); + const byAlbum = await dict(GetFilePathsByAlbums(albumIds, libraryID)); const paths: string[] = []; for (const id of albumIds) paths.push(...(byAlbum[id] ?? [])); @@ -1996,7 +1997,9 @@ export class ExploreArtistDetails extends LitElement implements ContextMenuHost if (!(track.inLibrary || track.localId) || !track.recordingMbid) return null; const libraryID = libraryStore.getSelectedLibraryId() ?? 0; - const byMBID = await GetFilePathsByRecordingMBIDs([track.recordingMbid], libraryID); + const byMBID = await dictByName( + GetFilePathsByRecordingMBIDs([track.recordingMbid], libraryID), + ); return byMBID[track.recordingMbid]?.[0] ?? null; } @@ -2145,7 +2148,9 @@ export class ExploreArtistDetails extends LitElement implements ContextMenuHost if (release.localId <= 0) return []; const libraryID = libraryStore.getSelectedLibraryId() ?? 0; - const byAlbum = await GetFilePathsByAlbums([release.localId], libraryID); + const byAlbum = await dict( + GetFilePathsByAlbums([release.localId], libraryID), + ); return byAlbum[release.localId] ?? []; } diff --git a/frontend/src/components/explore-view/explore-view.ts b/frontend/src/components/explore-view/explore-view.ts index c6554a0..fc4e47a 100644 --- a/frontend/src/components/explore-view/explore-view.ts +++ b/frontend/src/components/explore-view/explore-view.ts @@ -7,8 +7,8 @@ import { classMap } from 'lit/directives/class-map.js'; import '@components/page-header/page-header'; import { designTokens } from '../../styles/tokens.css'; import { srOnly } from '../../styles/sr-only.css'; -import { SearchLocal, SearchLyrics, GetThumbnail, GetThumbnails, GetArtistImageURL, GetArtistImagesCachedPaths, GetExploreShelves, RecordSearchClick } from '@go/explore/Service'; -import { GetFilePathsByAlbums, GetFilePathsByRecordingMBIDs } from '@go/library/Library'; +import { SearchLocal, SearchLyrics, GetThumbnail, GetThumbnails, GetArtistImageURL, GetArtistImagesCachedPaths, GetExploreShelves, RecordSearchClick } from '@go/explore/service.js'; +import { GetFilePathsByAlbums, GetFilePathsByRecordingMBIDs } from '@go/library/library.js'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; import { libraryStore } from '../../store/library-store'; @@ -21,7 +21,7 @@ import { describeError } from '../../utils/describe-error'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; import '../library-status-indicator/library-status-indicator.js'; import '../top-results-row/top-results-row.js'; -import { explore } from '@go/models'; +import * as explore from '@go/explore/models.js'; import { ViewLifecycleMixin } from '../../utils/view-lifecycle'; import { registerCacheProbe } from '../../utils/cache-stats'; import { LRUMap } from '../../utils/lru-map'; @@ -34,6 +34,7 @@ import type { ContextMenuHost } from '@utils/context-menu-controller.js'; import '@awesome.me/webawesome/dist/components/popup/popup.js'; import type WaPopup from '@awesome.me/webawesome/dist/components/popup/popup.js'; import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js'; +import { dict, dictByName } from '@utils/binding'; /** The region explore's own action failures (play/queue) are rendered in. */ export const ExploreRegion = 'explore'; @@ -833,10 +834,10 @@ export class ExploreView extends ViewLifecycleMixin(LitElement) implements Conte // a state this view already renders honestly. A *rejected* // call still reaches the catch below. if (!page || !Array.isArray(page.shelves)) { - this.shelves = explore.ShelfPage.createFrom({ + this.shelves = { shelves: [], state: 'no-index', - }); + }; return; } @@ -853,10 +854,10 @@ export class ExploreView extends ViewLifecycleMixin(LitElement) implements Conte // Inline, and quietly: the search box above still works, so // this is a panel that could not fill itself rather than // something the user asked for and did not get. - this.shelves = explore.ShelfPage.createFrom({ + this.shelves = { shelves: [], state: 'no-index', - }); + }; } finally { this.shelvesPending = false; } @@ -1043,13 +1044,12 @@ export class ExploreView extends ViewLifecycleMixin(LitElement) implements Conte // index has no hits, in which case we keep the owned-library // matches already displayed. const result = - (await SearchLocal(query)) ?? - explore.MBSearchResult.createFrom({ + (await SearchLocal(query)) ?? { artists: [], releaseGroups: [], recordings: [], topResults: [], - }); + }; // Discard stale response if (version !== this.searchVersion) { @@ -1131,14 +1131,16 @@ export class ExploreView extends ViewLifecycleMixin(LitElement) implements Conte */ private async albumFilePaths(localId: number): Promise { const libraryID = libraryStore.getSelectedLibraryId() ?? 0; - const byAlbum = await GetFilePathsByAlbums([localId], libraryID); + const byAlbum = await dict(GetFilePathsByAlbums([localId], libraryID)); return byAlbum[localId] ?? []; } private async recordingFilePath(mbid: string): Promise { const libraryID = libraryStore.getSelectedLibraryId() ?? 0; - const byMBID = await GetFilePathsByRecordingMBIDs([mbid], libraryID); + const byMBID = await dictByName( + GetFilePathsByRecordingMBIDs([mbid], libraryID), + ); return byMBID[mbid]?.[0] ?? null; } @@ -2018,7 +2020,7 @@ export class ExploreView extends ViewLifecycleMixin(LitElement) implements Conte // of nothing. if (!page) return nothing; - if (page.shelves.length === 0) { + if (!page.shelves?.length) { return html`
` : nothing} - ${page.shelves.map((shelf) => + ${(page.shelves ?? []).map((shelf) => shelf.artists?.length ? this.renderArtistsSection( shelf.artists, diff --git a/frontend/src/components/first-run-wizard/first-run-wizard.ts b/frontend/src/components/first-run-wizard/first-run-wizard.ts index ef7dfe4..96316ff 100644 --- a/frontend/src/components/first-run-wizard/first-run-wizard.ts +++ b/frontend/src/components/first-run-wizard/first-run-wizard.ts @@ -5,8 +5,8 @@ import '@awesome.me/webawesome/dist/components/icon/icon.js'; import { AddLibrary, GetAllLibrariesWithTrackCounts, -} from '@go/library/Library'; -import { DirectoryPicker } from '@go/frontendutil/FrontendUtil'; +} from '@go/library/library.js'; +import { DirectoryPicker } from '@go/frontendutil/frontendutil.js'; import { describeError, explainError } from '@utils/describe-error'; import { nameDialogsIn } from '@utils/name-dialog'; diff --git a/frontend/src/components/genre-details/genre-details.ts b/frontend/src/components/genre-details/genre-details.ts index 808ace1..beab7c3 100644 --- a/frontend/src/components/genre-details/genre-details.ts +++ b/frontend/src/components/genre-details/genre-details.ts @@ -4,11 +4,11 @@ import { property, state, } from 'lit/decorators.js'; -import { library } from '@go/models'; +import * as library from '@go/library/models.js'; import { GetTracksByGenre, GetTracksByGenreByLibrary, -} from '@go/library/Library'; +} from '@go/library/library.js'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; import { libraryStore } from '@store/library-store'; @@ -16,6 +16,7 @@ import { describeError } from '@utils/describe-error'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; import '@components/track-list/track-list.js'; import { designTokens } from '../../styles/tokens.css'; +import { list } from '@utils/binding'; @customElement('genre-details') export class GenreDetails extends LitElement { @@ -207,14 +208,14 @@ export class GenreDetails extends LitElement { const libId = libraryStore.getSelectedLibraryId(); - this.tracks = libId !== null - ? await GetTracksByGenreByLibrary( - this.genreName, - libId, - ) - : await GetTracksByGenre( - this.genreName, - ); + this.tracks = await list( + libId !== null + ? GetTracksByGenreByLibrary( + this.genreName, + libId, + ) + : GetTracksByGenre(this.genreName), + ); } catch (error) { console.error('Error loading genre tracks:', error); this.tracks = []; diff --git a/frontend/src/components/genres-view/genres-view.ts b/frontend/src/components/genres-view/genres-view.ts index 139a547..fb599b2 100644 --- a/frontend/src/components/genres-view/genres-view.ts +++ b/frontend/src/components/genres-view/genres-view.ts @@ -12,8 +12,8 @@ import type { import { grid } from '@lit-labs/virtualizer/layouts/grid.js'; import { GetFilePathsByGenres, -} from '@go/library/Library'; -import type { library } from '@go/models'; +} from '@go/library/library.js'; +import type * as library from '@go/library/models.js'; import { LibraryController } from '@store/controllers/library-controller'; import { SearchController } from '@store/controllers/search-controller'; import '@components/page-header/page-header'; @@ -33,6 +33,7 @@ import '@awesome.me/webawesome/dist/components/popup/popup.js'; import type WaPopup from '@awesome.me/webawesome/dist/components/popup/popup.js'; import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js'; import '@components/playlist-picker/playlist-picker.js'; +import { dictByName } from '@utils/binding'; /** Pixels to change card width per scroll tick. */ const ZOOM_STEP = 16; @@ -831,9 +832,8 @@ export class GenresView // read off them — 6 MB over the IPC for five // genres of a 50 000-track library. const names = Array.from(genreNames); - const byGenre = await GetFilePathsByGenres( - names, - libId ?? 0, + const byGenre = await dictByName( + GetFilePathsByGenres(names, libId ?? 0), ); // Still de-duplicated here: a track with two of diff --git a/frontend/src/components/home-view/home-view.ts b/frontend/src/components/home-view/home-view.ts index 38badab..cff79ad 100644 --- a/frontend/src/components/home-view/home-view.ts +++ b/frontend/src/components/home-view/home-view.ts @@ -2,9 +2,10 @@ import { LitElement, html, css, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; import '@awesome.me/webawesome/dist/components/button/button.js'; -import { GetShelves } from '@go/home/Service'; -import { GetAlbumTracks } from '@go/library/Library'; -import type { home, library } from '@go/models'; +import { GetShelves } from '@go/home/service.js'; +import { GetAlbumTracks } from '@go/library/library.js'; +import type * as home from '@go/home/models.js'; +import type * as library from '@go/library/models.js'; import { queueStore } from '@store/queue-store'; import { libraryStore } from '@store/library-store'; import { EventsOn } from '@runtime/runtime'; @@ -311,7 +312,7 @@ export class HomeView extends ViewLifecycleMixin(LitElement) {

${shelf.subtitle}

- ${shelf.albums.map((album) => this.renderCard(album))} + ${(shelf.albums ?? []).map((album) => this.renderCard(album))}
`; diff --git a/frontend/src/components/jobs/jobs-view.ts b/frontend/src/components/jobs/jobs-view.ts index a84f6d9..cdd1261 100644 --- a/frontend/src/components/jobs/jobs-view.ts +++ b/frontend/src/components/jobs/jobs-view.ts @@ -8,8 +8,8 @@ import { ScanLibrary, ScanAllLibraries, FullRescan, -} from '@go/library/Library'; -import type { library } from '@go/models'; +} from '@go/library/library.js'; +import type * as library from '@go/library/models.js'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; import { jobStore } from '@store/job-store'; diff --git a/frontend/src/components/library-filter/library-filter.ts b/frontend/src/components/library-filter/library-filter.ts index dcd8883..f62085c 100644 --- a/frontend/src/components/library-filter/library-filter.ts +++ b/frontend/src/components/library-filter/library-filter.ts @@ -2,7 +2,7 @@ import { LitElement, html, css } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { LibraryController } from '@store/controllers/library-controller'; import { libraryStore } from '@store/library-store'; -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { designTokens } from '../../styles/tokens.css'; /** diff --git a/frontend/src/components/phantom-resolver/phantom-resolver.ts b/frontend/src/components/phantom-resolver/phantom-resolver.ts index c98be2c..9f48396 100644 --- a/frontend/src/components/phantom-resolver/phantom-resolver.ts +++ b/frontend/src/components/phantom-resolver/phantom-resolver.ts @@ -13,10 +13,11 @@ import { SearchLibrary, ResolvePhantomTracks, RemovePhantomTracks, -} from '@go/playlist/Service'; -import type { playlist } from '@go/models'; +} from '@go/playlist/service.js'; +import type * as playlist from '@go/playlist/models.js'; import { formatMilliseconds } from '@utils/time'; import { nameDialogsIn } from '@utils/name-dialog'; +import { list } from '@utils/binding'; const SEARCH_DEBOUNCE_MS = 400; @@ -143,11 +144,12 @@ export class PhantomResolver extends LitElement { this.candidatesLoading = true; try { - this.candidates = - await GetPhantomCandidates( + this.candidates = await list( + GetPhantomCandidates( this.playlistId, this.selectedPhantom, - ); + ), + ); } catch (err) { console.error( 'Failed to load candidates:', @@ -171,8 +173,7 @@ export class PhantomResolver extends LitElement { this.searching = true; try { - this.searchResults = - await SearchLibrary(query); + this.searchResults = await list(SearchLibrary(query)); } catch (err) { console.error( 'Library search failed:', diff --git a/frontend/src/components/playlist-details/playlist-details.ts b/frontend/src/components/playlist-details/playlist-details.ts index 6bb41e1..c3ac7b8 100644 --- a/frontend/src/components/playlist-details/playlist-details.ts +++ b/frontend/src/components/playlist-details/playlist-details.ts @@ -19,8 +19,8 @@ import { RemoveTracksFromPlaylist, RemovePhantomTracks, FindDuplicateTracksInPlaylist, -} from '@go/playlist/Service'; -import type { playlist } from '@go/models'; +} from '@go/playlist/service.js'; +import type * as playlist from '@go/playlist/models.js'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; import { queueStore } from '@store/queue-store'; @@ -67,6 +67,7 @@ import { exploreLinkStyles, } from '@utils/explore-link'; import { designTokens } from '../../styles/tokens.css'; +import { list } from '@utils/binding'; /** One playlist row: the track and its position in the *playlist*, * which is not its position in the filtered view. */ @@ -276,8 +277,8 @@ export class PlaylistDetails if (!this.playlistId) return; try { - this.tracks = await GetPlaylistTracks( - this.playlistId, + this.tracks = await list( + GetPlaylistTracks(this.playlistId), ); } catch (error) { console.error( @@ -294,8 +295,8 @@ export class PlaylistDetails if (!this.playlistId) return; try { - this.tracks = await GetPlaylistTracks( - this.playlistId, + this.tracks = await list( + GetPlaylistTracks(this.playlistId), ); } catch (error) { console.error( diff --git a/frontend/src/components/playlist-picker/playlist-picker.ts b/frontend/src/components/playlist-picker/playlist-picker.ts index 777074d..8116f30 100644 --- a/frontend/src/components/playlist-picker/playlist-picker.ts +++ b/frontend/src/components/playlist-picker/playlist-picker.ts @@ -10,13 +10,14 @@ import { AddTracksToPlaylist, CreatePlaylistWithTracks, FindDuplicateTracksInPlaylist, -} from '@go/playlist/Service'; +} from '@go/playlist/service.js'; import { Events } from '../../events'; -import type { playlist } from '@go/models'; +import type * as playlist from '@go/playlist/models.js'; import '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js'; import { notificationStore } from '@store/notification-store'; import { describeError } from '@utils/describe-error'; import type { DuplicateTracksDialog } from '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js'; +import { list } from '@utils/binding'; /** * A reusable playlist picker that displays existing playlists @@ -156,7 +157,7 @@ export class PlaylistPicker extends LitElement { private async loadPlaylists() { try { - this.playlists = await GetAllPlaylists(); + this.playlists = await list(GetAllPlaylists()); } catch (err) { console.error('Failed to load playlists:', err); this.playlists = []; diff --git a/frontend/src/components/playlist-view/playlist-view.ts b/frontend/src/components/playlist-view/playlist-view.ts index 775d94f..0f69572 100644 --- a/frontend/src/components/playlist-view/playlist-view.ts +++ b/frontend/src/components/playlist-view/playlist-view.ts @@ -14,9 +14,9 @@ import { RenamePlaylist, ImportPlaylists, FindDuplicateTracksInPlaylist, -} from '@go/playlist/Service'; -import { PlaylistFilePicker } from '@go/frontendutil/FrontendUtil'; -import type { playlist } from '@go/models'; +} from '@go/playlist/service.js'; +import { PlaylistFilePicker } from '@go/frontendutil/frontendutil.js'; +import type * as playlist from '@go/playlist/models.js'; import { PlaylistController } from '@store/controllers/playlist-controller'; import { SearchController } from '@store/controllers/search-controller'; import { diff --git a/frontend/src/components/queue-panel/queue-panel.ts b/frontend/src/components/queue-panel/queue-panel.ts index 21cf1c9..ab6da06 100644 --- a/frontend/src/components/queue-panel/queue-panel.ts +++ b/frontend/src/components/queue-panel/queue-panel.ts @@ -49,7 +49,7 @@ import { removeDragImage, } from '@utils/drag-image'; import { libraryStore } from '@store/library-store'; -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { loadTrackDetails } from '@utils/lazy-track-details.js'; import { tracksByFilePath } from '@utils/track-index.js'; import type { TrackDetails } from '@components/track-details/track-details.js'; diff --git a/frontend/src/components/smart-playlist-details/smart-playlist-details.ts b/frontend/src/components/smart-playlist-details/smart-playlist-details.ts index 61a5839..d3f4e48 100644 --- a/frontend/src/components/smart-playlist-details/smart-playlist-details.ts +++ b/frontend/src/components/smart-playlist-details/smart-playlist-details.ts @@ -5,13 +5,13 @@ import { state, query, } from 'lit/decorators.js'; -import type { playlist } from '@go/models'; +import type * as playlist from '@go/playlist/models.js'; import { GetSmartPlaylistTracks, RefreshSmartPlaylist, GetSmartPlaylistRules, UpdateSmartPlaylistRules, -} from '@go/playlist/Service'; +} from '@go/playlist/service.js'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; import { queueStore } from '@store/queue-store'; @@ -58,6 +58,7 @@ import { } from '@utils/explore-link'; import '@components/smart-playlist-editor/smart-playlist-editor.js'; import { designTokens } from '../../styles/tokens.css'; +import { list } from '@utils/binding'; /** @@ -700,8 +701,8 @@ export class SmartPlaylistDetails if (!this.playlistId) return; try { - this.tracks = await GetSmartPlaylistTracks( - this.playlistId, + this.tracks = await list( + GetSmartPlaylistTracks(this.playlistId), ); } catch (error) { console.error( diff --git a/frontend/src/components/smart-playlist-editor/smart-playlist-editor.ts b/frontend/src/components/smart-playlist-editor/smart-playlist-editor.ts index 5f7a7da..5e723ec 100644 --- a/frontend/src/components/smart-playlist-editor/smart-playlist-editor.ts +++ b/frontend/src/components/smart-playlist-editor/smart-playlist-editor.ts @@ -1,7 +1,7 @@ import { LitElement, html, css, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { library } from '@go/models'; -import { PreviewSmartPlaylist } from '@go/playlist/Service'; +import * as library from '@go/library/models.js'; +import { PreviewSmartPlaylist } from '@go/playlist/service.js'; import { libraryStore } from '@store/library-store'; import { describeError } from '@utils/describe-error'; import { designTokens } from '../../styles/tokens.css'; diff --git a/frontend/src/components/top-results-row/top-results-row.ts b/frontend/src/components/top-results-row/top-results-row.ts index 7259d5c..af99e99 100644 --- a/frontend/src/components/top-results-row/top-results-row.ts +++ b/frontend/src/components/top-results-row/top-results-row.ts @@ -1,12 +1,12 @@ import { LitElement, html, css, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { designTokens } from '../../styles/tokens.css'; -import type { explore } from '@go/models'; +import type * as explore from '@go/explore/models.js'; import { GetArtistImageURL, GetThumbnail, RecordSearchClick, -} from '@go/explore/Service'; +} from '@go/explore/service.js'; import '../library-status-indicator/library-status-indicator.js'; import type { LibraryStatus } from '../library-status-indicator/library-status-indicator.js'; import { artistLink, exploreLinkStyles } from '../../utils/explore-link'; diff --git a/frontend/src/components/track-details/track-details.ts b/frontend/src/components/track-details/track-details.ts index e617c91..23cf502 100644 --- a/frontend/src/components/track-details/track-details.ts +++ b/frontend/src/components/track-details/track-details.ts @@ -4,7 +4,7 @@ import { state, query, } from 'lit/decorators.js'; -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { formatSampleRate, formatBitDepth, @@ -14,14 +14,14 @@ import { } from '@utils/format'; import { formatMilliseconds } from '@utils/time'; import { nameDialogsIn } from '@utils/name-dialog'; -import { WriteTrackTagsByPath } from '@go/tagwriter/TagWriter'; +import { WriteTrackTagsByPath } from '@go/tagwriter/tagwriter.js'; import { BatchWriteTrackTags, CancelBatchWrite, -} from '@go/tagwriter/TagWriter'; -import { GetTrackMBIDs } from '@go/library/Library'; +} from '@go/tagwriter/tagwriter.js'; +import { GetTrackMBIDs } from '@go/library/library.js'; type TrackMBIDs = library.TrackMBIDs; -import { ImageFilePicker, ReadFile } from '@go/frontendutil/FrontendUtil'; +import { ImageFilePicker, ReadFile } from '@go/frontendutil/frontendutil.js'; import { libraryStore } from '../../store/library-store'; import { EventsOn } from '@runtime/runtime'; import { Events } from '../../events'; diff --git a/frontend/src/components/track-list/columns.ts b/frontend/src/components/track-list/columns.ts index 7880f96..8504b60 100644 --- a/frontend/src/components/track-list/columns.ts +++ b/frontend/src/components/track-list/columns.ts @@ -1,4 +1,4 @@ -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { formatSampleRate, formatBitDepth, diff --git a/frontend/src/components/track-list/search-ranking.ts b/frontend/src/components/track-list/search-ranking.ts index 29cf5aa..e295e33 100644 --- a/frontend/src/components/track-list/search-ranking.ts +++ b/frontend/src/components/track-list/search-ranking.ts @@ -1,4 +1,4 @@ -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { html } from 'lit'; import type { TemplateResult } from 'lit'; diff --git a/frontend/src/components/track-list/track-list.ts b/frontend/src/components/track-list/track-list.ts index 594d44d..08c8a2f 100644 --- a/frontend/src/components/track-list/track-list.ts +++ b/frontend/src/components/track-list/track-list.ts @@ -1,4 +1,4 @@ -import { library } from '@go/models'; +import * as library from '@go/library/models.js'; import { LitElement, html, svg, css, nothing } from 'lit'; import { designTokens } from '../../styles/tokens.css'; import { srOnly } from '../../styles/sr-only.css'; @@ -65,7 +65,7 @@ import '@awesome.me/webawesome/dist/components/icon/icon.js'; import { describeError } from '@utils/describe-error'; import { notificationStore } from '@store/notification-store'; import { confirmAction } from '@components/confirm-dialog/confirm-dialog'; -import { RemoveFromLibrary } from '@go/library/Library'; +import { RemoveFromLibrary } from '@go/library/library.js'; import { loadTrackDetails } from '@utils/lazy-track-details.js'; import { tracksByFilePath, tracksForPaths } from '@utils/track-index.js'; import '@components/playlist-picker/playlist-picker.js'; diff --git a/frontend/src/services/keyboard-shortcut-service.ts b/frontend/src/services/keyboard-shortcut-service.ts index 9e0053d..ed8d049 100644 --- a/frontend/src/services/keyboard-shortcut-service.ts +++ b/frontend/src/services/keyboard-shortcut-service.ts @@ -14,7 +14,7 @@ import { shortcutsStore } from '@store/shortcuts-store'; import { ambientShortcutScope } from './shortcut-scope'; import { playerStore } from '@store/player-store'; import { queueStore } from '@store/queue-store'; -import * as Player from '@go/player/Player'; +import * as Player from '@go/player/player.js'; import type { SearchBar } from '@components/search-bar/search-bar'; // =================================================================== diff --git a/frontend/src/store/controllers/library-controller.ts b/frontend/src/store/controllers/library-controller.ts index 8e60d7f..7d48f64 100644 --- a/frontend/src/store/controllers/library-controller.ts +++ b/frontend/src/store/controllers/library-controller.ts @@ -1,5 +1,5 @@ import type { ReactiveController, ReactiveControllerHost } from 'lit'; -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; import { libraryStore } from '../library-store'; type ViewName = 'tracks' | 'albums' | 'artists' | 'genres'; diff --git a/frontend/src/store/controllers/playlist-controller.ts b/frontend/src/store/controllers/playlist-controller.ts index 8744aa2..15e4e09 100644 --- a/frontend/src/store/controllers/playlist-controller.ts +++ b/frontend/src/store/controllers/playlist-controller.ts @@ -1,5 +1,5 @@ import type { ReactiveController, ReactiveControllerHost } from 'lit'; -import type { playlist } from '@go/models'; +import type * as playlist from '@go/playlist/models.js'; import { playlistStore } from '../playlist-store'; /** diff --git a/frontend/src/store/download-store.ts b/frontend/src/store/download-store.ts index ef8806d..343a95c 100644 --- a/frontend/src/store/download-store.ts +++ b/frontend/src/store/download-store.ts @@ -19,8 +19,8 @@ import { StartDownload, TestProvider, UpdateProvider, -} from '@go/download/Service'; -import type { download } from '@go/models'; +} from '@go/download/service.js'; +import type * as download from '@go/download/models.js'; import { Events } from '../events'; export type DownloadCandidate = download.Candidate; diff --git a/frontend/src/store/favorites-store.ts b/frontend/src/store/favorites-store.ts index fc470e9..4f2ba61 100644 --- a/frontend/src/store/favorites-store.ts +++ b/frontend/src/store/favorites-store.ts @@ -5,7 +5,7 @@ import { ToggleDefaultPlaylistTrack, AddToDefaultPlaylist, RemoveFromDefaultPlaylist, -} from '@go/playlist/Service'; +} from '@go/playlist/service.js'; import { GetFavoritesIconStyle, GetFavoritesPlaylistID, @@ -13,7 +13,7 @@ import { SetFavoritesIconStyle, SetFavoritesPlaylistID, SetPinDefaultPlaylist, -} from '@go/config/Config'; +} from '@go/config/config.js'; import { Events } from '../events'; import { describeError } from '@utils/describe-error'; import { notificationStore } from './notification-store'; diff --git a/frontend/src/store/job-store.ts b/frontend/src/store/job-store.ts index 812daba..29e83f8 100644 --- a/frontend/src/store/job-store.ts +++ b/frontend/src/store/job-store.ts @@ -7,8 +7,8 @@ import { CancelJob, DismissJob, ClearFinishedJobs, -} from '@go/jobs/Service'; -import type { jobs } from '@go/models'; +} from '@go/jobs/service.js'; +import type * as jobs from '@go/jobs/models.js'; import { Events } from '../events'; export type Job = jobs.Job; diff --git a/frontend/src/store/library-store.ts b/frontend/src/store/library-store.ts index 0f79282..4ab7d88 100644 --- a/frontend/src/store/library-store.ts +++ b/frontend/src/store/library-store.ts @@ -11,8 +11,9 @@ import { GetAllGenresWithCountsByLibrary, GetAlbumsByArtistByLibrary, GetAllLibrariesWithTrackCounts, -} from '@go/library/Library'; -import type { library } from '@go/models'; +} from '@go/library/library.js'; +import type * as library from '@go/library/models.js'; +import { list } from '@utils/binding'; import { Events } from '../events'; type ViewName = 'tracks' | 'albums' | 'artists' | 'genres'; @@ -231,7 +232,7 @@ class LibraryStore { return this.track( 'tracks', - id !== null ? GetAllTracksByLibrary(id) : GetAllTracks(), + list(id !== null ? GetAllTracksByLibrary(id) : GetAllTracks()), (tracks) => { this.tracks = tracks; }, @@ -252,7 +253,7 @@ class LibraryStore { return this.track( 'albums', - id !== null ? GetAllAlbumsByLibrary(id) : GetAllAlbums(), + list(id !== null ? GetAllAlbumsByLibrary(id) : GetAllAlbums()), (albums) => { this.albums = albums; }, @@ -273,7 +274,7 @@ class LibraryStore { return this.track( 'artists', - id !== null ? GetAllArtistsByLibrary(id) : GetAllArtists(), + list(id !== null ? GetAllArtistsByLibrary(id) : GetAllArtists()), (artists) => { this.artists = artists; }, @@ -294,9 +295,11 @@ class LibraryStore { return this.track( 'genres', - id !== null - ? GetAllGenresWithCountsByLibrary(id) - : GetAllGenresWithCounts(), + list( + id !== null + ? GetAllGenresWithCountsByLibrary(id) + : GetAllGenresWithCounts(), + ), (genres) => { this.genres = genres; }, @@ -309,9 +312,11 @@ class LibraryStore { ): Promise { const id = this.selectedLibraryIdValue; - return id !== null - ? GetAlbumsByArtistByLibrary(artistID, id) - : GetAlbumsByArtist(artistID); + return list( + id !== null + ? GetAlbumsByArtistByLibrary(artistID, id) + : GetAlbumsByArtist(artistID), + ); } /** @@ -410,8 +415,7 @@ class LibraryStore { return this.libraries; } - const libs = - await GetAllLibrariesWithTrackCounts(); + const libs = await list(GetAllLibrariesWithTrackCounts()); this.libraries = libs; diff --git a/frontend/src/store/player-store.ts b/frontend/src/store/player-store.ts index b462ca2..9ed1ea5 100644 --- a/frontend/src/store/player-store.ts +++ b/frontend/src/store/player-store.ts @@ -1,6 +1,6 @@ import { EventsOn } from '@runtime/runtime'; import { Events } from '../events'; -import * as Player from '@go/player/Player'; +import * as Player from '@go/player/player.js'; import { notificationStore } from './notification-store'; /** The region `` renders these in: the player bar. */ diff --git a/frontend/src/store/playlist-store.ts b/frontend/src/store/playlist-store.ts index fbe4c69..621b821 100644 --- a/frontend/src/store/playlist-store.ts +++ b/frontend/src/store/playlist-store.ts @@ -3,8 +3,8 @@ import { GetAllPlaylists, GetAllPlaylistsWithTracks, GetPlaylistTracks, -} from '@go/playlist/Service'; -import type { playlist } from '@go/models'; +} from '@go/playlist/service.js'; +import type * as playlist from '@go/playlist/models.js'; import { Events } from '../events'; type Subscriber = () => void; diff --git a/frontend/src/store/queue-store.ts b/frontend/src/store/queue-store.ts index fb4b1ef..ed62dbb 100644 --- a/frontend/src/store/queue-store.ts +++ b/frontend/src/store/queue-store.ts @@ -1,6 +1,6 @@ import { EventsOn } from '@runtime/runtime'; import { Events } from '../events'; -import * as Queue from '@go/queue/Queue'; +import * as Queue from '@go/queue/queue.js'; // Types export interface QueueTrack { diff --git a/frontend/src/store/shortcuts-store.ts b/frontend/src/store/shortcuts-store.ts index be59c05..79d0880 100644 --- a/frontend/src/store/shortcuts-store.ts +++ b/frontend/src/store/shortcuts-store.ts @@ -4,8 +4,9 @@ import { SetShortcut, SetShortcuts, ResetShortcuts, -} from '@go/config/Config'; +} from '@go/config/config.js'; import { Events } from '../events'; +import { dictByName } from '@utils/binding'; export interface ShortcutsState { bindings: Map; // action → key combo @@ -56,7 +57,7 @@ class ShortcutsStore { private async loadFromBackend(): Promise { try { - const raw = await GetShortcuts(); + const raw = await dictByName(GetShortcuts()); this.state = { bindings: new Map(Object.entries(raw)), loaded: true, diff --git a/frontend/src/store/theme-store.ts b/frontend/src/store/theme-store.ts index 8b33599..770f728 100644 --- a/frontend/src/store/theme-store.ts +++ b/frontend/src/store/theme-store.ts @@ -4,7 +4,7 @@ import { GetThemeBackgroundShade, SetThemeAccentColor, SetThemeBackgroundShade, -} from '@go/config/Config'; +} from '@go/config/config.js'; import { Events } from '../events'; export type BackgroundShade = 'darker' | 'dark' | 'light'; diff --git a/frontend/src/store/tracklist-store.ts b/frontend/src/store/tracklist-store.ts index 8d2773a..e550912 100644 --- a/frontend/src/store/tracklist-store.ts +++ b/frontend/src/store/tracklist-store.ts @@ -2,10 +2,11 @@ import { EventsOn } from '@runtime/runtime'; import { GetTrackListColumns, SetTrackListColumns, -} from '@go/config/Config'; -import { tracklist } from '@go/models'; +} from '@go/config/config.js'; +import * as tracklist from '@go/tracklist/models.js'; import { Events } from '../events'; import { DEFAULT_COLUMN_IDS } from '@components/track-list/columns'; +import { list } from '@utils/binding'; export interface TrackListState { /** Ordered list of visible column IDs. */ @@ -47,12 +48,10 @@ class TrackListStore { private async loadFromBackend(): Promise { try { - const columns = await GetTrackListColumns(); + const columns = await list(GetTrackListColumns()); this.update({ - columnIds: columns.map( - (c: tracklist.Column) => c.id, - ), + columnIds: columns.map((c) => c.id), }); } catch { // Use defaults on failure. @@ -72,12 +71,9 @@ class TrackListStore { // =============================================================== async setColumns(columnIds: string[]): Promise { - const columns = columnIds.map((id) => { - const col = new tracklist.Column(); - col.id = id; - - return col; - }); + const columns: tracklist.Column[] = columnIds.map((id) => ({ + id: id as tracklist.ColumnID, + })); await SetTrackListColumns(columns); } diff --git a/frontend/src/utils/binding.ts b/frontend/src/utils/binding.ts new file mode 100644 index 0000000..1988e8d --- /dev/null +++ b/frontend/src/utils/binding.ts @@ -0,0 +1,88 @@ +/** + * The boundary between a Go return value and the app's own types. + * + * v2's binding generator typed a `[]T` return as `T[]`, which was a + * lie: a nil slice marshals to JSON `null`, and every list in this app + * has always been able to arrive that way. v3 types it honestly as + * `T[] | null`, which surfaced ~50 sites that were relying on the lie. + * + * The app's contract is the one it has always behaved as if it had — + * *an absent list is an empty list* — so it is stated once here rather + * than as `?? []` at every call site, and stated at the only place it + * is true: the moment a value crosses from Go. + * + * These helpers also return a plain `Promise`. v3 bindings return a + * `CancellablePromise`, and nothing in this app cancels one; letting + * that type leak inward would put a Wails type in the signature of + * every store method for a capability none of them use. + */ + +/** + * list awaits a binding returning a Go slice and yields `[]` for nil. + */ +export async function list( + request: PromiseLike, +): Promise { + return (await request) ?? []; +} + +/** + * dict awaits a binding returning a Go map and yields `{}` for nil. + * + * Two shapes of the same lie are undone here. The generator types a + * `map[int64]T` with a template-literal key (`` `${number}` ``), which + * cannot be indexed by a `number` even though every such key is one; + * and it types each value as nullable, because a map of slices can + * hold a nil one. A null-valued key is dropped rather than kept, + * which loses nothing: `noUncheckedIndexedAccess` already makes every + * read `V | undefined`, so an absent key and a nil value are + * indistinguishable to every consumer. + */ +export async function dict( + request: PromiseLike | null>, +): Promise> { + const raw = (await request) ?? {}; + const out: Record = {}; + + for (const [key, val] of Object.entries(raw)) { + if (val != null) out[Number(key)] = val; + } + + return out; +} + +/** + * dictByName is dict for a Go map keyed by something that is already a + * string — an MBID, a file path, a genre name. + */ +export async function dictByName( + request: PromiseLike | null>, +): Promise> { + return compact(await request); +} + +/** + * compact is dictByName for a map that arrived as a *field* rather + * than as a return value — a nested `map[string]string`, which the + * generator types with optional values because a JSON object need not + * carry every key. + */ +export function compact( + map: Record | null | undefined, +): Record { + const out: Record = {}; + + for (const [key, val] of Object.entries(map ?? {})) { + if (val != null) out[key] = val; + } + + return out; +} + +/** + * value awaits a binding whose result is used as-is, dropping only the + * cancellation the app never asks for. + */ +export async function value(request: PromiseLike): Promise { + return await request; +} diff --git a/frontend/src/utils/library-status.ts b/frontend/src/utils/library-status.ts index 50be773..d943d29 100644 --- a/frontend/src/utils/library-status.ts +++ b/frontend/src/utils/library-status.ts @@ -1,6 +1,6 @@ import { downloadStore } from '@store/download-store'; import { libraryStore } from '@store/library-store'; -import type { download } from '@go/models'; +import type * as download from '@go/download/models.js'; import type { LibraryStatus } from '../components/library-status-indicator/library-status-indicator'; /** diff --git a/frontend/src/utils/track-index.ts b/frontend/src/utils/track-index.ts index a5a1618..3685586 100644 --- a/frontend/src/utils/track-index.ts +++ b/frontend/src/utils/track-index.ts @@ -20,7 +20,7 @@ * given array and never again. */ -import type { library } from '@go/models'; +import type * as library from '@go/library/models.js'; const byArray = new WeakMap< readonly library.Track[], diff --git a/frontend/src/wails/runtime.ts b/frontend/src/wails/runtime.ts new file mode 100644 index 0000000..b08d29c --- /dev/null +++ b/frontend/src/wails/runtime.ts @@ -0,0 +1,33 @@ +/** + * The `@runtime/runtime` seam. + * + * 22 files import `EventsOn` from here and nothing else, so rather than + * rewriting 22 imports to v3's `Events.On` the alias points at this + * module — which is also what gives the Vitest fake exactly one seam to + * intercept instead of a `window.runtime` global that v3 does not have. + * (Plan 009, D4.) + * + * The one real difference between the two runtimes is the callback + * shape: v2 spread an event's data across the callback's arguments, + * v3 hands over a single `WailsEvent` object. Unwrapping `.data` here + * reproduces v2's shape for the single-value case, which is every emit + * in this tree — `backend/events` has no call site passing more than + * one data argument, and v3's `EventManager.Emit` only packs arguments + * into a slice when there is more than one, so there is nothing to + * un-spread. + */ + +import { Events } from '@wailsio/runtime'; + +/** + * EventsOn registers a listener for a backend event and returns the + * function that unregisters it. + */ +export function EventsOn( + eventName: string, + callback: (...data: any[]) => void, +): () => void { + return Events.On(eventName, (ev) => { + callback(ev.data); + }); +} diff --git a/frontend/test/components/library-status.test.ts b/frontend/test/components/library-status.test.ts index af069b9..d5483d4 100644 --- a/frontend/test/components/library-status.test.ts +++ b/frontend/test/components/library-status.test.ts @@ -34,7 +34,14 @@ import { fixture, shadow, shadowAll, update } from '@test/support/render'; const SEARCH = 'explore.Service.SearchLocal'; -function request(overrides: Partial): Request { +// v3's bindings type `state` and `entity` as real enums where v2 typed +// them as strings; the fixture widens both back to their value unions. +type RequestOverrides = Partial> & { + state?: `${Request['state']}`; + entity?: `${Request['entity']}`; +}; + +function request(overrides: RequestOverrides): Request { return { id: 1, mbid: 'rg-wanted', diff --git a/frontend/test/harness.test.ts b/frontend/test/harness.test.ts index 64753a3..e9d0daa 100644 --- a/frontend/test/harness.test.ts +++ b/frontend/test/harness.test.ts @@ -22,7 +22,7 @@ describe('component-tier harness', () => { it('routes generated bindings through the fake, not a module mock', async () => { // The import path under test is the real generated stub, which does // window['go']['queue']['Queue']['GetState'](). - const Queue = await import('@go/queue/Queue'); + const Queue = await import('@go/queue/queue.js'); wails.stub('queue.Queue.GetState', { currentIndex: 4 }); @@ -31,7 +31,7 @@ describe('component-tier harness', () => { }); it('resolves an unstubbed binding instead of hanging', async () => { - const Queue = await import('@go/queue/Queue'); + const Queue = await import('@go/queue/queue.js'); // The real trap this pays for is the reverse: a *real* binding // called with wrong argument types never settles. Here, silence is diff --git a/frontend/test/stores/download-store.test.ts b/frontend/test/stores/download-store.test.ts index 89139ad..9e502d2 100644 --- a/frontend/test/stores/download-store.test.ts +++ b/frontend/test/stores/download-store.test.ts @@ -33,10 +33,22 @@ function view(id: string, state: string): DownloadView { } function provider(id: number, enabled: boolean): DownloadProvider { - return { id, name: `p${id}`, enabled, kind: 'test' } as DownloadProvider; + return { + id, + name: `p${id}`, + enabled, + kind: 'test', + } as unknown as DownloadProvider; } -function request(overrides: Partial): Request { +// v3's bindings type `state` and `entity` as real enums where v2 typed +// them as strings; the fixture widens both back to their value unions. +type RequestOverrides = Partial> & { + state?: `${Request['state']}`; + entity?: `${Request['entity']}`; +}; + +function request(overrides: RequestOverrides): Request { return { id: 1, mbid: 'abc', diff --git a/frontend/test/stores/job-store.test.ts b/frontend/test/stores/job-store.test.ts index 248f8cf..9e352c5 100644 --- a/frontend/test/stores/job-store.test.ts +++ b/frontend/test/stores/job-store.test.ts @@ -17,7 +17,16 @@ import { import { Events } from '../../src/events'; import { emit, calls, stub, flush } from '@test/support/harness'; -function job(overrides: Partial & { id: string }): Job { +// v3's bindings type `state` and `kind` as real enums where v2 typed +// them as strings, so the fixture widens both back to their value +// unions — which is still literal-checked, just not enum-checked. +type JobOverrides = Partial> & { + id: string; + state?: `${Job['state']}`; + kind?: `${Job['kind']}`; +}; + +function job(overrides: JobOverrides): Job { return { kind: 'library-scan', state: 'running', @@ -43,7 +52,7 @@ describe('job predicates', () => { }); it('treats every in-flight state, including paused, as active', () => { - const states = ['queued', 'running', 'pausing', 'paused', 'cancelling']; + const states = ['queued', 'running', 'pausing', 'paused', 'cancelling'] as const; expect(states.map((state) => isActive(job({ id: state, state })))).toEqual([ true, diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 0a826f8..a668663 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -25,11 +25,11 @@ // module resolution "moduleResolution": "bundler", "paths": { // path aliases to find modules - "@go/*": ["./wailsjs/go/*"], + "@go/*": ["./bindings/yellowjacket/backend/*"], "@components/*": ["./src/components/*"], "@assets/*": ["./src/assets/*"], "@pages/*": ["./src/pages/*"], - "@runtime/*": ["./wailsjs/runtime/*"], + "@runtime/*": ["./src/wails/*"], "@utils/*": ["./src/utils/*"], "@store/*": ["./src/store/*"], "@test/*": ["./test/*"] @@ -44,8 +44,5 @@ {"name": "typescript-lit-html-plugin"} ] }, - "include": ["src", "wailsjs", "test"], - // Wails-generated JS stubs use @ts-check with untyped patterns that - // violate strict mode. The companion .d.ts files provide the types. - "exclude": ["wailsjs/go/**/*.js"] + "include": ["src", "bindings", "test"] } diff --git a/frontend/vite.config.mts b/frontend/vite.config.mts index f2b21e0..2143dd9 100644 --- a/frontend/vite.config.mts +++ b/frontend/vite.config.mts @@ -4,11 +4,15 @@ import { defineConfig } from "vite"; export default defineConfig({ resolve: { alias: { - "@go": path.resolve(__dirname, "wailsjs/go"), + // v3 generates bindings nested by Go import path. The + // alias absorbs the constant prefix, so a site imports + // "@go/library/library" rather than the full + // "bindings/yellowjacket/backend/library/library". + "@go": path.resolve(__dirname, "bindings/yellowjacket/backend"), "@components": path.resolve(__dirname, "src/components"), "@assets": path.resolve(__dirname, "src/assets"), "@pages": path.resolve(__dirname, "src/pages"), - "@runtime": path.resolve(__dirname, "wailsjs/runtime"), + "@runtime": path.resolve(__dirname, "src/wails"), "@utils": path.resolve(__dirname, "src/utils"), "@store": path.resolve(__dirname, "src/store"), }, diff --git a/frontend/wailsjs/go/autotagservice/Service.d.ts b/frontend/wailsjs/go/autotagservice/Service.d.ts deleted file mode 100755 index 76fd614..0000000 --- a/frontend/wailsjs/go/autotagservice/Service.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {autotagservice} from '../models'; -import {context} from '../models'; -import {jobs} from '../models'; - -export function AckLibraryWarning(arg1:number):Promise; - -export function Apply(arg1:string,arg2:string):Promise; - -export function ApplyAsync(arg1:string,arg2:string):Promise; - -export function ClearCompletedEntries(arg1:number):Promise; - -export function GetCandidateCoverArt(arg1:string,arg2:string):Promise; - -export function GetCandidates(arg1:string):Promise; - -export function GetCandidatesForPasteURL(arg1:string,arg2:string):Promise; - -export function GetLocalCoverArt(arg1:string):Promise; - -export function GetNextPending():Promise; - -export function GetPendingFolder(arg1:string):Promise; - -export function LeaveAsIs(arg1:string):Promise; - -export function ListPendingFolders(arg1:number):Promise>; - -export function RetagGroup(arg1:string):Promise; - -export function SearchCandidates(arg1:string,arg2:string,arg3:string):Promise>; - -export function SelectSearchCandidate(arg1:string,arg2:string,arg3:string):Promise; - -export function SetContext(arg1:context.Context):Promise; - -export function SetJobRegistry(arg1:jobs.Registry):Promise; - -export function Skip(arg1:string):Promise; - -export function SplitMixedFolder(arg1:string):Promise>; - -export function StartAutotagQueue(arg1:number):Promise; - -export function StartBackgroundPrefetch():Promise; - -export function WritesInFlight():Promise; diff --git a/frontend/wailsjs/go/autotagservice/Service.js b/frontend/wailsjs/go/autotagservice/Service.js deleted file mode 100755 index ff9fe74..0000000 --- a/frontend/wailsjs/go/autotagservice/Service.js +++ /dev/null @@ -1,91 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function AckLibraryWarning(arg1) { - return window['go']['autotagservice']['Service']['AckLibraryWarning'](arg1); -} - -export function Apply(arg1, arg2) { - return window['go']['autotagservice']['Service']['Apply'](arg1, arg2); -} - -export function ApplyAsync(arg1, arg2) { - return window['go']['autotagservice']['Service']['ApplyAsync'](arg1, arg2); -} - -export function ClearCompletedEntries(arg1) { - return window['go']['autotagservice']['Service']['ClearCompletedEntries'](arg1); -} - -export function GetCandidateCoverArt(arg1, arg2) { - return window['go']['autotagservice']['Service']['GetCandidateCoverArt'](arg1, arg2); -} - -export function GetCandidates(arg1) { - return window['go']['autotagservice']['Service']['GetCandidates'](arg1); -} - -export function GetCandidatesForPasteURL(arg1, arg2) { - return window['go']['autotagservice']['Service']['GetCandidatesForPasteURL'](arg1, arg2); -} - -export function GetLocalCoverArt(arg1) { - return window['go']['autotagservice']['Service']['GetLocalCoverArt'](arg1); -} - -export function GetNextPending() { - return window['go']['autotagservice']['Service']['GetNextPending'](); -} - -export function GetPendingFolder(arg1) { - return window['go']['autotagservice']['Service']['GetPendingFolder'](arg1); -} - -export function LeaveAsIs(arg1) { - return window['go']['autotagservice']['Service']['LeaveAsIs'](arg1); -} - -export function ListPendingFolders(arg1) { - return window['go']['autotagservice']['Service']['ListPendingFolders'](arg1); -} - -export function RetagGroup(arg1) { - return window['go']['autotagservice']['Service']['RetagGroup'](arg1); -} - -export function SearchCandidates(arg1, arg2, arg3) { - return window['go']['autotagservice']['Service']['SearchCandidates'](arg1, arg2, arg3); -} - -export function SelectSearchCandidate(arg1, arg2, arg3) { - return window['go']['autotagservice']['Service']['SelectSearchCandidate'](arg1, arg2, arg3); -} - -export function SetContext(arg1) { - return window['go']['autotagservice']['Service']['SetContext'](arg1); -} - -export function SetJobRegistry(arg1) { - return window['go']['autotagservice']['Service']['SetJobRegistry'](arg1); -} - -export function Skip(arg1) { - return window['go']['autotagservice']['Service']['Skip'](arg1); -} - -export function SplitMixedFolder(arg1) { - return window['go']['autotagservice']['Service']['SplitMixedFolder'](arg1); -} - -export function StartAutotagQueue(arg1) { - return window['go']['autotagservice']['Service']['StartAutotagQueue'](arg1); -} - -export function StartBackgroundPrefetch() { - return window['go']['autotagservice']['Service']['StartBackgroundPrefetch'](); -} - -export function WritesInFlight() { - return window['go']['autotagservice']['Service']['WritesInFlight'](); -} diff --git a/frontend/wailsjs/go/config/Config.d.ts b/frontend/wailsjs/go/config/Config.d.ts deleted file mode 100755 index 33d332f..0000000 --- a/frontend/wailsjs/go/config/Config.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {download} from '../models'; -import {tracklist} from '../models'; -import {context} from '../models'; - -export function GetDefaultPage():Promise; - -export function GetDownloadPreferences():Promise; - -export function GetFavoritesIconStyle():Promise; - -export function GetFavoritesPlaylistID():Promise; - -export function GetLibraryDirectory():Promise; - -export function GetPinDefaultPlaylist():Promise; - -export function GetQueueFallback():Promise; - -export function GetScanConcurrency():Promise; - -export function GetShortcuts():Promise>; - -export function GetThemeAccentColor():Promise; - -export function GetThemeBackgroundShade():Promise; - -export function GetTrackListColumns():Promise>; - -export function Load():Promise; - -export function ResetShortcuts():Promise; - -export function Save():Promise; - -export function SetContext(arg1:context.Context):Promise; - -export function SetDefaultPage(arg1:string):Promise; - -export function SetDownloadPreferences(arg1:download.AutoDownloadPrefs):Promise; - -export function SetFavoritesIconStyle(arg1:string):Promise; - -export function SetFavoritesPlaylistID(arg1:number):Promise; - -export function SetLibraryDirectory(arg1:string):Promise; - -export function SetPinDefaultPlaylist(arg1:boolean):Promise; - -export function SetQueueFallback(arg1:string):Promise; - -export function SetScanConcurrency(arg1:string):Promise; - -export function SetShortcut(arg1:string,arg2:string):Promise; - -export function SetShortcuts(arg1:Record):Promise; - -export function SetThemeAccentColor(arg1:string):Promise; - -export function SetThemeBackgroundShade(arg1:string):Promise; - -export function SetTrackListColumns(arg1:Array):Promise; - -export function Validate():Promise; diff --git a/frontend/wailsjs/go/config/Config.js b/frontend/wailsjs/go/config/Config.js deleted file mode 100755 index f28c2b0..0000000 --- a/frontend/wailsjs/go/config/Config.js +++ /dev/null @@ -1,123 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function GetDefaultPage() { - return window['go']['config']['Config']['GetDefaultPage'](); -} - -export function GetDownloadPreferences() { - return window['go']['config']['Config']['GetDownloadPreferences'](); -} - -export function GetFavoritesIconStyle() { - return window['go']['config']['Config']['GetFavoritesIconStyle'](); -} - -export function GetFavoritesPlaylistID() { - return window['go']['config']['Config']['GetFavoritesPlaylistID'](); -} - -export function GetLibraryDirectory() { - return window['go']['config']['Config']['GetLibraryDirectory'](); -} - -export function GetPinDefaultPlaylist() { - return window['go']['config']['Config']['GetPinDefaultPlaylist'](); -} - -export function GetQueueFallback() { - return window['go']['config']['Config']['GetQueueFallback'](); -} - -export function GetScanConcurrency() { - return window['go']['config']['Config']['GetScanConcurrency'](); -} - -export function GetShortcuts() { - return window['go']['config']['Config']['GetShortcuts'](); -} - -export function GetThemeAccentColor() { - return window['go']['config']['Config']['GetThemeAccentColor'](); -} - -export function GetThemeBackgroundShade() { - return window['go']['config']['Config']['GetThemeBackgroundShade'](); -} - -export function GetTrackListColumns() { - return window['go']['config']['Config']['GetTrackListColumns'](); -} - -export function Load() { - return window['go']['config']['Config']['Load'](); -} - -export function ResetShortcuts() { - return window['go']['config']['Config']['ResetShortcuts'](); -} - -export function Save() { - return window['go']['config']['Config']['Save'](); -} - -export function SetContext(arg1) { - return window['go']['config']['Config']['SetContext'](arg1); -} - -export function SetDefaultPage(arg1) { - return window['go']['config']['Config']['SetDefaultPage'](arg1); -} - -export function SetDownloadPreferences(arg1) { - return window['go']['config']['Config']['SetDownloadPreferences'](arg1); -} - -export function SetFavoritesIconStyle(arg1) { - return window['go']['config']['Config']['SetFavoritesIconStyle'](arg1); -} - -export function SetFavoritesPlaylistID(arg1) { - return window['go']['config']['Config']['SetFavoritesPlaylistID'](arg1); -} - -export function SetLibraryDirectory(arg1) { - return window['go']['config']['Config']['SetLibraryDirectory'](arg1); -} - -export function SetPinDefaultPlaylist(arg1) { - return window['go']['config']['Config']['SetPinDefaultPlaylist'](arg1); -} - -export function SetQueueFallback(arg1) { - return window['go']['config']['Config']['SetQueueFallback'](arg1); -} - -export function SetScanConcurrency(arg1) { - return window['go']['config']['Config']['SetScanConcurrency'](arg1); -} - -export function SetShortcut(arg1, arg2) { - return window['go']['config']['Config']['SetShortcut'](arg1, arg2); -} - -export function SetShortcuts(arg1) { - return window['go']['config']['Config']['SetShortcuts'](arg1); -} - -export function SetThemeAccentColor(arg1) { - return window['go']['config']['Config']['SetThemeAccentColor'](arg1); -} - -export function SetThemeBackgroundShade(arg1) { - return window['go']['config']['Config']['SetThemeBackgroundShade'](arg1); -} - -export function SetTrackListColumns(arg1) { - return window['go']['config']['Config']['SetTrackListColumns'](arg1); -} - -export function Validate() { - return window['go']['config']['Config']['Validate'](); -} diff --git a/frontend/wailsjs/go/download/Service.d.ts b/frontend/wailsjs/go/download/Service.d.ts deleted file mode 100755 index c3875ab..0000000 --- a/frontend/wailsjs/go/download/Service.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {download} from '../models'; -import {context} from '../models'; - -export function AddProvider(arg1:string,arg2:string,arg3:Record):Promise; - -export function AddRequest(arg1:download.RequestInput):Promise; - -export function Cancel(arg1:string):Promise; - -export function Candidates(arg1:string):Promise>; - -export function ClearFinished():Promise; - -export function ClearSatisfiedRequests():Promise; - -export function DeleteProvider(arg1:number):Promise; - -export function ImportExternalRequests(arg1:number,arg2:number):Promise; - -export function IsRequested(arg1:string,arg2:number):Promise; - -export function ListDownloads(arg1:number):Promise>; - -export function ListProviders():Promise>; - -export function ListRequests():Promise>; - -export function PauseRequest(arg1:number,arg2:boolean):Promise; - -export function Pick(arg1:string,arg2:string):Promise; - -export function ProviderKinds():Promise>; - -export function ReconcileRequests():Promise; - -export function RemoveRequest(arg1:number):Promise; - -export function SetContext(arg1:context.Context):Promise; - -export function SetPreferences(arg1:download.AutoDownloadPrefs):Promise; - -export function SetReconciler(arg1:download.Reconciler):Promise; - -export function StartDownload(arg1:download.SearchRequest):Promise; - -export function TestProvider(arg1:number):Promise; - -export function UpdateProvider(arg1:number,arg2:string,arg3:boolean,arg4:number,arg5:Record):Promise; diff --git a/frontend/wailsjs/go/download/Service.js b/frontend/wailsjs/go/download/Service.js deleted file mode 100755 index 4e41294..0000000 --- a/frontend/wailsjs/go/download/Service.js +++ /dev/null @@ -1,95 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function AddProvider(arg1, arg2, arg3) { - return window['go']['download']['Service']['AddProvider'](arg1, arg2, arg3); -} - -export function AddRequest(arg1) { - return window['go']['download']['Service']['AddRequest'](arg1); -} - -export function Cancel(arg1) { - return window['go']['download']['Service']['Cancel'](arg1); -} - -export function Candidates(arg1) { - return window['go']['download']['Service']['Candidates'](arg1); -} - -export function ClearFinished() { - return window['go']['download']['Service']['ClearFinished'](); -} - -export function ClearSatisfiedRequests() { - return window['go']['download']['Service']['ClearSatisfiedRequests'](); -} - -export function DeleteProvider(arg1) { - return window['go']['download']['Service']['DeleteProvider'](arg1); -} - -export function ImportExternalRequests(arg1, arg2) { - return window['go']['download']['Service']['ImportExternalRequests'](arg1, arg2); -} - -export function IsRequested(arg1, arg2) { - return window['go']['download']['Service']['IsRequested'](arg1, arg2); -} - -export function ListDownloads(arg1) { - return window['go']['download']['Service']['ListDownloads'](arg1); -} - -export function ListProviders() { - return window['go']['download']['Service']['ListProviders'](); -} - -export function ListRequests() { - return window['go']['download']['Service']['ListRequests'](); -} - -export function PauseRequest(arg1, arg2) { - return window['go']['download']['Service']['PauseRequest'](arg1, arg2); -} - -export function Pick(arg1, arg2) { - return window['go']['download']['Service']['Pick'](arg1, arg2); -} - -export function ProviderKinds() { - return window['go']['download']['Service']['ProviderKinds'](); -} - -export function ReconcileRequests() { - return window['go']['download']['Service']['ReconcileRequests'](); -} - -export function RemoveRequest(arg1) { - return window['go']['download']['Service']['RemoveRequest'](arg1); -} - -export function SetContext(arg1) { - return window['go']['download']['Service']['SetContext'](arg1); -} - -export function SetPreferences(arg1) { - return window['go']['download']['Service']['SetPreferences'](arg1); -} - -export function SetReconciler(arg1) { - return window['go']['download']['Service']['SetReconciler'](arg1); -} - -export function StartDownload(arg1) { - return window['go']['download']['Service']['StartDownload'](arg1); -} - -export function TestProvider(arg1) { - return window['go']['download']['Service']['TestProvider'](arg1); -} - -export function UpdateProvider(arg1, arg2, arg3, arg4, arg5) { - return window['go']['download']['Service']['UpdateProvider'](arg1, arg2, arg3, arg4, arg5); -} diff --git a/frontend/wailsjs/go/explore/Service.d.ts b/frontend/wailsjs/go/explore/Service.d.ts deleted file mode 100755 index 15092ef..0000000 --- a/frontend/wailsjs/go/explore/Service.d.ts +++ /dev/null @@ -1,120 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {explore} from '../models'; -import {context} from '../models'; -import {time} from '../models'; -import {jobs} from '../models'; - -export function AdoptPausedIndexBuild():Promise; - -export function BackfillLibraryDiscographies():Promise; - -export function BackfillLibraryLyrics():Promise; - -export function BackfillReleaseGroupMBIDs():Promise; - -export function BrowseReleaseGroups(arg1:string):Promise>; - -export function BrowseReleases(arg1:string):Promise>; - -export function CAALimiter():Promise; - -export function CheckLibraryMBIDs(arg1:Array):Promise>; - -export function CoreCatalogImported():Promise; - -export function CoverArtGroupURL(arg1:string):Promise; - -export function CoverArtURL(arg1:string):Promise; - -export function GenerateMix(arg1:context.Context,arg2:Array,arg3:boolean):Promise>; - -export function GetArtistImageCached(arg1:string):Promise; - -export function GetArtistImageCachedPath(arg1:string):Promise; - -export function GetArtistImageURL(arg1:string):Promise; - -export function GetArtistImagesCachedPaths(arg1:Array):Promise>; - -export function GetArtistMBID(arg1:string):Promise; - -export function GetArtistPlayCount(arg1:string):Promise; - -export function GetCandidateThumbnail(arg1:string,arg2:string):Promise; - -export function GetExploreShelves():Promise; - -export function GetIndexStatus():Promise; - -export function GetLibrarySimilarArtists(arg1:string):Promise>; - -export function GetThumbnail(arg1:string,arg2:string,arg3:string):Promise; - -export function GetThumbnails(arg1:Array):Promise>; - -export function GetTrackLyrics(arg1:number):Promise; - -export function GetTrackThumbnail(arg1:string,arg2:string,arg3:string,arg4:string):Promise; - -export function GetTrackThumbnails(arg1:Array):Promise>; - -export function IndexBaselineSeries():Promise; - -export function IndexImportComplete():Promise; - -export function IndexLastImported():Promise; - -export function InvalidateIndexDiscographies():Promise; - -export function InvalidateLibrarySync():Promise; - -export function IsIndexReady():Promise; - -export function LookupArtist(arg1:string):Promise; - -export function LookupReleaseGroup(arg1:string):Promise; - -export function MusicBrainz():Promise; - -export function PopulateLocalCrossReferences():Promise; - -export function PopulateLocalCrossReferencesIfNeeded():Promise; - -export function PrefetchReleases(arg1:Array):Promise; - -export function PrepareIndexRebuild():Promise; - -export function RebuildLyricsIndex():Promise; - -export function RebuildLyricsIndexIfNeeded():Promise; - -export function RecordSearchClick(arg1:string,arg2:string,arg3:string):Promise; - -export function RefreshIndexNow(arg1:time.Duration):Promise; - -export function RefreshListenCounts():Promise; - -export function ResolveReleaseGroupMBIDs(arg1:Array):Promise>; - -export function SearchLocal(arg1:string):Promise; - -export function SearchLyrics(arg1:string):Promise>; - -export function SetAlbumComplete(arg1:explore.AlbumCompleteFunc):Promise; - -export function SetContext(arg1:context.Context):Promise; - -export function SetJobRegistry(arg1:jobs.Registry):Promise; - -export function SimilarArtists(arg1:string):Promise>; - -export function StartIndexBuild():Promise; - -export function StopIndexBuild():Promise; - -export function TopRecordingsForArtist(arg1:string):Promise>; - -export function TopReleaseGroupsForArtist(arg1:string):Promise>; - -export function WaitForIndexIdle():Promise; diff --git a/frontend/wailsjs/go/explore/Service.js b/frontend/wailsjs/go/explore/Service.js deleted file mode 100755 index c54d63f..0000000 --- a/frontend/wailsjs/go/explore/Service.js +++ /dev/null @@ -1,231 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function AdoptPausedIndexBuild() { - return window['go']['explore']['Service']['AdoptPausedIndexBuild'](); -} - -export function BackfillLibraryDiscographies() { - return window['go']['explore']['Service']['BackfillLibraryDiscographies'](); -} - -export function BackfillLibraryLyrics() { - return window['go']['explore']['Service']['BackfillLibraryLyrics'](); -} - -export function BackfillReleaseGroupMBIDs() { - return window['go']['explore']['Service']['BackfillReleaseGroupMBIDs'](); -} - -export function BrowseReleaseGroups(arg1) { - return window['go']['explore']['Service']['BrowseReleaseGroups'](arg1); -} - -export function BrowseReleases(arg1) { - return window['go']['explore']['Service']['BrowseReleases'](arg1); -} - -export function CAALimiter() { - return window['go']['explore']['Service']['CAALimiter'](); -} - -export function CheckLibraryMBIDs(arg1) { - return window['go']['explore']['Service']['CheckLibraryMBIDs'](arg1); -} - -export function CoreCatalogImported() { - return window['go']['explore']['Service']['CoreCatalogImported'](); -} - -export function CoverArtGroupURL(arg1) { - return window['go']['explore']['Service']['CoverArtGroupURL'](arg1); -} - -export function CoverArtURL(arg1) { - return window['go']['explore']['Service']['CoverArtURL'](arg1); -} - -export function GenerateMix(arg1, arg2, arg3) { - return window['go']['explore']['Service']['GenerateMix'](arg1, arg2, arg3); -} - -export function GetArtistImageCached(arg1) { - return window['go']['explore']['Service']['GetArtistImageCached'](arg1); -} - -export function GetArtistImageCachedPath(arg1) { - return window['go']['explore']['Service']['GetArtistImageCachedPath'](arg1); -} - -export function GetArtistImageURL(arg1) { - return window['go']['explore']['Service']['GetArtistImageURL'](arg1); -} - -export function GetArtistImagesCachedPaths(arg1) { - return window['go']['explore']['Service']['GetArtistImagesCachedPaths'](arg1); -} - -export function GetArtistMBID(arg1) { - return window['go']['explore']['Service']['GetArtistMBID'](arg1); -} - -export function GetArtistPlayCount(arg1) { - return window['go']['explore']['Service']['GetArtistPlayCount'](arg1); -} - -export function GetCandidateThumbnail(arg1, arg2) { - return window['go']['explore']['Service']['GetCandidateThumbnail'](arg1, arg2); -} - -export function GetExploreShelves() { - return window['go']['explore']['Service']['GetExploreShelves'](); -} - -export function GetIndexStatus() { - return window['go']['explore']['Service']['GetIndexStatus'](); -} - -export function GetLibrarySimilarArtists(arg1) { - return window['go']['explore']['Service']['GetLibrarySimilarArtists'](arg1); -} - -export function GetThumbnail(arg1, arg2, arg3) { - return window['go']['explore']['Service']['GetThumbnail'](arg1, arg2, arg3); -} - -export function GetThumbnails(arg1) { - return window['go']['explore']['Service']['GetThumbnails'](arg1); -} - -export function GetTrackLyrics(arg1) { - return window['go']['explore']['Service']['GetTrackLyrics'](arg1); -} - -export function GetTrackThumbnail(arg1, arg2, arg3, arg4) { - return window['go']['explore']['Service']['GetTrackThumbnail'](arg1, arg2, arg3, arg4); -} - -export function GetTrackThumbnails(arg1) { - return window['go']['explore']['Service']['GetTrackThumbnails'](arg1); -} - -export function IndexBaselineSeries() { - return window['go']['explore']['Service']['IndexBaselineSeries'](); -} - -export function IndexImportComplete() { - return window['go']['explore']['Service']['IndexImportComplete'](); -} - -export function IndexLastImported() { - return window['go']['explore']['Service']['IndexLastImported'](); -} - -export function InvalidateIndexDiscographies() { - return window['go']['explore']['Service']['InvalidateIndexDiscographies'](); -} - -export function InvalidateLibrarySync() { - return window['go']['explore']['Service']['InvalidateLibrarySync'](); -} - -export function IsIndexReady() { - return window['go']['explore']['Service']['IsIndexReady'](); -} - -export function LookupArtist(arg1) { - return window['go']['explore']['Service']['LookupArtist'](arg1); -} - -export function LookupReleaseGroup(arg1) { - return window['go']['explore']['Service']['LookupReleaseGroup'](arg1); -} - -export function MusicBrainz() { - return window['go']['explore']['Service']['MusicBrainz'](); -} - -export function PopulateLocalCrossReferences() { - return window['go']['explore']['Service']['PopulateLocalCrossReferences'](); -} - -export function PopulateLocalCrossReferencesIfNeeded() { - return window['go']['explore']['Service']['PopulateLocalCrossReferencesIfNeeded'](); -} - -export function PrefetchReleases(arg1) { - return window['go']['explore']['Service']['PrefetchReleases'](arg1); -} - -export function PrepareIndexRebuild() { - return window['go']['explore']['Service']['PrepareIndexRebuild'](); -} - -export function RebuildLyricsIndex() { - return window['go']['explore']['Service']['RebuildLyricsIndex'](); -} - -export function RebuildLyricsIndexIfNeeded() { - return window['go']['explore']['Service']['RebuildLyricsIndexIfNeeded'](); -} - -export function RecordSearchClick(arg1, arg2, arg3) { - return window['go']['explore']['Service']['RecordSearchClick'](arg1, arg2, arg3); -} - -export function RefreshIndexNow(arg1) { - return window['go']['explore']['Service']['RefreshIndexNow'](arg1); -} - -export function RefreshListenCounts() { - return window['go']['explore']['Service']['RefreshListenCounts'](); -} - -export function ResolveReleaseGroupMBIDs(arg1) { - return window['go']['explore']['Service']['ResolveReleaseGroupMBIDs'](arg1); -} - -export function SearchLocal(arg1) { - return window['go']['explore']['Service']['SearchLocal'](arg1); -} - -export function SearchLyrics(arg1) { - return window['go']['explore']['Service']['SearchLyrics'](arg1); -} - -export function SetAlbumComplete(arg1) { - return window['go']['explore']['Service']['SetAlbumComplete'](arg1); -} - -export function SetContext(arg1) { - return window['go']['explore']['Service']['SetContext'](arg1); -} - -export function SetJobRegistry(arg1) { - return window['go']['explore']['Service']['SetJobRegistry'](arg1); -} - -export function SimilarArtists(arg1) { - return window['go']['explore']['Service']['SimilarArtists'](arg1); -} - -export function StartIndexBuild() { - return window['go']['explore']['Service']['StartIndexBuild'](); -} - -export function StopIndexBuild() { - return window['go']['explore']['Service']['StopIndexBuild'](); -} - -export function TopRecordingsForArtist(arg1) { - return window['go']['explore']['Service']['TopRecordingsForArtist'](arg1); -} - -export function TopReleaseGroupsForArtist(arg1) { - return window['go']['explore']['Service']['TopReleaseGroupsForArtist'](arg1); -} - -export function WaitForIndexIdle() { - return window['go']['explore']['Service']['WaitForIndexIdle'](); -} diff --git a/frontend/wailsjs/go/frontendutil/FrontendUtil.d.ts b/frontend/wailsjs/go/frontendutil/FrontendUtil.d.ts deleted file mode 100755 index 3c75085..0000000 --- a/frontend/wailsjs/go/frontendutil/FrontendUtil.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {context} from '../models'; - -export function DirectoryPicker():Promise; - -export function ImageFilePicker():Promise; - -export function PlaylistFilePicker():Promise>; - -export function ReadFile(arg1:string):Promise>; - -export function SetContext(arg1:context.Context):Promise; diff --git a/frontend/wailsjs/go/frontendutil/FrontendUtil.js b/frontend/wailsjs/go/frontendutil/FrontendUtil.js deleted file mode 100755 index b0e52a4..0000000 --- a/frontend/wailsjs/go/frontendutil/FrontendUtil.js +++ /dev/null @@ -1,23 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function DirectoryPicker() { - return window['go']['frontendutil']['FrontendUtil']['DirectoryPicker'](); -} - -export function ImageFilePicker() { - return window['go']['frontendutil']['FrontendUtil']['ImageFilePicker'](); -} - -export function PlaylistFilePicker() { - return window['go']['frontendutil']['FrontendUtil']['PlaylistFilePicker'](); -} - -export function ReadFile(arg1) { - return window['go']['frontendutil']['FrontendUtil']['ReadFile'](arg1); -} - -export function SetContext(arg1) { - return window['go']['frontendutil']['FrontendUtil']['SetContext'](arg1); -} diff --git a/frontend/wailsjs/go/jobs/Service.d.ts b/frontend/wailsjs/go/jobs/Service.d.ts deleted file mode 100755 index 8beaa9f..0000000 --- a/frontend/wailsjs/go/jobs/Service.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {jobs} from '../models'; - -export function CancelJob(arg1:string):Promise; - -export function ClearFinishedJobs():Promise; - -export function DismissJob(arg1:string):Promise; - -export function GetJobLog(arg1:string):Promise>; - -export function GetJobs():Promise>; - -export function PauseJob(arg1:string):Promise; - -export function ResumeJob(arg1:string):Promise; diff --git a/frontend/wailsjs/go/jobs/Service.js b/frontend/wailsjs/go/jobs/Service.js deleted file mode 100755 index 523f94c..0000000 --- a/frontend/wailsjs/go/jobs/Service.js +++ /dev/null @@ -1,31 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function CancelJob(arg1) { - return window['go']['jobs']['Service']['CancelJob'](arg1); -} - -export function ClearFinishedJobs() { - return window['go']['jobs']['Service']['ClearFinishedJobs'](); -} - -export function DismissJob(arg1) { - return window['go']['jobs']['Service']['DismissJob'](arg1); -} - -export function GetJobLog(arg1) { - return window['go']['jobs']['Service']['GetJobLog'](arg1); -} - -export function GetJobs() { - return window['go']['jobs']['Service']['GetJobs'](); -} - -export function PauseJob(arg1) { - return window['go']['jobs']['Service']['PauseJob'](arg1); -} - -export function ResumeJob(arg1) { - return window['go']['jobs']['Service']['ResumeJob'](arg1); -} diff --git a/frontend/wailsjs/go/library/Library.d.ts b/frontend/wailsjs/go/library/Library.d.ts deleted file mode 100755 index 49440c0..0000000 --- a/frontend/wailsjs/go/library/Library.d.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {sqlcgen} from '../models'; -import {library} from '../models'; -import {context} from '../models'; -import {jobs} from '../models'; - -export function AcquirePipelineLock():Promise; - -export function AddLibrary(arg1:string):Promise; - -export function CancelAllScans():Promise; - -export function CancelCurrentScan():Promise; - -export function CancelScan():Promise; - -export function FullRescan():Promise; - -export function GetAlbumCompleteness(arg1:number):Promise; - -export function GetAlbumTracks(arg1:number):Promise>; - -export function GetAlbumTracksByLibrary(arg1:number,arg2:number):Promise>; - -export function GetAlbumsByArtist(arg1:number):Promise>; - -export function GetAlbumsByArtistByLibrary(arg1:number,arg2:number):Promise>; - -export function GetAllAlbums():Promise>; - -export function GetAllAlbumsByLibrary(arg1:number):Promise>; - -export function GetAllArtists():Promise>; - -export function GetAllArtistsByLibrary(arg1:number):Promise>; - -export function GetAllGenresWithCounts():Promise>; - -export function GetAllGenresWithCountsByLibrary(arg1:number):Promise>; - -export function GetAllLibrariesWithTrackCounts():Promise>; - -export function GetAllTracks():Promise>; - -export function GetAllTracksByLibrary(arg1:number):Promise>; - -export function GetFilePathsByAlbums(arg1:Array,arg2:number):Promise>>; - -export function GetFilePathsByGenres(arg1:Array,arg2:number):Promise>>; - -export function GetFilePathsByRecordingMBIDs(arg1:Array,arg2:number):Promise>>; - -export function GetRemovalImpact(arg1:number):Promise; - -export function GetScanQueueLength():Promise; - -export function GetTrackMBIDs(arg1:string):Promise; - -export function GetTracksByGenre(arg1:string):Promise>; - -export function GetTracksByGenreByLibrary(arg1:string,arg2:number):Promise>; - -export function IsScanActive():Promise; - -export function IsScanPaused():Promise; - -export function LibraryPath(arg1:number):Promise; - -export function PauseScan():Promise; - -export function QueuedLibraryNames():Promise>; - -export function ReleasePipelineLock():Promise; - -export function RemoveFromLibrary(arg1:Array):Promise; - -export function RemoveLibrary(arg1:number):Promise; - -export function RenameLibrary(arg1:number,arg2:string):Promise; - -export function RestorePausedScans():Promise; - -export function ResumeScan():Promise; - -export function ScanAllLibraries():Promise; - -export function ScanLibrary(arg1:number):Promise; - -export function SearchTracks(arg1:string):Promise>; - -export function SearchTracksByLibrary(arg1:string,arg2:number):Promise>; - -export function SetContext(arg1:context.Context):Promise; - -export function SetJobRegistry(arg1:jobs.Registry):Promise; - -export function SetRemovalHooks(arg1:library.RemovalHooks):Promise; - -export function SetRescanHooks(arg1:library.RescanHooks):Promise; - -export function SetScanHooks(arg1:library.ScanHooks):Promise; - -export function SoftScanAllLibraries():Promise; diff --git a/frontend/wailsjs/go/library/Library.js b/frontend/wailsjs/go/library/Library.js deleted file mode 100755 index f8e8e46..0000000 --- a/frontend/wailsjs/go/library/Library.js +++ /dev/null @@ -1,199 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function AcquirePipelineLock() { - return window['go']['library']['Library']['AcquirePipelineLock'](); -} - -export function AddLibrary(arg1) { - return window['go']['library']['Library']['AddLibrary'](arg1); -} - -export function CancelAllScans() { - return window['go']['library']['Library']['CancelAllScans'](); -} - -export function CancelCurrentScan() { - return window['go']['library']['Library']['CancelCurrentScan'](); -} - -export function CancelScan() { - return window['go']['library']['Library']['CancelScan'](); -} - -export function FullRescan() { - return window['go']['library']['Library']['FullRescan'](); -} - -export function GetAlbumCompleteness(arg1) { - return window['go']['library']['Library']['GetAlbumCompleteness'](arg1); -} - -export function GetAlbumTracks(arg1) { - return window['go']['library']['Library']['GetAlbumTracks'](arg1); -} - -export function GetAlbumTracksByLibrary(arg1, arg2) { - return window['go']['library']['Library']['GetAlbumTracksByLibrary'](arg1, arg2); -} - -export function GetAlbumsByArtist(arg1) { - return window['go']['library']['Library']['GetAlbumsByArtist'](arg1); -} - -export function GetAlbumsByArtistByLibrary(arg1, arg2) { - return window['go']['library']['Library']['GetAlbumsByArtistByLibrary'](arg1, arg2); -} - -export function GetAllAlbums() { - return window['go']['library']['Library']['GetAllAlbums'](); -} - -export function GetAllAlbumsByLibrary(arg1) { - return window['go']['library']['Library']['GetAllAlbumsByLibrary'](arg1); -} - -export function GetAllArtists() { - return window['go']['library']['Library']['GetAllArtists'](); -} - -export function GetAllArtistsByLibrary(arg1) { - return window['go']['library']['Library']['GetAllArtistsByLibrary'](arg1); -} - -export function GetAllGenresWithCounts() { - return window['go']['library']['Library']['GetAllGenresWithCounts'](); -} - -export function GetAllGenresWithCountsByLibrary(arg1) { - return window['go']['library']['Library']['GetAllGenresWithCountsByLibrary'](arg1); -} - -export function GetAllLibrariesWithTrackCounts() { - return window['go']['library']['Library']['GetAllLibrariesWithTrackCounts'](); -} - -export function GetAllTracks() { - return window['go']['library']['Library']['GetAllTracks'](); -} - -export function GetAllTracksByLibrary(arg1) { - return window['go']['library']['Library']['GetAllTracksByLibrary'](arg1); -} - -export function GetFilePathsByAlbums(arg1, arg2) { - return window['go']['library']['Library']['GetFilePathsByAlbums'](arg1, arg2); -} - -export function GetFilePathsByGenres(arg1, arg2) { - return window['go']['library']['Library']['GetFilePathsByGenres'](arg1, arg2); -} - -export function GetFilePathsByRecordingMBIDs(arg1, arg2) { - return window['go']['library']['Library']['GetFilePathsByRecordingMBIDs'](arg1, arg2); -} - -export function GetRemovalImpact(arg1) { - return window['go']['library']['Library']['GetRemovalImpact'](arg1); -} - -export function GetScanQueueLength() { - return window['go']['library']['Library']['GetScanQueueLength'](); -} - -export function GetTrackMBIDs(arg1) { - return window['go']['library']['Library']['GetTrackMBIDs'](arg1); -} - -export function GetTracksByGenre(arg1) { - return window['go']['library']['Library']['GetTracksByGenre'](arg1); -} - -export function GetTracksByGenreByLibrary(arg1, arg2) { - return window['go']['library']['Library']['GetTracksByGenreByLibrary'](arg1, arg2); -} - -export function IsScanActive() { - return window['go']['library']['Library']['IsScanActive'](); -} - -export function IsScanPaused() { - return window['go']['library']['Library']['IsScanPaused'](); -} - -export function LibraryPath(arg1) { - return window['go']['library']['Library']['LibraryPath'](arg1); -} - -export function PauseScan() { - return window['go']['library']['Library']['PauseScan'](); -} - -export function QueuedLibraryNames() { - return window['go']['library']['Library']['QueuedLibraryNames'](); -} - -export function ReleasePipelineLock() { - return window['go']['library']['Library']['ReleasePipelineLock'](); -} - -export function RemoveFromLibrary(arg1) { - return window['go']['library']['Library']['RemoveFromLibrary'](arg1); -} - -export function RemoveLibrary(arg1) { - return window['go']['library']['Library']['RemoveLibrary'](arg1); -} - -export function RenameLibrary(arg1, arg2) { - return window['go']['library']['Library']['RenameLibrary'](arg1, arg2); -} - -export function RestorePausedScans() { - return window['go']['library']['Library']['RestorePausedScans'](); -} - -export function ResumeScan() { - return window['go']['library']['Library']['ResumeScan'](); -} - -export function ScanAllLibraries() { - return window['go']['library']['Library']['ScanAllLibraries'](); -} - -export function ScanLibrary(arg1) { - return window['go']['library']['Library']['ScanLibrary'](arg1); -} - -export function SearchTracks(arg1) { - return window['go']['library']['Library']['SearchTracks'](arg1); -} - -export function SearchTracksByLibrary(arg1, arg2) { - return window['go']['library']['Library']['SearchTracksByLibrary'](arg1, arg2); -} - -export function SetContext(arg1) { - return window['go']['library']['Library']['SetContext'](arg1); -} - -export function SetJobRegistry(arg1) { - return window['go']['library']['Library']['SetJobRegistry'](arg1); -} - -export function SetRemovalHooks(arg1) { - return window['go']['library']['Library']['SetRemovalHooks'](arg1); -} - -export function SetRescanHooks(arg1) { - return window['go']['library']['Library']['SetRescanHooks'](arg1); -} - -export function SetScanHooks(arg1) { - return window['go']['library']['Library']['SetScanHooks'](arg1); -} - -export function SoftScanAllLibraries() { - return window['go']['library']['Library']['SoftScanAllLibraries'](); -} diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts deleted file mode 100755 index 601ebe9..0000000 --- a/frontend/wailsjs/go/models.ts +++ /dev/null @@ -1,2534 +0,0 @@ -export namespace autotagservice { - - export class AlignmentView { - localIndex: number; - localTitle: string; - localLengthMillis: number; - candidatePosition: number; - candidateDiscNumber: number; - candidateTitle: string; - candidateMbid: string; - candidateLength: number; - titleScore: number; - lengthDeltaMs: number; - trackNumberOk: boolean; - status: string; - - static createFrom(source: any = {}) { - return new AlignmentView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.localIndex = source["localIndex"]; - this.localTitle = source["localTitle"]; - this.localLengthMillis = source["localLengthMillis"]; - this.candidatePosition = source["candidatePosition"]; - this.candidateDiscNumber = source["candidateDiscNumber"]; - this.candidateTitle = source["candidateTitle"]; - this.candidateMbid = source["candidateMbid"]; - this.candidateLength = source["candidateLength"]; - this.titleScore = source["titleScore"]; - this.lengthDeltaMs = source["lengthDeltaMs"]; - this.trackNumberOk = source["trackNumberOk"]; - this.status = source["status"]; - } - } - export class FailureView { - filePath: string; - error: string; - - static createFrom(source: any = {}) { - return new FailureView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.filePath = source["filePath"]; - this.error = source["error"]; - } - } - export class ApplyResultView { - groupKey: string; - succeeded: number; - failed: number; - failures: FailureView[]; - - static createFrom(source: any = {}) { - return new ApplyResultView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.groupKey = source["groupKey"]; - this.succeeded = source["succeeded"]; - this.failed = source["failed"]; - this.failures = this.convertValues(source["failures"], FailureView); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class ScoreBreakdownView { - titleAvg: number; - lengthAvg: number; - artistFit: number; - albumFit: number; - trackCountFit: number; - releaseMeta: number; - evidence: number; - - static createFrom(source: any = {}) { - return new ScoreBreakdownView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.titleAvg = source["titleAvg"]; - this.lengthAvg = source["lengthAvg"]; - this.artistFit = source["artistFit"]; - this.albumFit = source["albumFit"]; - this.trackCountFit = source["trackCountFit"]; - this.releaseMeta = source["releaseMeta"]; - this.evidence = source["evidence"]; - } - } - export class CandidateView { - releaseMbid: string; - releaseGroupMbid: string; - title: string; - artistCredit: string; - date: string; - originalDate: string; - country: string; - status: string; - primaryType: string; - trackCount: number; - score: number; - breakdown: ScoreBreakdownView; - source: string; - provenance: string; - coverArtUrl: string; - alignments: AlignmentView[]; - - static createFrom(source: any = {}) { - return new CandidateView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.releaseMbid = source["releaseMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.title = source["title"]; - this.artistCredit = source["artistCredit"]; - this.date = source["date"]; - this.originalDate = source["originalDate"]; - this.country = source["country"]; - this.status = source["status"]; - this.primaryType = source["primaryType"]; - this.trackCount = source["trackCount"]; - this.score = source["score"]; - this.breakdown = this.convertValues(source["breakdown"], ScoreBreakdownView); - this.source = source["source"]; - this.provenance = source["provenance"]; - this.coverArtUrl = source["coverArtUrl"]; - this.alignments = this.convertValues(source["alignments"], AlignmentView); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - - export class LocalTrackView { - audioFileId: number; - filePath: string; - title: string; - artist: string; - trackNumber: number; - discNumber: number; - lengthMillis: number; - recordingMbid: string; - - static createFrom(source: any = {}) { - return new LocalTrackView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.audioFileId = source["audioFileId"]; - this.filePath = source["filePath"]; - this.title = source["title"]; - this.artist = source["artist"]; - this.trackNumber = source["trackNumber"]; - this.discNumber = source["discNumber"]; - this.lengthMillis = source["lengthMillis"]; - this.recordingMbid = source["recordingMbid"]; - } - } - export class PendingItem { - groupKey: string; - libraryId: number; - libraryName: string; - folderSubPath: string; - trackCount: number; - albumName: string; - albumArtist: string; - discNumber: number; - bestMatchReleaseMbid: string; - score: number; - status: string; - synthetic: boolean; - likelyMixedBag: boolean; - - static createFrom(source: any = {}) { - return new PendingItem(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.groupKey = source["groupKey"]; - this.libraryId = source["libraryId"]; - this.libraryName = source["libraryName"]; - this.folderSubPath = source["folderSubPath"]; - this.trackCount = source["trackCount"]; - this.albumName = source["albumName"]; - this.albumArtist = source["albumArtist"]; - this.discNumber = source["discNumber"]; - this.bestMatchReleaseMbid = source["bestMatchReleaseMbid"]; - this.score = source["score"]; - this.status = source["status"]; - this.synthetic = source["synthetic"]; - this.likelyMixedBag = source["likelyMixedBag"]; - } - } - - export class ScoreView { - groupKey: string; - localTracks: LocalTrackView[]; - candidates: CandidateView[]; - recommendation: string; - mixedBag: boolean; - synthetic: boolean; - - static createFrom(source: any = {}) { - return new ScoreView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.groupKey = source["groupKey"]; - this.localTracks = this.convertValues(source["localTracks"], LocalTrackView); - this.candidates = this.convertValues(source["candidates"], CandidateView); - this.recommendation = source["recommendation"]; - this.mixedBag = source["mixedBag"]; - this.synthetic = source["synthetic"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class SearchHitView { - mbid: string; - kind: string; - title: string; - artist: string; - detail: string; - - static createFrom(source: any = {}) { - return new SearchHitView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.mbid = source["mbid"]; - this.kind = source["kind"]; - this.title = source["title"]; - this.artist = source["artist"]; - this.detail = source["detail"]; - } - } - -} - -export namespace download { - - export class AutoDownloadPrefs { - minSizeMb: number; - maxSizeMb: number; - preferredSizeMb: number; - allowedFormats: string[]; - - static createFrom(source: any = {}) { - return new AutoDownloadPrefs(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.minSizeMb = source["minSizeMb"]; - this.maxSizeMb = source["maxSizeMb"]; - this.preferredSizeMb = source["preferredSizeMb"]; - this.allowedFormats = source["allowedFormats"]; - } - } - export class QualityScore { - overall: number; - formatRank: number; - bitrate: number; - health: number; - priority: number; - sizeFit: number; - mixed: boolean; - - static createFrom(source: any = {}) { - return new QualityScore(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.overall = source["overall"]; - this.formatRank = source["formatRank"]; - this.bitrate = source["bitrate"]; - this.health = source["health"]; - this.priority = source["priority"]; - this.sizeFit = source["sizeFit"]; - this.mixed = source["mixed"]; - } - } - export class MatchScore { - overall: number; - titleFit: number; - artistFit: number; - albumFit: number; - completeness: number; - anchored: boolean; - - static createFrom(source: any = {}) { - return new MatchScore(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.overall = source["overall"]; - this.titleFit = source["titleFit"]; - this.artistFit = source["artistFit"]; - this.albumFit = source["albumFit"]; - this.completeness = source["completeness"]; - this.anchored = source["anchored"]; - } - } - export class CandidateFile { - path: string; - size: number; - format: string; - bitrate?: number; - isAudio: boolean; - matchedTo?: number; - - static createFrom(source: any = {}) { - return new CandidateFile(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.path = source["path"]; - this.size = source["size"]; - this.format = source["format"]; - this.bitrate = source["bitrate"]; - this.isAudio = source["isAudio"]; - this.matchedTo = source["matchedTo"]; - } - } - export class Candidate { - id: string; - providerId: number; - kind: string; - protocol: string; - title: string; - artist?: string; - origin?: string; - files: CandidateFile[]; - totalSize: number; - health: number; - match: MatchScore; - quality: QualityScore; - score: number; - - static createFrom(source: any = {}) { - return new Candidate(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.providerId = source["providerId"]; - this.kind = source["kind"]; - this.protocol = source["protocol"]; - this.title = source["title"]; - this.artist = source["artist"]; - this.origin = source["origin"]; - this.files = this.convertValues(source["files"], CandidateFile); - this.totalSize = source["totalSize"]; - this.health = source["health"]; - this.match = this.convertValues(source["match"], MatchScore); - this.quality = this.convertValues(source["quality"], QualityScore); - this.score = source["score"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - - export class Caps { - canSearch: boolean; - canTransport: boolean; - canDelegate: boolean; - canList: boolean; - canResume: boolean; - canCancel: boolean; - reportsSize: boolean; - transports: string[]; - - static createFrom(source: any = {}) { - return new Caps(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.canSearch = source["canSearch"]; - this.canTransport = source["canTransport"]; - this.canDelegate = source["canDelegate"]; - this.canList = source["canList"]; - this.canResume = source["canResume"]; - this.canCancel = source["canCancel"]; - this.reportsSize = source["reportsSize"]; - this.transports = source["transports"]; - } - } - export class Config { - id: number; - kind: string; - name: string; - enabled: boolean; - priority: number; - settings: Record; - setSecrets?: Record; - - static createFrom(source: any = {}) { - return new Config(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.kind = source["kind"]; - this.name = source["name"]; - this.enabled = source["enabled"]; - this.priority = source["priority"]; - this.settings = source["settings"]; - this.setSecrets = source["setSecrets"]; - } - } - export class Field { - key: string; - label: string; - placeholder?: string; - help?: string; - secret: boolean; - path: boolean; - required: boolean; - default?: string; - - static createFrom(source: any = {}) { - return new Field(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.key = source["key"]; - this.label = source["label"]; - this.placeholder = source["placeholder"]; - this.help = source["help"]; - this.secret = source["secret"]; - this.path = source["path"]; - this.required = source["required"]; - this.default = source["default"]; - } - } - export class Descriptor { - kind: string; - name: string; - summary: string; - caps: Caps; - fields: Field[]; - requiresExternal?: string; - - static createFrom(source: any = {}) { - return new Descriptor(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.kind = source["kind"]; - this.name = source["name"]; - this.summary = source["summary"]; - this.caps = this.convertValues(source["caps"], Caps); - this.fields = this.convertValues(source["fields"], Field); - this.requiresExternal = source["requiresExternal"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class DownloadItem { - id: string; - downloadId: string; - providerId: number; - transportId?: number; - externalId?: string; - candidate: Candidate; - state: string; - bytesDone: number; - bytesTotal: number; - error?: string; - createdAt: time.Time; - updatedAt: time.Time; - - static createFrom(source: any = {}) { - return new DownloadItem(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.downloadId = source["downloadId"]; - this.providerId = source["providerId"]; - this.transportId = source["transportId"]; - this.externalId = source["externalId"]; - this.candidate = this.convertValues(source["candidate"], Candidate); - this.state = source["state"]; - this.bytesDone = source["bytesDone"]; - this.bytesTotal = source["bytesTotal"]; - this.error = source["error"]; - this.createdAt = this.convertValues(source["createdAt"], time.Time); - this.updatedAt = this.convertValues(source["updatedAt"], time.Time); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class ExpectedTrack { - position: number; - discNumber: number; - title: string; - artist: string; - lengthMillis: number; - - static createFrom(source: any = {}) { - return new ExpectedTrack(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.position = source["position"]; - this.discNumber = source["discNumber"]; - this.title = source["title"]; - this.artist = source["artist"]; - this.lengthMillis = source["lengthMillis"]; - } - } - export class DownloadView { - id: string; - releaseMbid?: string; - releaseGroupMbid?: string; - recordingMbid?: string; - requestId?: number; - source?: string; - artist: string; - album: string; - query?: string; - expected?: ExpectedTrack[]; - libraryId: number; - createdAt: time.Time; - state: string; - error?: string; - items: DownloadItem[]; - - static createFrom(source: any = {}) { - return new DownloadView(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.releaseMbid = source["releaseMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.recordingMbid = source["recordingMbid"]; - this.requestId = source["requestId"]; - this.source = source["source"]; - this.artist = source["artist"]; - this.album = source["album"]; - this.query = source["query"]; - this.expected = this.convertValues(source["expected"], ExpectedTrack); - this.libraryId = source["libraryId"]; - this.createdAt = this.convertValues(source["createdAt"], time.Time); - this.state = source["state"]; - this.error = source["error"]; - this.items = this.convertValues(source["items"], DownloadItem); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - - - - - export class Reconciler { - - - static createFrom(source: any = {}) { - return new Reconciler(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - export class Request { - id: number; - mbid: string; - entity: string; - libraryId: number; - artist: string; - title: string; - scope: string; - secondary: boolean; - state: string; - parentId?: number; - attempts: number; - lastError?: string; - lastTriedAt?: time.Time; - nextTryAt?: time.Time; - externalIds?: Record; - createdAt: time.Time; - updatedAt: time.Time; - - static createFrom(source: any = {}) { - return new Request(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.mbid = source["mbid"]; - this.entity = source["entity"]; - this.libraryId = source["libraryId"]; - this.artist = source["artist"]; - this.title = source["title"]; - this.scope = source["scope"]; - this.secondary = source["secondary"]; - this.state = source["state"]; - this.parentId = source["parentId"]; - this.attempts = source["attempts"]; - this.lastError = source["lastError"]; - this.lastTriedAt = this.convertValues(source["lastTriedAt"], time.Time); - this.nextTryAt = this.convertValues(source["nextTryAt"], time.Time); - this.externalIds = source["externalIds"]; - this.createdAt = this.convertValues(source["createdAt"], time.Time); - this.updatedAt = this.convertValues(source["updatedAt"], time.Time); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class RequestInput { - mbid: string; - entity: string; - libraryId: number; - artist: string; - title: string; - scope: string; - secondary: boolean; - - static createFrom(source: any = {}) { - return new RequestInput(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.mbid = source["mbid"]; - this.entity = source["entity"]; - this.libraryId = source["libraryId"]; - this.artist = source["artist"]; - this.title = source["title"]; - this.scope = source["scope"]; - this.secondary = source["secondary"]; - } - } - export class SearchRequest { - libraryId: number; - releaseMbid: string; - releaseGroupMbid: string; - artist: string; - album: string; - query: string; - expected: ExpectedTrack[]; - - static createFrom(source: any = {}) { - return new SearchRequest(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.libraryId = source["libraryId"]; - this.releaseMbid = source["releaseMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.artist = source["artist"]; - this.album = source["album"]; - this.query = source["query"]; - this.expected = this.convertValues(source["expected"], ExpectedTrack); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class StartResult { - downloadId: string; - candidates: Candidate[]; - autoPicked: boolean; - - static createFrom(source: any = {}) { - return new StartResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.downloadId = source["downloadId"]; - this.candidates = this.convertValues(source["candidates"], Candidate); - this.autoPicked = source["autoPicked"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class Summary { - expanded: number; - satisfied: number; - attempted: number; - started: number; - synced: number; - waiting: number; - noProviders: boolean; - - static createFrom(source: any = {}) { - return new Summary(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.expanded = source["expanded"]; - this.satisfied = source["satisfied"]; - this.attempted = source["attempted"]; - this.started = source["started"]; - this.synced = source["synced"]; - this.waiting = source["waiting"]; - this.noProviders = source["noProviders"]; - } - } - -} - -export namespace explore { - - export class TierStatus { - name: string; - state: string; - total: number; - completed: number; - error?: string; - detail?: string; - - static createFrom(source: any = {}) { - return new TierStatus(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.name = source["name"]; - this.state = source["state"]; - this.total = source["total"]; - this.completed = source["completed"]; - this.error = source["error"]; - this.detail = source["detail"]; - } - } - export class IndexStatus { - building: boolean; - ready: boolean; - lastBuilt?: string; - tiers: TierStatus[]; - artists: number; - recordings: number; - releaseGroups: number; - totalRows: number; - - static createFrom(source: any = {}) { - return new IndexStatus(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.building = source["building"]; - this.ready = source["ready"]; - this.lastBuilt = source["lastBuilt"]; - this.tiers = this.convertValues(source["tiers"], TierStatus); - this.artists = source["artists"]; - this.recordings = source["recordings"]; - this.releaseGroups = source["releaseGroups"]; - this.totalRows = source["totalRows"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class LBSimilarArtist { - artistMbid: string; - name: string; - score: number; - - static createFrom(source: any = {}) { - return new LBSimilarArtist(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.artistMbid = source["artistMbid"]; - this.name = source["name"]; - this.score = source["score"]; - } - } - export class LBTopRecording { - recordingMbid: string; - artistName: string; - trackName: string; - totalListenCount: number; - caaReleaseMbid: string; - releaseGroupMbid?: string; - releaseName: string; - length: number; - inLibrary: boolean; - localId?: number; - - static createFrom(source: any = {}) { - return new LBTopRecording(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.recordingMbid = source["recordingMbid"]; - this.artistName = source["artistName"]; - this.trackName = source["trackName"]; - this.totalListenCount = source["totalListenCount"]; - this.caaReleaseMbid = source["caaReleaseMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.releaseName = source["releaseName"]; - this.length = source["length"]; - this.inLibrary = source["inLibrary"]; - this.localId = source["localId"]; - } - } - export class LBTopReleaseGroup { - releaseGroupMbid: string; - title: string; - artistName: string; - type: string; - date: string; - totalListenCount: number; - caaReleaseMbid: string; - inLibrary: boolean; - localId?: number; - - static createFrom(source: any = {}) { - return new LBTopReleaseGroup(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.title = source["title"]; - this.artistName = source["artistName"]; - this.type = source["type"]; - this.date = source["date"]; - this.totalListenCount = source["totalListenCount"]; - this.caaReleaseMbid = source["caaReleaseMbid"]; - this.inLibrary = source["inLibrary"]; - this.localId = source["localId"]; - } - } - export class LyricsResult { - recordingId: number; - filePath: string; - lengthMs: number; - title: string; - artist: string; - album: string; - - static createFrom(source: any = {}) { - return new LyricsResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.recordingId = source["recordingId"]; - this.filePath = source["filePath"]; - this.lengthMs = source["lengthMs"]; - this.title = source["title"]; - this.artist = source["artist"]; - this.album = source["album"]; - } - } - export class MBArtist { - mbid: string; - name: string; - sortName: string; - englishName?: string; - type: string; - country: string; - disambiguation: string; - score: number; - popularity: number; - listenerCount: number; - inLibrary: boolean; - localId?: number; - - static createFrom(source: any = {}) { - return new MBArtist(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.mbid = source["mbid"]; - this.name = source["name"]; - this.sortName = source["sortName"]; - this.englishName = source["englishName"]; - this.type = source["type"]; - this.country = source["country"]; - this.disambiguation = source["disambiguation"]; - this.score = source["score"]; - this.popularity = source["popularity"]; - this.listenerCount = source["listenerCount"]; - this.inLibrary = source["inLibrary"]; - this.localId = source["localId"]; - } - } - export class MBRecording { - mbid: string; - title: string; - length: number; - artistCredit: string; - artistMbid?: string; - score: number; - popularity: number; - listenerCount: number; - caaReleaseMbid?: string; - releaseGroupMbid?: string; - releaseName?: string; - inLibrary: boolean; - localId?: number; - - static createFrom(source: any = {}) { - return new MBRecording(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.mbid = source["mbid"]; - this.title = source["title"]; - this.length = source["length"]; - this.artistCredit = source["artistCredit"]; - this.artistMbid = source["artistMbid"]; - this.score = source["score"]; - this.popularity = source["popularity"]; - this.listenerCount = source["listenerCount"]; - this.caaReleaseMbid = source["caaReleaseMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.releaseName = source["releaseName"]; - this.inLibrary = source["inLibrary"]; - this.localId = source["localId"]; - } - } - export class MBTrack { - position: number; - discNumber: number; - title: string; - length: number; - mbid: string; - inLibrary: boolean; - localId?: number; - - static createFrom(source: any = {}) { - return new MBTrack(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.position = source["position"]; - this.discNumber = source["discNumber"]; - this.title = source["title"]; - this.length = source["length"]; - this.mbid = source["mbid"]; - this.inLibrary = source["inLibrary"]; - this.localId = source["localId"]; - } - } - export class MBRelease { - mbid: string; - title: string; - date: string; - country: string; - status: string; - artistCredit?: string; - tracks?: MBTrack[]; - releaseGroupMbid?: string; - - static createFrom(source: any = {}) { - return new MBRelease(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.mbid = source["mbid"]; - this.title = source["title"]; - this.date = source["date"]; - this.country = source["country"]; - this.status = source["status"]; - this.artistCredit = source["artistCredit"]; - this.tracks = this.convertValues(source["tracks"], MBTrack); - this.releaseGroupMbid = source["releaseGroupMbid"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class MBReleaseGroup { - mbid: string; - title: string; - primaryType: string; - secondaryTypes?: string[]; - firstReleaseDate: string; - artistCredit: string; - artistMbid?: string; - popularity: number; - listenerCount: number; - inLibrary: boolean; - localId?: number; - - static createFrom(source: any = {}) { - return new MBReleaseGroup(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.mbid = source["mbid"]; - this.title = source["title"]; - this.primaryType = source["primaryType"]; - this.secondaryTypes = source["secondaryTypes"]; - this.firstReleaseDate = source["firstReleaseDate"]; - this.artistCredit = source["artistCredit"]; - this.artistMbid = source["artistMbid"]; - this.popularity = source["popularity"]; - this.listenerCount = source["listenerCount"]; - this.inLibrary = source["inLibrary"]; - this.localId = source["localId"]; - } - } - export class TopResult { - entityType: string; - mbid: string; - name: string; - artistCredit?: string; - artistMbid?: string; - intentScore: number; - artistType?: string; - country?: string; - primaryType?: string; - year?: string; - length?: number; - caaReleaseMbid?: string; - releaseGroupMbid?: string; - releaseName?: string; - inLibrary: boolean; - - static createFrom(source: any = {}) { - return new TopResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.entityType = source["entityType"]; - this.mbid = source["mbid"]; - this.name = source["name"]; - this.artistCredit = source["artistCredit"]; - this.artistMbid = source["artistMbid"]; - this.intentScore = source["intentScore"]; - this.artistType = source["artistType"]; - this.country = source["country"]; - this.primaryType = source["primaryType"]; - this.year = source["year"]; - this.length = source["length"]; - this.caaReleaseMbid = source["caaReleaseMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.releaseName = source["releaseName"]; - this.inLibrary = source["inLibrary"]; - } - } - export class MBSearchResult { - artists?: MBArtist[]; - releaseGroups?: MBReleaseGroup[]; - recordings?: MBRecording[]; - topResults?: TopResult[]; - - static createFrom(source: any = {}) { - return new MBSearchResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.artists = this.convertValues(source["artists"], MBArtist); - this.releaseGroups = this.convertValues(source["releaseGroups"], MBReleaseGroup); - this.recordings = this.convertValues(source["recordings"], MBRecording); - this.topResults = this.convertValues(source["topResults"], TopResult); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - - export class MusicBrainzClient { - - - static createFrom(source: any = {}) { - return new MusicBrainzClient(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - export class RateLimiter { - - - static createFrom(source: any = {}) { - return new RateLimiter(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - export class Shelf { - id: string; - kind: string; - title: string; - subtitle: string; - albums?: MBReleaseGroup[]; - artists?: MBArtist[]; - - static createFrom(source: any = {}) { - return new Shelf(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.kind = source["kind"]; - this.title = source["title"]; - this.subtitle = source["subtitle"]; - this.albums = this.convertValues(source["albums"], MBReleaseGroup); - this.artists = this.convertValues(source["artists"], MBArtist); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class ShelfPage { - shelves: Shelf[]; - state: string; - - static createFrom(source: any = {}) { - return new ShelfPage(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.shelves = this.convertValues(source["shelves"], Shelf); - this.state = source["state"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class ThumbnailRequest { - mbid: string; - albumName: string; - artistName: string; - - static createFrom(source: any = {}) { - return new ThumbnailRequest(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.mbid = source["mbid"]; - this.albumName = source["albumName"]; - this.artistName = source["artistName"]; - } - } - - - export class TrackLyrics { - plain: string; - synced: string; - instrumental: boolean; - source: string; - - static createFrom(source: any = {}) { - return new TrackLyrics(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.plain = source["plain"]; - this.synced = source["synced"]; - this.instrumental = source["instrumental"]; - this.source = source["source"]; - } - } - export class TrackThumbnailRequest { - key: string; - releaseMbid: string; - releaseGroupMbid: string; - albumName: string; - artistName: string; - - static createFrom(source: any = {}) { - return new TrackThumbnailRequest(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.key = source["key"]; - this.releaseMbid = source["releaseMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.albumName = source["albumName"]; - this.artistName = source["artistName"]; - } - } - -} - -export namespace home { - - export class Shelf { - id: string; - kind: string; - title: string; - subtitle: string; - albums: library.Album[]; - - static createFrom(source: any = {}) { - return new Shelf(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.kind = source["kind"]; - this.title = source["title"]; - this.subtitle = source["subtitle"]; - this.albums = this.convertValues(source["albums"], library.Album); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - -} - -export namespace jobs { - - export class Caps { - pausable: boolean; - cancellable: boolean; - - static createFrom(source: any = {}) { - return new Caps(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.pausable = source["pausable"]; - this.cancellable = source["cancellable"]; - } - } - export class Stat { - label: string; - value: string; - - static createFrom(source: any = {}) { - return new Stat(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.label = source["label"]; - this.value = source["value"]; - } - } - export class Stage { - name: string; - state: string; - current: number; - total: number; - error?: string; - - static createFrom(source: any = {}) { - return new Stage(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.name = source["name"]; - this.state = source["state"]; - this.current = source["current"]; - this.total = source["total"]; - this.error = source["error"]; - } - } - export class Job { - id: string; - kind: string; - title: string; - subtitle?: string; - state: string; - phase?: string; - current: number; - total: number; - caps: Caps; - stages: Stage[]; - stats: Stat[]; - error?: string; - startedAt: number; - updatedAt: number; - endedAt?: number; - logCount: number; - warnCount: number; - errorCount: number; - - static createFrom(source: any = {}) { - return new Job(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.kind = source["kind"]; - this.title = source["title"]; - this.subtitle = source["subtitle"]; - this.state = source["state"]; - this.phase = source["phase"]; - this.current = source["current"]; - this.total = source["total"]; - this.caps = this.convertValues(source["caps"], Caps); - this.stages = this.convertValues(source["stages"], Stage); - this.stats = this.convertValues(source["stats"], Stat); - this.error = source["error"]; - this.startedAt = source["startedAt"]; - this.updatedAt = source["updatedAt"]; - this.endedAt = source["endedAt"]; - this.logCount = source["logCount"]; - this.warnCount = source["warnCount"]; - this.errorCount = source["errorCount"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class LogEntry { - time: number; - level: string; - message: string; - detail?: string; - - static createFrom(source: any = {}) { - return new LogEntry(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.time = source["time"]; - this.level = source["level"]; - this.message = source["message"]; - this.detail = source["detail"]; - } - } - export class Registry { - - - static createFrom(source: any = {}) { - return new Registry(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - - -} - -export namespace library { - - export class Album { - ID: number; - Name: string; - ArtistName: string; - ArtistMBID: string; - MBID: string; - CoverArtPath: string; - CoverArtSmall: string; - CoverArtMedium: string; - CoverArtLarge: string; - Year: number; - ReleaseYear: number; - - static createFrom(source: any = {}) { - return new Album(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.ID = source["ID"]; - this.Name = source["Name"]; - this.ArtistName = source["ArtistName"]; - this.ArtistMBID = source["ArtistMBID"]; - this.MBID = source["MBID"]; - this.CoverArtPath = source["CoverArtPath"]; - this.CoverArtSmall = source["CoverArtSmall"]; - this.CoverArtMedium = source["CoverArtMedium"]; - this.CoverArtLarge = source["CoverArtLarge"]; - this.Year = source["Year"]; - this.ReleaseYear = source["ReleaseYear"]; - } - } - export class AlbumCompleteness { - owned: number; - expected: number; - known: boolean; - complete: boolean; - - static createFrom(source: any = {}) { - return new AlbumCompleteness(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.owned = source["owned"]; - this.expected = source["expected"]; - this.known = source["known"]; - this.complete = source["complete"]; - } - } - export class Artist { - ID: number; - Name: string; - MBID: string; - ImageSmall: string; - ImageMedium: string; - ImageLarge: string; - - static createFrom(source: any = {}) { - return new Artist(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.ID = source["ID"]; - this.Name = source["Name"]; - this.MBID = source["MBID"]; - this.ImageSmall = source["ImageSmall"]; - this.ImageMedium = source["ImageMedium"]; - this.ImageLarge = source["ImageLarge"]; - } - } - export class GenreWithCount { - Name: string; - TrackCount: number; - - static createFrom(source: any = {}) { - return new GenreWithCount(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.Name = source["Name"]; - this.TrackCount = source["TrackCount"]; - } - } - export class Info { - id: number; - name: string; - path: string; - trackCount: number; - - static createFrom(source: any = {}) { - return new Info(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.name = source["name"]; - this.path = source["path"]; - this.trackCount = source["trackCount"]; - } - } - export class RemovalHooks { - - - static createFrom(source: any = {}) { - return new RemovalHooks(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - export class RemovalImpact { - trackCount: number; - playlistsAffected: number; - queueItemCount: number; - - static createFrom(source: any = {}) { - return new RemovalImpact(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.trackCount = source["trackCount"]; - this.playlistsAffected = source["playlistsAffected"]; - this.queueItemCount = source["queueItemCount"]; - } - } - export class RemovalResult { - tracksRemoved: number; - pathsExcluded: number; - - static createFrom(source: any = {}) { - return new RemovalResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.tracksRemoved = source["tracksRemoved"]; - this.pathsExcluded = source["pathsExcluded"]; - } - } - export class RemovalSummary { - tracksDeleted: number; - artistsRemoved: number; - albumsRemoved: number; - genresRemoved: number; - playlistsAffected: number; - queueItemsRemoved: number; - - static createFrom(source: any = {}) { - return new RemovalSummary(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.tracksDeleted = source["tracksDeleted"]; - this.artistsRemoved = source["artistsRemoved"]; - this.albumsRemoved = source["albumsRemoved"]; - this.genresRemoved = source["genresRemoved"]; - this.playlistsAffected = source["playlistsAffected"]; - this.queueItemsRemoved = source["queueItemsRemoved"]; - } - } - export class RescanHooks { - - - static createFrom(source: any = {}) { - return new RescanHooks(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - export class ScanHooks { - - - static createFrom(source: any = {}) { - return new ScanHooks(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - export class ScanWarning { - filePath: string; - phase: string; - err: string; - - static createFrom(source: any = {}) { - return new ScanWarning(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.filePath = source["filePath"]; - this.phase = source["phase"]; - this.err = source["err"]; - } - } - export class ScanMetrics { - total: number; - loadExisting: number; - walkDuration: number; - extractionWallClock: number; - dbWritesWallClock: number; - orphanCleanup: number; - postScanVariants: number; - formatExtraction: Record; - formatCount: Record; - tagExtraction: number; - durationExtraction: number; - batchCommits: number; - coverArtSave: number; - thumbnailWallClock: number; - thumbnailGeneration: number; - thumbnailSmall: number; - thumbnailMedium: number; - thumbnailLarge: number; - clearQueue: number; - clearDatabase: number; - clearCoverFiles: number; - added: number; - updated: number; - skipped: number; - removed: number; - cancelled: boolean; - libraryId: number; - libraryName: string; - warnings: ScanWarning[]; - - static createFrom(source: any = {}) { - return new ScanMetrics(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.total = source["total"]; - this.loadExisting = source["loadExisting"]; - this.walkDuration = source["walkDuration"]; - this.extractionWallClock = source["extractionWallClock"]; - this.dbWritesWallClock = source["dbWritesWallClock"]; - this.orphanCleanup = source["orphanCleanup"]; - this.postScanVariants = source["postScanVariants"]; - this.formatExtraction = source["formatExtraction"]; - this.formatCount = source["formatCount"]; - this.tagExtraction = source["tagExtraction"]; - this.durationExtraction = source["durationExtraction"]; - this.batchCommits = source["batchCommits"]; - this.coverArtSave = source["coverArtSave"]; - this.thumbnailWallClock = source["thumbnailWallClock"]; - this.thumbnailGeneration = source["thumbnailGeneration"]; - this.thumbnailSmall = source["thumbnailSmall"]; - this.thumbnailMedium = source["thumbnailMedium"]; - this.thumbnailLarge = source["thumbnailLarge"]; - this.clearQueue = source["clearQueue"]; - this.clearDatabase = source["clearDatabase"]; - this.clearCoverFiles = source["clearCoverFiles"]; - this.added = source["added"]; - this.updated = source["updated"]; - this.skipped = source["skipped"]; - this.removed = source["removed"]; - this.cancelled = source["cancelled"]; - this.libraryId = source["libraryId"]; - this.libraryName = source["libraryName"]; - this.warnings = this.convertValues(source["warnings"], ScanWarning); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - - export class Track { - TrackName: string; - ArtistName: string; - TrackLength: string; - FilePath: string; - TrackNumber: number; - DiscNumber: number; - Album: string; - Genre: string[]; - Year: number; - Composer: string; - FileType: string; - SampleRate: number; - BitDepth: number; - Channels: number; - Bitrate: number; - FileSize: number; - PlayCount: number; - LastPlayed: string; - RecordingMBID: string; - ArtistMBID: string; - ReleaseGroupMBID: string; - CoverArtPath: string; - CoverArtSmall: string; - CoverArtMedium: string; - CoverArtLarge: string; - - static createFrom(source: any = {}) { - return new Track(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.TrackName = source["TrackName"]; - this.ArtistName = source["ArtistName"]; - this.TrackLength = source["TrackLength"]; - this.FilePath = source["FilePath"]; - this.TrackNumber = source["TrackNumber"]; - this.DiscNumber = source["DiscNumber"]; - this.Album = source["Album"]; - this.Genre = source["Genre"]; - this.Year = source["Year"]; - this.Composer = source["Composer"]; - this.FileType = source["FileType"]; - this.SampleRate = source["SampleRate"]; - this.BitDepth = source["BitDepth"]; - this.Channels = source["Channels"]; - this.Bitrate = source["Bitrate"]; - this.FileSize = source["FileSize"]; - this.PlayCount = source["PlayCount"]; - this.LastPlayed = source["LastPlayed"]; - this.RecordingMBID = source["RecordingMBID"]; - this.ArtistMBID = source["ArtistMBID"]; - this.ReleaseGroupMBID = source["ReleaseGroupMBID"]; - this.CoverArtPath = source["CoverArtPath"]; - this.CoverArtSmall = source["CoverArtSmall"]; - this.CoverArtMedium = source["CoverArtMedium"]; - this.CoverArtLarge = source["CoverArtLarge"]; - } - } - export class TrackMBIDs { - recordingMbid: string; - releaseGroupMbid: string; - artistMbid: string; - - static createFrom(source: any = {}) { - return new TrackMBIDs(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.recordingMbid = source["recordingMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.artistMbid = source["artistMbid"]; - } - } - -} - -export namespace player { - - export class TrackInfo { - fileName: string; - filePath: string; - state: string; - title: string; - artist: string; - album: string; - coverArt: string; - coverArtSmall: string; - coverArtMedium: string; - coverArtLarge: string; - trackLength: number; - seekPosition: number; - trackChangeId: number; - artistMbid: string; - releaseGroupMbid: string; - recordingMbid: string; - - static createFrom(source: any = {}) { - return new TrackInfo(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.fileName = source["fileName"]; - this.filePath = source["filePath"]; - this.state = source["state"]; - this.title = source["title"]; - this.artist = source["artist"]; - this.album = source["album"]; - this.coverArt = source["coverArt"]; - this.coverArtSmall = source["coverArtSmall"]; - this.coverArtMedium = source["coverArtMedium"]; - this.coverArtLarge = source["coverArtLarge"]; - this.trackLength = source["trackLength"]; - this.seekPosition = source["seekPosition"]; - this.trackChangeId = source["trackChangeId"]; - this.artistMbid = source["artistMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.recordingMbid = source["recordingMbid"]; - } - } - -} - -export namespace playlist { - - export class CandidateTrack { - FilePath: string; - Title: string; - Artist: string; - Album: string; - Duration: string; - Score: number; - - static createFrom(source: any = {}) { - return new CandidateTrack(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.FilePath = source["FilePath"]; - this.Title = source["Title"]; - this.Artist = source["Artist"]; - this.Album = source["Album"]; - this.Duration = source["Duration"]; - this.Score = source["Score"]; - } - } - export class DuplicateTrackInfo { - FilePath: string; - Title: string; - Artist: string; - Album: string; - Duration: string; - - static createFrom(source: any = {}) { - return new DuplicateTrackInfo(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.FilePath = source["FilePath"]; - this.Title = source["Title"]; - this.Artist = source["Artist"]; - this.Album = source["Album"]; - this.Duration = source["Duration"]; - } - } - export class DuplicateCheckResult { - Duplicates: DuplicateTrackInfo[]; - Unique: string[]; - - static createFrom(source: any = {}) { - return new DuplicateCheckResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.Duplicates = this.convertValues(source["Duplicates"], DuplicateTrackInfo); - this.Unique = source["Unique"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - - export class PhantomMatch { - PhantomPath: string; - PhantomTitle: string; - Candidate: CandidateTrack; - - static createFrom(source: any = {}) { - return new PhantomMatch(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.PhantomPath = source["PhantomPath"]; - this.PhantomTitle = source["PhantomTitle"]; - this.Candidate = this.convertValues(source["Candidate"], CandidateTrack); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class PhantomSearchResult { - AutoMatched: PhantomMatch[]; - Unmatched: string[]; - - static createFrom(source: any = {}) { - return new PhantomSearchResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.AutoMatched = this.convertValues(source["AutoMatched"], PhantomMatch); - this.Unmatched = source["Unmatched"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - export class Summary { - ID: number; - Name: string; - CreatedAt: string; - UpdatedAt: string; - IsSmart: boolean; - - static createFrom(source: any = {}) { - return new Summary(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.ID = source["ID"]; - this.Name = source["Name"]; - this.CreatedAt = source["CreatedAt"]; - this.UpdatedAt = source["UpdatedAt"]; - this.IsSmart = source["IsSmart"]; - } - } - export class Track { - ID: number; - Position: number; - FilePath: string; - Title: string; - Artist: string; - Album: string; - CoverArtPath: string; - CoverArtSmall: string; - CoverArtMedium: string; - CoverArtLarge: string; - Duration: string; - Phantom: boolean; - ArtistMBID: string; - ReleaseGroupMBID: string; - RecordingMBID: string; - - static createFrom(source: any = {}) { - return new Track(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.ID = source["ID"]; - this.Position = source["Position"]; - this.FilePath = source["FilePath"]; - this.Title = source["Title"]; - this.Artist = source["Artist"]; - this.Album = source["Album"]; - this.CoverArtPath = source["CoverArtPath"]; - this.CoverArtSmall = source["CoverArtSmall"]; - this.CoverArtMedium = source["CoverArtMedium"]; - this.CoverArtLarge = source["CoverArtLarge"]; - this.Duration = source["Duration"]; - this.Phantom = source["Phantom"]; - this.ArtistMBID = source["ArtistMBID"]; - this.ReleaseGroupMBID = source["ReleaseGroupMBID"]; - this.RecordingMBID = source["RecordingMBID"]; - } - } - export class WithTracks { - Summary: Summary; - Tracks: Track[]; - - static createFrom(source: any = {}) { - return new WithTracks(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.Summary = this.convertValues(source["Summary"], Summary); - this.Tracks = this.convertValues(source["Tracks"], Track); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - -} - -export namespace queue { - - export class Source { - type: string; - id: number; - label: string; - - static createFrom(source: any = {}) { - return new Source(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.type = source["type"]; - this.id = source["id"]; - this.label = source["label"]; - } - } - export class Track { - id: number; - audioFileId: number; - filePath: string; - position: number; - title: string; - artist: string; - album: string; - coverArtPath: string; - artistMbid: string; - releaseGroupMbid: string; - recordingMbid: string; - - static createFrom(source: any = {}) { - return new Track(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - this.audioFileId = source["audioFileId"]; - this.filePath = source["filePath"]; - this.position = source["position"]; - this.title = source["title"]; - this.artist = source["artist"]; - this.album = source["album"]; - this.coverArtPath = source["coverArtPath"]; - this.artistMbid = source["artistMbid"]; - this.releaseGroupMbid = source["releaseGroupMbid"]; - this.recordingMbid = source["recordingMbid"]; - } - } - export class State { - tracks: Track[]; - currentIndex: number; - shuffleMode: boolean; - repeatMode: string; - source: Source; - - static createFrom(source: any = {}) { - return new State(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.tracks = this.convertValues(source["tracks"], Track); - this.currentIndex = source["currentIndex"]; - this.shuffleMode = source["shuffleMode"]; - this.repeatMode = source["repeatMode"]; - this.source = this.convertValues(source["source"], Source); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - -} - -export namespace sqlcgen { - - export class Library { - ID: number; - Name: string; - Path: string; - CreatedAt: time.Time; - AutotagWarningAcked: number; - - static createFrom(source: any = {}) { - return new Library(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.ID = source["ID"]; - this.Name = source["Name"]; - this.Path = source["Path"]; - this.CreatedAt = this.convertValues(source["CreatedAt"], time.Time); - this.AutotagWarningAcked = source["AutotagWarningAcked"]; - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - -} - -export namespace tagwriter { - - export class BatchFailure { - filePath: string; - error: string; - - static createFrom(source: any = {}) { - return new BatchFailure(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.filePath = source["filePath"]; - this.error = source["error"]; - } - } - export class BatchResult { - total: number; - succeeded: number; - failed: number; - cancelled: boolean; - failures: BatchFailure[]; - - static createFrom(source: any = {}) { - return new BatchResult(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.total = source["total"]; - this.succeeded = source["succeeded"]; - this.failed = source["failed"]; - this.cancelled = source["cancelled"]; - this.failures = this.convertValues(source["failures"], BatchFailure); - } - - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice && a.map) { - return (a as any[]).map(elem => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } - } - -} - -export namespace time { - - export class Time { - - - static createFrom(source: any = {}) { - return new Time(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - -} - -export namespace tracklist { - - export class Column { - id: string; - - static createFrom(source: any = {}) { - return new Column(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.id = source["id"]; - } - } - -} - diff --git a/frontend/wailsjs/go/player/Player.d.ts b/frontend/wailsjs/go/player/Player.d.ts deleted file mode 100755 index 2a948a6..0000000 --- a/frontend/wailsjs/go/player/Player.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {player} from '../models'; -import {context} from '../models'; -import {mediacontrols} from '../models'; - -export function ChangeVolume(arg1:number):Promise; - -export function CurrentPosition():Promise; - -export function CurrentPositionSeconds():Promise; - -export function EmitCurrentState():Promise; - -export function GetCurrentTrackInfo():Promise; - -export function InitSpeaker():Promise; - -export function IsPlaying():Promise; - -export function LoadFile(arg1:string):Promise; - -export function MuteToggle():Promise; - -export function Muted():Promise; - -export function Pause():Promise; - -export function Play():Promise; - -export function RestoreState():Promise; - -export function SaveState():Promise; - -export function Seek(arg1:number):Promise; - -export function SetContext(arg1:context.Context):Promise; - -export function SetMediaControls(arg1:mediacontrols.Handler):Promise; - -export function SetPlaybackFinishedHandler(arg1:any):Promise; - -export function SetVolume(arg1:player.UserVolume):Promise; - -export function TrackLengthInSeconds():Promise; - -export function UnloadTrack():Promise; diff --git a/frontend/wailsjs/go/player/Player.js b/frontend/wailsjs/go/player/Player.js deleted file mode 100755 index e3885be..0000000 --- a/frontend/wailsjs/go/player/Player.js +++ /dev/null @@ -1,87 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function ChangeVolume(arg1) { - return window['go']['player']['Player']['ChangeVolume'](arg1); -} - -export function CurrentPosition() { - return window['go']['player']['Player']['CurrentPosition'](); -} - -export function CurrentPositionSeconds() { - return window['go']['player']['Player']['CurrentPositionSeconds'](); -} - -export function EmitCurrentState() { - return window['go']['player']['Player']['EmitCurrentState'](); -} - -export function GetCurrentTrackInfo() { - return window['go']['player']['Player']['GetCurrentTrackInfo'](); -} - -export function InitSpeaker() { - return window['go']['player']['Player']['InitSpeaker'](); -} - -export function IsPlaying() { - return window['go']['player']['Player']['IsPlaying'](); -} - -export function LoadFile(arg1) { - return window['go']['player']['Player']['LoadFile'](arg1); -} - -export function MuteToggle() { - return window['go']['player']['Player']['MuteToggle'](); -} - -export function Muted() { - return window['go']['player']['Player']['Muted'](); -} - -export function Pause() { - return window['go']['player']['Player']['Pause'](); -} - -export function Play() { - return window['go']['player']['Player']['Play'](); -} - -export function RestoreState() { - return window['go']['player']['Player']['RestoreState'](); -} - -export function SaveState() { - return window['go']['player']['Player']['SaveState'](); -} - -export function Seek(arg1) { - return window['go']['player']['Player']['Seek'](arg1); -} - -export function SetContext(arg1) { - return window['go']['player']['Player']['SetContext'](arg1); -} - -export function SetMediaControls(arg1) { - return window['go']['player']['Player']['SetMediaControls'](arg1); -} - -export function SetPlaybackFinishedHandler(arg1) { - return window['go']['player']['Player']['SetPlaybackFinishedHandler'](arg1); -} - -export function SetVolume(arg1) { - return window['go']['player']['Player']['SetVolume'](arg1); -} - -export function TrackLengthInSeconds() { - return window['go']['player']['Player']['TrackLengthInSeconds'](); -} - -export function UnloadTrack() { - return window['go']['player']['Player']['UnloadTrack'](); -} diff --git a/frontend/wailsjs/go/playlist/Service.d.ts b/frontend/wailsjs/go/playlist/Service.d.ts deleted file mode 100755 index b569a8c..0000000 --- a/frontend/wailsjs/go/playlist/Service.d.ts +++ /dev/null @@ -1,77 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {playlist} from '../models'; -import {library} from '../models'; -import {context} from '../models'; - -export function AddToDefaultPlaylist(arg1:Array):Promise; - -export function AddTracksToPlaylist(arg1:number,arg2:Array):Promise; - -export function CreatePlaylist(arg1:string):Promise; - -export function CreatePlaylistWithTracks(arg1:string,arg2:Array):Promise; - -export function CreateSmartPlaylist(arg1:string,arg2:string):Promise; - -export function DeletePlaylist(arg1:number):Promise; - -export function EnsureDefaultPlaylist():Promise; - -export function EvaluateSmartPlaylist(arg1:number):Promise>; - -export function FindDuplicateTracksInPlaylist(arg1:number,arg2:Array):Promise; - -export function FindPhantomMatches(arg1:number,arg2:Array):Promise; - -export function GetAllPlaylists():Promise>; - -export function GetAllPlaylistsWithTracks():Promise>; - -export function GetDefaultPlaylistInfo():Promise; - -export function GetDefaultPlaylistTrackPaths():Promise>; - -export function GetPhantomCandidates(arg1:number,arg2:string):Promise>; - -export function GetPlaylistTracks(arg1:number):Promise>; - -export function GetSmartPlaylistRules(arg1:number):Promise; - -export function GetSmartPlaylistTracks(arg1:number):Promise>; - -export function ImportPlaylist(arg1:string):Promise; - -export function ImportPlaylists(arg1:Array):Promise>; - -export function MaterializeUnmaterializedSmartPlaylists():Promise; - -export function PreviewSmartPlaylist(arg1:string):Promise>; - -export function RefreshSmartPlaylist(arg1:number):Promise; - -export function RemoveFromDefaultPlaylist(arg1:Array):Promise; - -export function RemovePhantomTracks(arg1:number,arg2:Array):Promise; - -export function RemoveTracksFromPlaylist(arg1:number,arg2:Array):Promise; - -export function RenamePlaylist(arg1:number,arg2:string):Promise; - -export function RepopulateFromM3U():Promise; - -export function ResolvePhantomTracks(arg1:number,arg2:Record):Promise; - -export function ResolvePhantomTracksAfterScan():Promise; - -export function RestoreAllPlaylists():Promise; - -export function SearchLibrary(arg1:string):Promise>; - -export function SetContext(arg1:context.Context):Promise; - -export function SetFavoritesConfig(arg1:playlist.FavoritesConfigProvider):Promise; - -export function ToggleDefaultPlaylistTrack(arg1:string):Promise; - -export function UpdateSmartPlaylistRules(arg1:number,arg2:string):Promise; diff --git a/frontend/wailsjs/go/playlist/Service.js b/frontend/wailsjs/go/playlist/Service.js deleted file mode 100755 index c144cb4..0000000 --- a/frontend/wailsjs/go/playlist/Service.js +++ /dev/null @@ -1,147 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function AddToDefaultPlaylist(arg1) { - return window['go']['playlist']['Service']['AddToDefaultPlaylist'](arg1); -} - -export function AddTracksToPlaylist(arg1, arg2) { - return window['go']['playlist']['Service']['AddTracksToPlaylist'](arg1, arg2); -} - -export function CreatePlaylist(arg1) { - return window['go']['playlist']['Service']['CreatePlaylist'](arg1); -} - -export function CreatePlaylistWithTracks(arg1, arg2) { - return window['go']['playlist']['Service']['CreatePlaylistWithTracks'](arg1, arg2); -} - -export function CreateSmartPlaylist(arg1, arg2) { - return window['go']['playlist']['Service']['CreateSmartPlaylist'](arg1, arg2); -} - -export function DeletePlaylist(arg1) { - return window['go']['playlist']['Service']['DeletePlaylist'](arg1); -} - -export function EnsureDefaultPlaylist() { - return window['go']['playlist']['Service']['EnsureDefaultPlaylist'](); -} - -export function EvaluateSmartPlaylist(arg1) { - return window['go']['playlist']['Service']['EvaluateSmartPlaylist'](arg1); -} - -export function FindDuplicateTracksInPlaylist(arg1, arg2) { - return window['go']['playlist']['Service']['FindDuplicateTracksInPlaylist'](arg1, arg2); -} - -export function FindPhantomMatches(arg1, arg2) { - return window['go']['playlist']['Service']['FindPhantomMatches'](arg1, arg2); -} - -export function GetAllPlaylists() { - return window['go']['playlist']['Service']['GetAllPlaylists'](); -} - -export function GetAllPlaylistsWithTracks() { - return window['go']['playlist']['Service']['GetAllPlaylistsWithTracks'](); -} - -export function GetDefaultPlaylistInfo() { - return window['go']['playlist']['Service']['GetDefaultPlaylistInfo'](); -} - -export function GetDefaultPlaylistTrackPaths() { - return window['go']['playlist']['Service']['GetDefaultPlaylistTrackPaths'](); -} - -export function GetPhantomCandidates(arg1, arg2) { - return window['go']['playlist']['Service']['GetPhantomCandidates'](arg1, arg2); -} - -export function GetPlaylistTracks(arg1) { - return window['go']['playlist']['Service']['GetPlaylistTracks'](arg1); -} - -export function GetSmartPlaylistRules(arg1) { - return window['go']['playlist']['Service']['GetSmartPlaylistRules'](arg1); -} - -export function GetSmartPlaylistTracks(arg1) { - return window['go']['playlist']['Service']['GetSmartPlaylistTracks'](arg1); -} - -export function ImportPlaylist(arg1) { - return window['go']['playlist']['Service']['ImportPlaylist'](arg1); -} - -export function ImportPlaylists(arg1) { - return window['go']['playlist']['Service']['ImportPlaylists'](arg1); -} - -export function MaterializeUnmaterializedSmartPlaylists() { - return window['go']['playlist']['Service']['MaterializeUnmaterializedSmartPlaylists'](); -} - -export function PreviewSmartPlaylist(arg1) { - return window['go']['playlist']['Service']['PreviewSmartPlaylist'](arg1); -} - -export function RefreshSmartPlaylist(arg1) { - return window['go']['playlist']['Service']['RefreshSmartPlaylist'](arg1); -} - -export function RemoveFromDefaultPlaylist(arg1) { - return window['go']['playlist']['Service']['RemoveFromDefaultPlaylist'](arg1); -} - -export function RemovePhantomTracks(arg1, arg2) { - return window['go']['playlist']['Service']['RemovePhantomTracks'](arg1, arg2); -} - -export function RemoveTracksFromPlaylist(arg1, arg2) { - return window['go']['playlist']['Service']['RemoveTracksFromPlaylist'](arg1, arg2); -} - -export function RenamePlaylist(arg1, arg2) { - return window['go']['playlist']['Service']['RenamePlaylist'](arg1, arg2); -} - -export function RepopulateFromM3U() { - return window['go']['playlist']['Service']['RepopulateFromM3U'](); -} - -export function ResolvePhantomTracks(arg1, arg2) { - return window['go']['playlist']['Service']['ResolvePhantomTracks'](arg1, arg2); -} - -export function ResolvePhantomTracksAfterScan() { - return window['go']['playlist']['Service']['ResolvePhantomTracksAfterScan'](); -} - -export function RestoreAllPlaylists() { - return window['go']['playlist']['Service']['RestoreAllPlaylists'](); -} - -export function SearchLibrary(arg1) { - return window['go']['playlist']['Service']['SearchLibrary'](arg1); -} - -export function SetContext(arg1) { - return window['go']['playlist']['Service']['SetContext'](arg1); -} - -export function SetFavoritesConfig(arg1) { - return window['go']['playlist']['Service']['SetFavoritesConfig'](arg1); -} - -export function ToggleDefaultPlaylistTrack(arg1) { - return window['go']['playlist']['Service']['ToggleDefaultPlaylistTrack'](arg1); -} - -export function UpdateSmartPlaylistRules(arg1, arg2) { - return window['go']['playlist']['Service']['UpdateSmartPlaylistRules'](arg1, arg2); -} diff --git a/frontend/wailsjs/go/queue/Queue.d.ts b/frontend/wailsjs/go/queue/Queue.d.ts deleted file mode 100755 index 11993e0..0000000 --- a/frontend/wailsjs/go/queue/Queue.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {queue} from '../models'; -import {context} from '../models'; - -export function AddTrack(arg1:string):Promise; - -export function AddTracks(arg1:Array):Promise; - -export function Clear():Promise; - -export function CompactAfterLibraryRemoval():Promise; - -export function CycleRepeat():Promise; - -export function EmitCurrentState():Promise; - -export function GetState():Promise; - -export function InsertNext(arg1:string):Promise; - -export function InsertNextTracks(arg1:Array):Promise; - -export function InsertTracksAt(arg1:Array,arg2:number):Promise; - -export function MoveQueueTracks(arg1:Array,arg2:number):Promise; - -export function Next():Promise; - -export function OnPlaybackFinished():Promise; - -export function Play():Promise; - -export function PlayIndex(arg1:number):Promise; - -export function Previous():Promise; - -export function RemoveTrack(arg1:number):Promise; - -export function RemoveTracks(arg1:Array):Promise; - -export function RestoreState():Promise; - -export function SaveState():Promise; - -export function SetContext(arg1:context.Context):Promise; - -export function SetFallbackSource(arg1:queue.FallbackSource):Promise; - -export function SetPlayer(arg1:queue.TrackLoader):Promise; - -export function SetQueue(arg1:Array,arg2:number,arg3:boolean,arg4:queue.Source):Promise; - -export function ToggleShuffle():Promise; diff --git a/frontend/wailsjs/go/queue/Queue.js b/frontend/wailsjs/go/queue/Queue.js deleted file mode 100755 index ed93425..0000000 --- a/frontend/wailsjs/go/queue/Queue.js +++ /dev/null @@ -1,103 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function AddTrack(arg1) { - return window['go']['queue']['Queue']['AddTrack'](arg1); -} - -export function AddTracks(arg1) { - return window['go']['queue']['Queue']['AddTracks'](arg1); -} - -export function Clear() { - return window['go']['queue']['Queue']['Clear'](); -} - -export function CompactAfterLibraryRemoval() { - return window['go']['queue']['Queue']['CompactAfterLibraryRemoval'](); -} - -export function CycleRepeat() { - return window['go']['queue']['Queue']['CycleRepeat'](); -} - -export function EmitCurrentState() { - return window['go']['queue']['Queue']['EmitCurrentState'](); -} - -export function GetState() { - return window['go']['queue']['Queue']['GetState'](); -} - -export function InsertNext(arg1) { - return window['go']['queue']['Queue']['InsertNext'](arg1); -} - -export function InsertNextTracks(arg1) { - return window['go']['queue']['Queue']['InsertNextTracks'](arg1); -} - -export function InsertTracksAt(arg1, arg2) { - return window['go']['queue']['Queue']['InsertTracksAt'](arg1, arg2); -} - -export function MoveQueueTracks(arg1, arg2) { - return window['go']['queue']['Queue']['MoveQueueTracks'](arg1, arg2); -} - -export function Next() { - return window['go']['queue']['Queue']['Next'](); -} - -export function OnPlaybackFinished() { - return window['go']['queue']['Queue']['OnPlaybackFinished'](); -} - -export function Play() { - return window['go']['queue']['Queue']['Play'](); -} - -export function PlayIndex(arg1) { - return window['go']['queue']['Queue']['PlayIndex'](arg1); -} - -export function Previous() { - return window['go']['queue']['Queue']['Previous'](); -} - -export function RemoveTrack(arg1) { - return window['go']['queue']['Queue']['RemoveTrack'](arg1); -} - -export function RemoveTracks(arg1) { - return window['go']['queue']['Queue']['RemoveTracks'](arg1); -} - -export function RestoreState() { - return window['go']['queue']['Queue']['RestoreState'](); -} - -export function SaveState() { - return window['go']['queue']['Queue']['SaveState'](); -} - -export function SetContext(arg1) { - return window['go']['queue']['Queue']['SetContext'](arg1); -} - -export function SetFallbackSource(arg1) { - return window['go']['queue']['Queue']['SetFallbackSource'](arg1); -} - -export function SetPlayer(arg1) { - return window['go']['queue']['Queue']['SetPlayer'](arg1); -} - -export function SetQueue(arg1, arg2, arg3, arg4) { - return window['go']['queue']['Queue']['SetQueue'](arg1, arg2, arg3, arg4); -} - -export function ToggleShuffle() { - return window['go']['queue']['Queue']['ToggleShuffle'](); -} diff --git a/frontend/wailsjs/go/tagwriter/TagWriter.d.ts b/frontend/wailsjs/go/tagwriter/TagWriter.d.ts deleted file mode 100755 index 9657f52..0000000 --- a/frontend/wailsjs/go/tagwriter/TagWriter.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT -import {tagwriter} from '../models'; -import {context} from '../models'; - -export function BatchWriteTrackTags(arg1:Array,arg2:tagwriter.TagChanges):Promise; - -export function CancelBatchWrite():Promise; - -export function SetContext(arg1:context.Context):Promise; - -export function WriteTrackTags(arg1:number,arg2:tagwriter.TagChanges):Promise; - -export function WriteTrackTagsByPath(arg1:string,arg2:tagwriter.TagChanges):Promise; - -export function WriteUntrackedFileTags(arg1:string,arg2:tagwriter.TagChanges):Promise; diff --git a/frontend/wailsjs/go/tagwriter/TagWriter.js b/frontend/wailsjs/go/tagwriter/TagWriter.js deleted file mode 100755 index bb5fa8b..0000000 --- a/frontend/wailsjs/go/tagwriter/TagWriter.js +++ /dev/null @@ -1,27 +0,0 @@ -// @ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -export function BatchWriteTrackTags(arg1, arg2) { - return window['go']['tagwriter']['TagWriter']['BatchWriteTrackTags'](arg1, arg2); -} - -export function CancelBatchWrite() { - return window['go']['tagwriter']['TagWriter']['CancelBatchWrite'](); -} - -export function SetContext(arg1) { - return window['go']['tagwriter']['TagWriter']['SetContext'](arg1); -} - -export function WriteTrackTags(arg1, arg2) { - return window['go']['tagwriter']['TagWriter']['WriteTrackTags'](arg1, arg2); -} - -export function WriteTrackTagsByPath(arg1, arg2) { - return window['go']['tagwriter']['TagWriter']['WriteTrackTagsByPath'](arg1, arg2); -} - -export function WriteUntrackedFileTags(arg1, arg2) { - return window['go']['tagwriter']['TagWriter']['WriteUntrackedFileTags'](arg1, arg2); -} diff --git a/frontend/wailsjs/runtime/package.json b/frontend/wailsjs/runtime/package.json deleted file mode 100644 index 1e7c8a5..0000000 --- a/frontend/wailsjs/runtime/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@wailsapp/runtime", - "version": "2.0.0", - "description": "Wails Javascript runtime library", - "main": "runtime.js", - "types": "runtime.d.ts", - "scripts": { - }, - "repository": { - "type": "git", - "url": "git+https://github.com/wailsapp/wails.git" - }, - "keywords": [ - "Wails", - "Javascript", - "Go" - ], - "author": "Lea Anthony ", - "license": "MIT", - "bugs": { - "url": "https://github.com/wailsapp/wails/issues" - }, - "homepage": "https://github.com/wailsapp/wails#readme" -} diff --git a/frontend/wailsjs/runtime/runtime.d.ts b/frontend/wailsjs/runtime/runtime.d.ts deleted file mode 100644 index 4445dac..0000000 --- a/frontend/wailsjs/runtime/runtime.d.ts +++ /dev/null @@ -1,249 +0,0 @@ -/* - _ __ _ __ -| | / /___ _(_) /____ -| | /| / / __ `/ / / ___/ -| |/ |/ / /_/ / / (__ ) -|__/|__/\__,_/_/_/____/ -The electron alternative for Go -(c) Lea Anthony 2019-present -*/ - -export interface Position { - x: number; - y: number; -} - -export interface Size { - w: number; - h: number; -} - -export interface Screen { - isCurrent: boolean; - isPrimary: boolean; - width : number - height : number -} - -// Environment information such as platform, buildtype, ... -export interface EnvironmentInfo { - buildType: string; - platform: string; - arch: string; -} - -// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit) -// emits the given event. Optional data may be passed with the event. -// This will trigger any event listeners. -export function EventsEmit(eventName: string, ...data: any): void; - -// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name. -export function EventsOn(eventName: string, callback: (...data: any) => void): () => void; - -// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple) -// sets up a listener for the given event name, but will only trigger a given number times. -export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void; - -// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce) -// sets up a listener for the given event name, but will only trigger once. -export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void; - -// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff) -// unregisters the listener for the given event name. -export function EventsOff(eventName: string, ...additionalEventNames: string[]): void; - -// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall) -// unregisters all listeners. -export function EventsOffAll(): void; - -// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint) -// logs the given message as a raw message -export function LogPrint(message: string): void; - -// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace) -// logs the given message at the `trace` log level. -export function LogTrace(message: string): void; - -// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug) -// logs the given message at the `debug` log level. -export function LogDebug(message: string): void; - -// [LogError](https://wails.io/docs/reference/runtime/log#logerror) -// logs the given message at the `error` log level. -export function LogError(message: string): void; - -// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal) -// logs the given message at the `fatal` log level. -// The application will quit after calling this method. -export function LogFatal(message: string): void; - -// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo) -// logs the given message at the `info` log level. -export function LogInfo(message: string): void; - -// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning) -// logs the given message at the `warning` log level. -export function LogWarning(message: string): void; - -// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload) -// Forces a reload by the main application as well as connected browsers. -export function WindowReload(): void; - -// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp) -// Reloads the application frontend. -export function WindowReloadApp(): void; - -// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop) -// Sets the window AlwaysOnTop or not on top. -export function WindowSetAlwaysOnTop(b: boolean): void; - -// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme) -// *Windows only* -// Sets window theme to system default (dark/light). -export function WindowSetSystemDefaultTheme(): void; - -// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme) -// *Windows only* -// Sets window to light theme. -export function WindowSetLightTheme(): void; - -// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme) -// *Windows only* -// Sets window to dark theme. -export function WindowSetDarkTheme(): void; - -// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter) -// Centers the window on the monitor the window is currently on. -export function WindowCenter(): void; - -// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle) -// Sets the text in the window title bar. -export function WindowSetTitle(title: string): void; - -// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen) -// Makes the window full screen. -export function WindowFullscreen(): void; - -// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen) -// Restores the previous window dimensions and position prior to full screen. -export function WindowUnfullscreen(): void; - -// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen) -// Returns the state of the window, i.e. whether the window is in full screen mode or not. -export function WindowIsFullscreen(): Promise; - -// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize) -// Sets the width and height of the window. -export function WindowSetSize(width: number, height: number): void; - -// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize) -// Gets the width and height of the window. -export function WindowGetSize(): Promise; - -// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize) -// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions. -// Setting a size of 0,0 will disable this constraint. -export function WindowSetMaxSize(width: number, height: number): void; - -// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize) -// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions. -// Setting a size of 0,0 will disable this constraint. -export function WindowSetMinSize(width: number, height: number): void; - -// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition) -// Sets the window position relative to the monitor the window is currently on. -export function WindowSetPosition(x: number, y: number): void; - -// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition) -// Gets the window position relative to the monitor the window is currently on. -export function WindowGetPosition(): Promise; - -// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide) -// Hides the window. -export function WindowHide(): void; - -// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow) -// Shows the window, if it is currently hidden. -export function WindowShow(): void; - -// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise) -// Maximises the window to fill the screen. -export function WindowMaximise(): void; - -// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise) -// Toggles between Maximised and UnMaximised. -export function WindowToggleMaximise(): void; - -// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise) -// Restores the window to the dimensions and position prior to maximising. -export function WindowUnmaximise(): void; - -// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised) -// Returns the state of the window, i.e. whether the window is maximised or not. -export function WindowIsMaximised(): Promise; - -// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise) -// Minimises the window. -export function WindowMinimise(): void; - -// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise) -// Restores the window to the dimensions and position prior to minimising. -export function WindowUnminimise(): void; - -// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised) -// Returns the state of the window, i.e. whether the window is minimised or not. -export function WindowIsMinimised(): Promise; - -// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal) -// Returns the state of the window, i.e. whether the window is normal or not. -export function WindowIsNormal(): Promise; - -// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour) -// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels. -export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void; - -// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall) -// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system. -export function ScreenGetAll(): Promise; - -// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl) -// Opens the given URL in the system browser. -export function BrowserOpenURL(url: string): void; - -// [Environment](https://wails.io/docs/reference/runtime/intro#environment) -// Returns information about the environment -export function Environment(): Promise; - -// [Quit](https://wails.io/docs/reference/runtime/intro#quit) -// Quits the application. -export function Quit(): void; - -// [Hide](https://wails.io/docs/reference/runtime/intro#hide) -// Hides the application. -export function Hide(): void; - -// [Show](https://wails.io/docs/reference/runtime/intro#show) -// Shows the application. -export function Show(): void; - -// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext) -// Returns the current text stored on clipboard -export function ClipboardGetText(): Promise; - -// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext) -// Sets a text on the clipboard -export function ClipboardSetText(text: string): Promise; - -// [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop) -// OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings. -export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void - -// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff) -// OnFileDropOff removes the drag and drop listeners and handlers. -export function OnFileDropOff() :void - -// Check if the file path resolver is available -export function CanResolveFilePaths(): boolean; - -// Resolves file paths for an array of files -export function ResolveFilePaths(files: File[]): void \ No newline at end of file diff --git a/frontend/wailsjs/runtime/runtime.js b/frontend/wailsjs/runtime/runtime.js deleted file mode 100644 index 623397b..0000000 --- a/frontend/wailsjs/runtime/runtime.js +++ /dev/null @@ -1,238 +0,0 @@ -/* - _ __ _ __ -| | / /___ _(_) /____ -| | /| / / __ `/ / / ___/ -| |/ |/ / /_/ / / (__ ) -|__/|__/\__,_/_/_/____/ -The electron alternative for Go -(c) Lea Anthony 2019-present -*/ - -export function LogPrint(message) { - window.runtime.LogPrint(message); -} - -export function LogTrace(message) { - window.runtime.LogTrace(message); -} - -export function LogDebug(message) { - window.runtime.LogDebug(message); -} - -export function LogInfo(message) { - window.runtime.LogInfo(message); -} - -export function LogWarning(message) { - window.runtime.LogWarning(message); -} - -export function LogError(message) { - window.runtime.LogError(message); -} - -export function LogFatal(message) { - window.runtime.LogFatal(message); -} - -export function EventsOnMultiple(eventName, callback, maxCallbacks) { - return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks); -} - -export function EventsOn(eventName, callback) { - return EventsOnMultiple(eventName, callback, -1); -} - -export function EventsOff(eventName, ...additionalEventNames) { - return window.runtime.EventsOff(eventName, ...additionalEventNames); -} - -export function EventsOnce(eventName, callback) { - return EventsOnMultiple(eventName, callback, 1); -} - -export function EventsEmit(eventName) { - let args = [eventName].slice.call(arguments); - return window.runtime.EventsEmit.apply(null, args); -} - -export function WindowReload() { - window.runtime.WindowReload(); -} - -export function WindowReloadApp() { - window.runtime.WindowReloadApp(); -} - -export function WindowSetAlwaysOnTop(b) { - window.runtime.WindowSetAlwaysOnTop(b); -} - -export function WindowSetSystemDefaultTheme() { - window.runtime.WindowSetSystemDefaultTheme(); -} - -export function WindowSetLightTheme() { - window.runtime.WindowSetLightTheme(); -} - -export function WindowSetDarkTheme() { - window.runtime.WindowSetDarkTheme(); -} - -export function WindowCenter() { - window.runtime.WindowCenter(); -} - -export function WindowSetTitle(title) { - window.runtime.WindowSetTitle(title); -} - -export function WindowFullscreen() { - window.runtime.WindowFullscreen(); -} - -export function WindowUnfullscreen() { - window.runtime.WindowUnfullscreen(); -} - -export function WindowIsFullscreen() { - return window.runtime.WindowIsFullscreen(); -} - -export function WindowGetSize() { - return window.runtime.WindowGetSize(); -} - -export function WindowSetSize(width, height) { - window.runtime.WindowSetSize(width, height); -} - -export function WindowSetMaxSize(width, height) { - window.runtime.WindowSetMaxSize(width, height); -} - -export function WindowSetMinSize(width, height) { - window.runtime.WindowSetMinSize(width, height); -} - -export function WindowSetPosition(x, y) { - window.runtime.WindowSetPosition(x, y); -} - -export function WindowGetPosition() { - return window.runtime.WindowGetPosition(); -} - -export function WindowHide() { - window.runtime.WindowHide(); -} - -export function WindowShow() { - window.runtime.WindowShow(); -} - -export function WindowMaximise() { - window.runtime.WindowMaximise(); -} - -export function WindowToggleMaximise() { - window.runtime.WindowToggleMaximise(); -} - -export function WindowUnmaximise() { - window.runtime.WindowUnmaximise(); -} - -export function WindowIsMaximised() { - return window.runtime.WindowIsMaximised(); -} - -export function WindowMinimise() { - window.runtime.WindowMinimise(); -} - -export function WindowUnminimise() { - window.runtime.WindowUnminimise(); -} - -export function WindowSetBackgroundColour(R, G, B, A) { - window.runtime.WindowSetBackgroundColour(R, G, B, A); -} - -export function ScreenGetAll() { - return window.runtime.ScreenGetAll(); -} - -export function WindowIsMinimised() { - return window.runtime.WindowIsMinimised(); -} - -export function WindowIsNormal() { - return window.runtime.WindowIsNormal(); -} - -export function BrowserOpenURL(url) { - window.runtime.BrowserOpenURL(url); -} - -export function Environment() { - return window.runtime.Environment(); -} - -export function Quit() { - window.runtime.Quit(); -} - -export function Hide() { - window.runtime.Hide(); -} - -export function Show() { - window.runtime.Show(); -} - -export function ClipboardGetText() { - return window.runtime.ClipboardGetText(); -} - -export function ClipboardSetText(text) { - return window.runtime.ClipboardSetText(text); -} - -/** - * Callback for OnFileDrop returns a slice of file path strings when a drop is finished. - * - * @export - * @callback OnFileDropCallback - * @param {number} x - x coordinate of the drop - * @param {number} y - y coordinate of the drop - * @param {string[]} paths - A list of file paths. - */ - -/** - * OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings. - * - * @export - * @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished. - * @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target) - */ -export function OnFileDrop(callback, useDropTarget) { - return window.runtime.OnFileDrop(callback, useDropTarget); -} - -/** - * OnFileDropOff removes the drag and drop listeners and handlers. - */ -export function OnFileDropOff() { - return window.runtime.OnFileDropOff(); -} - -export function CanResolveFilePaths() { - return window.runtime.CanResolveFilePaths(); -} - -export function ResolveFilePaths(files) { - return window.runtime.ResolveFilePaths(files); -} \ No newline at end of file diff --git a/lefthook.yml b/lefthook.yml index 4afa86d..4cfaafd 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -30,8 +30,11 @@ pre-commit: exit 1 fi - # frontend/wailsjs is generated by `wails`, not `go generate`, so the - # check above does not cover it. Takes ~1.5s. + # frontend/bindings is generated by `wails3`, not `go generate`, so + # the check above does not cover it. ~20s: v3's generator is a + # static analyser over the whole package graph, where v2's built the + # app with a `bindings` tag and ran it. Slower than v2's ~1.5s and + # still the cheapest place to catch a renamed field. bindings-check: glob: "*.go" run: ./scripts/bindings-check.sh diff --git a/scripts/bindings-check.sh b/scripts/bindings-check.sh index 7623b8d..6f8025f 100755 --- a/scripts/bindings-check.sh +++ b/scripts/bindings-check.sh @@ -1,21 +1,26 @@ #!/usr/bin/env bash # -# Fails when frontend/wailsjs/ is stale against the bound Go structs. +# Fails when frontend/bindings/ is stale against the bound Go services. # -# frontend/wailsjs/ is generated by `wails`, not by `go generate`, so the -# pre-commit codegen check does not cover it at all. Without this, a +# frontend/bindings/ is generated by `wails3`, not by `go generate`, so +# the pre-commit codegen check does not cover it at all. Without this, a # renamed Go struct field or a changed method signature first shows up at # runtime, inside a window, as a binding that never settles. # -# `wails generate module` builds the app with the `bindings` tag and runs -# it to dump the bindings — about three seconds, so it is cheap enough to -# gate a commit on. +# v3's generator is a static analyser rather than a build-and-run of the +# app, which is what makes this cheap enough to gate a commit on — and +# also what makes the build tags matter. It sees only the configuration +# it is told about; the one that matters is the one users run, which is +# the default tag set (GTK4 + WebKitGTK 6.0). The other two +# configurations this repo builds — `indexbuild` and `dev` — add no bound +# service, so generating under them would only widen the surface past +# what ships. set -euo pipefail cd "$(dirname "$0")/.." -TARGET="frontend/wailsjs" +TARGET="frontend/bindings" if [ -n "$(git status --porcelain -- "$TARGET")" ]; then echo "bindings-check: $TARGET has uncommitted changes; stage or stash them first" >&2 @@ -23,21 +28,21 @@ if [ -n "$(git status --porcelain -- "$TARGET")" ]; then exit 1 fi -go tool wails generate module >/dev/null 2>&1 +go tool wails3 generate bindings -clean=true -ts -i >/dev/null 2>&1 -# `wails generate module` rewrites the three runtime files as 755 every -# time. That is not drift, so compare content only. -if ! git -c core.fileMode=false diff --quiet -- "$TARGET"; then +if ! git diff --quiet -- "$TARGET"; then echo "bindings-check: $TARGET is out of date with the Go bindings." >&2 echo "Run 'make bindings' and stage the result." >&2 - git -c core.fileMode=false diff --stat -- "$TARGET" >&2 + git diff --stat -- "$TARGET" >&2 exit 1 fi -# Restore the modes the generator churned, so the tree is left clean. -chmod 644 \ - frontend/wailsjs/runtime/runtime.js \ - frontend/wailsjs/runtime/runtime.d.ts \ - frontend/wailsjs/runtime/package.json +# An added or removed *file* is a rename or a new service, which a diff +# of tracked paths alone does not see. +if [ -n "$(git status --porcelain -- "$TARGET")" ]; then + echo "bindings-check: $TARGET gained or lost files." >&2 + git status --short -- "$TARGET" >&2 + exit 1 +fi -echo "bindings-check: frontend/wailsjs is current" +echo "bindings-check: frontend/bindings is current"