mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Compare commits
12 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47a2e06a17 | |||
| 8d9690dad6 | |||
| 66af523d5a | |||
| 45338c1bf9 | |||
| c50ef90e9b | |||
| 48cfdd354a | |||
| 91b68b48e6 | |||
| 3c65905a37 | |||
| 1252ca0a1b | |||
|
|
5917b0ac36 | ||
| 8903ce7d2c | |||
| 41c2a9c1fc |
42 changed files with 873 additions and 319 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1880,7 +1880,6 @@ dependencies = [
|
||||||
"glib",
|
"glib",
|
||||||
"gstreamer-play",
|
"gstreamer-play",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"lazy_static",
|
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
"log",
|
"log",
|
||||||
"mpris-server",
|
"mpris-server",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ gettext-rs = { version = "0.7", features = ["gettext-system"] }
|
||||||
glib = { version = "0.20", features = ["v2_84"] }
|
glib = { version = "0.20", features = ["v2_84"] }
|
||||||
gstreamer-play = "0.23"
|
gstreamer-play = "0.23"
|
||||||
gtk = { package = "gtk4", version = "0.9", features = ["v4_18", "blueprint"] }
|
gtk = { package = "gtk4", version = "0.9", features = ["v4_18", "blueprint"] }
|
||||||
lazy_static = "1"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mpris-server = "0.8"
|
mpris-server = "0.8"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,12 @@
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchbar .searchtag {
|
.rounded-entry {
|
||||||
background-color: alpha(currentColor, 0.1);
|
border-radius: 999px;
|
||||||
border-radius: 100px;
|
padding-left: 12px;
|
||||||
}
|
padding-right: 12px;
|
||||||
|
padding-top: 3px;
|
||||||
.searchbar .searchtag>button {
|
padding-bottom: 3px;
|
||||||
min-width: 24px;
|
|
||||||
min-height: 24px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile {
|
.tile {
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,15 @@ template $MusicusAlbumEditor: Adw.NavigationPage {
|
||||||
margin-top: 24;
|
margin-top: 24;
|
||||||
|
|
||||||
styles [
|
styles [
|
||||||
"boxed-list",
|
"boxed-list-separate",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Adw.SwitchRow enable_updates_row {
|
||||||
|
title: _("Enable updates");
|
||||||
|
subtitle: _("Keep this item up to date with the online metadata library");
|
||||||
|
active: true;
|
||||||
|
}
|
||||||
|
|
||||||
Adw.ButtonRow save_row {
|
Adw.ButtonRow save_row {
|
||||||
title: _("_Create album");
|
title: _("_Create album");
|
||||||
use-underline: true;
|
use-underline: true;
|
||||||
|
|
|
||||||
|
|
@ -50,16 +50,24 @@ template $MusicusEmptyPage: Adw.NavigationPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
menu primary_menu {
|
menu primary_menu {
|
||||||
|
section {
|
||||||
item {
|
item {
|
||||||
label: _("_Import music");
|
label: _("_Import music");
|
||||||
action: "win.import";
|
action: "win.import";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item {
|
||||||
|
label: _("_Create album");
|
||||||
|
action: "win.create-album";
|
||||||
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
label: _("_Library manager");
|
label: _("_Library manager");
|
||||||
action: "win.library";
|
action: "win.library";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
item {
|
item {
|
||||||
label: _("_Preferences");
|
label: _("_Preferences");
|
||||||
action: "win.preferences";
|
action: "win.preferences";
|
||||||
|
|
@ -69,4 +77,5 @@ menu primary_menu {
|
||||||
label: _("_About Musicus");
|
label: _("_About Musicus");
|
||||||
action: "app.about";
|
action: "app.about";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,10 @@ template $MusicusSearchPage: Adw.NavigationPage {
|
||||||
placeholder-text: _("Enter composers, performers, works…");
|
placeholder-text: _("Enter composers, performers, works…");
|
||||||
margin-top: 24;
|
margin-top: 24;
|
||||||
activate => $select() swapped;
|
activate => $select() swapped;
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"rounded-entry",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Stack stack {
|
Gtk.Stack stack {
|
||||||
|
|
@ -260,16 +264,24 @@ template $MusicusSearchPage: Adw.NavigationPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
menu primary_menu {
|
menu primary_menu {
|
||||||
|
section {
|
||||||
item {
|
item {
|
||||||
label: _("_Import music");
|
label: _("_Import music");
|
||||||
action: "win.import";
|
action: "win.import";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item {
|
||||||
|
label: _("_Create album");
|
||||||
|
action: "win.create-album";
|
||||||
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
label: _("_Library manager");
|
label: _("_Library manager");
|
||||||
action: "win.library";
|
action: "win.library";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
item {
|
item {
|
||||||
label: _("_Preferences");
|
label: _("_Preferences");
|
||||||
action: "win.preferences";
|
action: "win.preferences";
|
||||||
|
|
@ -279,6 +291,7 @@ menu primary_menu {
|
||||||
label: _("_About Musicus");
|
label: _("_About Musicus");
|
||||||
action: "app.about";
|
action: "app.about";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
menu item_menu {
|
menu item_menu {
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
using Gtk 4.0;
|
|
||||||
|
|
||||||
template $MusicusSearchTag : Gtk.Box {
|
|
||||||
styles ["searchtag"]
|
|
||||||
|
|
||||||
margin-start: 6;
|
|
||||||
margin-end: 6;
|
|
||||||
|
|
||||||
Gtk.Label label {
|
|
||||||
styles ["caption-heading"]
|
|
||||||
margin-start: 12;
|
|
||||||
margin-end: 6;
|
|
||||||
max-width-chars: 15;
|
|
||||||
ellipsize: end;
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.Button button {
|
|
||||||
styles ["flat", "circular"]
|
|
||||||
icon-name: "window-close-symbolic";
|
|
||||||
clicked => $remove() swapped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"sdk": "org.gnome.Sdk",
|
"sdk": "org.gnome.Sdk",
|
||||||
"sdk-extensions": [
|
"sdk-extensions": [
|
||||||
"org.freedesktop.Sdk.Extension.rust-stable",
|
"org.freedesktop.Sdk.Extension.rust-stable",
|
||||||
"org.freedesktop.Sdk.Extension.llvm18"
|
"org.freedesktop.Sdk.Extension.llvm20"
|
||||||
],
|
],
|
||||||
"command": "musicus",
|
"command": "musicus",
|
||||||
"finish-args": [
|
"finish-args": [
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
"--env=G_MESSAGES_DEBUG=none"
|
"--env=G_MESSAGES_DEBUG=none"
|
||||||
],
|
],
|
||||||
"build-options": {
|
"build-options": {
|
||||||
"append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm18/bin",
|
"append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin",
|
||||||
"build-args": [
|
"build-args": [
|
||||||
"--share=network"
|
"--share=network"
|
||||||
],
|
],
|
||||||
|
|
@ -31,17 +31,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
|
||||||
"name": "blueprint-compiler",
|
|
||||||
"buildsystem": "meson",
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://gitlab.gnome.org/jwestman/blueprint-compiler.git",
|
|
||||||
"tag": "v0.16.0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "musicus",
|
"name": "musicus",
|
||||||
"buildsystem": "meson",
|
"buildsystem": "meson",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"sdk": "org.gnome.Sdk",
|
"sdk": "org.gnome.Sdk",
|
||||||
"sdk-extensions": [
|
"sdk-extensions": [
|
||||||
"org.freedesktop.Sdk.Extension.rust-stable",
|
"org.freedesktop.Sdk.Extension.rust-stable",
|
||||||
"org.freedesktop.Sdk.Extension.llvm18"
|
"org.freedesktop.Sdk.Extension.llvm20"
|
||||||
],
|
],
|
||||||
"command": "musicus",
|
"command": "musicus",
|
||||||
"finish-args": [
|
"finish-args": [
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
"--env=G_MESSAGES_DEBUG=none"
|
"--env=G_MESSAGES_DEBUG=none"
|
||||||
],
|
],
|
||||||
"build-options": {
|
"build-options": {
|
||||||
"append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm18/bin",
|
"append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin",
|
||||||
"build-args": [
|
"build-args": [
|
||||||
"--share=network"
|
"--share=network"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
248
migrations/2025-08-10-104302_source/down.sql
Normal file
248
migrations/2025-08-10-104302_source/down.sql
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
CREATE TABLE persons_old (
|
||||||
|
person_id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP,
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE roles_old (
|
||||||
|
role_id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE instruments_old (
|
||||||
|
instrument_id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP,
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE works_old (
|
||||||
|
work_id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
parent_work_id TEXT REFERENCES works(work_id),
|
||||||
|
sequence_number INTEGER,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP,
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ensembles_old (
|
||||||
|
ensemble_id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP,
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE recordings_old (
|
||||||
|
recording_id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
work_id TEXT NOT NULL REFERENCES works(work_id),
|
||||||
|
year INTEGER,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP,
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE mediums_old (
|
||||||
|
medium_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
discid TEXT NOT NULL,
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE albums_old (
|
||||||
|
album_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO persons_old (
|
||||||
|
person_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
)
|
||||||
|
SELECT person_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
FROM persons;
|
||||||
|
DROP TABLE persons;
|
||||||
|
ALTER TABLE persons_old
|
||||||
|
RENAME TO persons;
|
||||||
|
|
||||||
|
INSERT INTO roles_old (
|
||||||
|
role_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
enable_updates
|
||||||
|
)
|
||||||
|
SELECT role_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
enable_updates
|
||||||
|
FROM roles;
|
||||||
|
DROP TABLE roles;
|
||||||
|
ALTER TABLE roles_old
|
||||||
|
RENAME TO roles;
|
||||||
|
|
||||||
|
INSERT INTO instruments_old (
|
||||||
|
instrument_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
)
|
||||||
|
SELECT instrument_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
FROM instruments;
|
||||||
|
DROP TABLE instruments;
|
||||||
|
ALTER TABLE instruments_old
|
||||||
|
RENAME TO instruments;
|
||||||
|
|
||||||
|
INSERT INTO works_old (
|
||||||
|
work_id,
|
||||||
|
parent_work_id,
|
||||||
|
sequence_number,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
)
|
||||||
|
SELECT work_id,
|
||||||
|
parent_work_id,
|
||||||
|
sequence_number,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
FROM works;
|
||||||
|
DROP TABLE works;
|
||||||
|
ALTER TABLE works_old
|
||||||
|
RENAME TO works;
|
||||||
|
|
||||||
|
INSERT INTO ensembles_old (
|
||||||
|
ensemble_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
)
|
||||||
|
SELECT ensemble_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
FROM ensembles;
|
||||||
|
DROP TABLE ensembles;
|
||||||
|
ALTER TABLE ensembles_old
|
||||||
|
RENAME TO ensembles;
|
||||||
|
|
||||||
|
INSERT INTO recordings_old (
|
||||||
|
recording_id,
|
||||||
|
work_id,
|
||||||
|
year,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
)
|
||||||
|
SELECT recording_id,
|
||||||
|
work_id,
|
||||||
|
year,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at,
|
||||||
|
enable_updates
|
||||||
|
FROM recordings;
|
||||||
|
DROP TABLE recordings;
|
||||||
|
ALTER TABLE recordings_old
|
||||||
|
RENAME TO recordings;
|
||||||
|
|
||||||
|
INSERT INTO mediums_old (
|
||||||
|
medium_id,
|
||||||
|
discid,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT medium_id,
|
||||||
|
discid,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM mediums;
|
||||||
|
DROP TABLE mediums;
|
||||||
|
ALTER TABLE mediums_old
|
||||||
|
RENAME TO mediums;
|
||||||
|
|
||||||
|
INSERT INTO albums_old (
|
||||||
|
album_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT album_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM albums;
|
||||||
|
DROP TABLE albums;
|
||||||
|
ALTER TABLE albums_old
|
||||||
|
RENAME TO albums;
|
||||||
245
migrations/2025-08-10-104302_source/up.sql
Normal file
245
migrations/2025-08-10-104302_source/up.sql
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
CREATE TABLE persons_new (
|
||||||
|
person_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE roles_new (
|
||||||
|
role_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime'))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE instruments_new (
|
||||||
|
instrument_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE works_new (
|
||||||
|
work_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
parent_work_id TEXT REFERENCES works(work_id),
|
||||||
|
sequence_number INTEGER,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ensembles_new (
|
||||||
|
ensemble_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE recordings_new (
|
||||||
|
recording_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
work_id TEXT NOT NULL REFERENCES works(work_id),
|
||||||
|
year INTEGER,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE mediums_new (
|
||||||
|
medium_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
discid TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE albums_new (
|
||||||
|
album_id TEXT NOT NULL PRIMARY KEY REFERENCES item_state(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'user',
|
||||||
|
enable_updates BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
edited_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_used_at TIMESTAMP NOT NULL DEFAULT (DATETIME('now', 'localtime')),
|
||||||
|
last_played_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO persons_new (
|
||||||
|
person_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT person_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM persons;
|
||||||
|
DROP TABLE persons;
|
||||||
|
ALTER TABLE persons_new
|
||||||
|
RENAME TO persons;
|
||||||
|
|
||||||
|
INSERT INTO roles_new (
|
||||||
|
role_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at
|
||||||
|
)
|
||||||
|
SELECT role_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at
|
||||||
|
FROM roles;
|
||||||
|
DROP TABLE roles;
|
||||||
|
ALTER TABLE roles_new
|
||||||
|
RENAME TO roles;
|
||||||
|
|
||||||
|
INSERT INTO instruments_new (
|
||||||
|
instrument_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT instrument_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM instruments;
|
||||||
|
DROP TABLE instruments;
|
||||||
|
ALTER TABLE instruments_new
|
||||||
|
RENAME TO instruments;
|
||||||
|
|
||||||
|
INSERT INTO works_new (
|
||||||
|
work_id,
|
||||||
|
parent_work_id,
|
||||||
|
sequence_number,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT work_id,
|
||||||
|
parent_work_id,
|
||||||
|
sequence_number,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM works;
|
||||||
|
DROP TABLE works;
|
||||||
|
ALTER TABLE works_new
|
||||||
|
RENAME TO works;
|
||||||
|
|
||||||
|
INSERT INTO ensembles_new (
|
||||||
|
ensemble_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT ensemble_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM ensembles;
|
||||||
|
DROP TABLE ensembles;
|
||||||
|
ALTER TABLE ensembles_new
|
||||||
|
RENAME TO ensembles;
|
||||||
|
|
||||||
|
INSERT INTO recordings_new (
|
||||||
|
recording_id,
|
||||||
|
work_id,
|
||||||
|
year,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT recording_id,
|
||||||
|
work_id,
|
||||||
|
year,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM recordings;
|
||||||
|
DROP TABLE recordings;
|
||||||
|
ALTER TABLE recordings_new
|
||||||
|
RENAME TO recordings;
|
||||||
|
|
||||||
|
INSERT INTO mediums_new (
|
||||||
|
medium_id,
|
||||||
|
discid,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT medium_id,
|
||||||
|
discid,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM mediums;
|
||||||
|
DROP TABLE mediums;
|
||||||
|
ALTER TABLE mediums_new
|
||||||
|
RENAME TO mediums;
|
||||||
|
|
||||||
|
INSERT INTO albums_new (
|
||||||
|
album_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
)
|
||||||
|
SELECT album_id,
|
||||||
|
name,
|
||||||
|
created_at,
|
||||||
|
edited_at,
|
||||||
|
last_used_at,
|
||||||
|
last_played_at
|
||||||
|
FROM albums;
|
||||||
|
DROP TABLE albums;
|
||||||
|
ALTER TABLE albums_new
|
||||||
|
RENAME TO albums;
|
||||||
16
po/de.po
16
po/de.po
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-27 17:54+0200\n"
|
"POT-Creation-Date: 2025-05-30 15:27+0200\n"
|
||||||
"PO-Revision-Date: 2025-04-27 18:23+0200\n"
|
"PO-Revision-Date: 2025-04-27 18:23+0200\n"
|
||||||
"Last-Translator: elias@johrpan.de\n"
|
"Last-Translator: elias@johrpan.de\n"
|
||||||
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
|
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
|
||||||
|
|
@ -818,7 +818,7 @@ msgstr "Bibliothek exportieren"
|
||||||
msgid "Exporting music library to {}"
|
msgid "Exporting music library to {}"
|
||||||
msgstr "Bibliothek wird nach {} exportiert"
|
msgstr "Bibliothek wird nach {} exportiert"
|
||||||
|
|
||||||
#: src/library_manager.rs:234 src/window.rs:282
|
#: src/library_manager.rs:234 src/window.rs:305
|
||||||
msgid "Updating metadata"
|
msgid "Updating metadata"
|
||||||
msgstr "Metadaten werden aktualisiert"
|
msgstr "Metadaten werden aktualisiert"
|
||||||
|
|
||||||
|
|
@ -826,19 +826,23 @@ msgstr "Metadaten werden aktualisiert"
|
||||||
msgid "Updating music library"
|
msgid "Updating music library"
|
||||||
msgstr "Musikbibliothek wird aktualisiert"
|
msgstr "Musikbibliothek wird aktualisiert"
|
||||||
|
|
||||||
#: src/window.rs:167
|
#: src/window.rs:166
|
||||||
|
msgid "Currently playing music"
|
||||||
|
msgstr "Musik wird abgespielt"
|
||||||
|
|
||||||
|
#: src/window.rs:190
|
||||||
msgid "Close window?"
|
msgid "Close window?"
|
||||||
msgstr "Fenster schließen?"
|
msgstr "Fenster schließen?"
|
||||||
|
|
||||||
#: src/window.rs:169
|
#: src/window.rs:192
|
||||||
msgid "There are ongoing processes that will be canceled."
|
msgid "There are ongoing processes that will be canceled."
|
||||||
msgstr "Es gibt laufende Prozesse, die abgebrochen werden."
|
msgstr "Es gibt laufende Prozesse, die abgebrochen werden."
|
||||||
|
|
||||||
#: src/window.rs:174
|
#: src/window.rs:197
|
||||||
msgid "Keep open"
|
msgid "Keep open"
|
||||||
msgstr "Nicht schließen"
|
msgstr "Nicht schließen"
|
||||||
|
|
||||||
#: src/window.rs:175
|
#: src/window.rs:198
|
||||||
msgid "Close window"
|
msgid "Close window"
|
||||||
msgstr "Fenster schließen"
|
msgstr "Fenster schließen"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-27 17:54+0200\n"
|
"POT-Creation-Date: 2025-05-30 15:27+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -784,7 +784,7 @@ msgstr ""
|
||||||
msgid "Exporting music library to {}"
|
msgid "Exporting music library to {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/library_manager.rs:234 src/window.rs:282
|
#: src/library_manager.rs:234 src/window.rs:305
|
||||||
msgid "Updating metadata"
|
msgid "Updating metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -792,18 +792,22 @@ msgstr ""
|
||||||
msgid "Updating music library"
|
msgid "Updating music library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/window.rs:167
|
#: src/window.rs:166
|
||||||
|
msgid "Currently playing music"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/window.rs:190
|
||||||
msgid "Close window?"
|
msgid "Close window?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/window.rs:169
|
#: src/window.rs:192
|
||||||
msgid "There are ongoing processes that will be canceled."
|
msgid "There are ongoing processes that will be canceled."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/window.rs:174
|
#: src/window.rs:197
|
||||||
msgid "Keep open"
|
msgid "Keep open"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/window.rs:175
|
#: src/window.rs:198
|
||||||
msgid "Close window"
|
msgid "Close window"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,7 @@ mod imp {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.recordings
|
.recordings
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| obj.player().recording_to_playlist(r))
|
.flat_map(|r| obj.player().recording_to_playlist(r))
|
||||||
.flatten()
|
|
||||||
.collect::<Vec<PlaylistItem>>();
|
.collect::<Vec<PlaylistItem>>();
|
||||||
|
|
||||||
if let Err(err) = obj.player().append(playlist) {
|
if let Err(err) = obj.player().append(playlist) {
|
||||||
|
|
@ -165,8 +164,7 @@ impl AlbumPage {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.recordings
|
.recordings
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| self.player().recording_to_playlist(r))
|
.flat_map(|r| self.player().recording_to_playlist(r))
|
||||||
.flatten()
|
|
||||||
.collect::<Vec<PlaylistItem>>();
|
.collect::<Vec<PlaylistItem>>();
|
||||||
|
|
||||||
self.player().append_and_play(playlist);
|
self.player().append_and_play(playlist);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ impl AlbumTile {
|
||||||
pub fn new(album: &Album) -> Self {
|
pub fn new(album: &Album) -> Self {
|
||||||
let obj: Self = glib::Object::new();
|
let obj: Self = glib::Object::new();
|
||||||
|
|
||||||
obj.imp().title_label.set_label(&album.name.get());
|
obj.imp().title_label.set_label(album.name.get());
|
||||||
obj.imp().album.set(album.clone()).unwrap();
|
obj.imp().album.set(album.clone()).unwrap();
|
||||||
|
|
||||||
obj
|
obj
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ pub struct Album {
|
||||||
pub album_id: String,
|
pub album_id: String,
|
||||||
pub name: TranslatedString,
|
pub name: TranslatedString,
|
||||||
pub recordings: Vec<Recording>,
|
pub recordings: Vec<Recording>,
|
||||||
|
pub enable_updates: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Person {}
|
impl Eq for Person {}
|
||||||
|
|
@ -433,6 +434,7 @@ impl Album {
|
||||||
album_id: data.album_id,
|
album_id: data.album_id,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
recordings,
|
recordings,
|
||||||
|
enable_updates: data.enable_updates,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ diesel::table! {
|
||||||
albums (album_id) {
|
albums (album_id) {
|
||||||
album_id -> Text,
|
album_id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
|
|
@ -40,11 +42,12 @@ diesel::table! {
|
||||||
ensembles (ensemble_id) {
|
ensembles (ensemble_id) {
|
||||||
ensemble_id -> Text,
|
ensemble_id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
last_played_at -> Nullable<Timestamp>,
|
last_played_at -> Nullable<Timestamp>,
|
||||||
enable_updates -> Bool,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,11 +55,12 @@ diesel::table! {
|
||||||
instruments (instrument_id) {
|
instruments (instrument_id) {
|
||||||
instrument_id -> Text,
|
instrument_id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
last_played_at -> Nullable<Timestamp>,
|
last_played_at -> Nullable<Timestamp>,
|
||||||
enable_updates -> Bool,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,6 +68,8 @@ diesel::table! {
|
||||||
mediums (medium_id) {
|
mediums (medium_id) {
|
||||||
medium_id -> Text,
|
medium_id -> Text,
|
||||||
discid -> Text,
|
discid -> Text,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
|
|
@ -75,16 +81,17 @@ diesel::table! {
|
||||||
persons (person_id) {
|
persons (person_id) {
|
||||||
person_id -> Text,
|
person_id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
last_played_at -> Nullable<Timestamp>,
|
last_played_at -> Nullable<Timestamp>,
|
||||||
enable_updates -> Bool,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
recording_ensembles (recording_id, ensemble_id) {
|
recording_ensembles (recording_id, ensemble_id, sequence_number) {
|
||||||
recording_id -> Text,
|
recording_id -> Text,
|
||||||
ensemble_id -> Text,
|
ensemble_id -> Text,
|
||||||
role_id -> Nullable<Text>,
|
role_id -> Nullable<Text>,
|
||||||
|
|
@ -93,7 +100,7 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
recording_persons (recording_id, person_id) {
|
recording_persons (recording_id, person_id, sequence_number) {
|
||||||
recording_id -> Text,
|
recording_id -> Text,
|
||||||
person_id -> Text,
|
person_id -> Text,
|
||||||
role_id -> Nullable<Text>,
|
role_id -> Nullable<Text>,
|
||||||
|
|
@ -107,11 +114,12 @@ diesel::table! {
|
||||||
recording_id -> Text,
|
recording_id -> Text,
|
||||||
work_id -> Text,
|
work_id -> Text,
|
||||||
year -> Nullable<Integer>,
|
year -> Nullable<Integer>,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
last_played_at -> Nullable<Timestamp>,
|
last_played_at -> Nullable<Timestamp>,
|
||||||
enable_updates -> Bool,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,10 +127,11 @@ diesel::table! {
|
||||||
roles (role_id) {
|
roles (role_id) {
|
||||||
role_id -> Text,
|
role_id -> Text,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
enable_updates -> Bool,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,7 +167,7 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
work_persons (work_id, person_id) {
|
work_persons (work_id, person_id, sequence_number) {
|
||||||
work_id -> Text,
|
work_id -> Text,
|
||||||
person_id -> Text,
|
person_id -> Text,
|
||||||
role_id -> Nullable<Text>,
|
role_id -> Nullable<Text>,
|
||||||
|
|
@ -172,11 +181,12 @@ diesel::table! {
|
||||||
parent_work_id -> Nullable<Text>,
|
parent_work_id -> Nullable<Text>,
|
||||||
sequence_number -> Nullable<Integer>,
|
sequence_number -> Nullable<Integer>,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
|
source -> Text,
|
||||||
|
enable_updates -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
edited_at -> Timestamp,
|
edited_at -> Timestamp,
|
||||||
last_used_at -> Timestamp,
|
last_used_at -> Timestamp,
|
||||||
last_played_at -> Nullable<Timestamp>,
|
last_played_at -> Nullable<Timestamp>,
|
||||||
enable_updates -> Bool,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,12 @@ use super::{schema::*, TranslatedString};
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
pub person_id: String,
|
pub person_id: String,
|
||||||
pub name: TranslatedString,
|
pub name: TranslatedString,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
pub last_played_at: Option<NaiveDateTime>,
|
pub last_played_at: Option<NaiveDateTime>,
|
||||||
pub enable_updates: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Boxed, Insertable, Queryable, Selectable, Clone, Debug)]
|
#[derive(Boxed, Insertable, Queryable, Selectable, Clone, Debug)]
|
||||||
|
|
@ -37,10 +38,11 @@ pub struct Person {
|
||||||
pub struct Role {
|
pub struct Role {
|
||||||
pub role_id: String,
|
pub role_id: String,
|
||||||
pub name: TranslatedString,
|
pub name: TranslatedString,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
pub enable_updates: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Boxed, Insertable, Queryable, Selectable, Clone, Debug)]
|
#[derive(Boxed, Insertable, Queryable, Selectable, Clone, Debug)]
|
||||||
|
|
@ -49,11 +51,12 @@ pub struct Role {
|
||||||
pub struct Instrument {
|
pub struct Instrument {
|
||||||
pub instrument_id: String,
|
pub instrument_id: String,
|
||||||
pub name: TranslatedString,
|
pub name: TranslatedString,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
pub last_played_at: Option<NaiveDateTime>,
|
pub last_played_at: Option<NaiveDateTime>,
|
||||||
pub enable_updates: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
||||||
|
|
@ -63,11 +66,12 @@ pub struct Work {
|
||||||
pub parent_work_id: Option<String>,
|
pub parent_work_id: Option<String>,
|
||||||
pub sequence_number: Option<i32>,
|
pub sequence_number: Option<i32>,
|
||||||
pub name: TranslatedString,
|
pub name: TranslatedString,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
pub last_played_at: Option<NaiveDateTime>,
|
pub last_played_at: Option<NaiveDateTime>,
|
||||||
pub enable_updates: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
||||||
|
|
@ -92,11 +96,12 @@ pub struct WorkInstrument {
|
||||||
pub struct Ensemble {
|
pub struct Ensemble {
|
||||||
pub ensemble_id: String,
|
pub ensemble_id: String,
|
||||||
pub name: TranslatedString,
|
pub name: TranslatedString,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
pub last_played_at: Option<NaiveDateTime>,
|
pub last_played_at: Option<NaiveDateTime>,
|
||||||
pub enable_updates: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
||||||
|
|
@ -114,11 +119,12 @@ pub struct Recording {
|
||||||
pub recording_id: String,
|
pub recording_id: String,
|
||||||
pub work_id: String,
|
pub work_id: String,
|
||||||
pub year: Option<i32>,
|
pub year: Option<i32>,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
pub last_played_at: Option<NaiveDateTime>,
|
pub last_played_at: Option<NaiveDateTime>,
|
||||||
pub enable_updates: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
#[derive(Insertable, Queryable, Selectable, Clone, Debug)]
|
||||||
|
|
@ -168,6 +174,8 @@ pub struct TrackWork {
|
||||||
pub struct Medium {
|
pub struct Medium {
|
||||||
pub medium_id: String,
|
pub medium_id: String,
|
||||||
pub discid: String,
|
pub discid: String,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
|
|
@ -179,6 +187,8 @@ pub struct Medium {
|
||||||
pub struct Album {
|
pub struct Album {
|
||||||
pub album_id: String,
|
pub album_id: String,
|
||||||
pub name: TranslatedString,
|
pub name: TranslatedString,
|
||||||
|
pub source: Source,
|
||||||
|
pub enable_updates: bool,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub edited_at: NaiveDateTime,
|
pub edited_at: NaiveDateTime,
|
||||||
pub last_used_at: NaiveDateTime,
|
pub last_used_at: NaiveDateTime,
|
||||||
|
|
@ -256,3 +266,40 @@ impl AsRef<Path> for PathBufWrapper {
|
||||||
self.0.as_ref()
|
self.0.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(AsExpression, FromSqlRow, Copy, Clone, Debug)]
|
||||||
|
#[diesel(sql_type = Text)]
|
||||||
|
pub enum Source {
|
||||||
|
Metadata,
|
||||||
|
User,
|
||||||
|
Import,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToSql<Text, Sqlite> for Source {
|
||||||
|
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> diesel::serialize::Result {
|
||||||
|
out.set_value(match self {
|
||||||
|
Source::Metadata => "metadata",
|
||||||
|
Source::User => "user",
|
||||||
|
Source::Import => "import",
|
||||||
|
Source::Unknown => "unknown",
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(IsNull::No)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DB> FromSql<Text, DB> for Source
|
||||||
|
where
|
||||||
|
DB: Backend,
|
||||||
|
String: FromSql<Text, DB>,
|
||||||
|
{
|
||||||
|
fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
|
||||||
|
Ok(match String::from_sql(bytes)?.as_str() {
|
||||||
|
"metadata" => Source::Metadata,
|
||||||
|
"user" => Source::User,
|
||||||
|
"import" => Source::Import,
|
||||||
|
_ => Source::Unknown,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ mod imp {
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub recordings_list: TemplateChild<gtk::ListBox>,
|
pub recordings_list: TemplateChild<gtk::ListBox>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
|
pub enable_updates_row: TemplateChild<adw::SwitchRow>,
|
||||||
|
#[template_child]
|
||||||
pub save_row: TemplateChild<adw::ButtonRow>,
|
pub save_row: TemplateChild<adw::ButtonRow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,6 +128,10 @@ impl AlbumEditor {
|
||||||
for recording in &album.recordings {
|
for recording in &album.recordings {
|
||||||
obj.add_recording(recording.to_owned());
|
obj.add_recording(recording.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj.imp()
|
||||||
|
.enable_updates_row
|
||||||
|
.set_active(album.enable_updates);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj
|
obj
|
||||||
|
|
@ -191,10 +197,16 @@ impl AlbumEditor {
|
||||||
.map(|r| r.recording())
|
.map(|r| r.recording())
|
||||||
.collect::<Vec<Recording>>();
|
.collect::<Vec<Recording>>();
|
||||||
|
|
||||||
|
let enable_updates = self.imp().enable_updates_row.is_active();
|
||||||
|
|
||||||
if let Some(album_id) = self.imp().album_id.get() {
|
if let Some(album_id) = self.imp().album_id.get() {
|
||||||
library.update_album(album_id, name, recordings).unwrap();
|
library
|
||||||
|
.update_album(album_id, name, recordings, enable_updates)
|
||||||
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
let album = library.create_album(name, recordings).unwrap();
|
let album = library
|
||||||
|
.create_album(name, recordings, enable_updates)
|
||||||
|
.unwrap();
|
||||||
self.emit_by_name::<()>("created", &[&album]);
|
self.emit_by_name::<()>("created", &[&album]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ mod imp {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
||||||
let set_design_action = gio::ActionEntry::builder("set-design")
|
let set_design_action = gio::ActionEntry::builder("set-design")
|
||||||
.parameter_type(Some(&glib::VariantTy::STRING))
|
.parameter_type(Some(glib::VariantTy::STRING))
|
||||||
.state(glib::Variant::from("program-1"))
|
.state(glib::Variant::from("program-1"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ impl RecordingEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_work(&self, work: Work) {
|
fn set_work(&self, work: Work) {
|
||||||
self.imp().work_row.set_title(&work.name.get());
|
self.imp().work_row.set_title(work.name.get());
|
||||||
self.imp().work_row.set_subtitle(
|
self.imp().work_row.set_subtitle(
|
||||||
&work
|
&work
|
||||||
.composers_string()
|
.composers_string()
|
||||||
|
|
|
||||||
|
|
@ -245,8 +245,7 @@ impl TracksEditor {
|
||||||
.track_rows
|
.track_rows
|
||||||
.borrow()
|
.borrow()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| t.track_data().parts.clone())
|
.flat_map(|t| t.track_data().parts.clone())
|
||||||
.flatten()
|
|
||||||
.collect::<Vec<Work>>()
|
.collect::<Vec<Work>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -390,8 +390,7 @@ impl WorkEditor {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.emit_by_name::<()>("created", &[&part]);
|
self.emit_by_name::<()>("created", &[&part]);
|
||||||
} else {
|
} else if let Some(work_id) = self.imp().work_id.get() {
|
||||||
if let Some(work_id) = self.imp().work_id.get() {
|
|
||||||
library
|
library
|
||||||
.update_work(work_id, name, parts, composers, instruments, enable_updates)
|
.update_work(work_id, name, parts, composers, instruments, enable_updates)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -401,7 +400,6 @@ impl WorkEditor {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.emit_by_name::<()>("created", &[&work]);
|
self.emit_by_name::<()>("created", &[&work]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.imp().navigation.get().unwrap().pop();
|
self.imp().navigation.get().unwrap().pop();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ impl WorkEditorPartRow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_part(&self, part: Work) {
|
fn set_part(&self, part: Work) {
|
||||||
self.set_title(&part.name.get());
|
self.set_title(part.name.get());
|
||||||
|
|
||||||
if !part.parts.is_empty() {
|
if !part.parts.is_empty() {
|
||||||
self.set_subtitle(
|
self.set_subtitle(
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use gtk::{gio, glib, glib::subclass::Signal};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config, library::Library, process::Process, process_manager::ProcessManager,
|
config, db::tables::Source, library::Library, process::Process,
|
||||||
process_row::ProcessRow,
|
process_manager::ProcessManager, process_row::ProcessRow,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
|
|
@ -92,8 +92,8 @@ impl EmptyPage {
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
async fn download_library(&self) {
|
async fn download_library(&self) {
|
||||||
let dialog = adw::AlertDialog::builder()
|
let dialog = adw::AlertDialog::builder()
|
||||||
.heading(&gettext("Disclaimer"))
|
.heading(gettext("Disclaimer"))
|
||||||
.body(&gettext("You are about to download a library of audio files. These are from recordings that are in the public domain under EU law and are hosted on a server within the EU. Please ensure that you comply with the copyright laws of you country."))
|
.body(gettext("You are about to download a library of audio files. These are from recordings that are in the public domain under EU law and are hosted on a server within the EU. Please ensure that you comply with the copyright laws of you country."))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
dialog.add_response("continue", &gettext("Continue"));
|
dialog.add_response("continue", &gettext("Continue"));
|
||||||
|
|
@ -119,7 +119,7 @@ impl EmptyPage {
|
||||||
.library
|
.library
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.import_library_from_url(&url)
|
.import_library_from_url(&url, Source::Metadata)
|
||||||
{
|
{
|
||||||
Ok(receiver) => {
|
Ok(receiver) => {
|
||||||
let process = Process::new(&gettext("Downloading music library"), receiver);
|
let process = Process::new(&gettext("Downloading music library"), receiver);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,13 @@ use chrono::prelude::*;
|
||||||
use diesel::{prelude::*, QueryDsl, SqliteConnection};
|
use diesel::{prelude::*, QueryDsl, SqliteConnection};
|
||||||
|
|
||||||
use super::Library;
|
use super::Library;
|
||||||
use crate::db::{self, models::*, schema::*, tables, TranslatedString};
|
use crate::db::{
|
||||||
|
self,
|
||||||
|
models::*,
|
||||||
|
schema::*,
|
||||||
|
tables::{self, Source},
|
||||||
|
TranslatedString,
|
||||||
|
};
|
||||||
|
|
||||||
impl Library {
|
impl Library {
|
||||||
pub fn create_person(&self, name: TranslatedString, enable_updates: bool) -> Result<Person> {
|
pub fn create_person(&self, name: TranslatedString, enable_updates: bool) -> Result<Person> {
|
||||||
|
|
@ -21,6 +27,7 @@ impl Library {
|
||||||
let person = Person {
|
let person = Person {
|
||||||
person_id: db::generate_id(),
|
person_id: db::generate_id(),
|
||||||
name,
|
name,
|
||||||
|
source: Source::User,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
edited_at: now,
|
edited_at: now,
|
||||||
last_used_at: now,
|
last_used_at: now,
|
||||||
|
|
@ -86,6 +93,7 @@ impl Library {
|
||||||
let instrument = Instrument {
|
let instrument = Instrument {
|
||||||
instrument_id: db::generate_id(),
|
instrument_id: db::generate_id(),
|
||||||
name,
|
name,
|
||||||
|
source: Source::User,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
edited_at: now,
|
edited_at: now,
|
||||||
last_used_at: now,
|
last_used_at: now,
|
||||||
|
|
@ -147,6 +155,7 @@ impl Library {
|
||||||
let role = Role {
|
let role = Role {
|
||||||
role_id: db::generate_id(),
|
role_id: db::generate_id(),
|
||||||
name,
|
name,
|
||||||
|
source: Source::User,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
edited_at: now,
|
edited_at: now,
|
||||||
last_used_at: now,
|
last_used_at: now,
|
||||||
|
|
@ -209,7 +218,7 @@ impl Library {
|
||||||
) -> Result<Work> {
|
) -> Result<Work> {
|
||||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||||
|
|
||||||
let work = self.create_work_priv(
|
let work = Self::create_work_priv(
|
||||||
connection,
|
connection,
|
||||||
name,
|
name,
|
||||||
parts,
|
parts,
|
||||||
|
|
@ -226,7 +235,6 @@ impl Library {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_work_priv(
|
fn create_work_priv(
|
||||||
&self,
|
|
||||||
connection: &mut SqliteConnection,
|
connection: &mut SqliteConnection,
|
||||||
name: TranslatedString,
|
name: TranslatedString,
|
||||||
parts: Vec<Work>,
|
parts: Vec<Work>,
|
||||||
|
|
@ -242,8 +250,9 @@ impl Library {
|
||||||
let work_data = tables::Work {
|
let work_data = tables::Work {
|
||||||
work_id: work_id.clone(),
|
work_id: work_id.clone(),
|
||||||
parent_work_id: parent_work_id.map(|w| w.to_string()),
|
parent_work_id: parent_work_id.map(|w| w.to_string()),
|
||||||
sequence_number: sequence_number,
|
sequence_number,
|
||||||
name,
|
name,
|
||||||
|
source: Source::User,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
edited_at: now,
|
edited_at: now,
|
||||||
last_used_at: now,
|
last_used_at: now,
|
||||||
|
|
@ -256,7 +265,7 @@ impl Library {
|
||||||
.execute(connection)?;
|
.execute(connection)?;
|
||||||
|
|
||||||
for (index, part) in parts.into_iter().enumerate() {
|
for (index, part) in parts.into_iter().enumerate() {
|
||||||
self.create_work_priv(
|
Self::create_work_priv(
|
||||||
connection,
|
connection,
|
||||||
part.name,
|
part.name,
|
||||||
part.parts,
|
part.parts,
|
||||||
|
|
@ -309,7 +318,7 @@ impl Library {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||||
|
|
||||||
self.update_work_priv(
|
Self::update_work_priv(
|
||||||
connection,
|
connection,
|
||||||
work_id,
|
work_id,
|
||||||
name,
|
name,
|
||||||
|
|
@ -327,7 +336,6 @@ impl Library {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_work_priv(
|
fn update_work_priv(
|
||||||
&self,
|
|
||||||
connection: &mut SqliteConnection,
|
connection: &mut SqliteConnection,
|
||||||
work_id: &str,
|
work_id: &str,
|
||||||
name: TranslatedString,
|
name: TranslatedString,
|
||||||
|
|
@ -367,7 +375,7 @@ impl Library {
|
||||||
.optional()?
|
.optional()?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
self.update_work_priv(
|
Self::update_work_priv(
|
||||||
connection,
|
connection,
|
||||||
&part.work_id,
|
&part.work_id,
|
||||||
part.name,
|
part.name,
|
||||||
|
|
@ -381,7 +389,7 @@ impl Library {
|
||||||
} else {
|
} else {
|
||||||
// Note: The previously used ID is discarded. This should be OK, because
|
// Note: The previously used ID is discarded. This should be OK, because
|
||||||
// at this point, the part ID should not have been used anywhere.
|
// at this point, the part ID should not have been used anywhere.
|
||||||
self.create_work_priv(
|
Self::create_work_priv(
|
||||||
connection,
|
connection,
|
||||||
part.name,
|
part.name,
|
||||||
part.parts,
|
part.parts,
|
||||||
|
|
@ -454,6 +462,7 @@ impl Library {
|
||||||
let ensemble_data = tables::Ensemble {
|
let ensemble_data = tables::Ensemble {
|
||||||
ensemble_id: db::generate_id(),
|
ensemble_id: db::generate_id(),
|
||||||
name,
|
name,
|
||||||
|
source: Source::User,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
edited_at: now,
|
edited_at: now,
|
||||||
last_used_at: now,
|
last_used_at: now,
|
||||||
|
|
@ -530,6 +539,7 @@ impl Library {
|
||||||
recording_id: recording_id.clone(),
|
recording_id: recording_id.clone(),
|
||||||
work_id: work.work_id.clone(),
|
work_id: work.work_id.clone(),
|
||||||
year,
|
year,
|
||||||
|
source: Source::User,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
edited_at: now,
|
edited_at: now,
|
||||||
last_used_at: now,
|
last_used_at: now,
|
||||||
|
|
@ -693,6 +703,7 @@ impl Library {
|
||||||
&self,
|
&self,
|
||||||
name: TranslatedString,
|
name: TranslatedString,
|
||||||
recordings: Vec<Recording>,
|
recordings: Vec<Recording>,
|
||||||
|
enable_updates: bool,
|
||||||
) -> Result<Album> {
|
) -> Result<Album> {
|
||||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||||
|
|
||||||
|
|
@ -702,6 +713,8 @@ impl Library {
|
||||||
let album_data = tables::Album {
|
let album_data = tables::Album {
|
||||||
album_id: album_id.clone(),
|
album_id: album_id.clone(),
|
||||||
name,
|
name,
|
||||||
|
source: Source::User,
|
||||||
|
enable_updates,
|
||||||
created_at: now,
|
created_at: now,
|
||||||
edited_at: now,
|
edited_at: now,
|
||||||
last_used_at: now,
|
last_used_at: now,
|
||||||
|
|
@ -736,6 +749,7 @@ impl Library {
|
||||||
album_id: &str,
|
album_id: &str,
|
||||||
name: TranslatedString,
|
name: TranslatedString,
|
||||||
recordings: Vec<Recording>,
|
recordings: Vec<Recording>,
|
||||||
|
enable_updates: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||||
|
|
||||||
|
|
@ -745,6 +759,7 @@ impl Library {
|
||||||
.filter(albums::album_id.eq(album_id))
|
.filter(albums::album_id.eq(album_id))
|
||||||
.set((
|
.set((
|
||||||
albums::name.eq(name),
|
albums::name.eq(name),
|
||||||
|
albums::enable_updates.eq(enable_updates),
|
||||||
albums::edited_at.eq(now),
|
albums::edited_at.eq(now),
|
||||||
albums::last_used_at.eq(now),
|
albums::last_used_at.eq(now),
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,11 @@ use zip::{write::SimpleFileOptions, ZipWriter};
|
||||||
|
|
||||||
use super::Library;
|
use super::Library;
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{self, schema::*, tables},
|
db::{
|
||||||
|
self,
|
||||||
|
schema::*,
|
||||||
|
tables::{self, Source},
|
||||||
|
},
|
||||||
process::ProcessMsg,
|
process::ProcessMsg,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -28,17 +32,27 @@ impl Library {
|
||||||
pub fn import_library_from_zip(
|
pub fn import_library_from_zip(
|
||||||
&self,
|
&self,
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
|
source: Source,
|
||||||
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
||||||
log::info!("Importing library from ZIP at {}", path.as_ref().to_string_lossy());
|
log::info!(
|
||||||
|
"Importing library from ZIP at {}",
|
||||||
|
path.as_ref().to_string_lossy()
|
||||||
|
);
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
let library_folder = PathBuf::from(&self.folder());
|
let library_folder = PathBuf::from(&self.folder());
|
||||||
let this_connection = self.imp().connection.get().unwrap().clone();
|
let this_connection = self.imp().connection.get().unwrap().clone();
|
||||||
|
|
||||||
let (sender, receiver) = async_channel::unbounded::<ProcessMsg>();
|
let (sender, receiver) = async_channel::unbounded::<ProcessMsg>();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
if let Err(err) = sender.send_blocking(ProcessMsg::Result(
|
if let Err(err) =
|
||||||
import_library_from_zip_priv(path, library_folder, this_connection, &sender),
|
sender.send_blocking(ProcessMsg::Result(import_library_from_zip_priv(
|
||||||
)) {
|
path,
|
||||||
|
library_folder,
|
||||||
|
source,
|
||||||
|
this_connection,
|
||||||
|
&sender,
|
||||||
|
)))
|
||||||
|
{
|
||||||
log::error!("Failed to send library action result: {err:?}");
|
log::error!("Failed to send library action result: {err:?}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -52,7 +66,10 @@ impl Library {
|
||||||
&self,
|
&self,
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
||||||
log::info!("Exporting library to ZIP at {}", path.as_ref().to_string_lossy());
|
log::info!(
|
||||||
|
"Exporting library to ZIP at {}",
|
||||||
|
path.as_ref().to_string_lossy()
|
||||||
|
);
|
||||||
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap();
|
||||||
|
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
|
|
@ -78,6 +95,7 @@ impl Library {
|
||||||
pub fn import_library_from_url(
|
pub fn import_library_from_url(
|
||||||
&self,
|
&self,
|
||||||
url: &str,
|
url: &str,
|
||||||
|
source: Source,
|
||||||
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
||||||
log::info!("Importing library from URL {url}");
|
log::info!("Importing library from URL {url}");
|
||||||
let url = url.to_owned();
|
let url = url.to_owned();
|
||||||
|
|
@ -88,7 +106,7 @@ impl Library {
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
if let Err(err) = sender.send_blocking(ProcessMsg::Result(
|
if let Err(err) = sender.send_blocking(ProcessMsg::Result(
|
||||||
import_library_from_url_priv(url, library_folder, this_connection, &sender),
|
import_library_from_url_priv(url, library_folder, source, this_connection, &sender),
|
||||||
)) {
|
)) {
|
||||||
log::error!("Failed to send library action result: {err:?}");
|
log::error!("Failed to send library action result: {err:?}");
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +119,7 @@ impl Library {
|
||||||
pub fn import_metadata_from_url(
|
pub fn import_metadata_from_url(
|
||||||
&self,
|
&self,
|
||||||
url: &str,
|
url: &str,
|
||||||
|
source: Source,
|
||||||
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
) -> Result<async_channel::Receiver<ProcessMsg>> {
|
||||||
log::info!("Importing metadata from URL {url}");
|
log::info!("Importing metadata from URL {url}");
|
||||||
|
|
||||||
|
|
@ -111,7 +130,7 @@ impl Library {
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
if let Err(err) = sender.send_blocking(ProcessMsg::Result(
|
if let Err(err) = sender.send_blocking(ProcessMsg::Result(
|
||||||
import_metadata_from_url_priv(url, this_connection, &sender),
|
import_metadata_from_url_priv(url, source, this_connection, &sender),
|
||||||
)) {
|
)) {
|
||||||
log::error!("Failed to send library action result: {err:?}");
|
log::error!("Failed to send library action result: {err:?}");
|
||||||
}
|
}
|
||||||
|
|
@ -125,6 +144,7 @@ impl Library {
|
||||||
fn import_library_from_zip_priv(
|
fn import_library_from_zip_priv(
|
||||||
zip_path: impl AsRef<Path>,
|
zip_path: impl AsRef<Path>,
|
||||||
library_folder: impl AsRef<Path>,
|
library_folder: impl AsRef<Path>,
|
||||||
|
source: Source,
|
||||||
this_connection: Arc<Mutex<SqliteConnection>>,
|
this_connection: Arc<Mutex<SqliteConnection>>,
|
||||||
sender: &async_channel::Sender<ProcessMsg>,
|
sender: &async_channel::Sender<ProcessMsg>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
|
@ -138,7 +158,7 @@ fn import_library_from_zip_priv(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Import metadata.
|
// Import metadata.
|
||||||
let tracks = import_metadata_from_file(tmp_db_file.path(), this_connection, false)?;
|
let tracks = import_metadata_from_file(tmp_db_file.path(), source, this_connection, false)?;
|
||||||
|
|
||||||
// Import audio files.
|
// Import audio files.
|
||||||
let n_tracks = tracks.len();
|
let n_tracks = tracks.len();
|
||||||
|
|
@ -212,6 +232,7 @@ fn add_file_to_zip(
|
||||||
|
|
||||||
fn import_metadata_from_url_priv(
|
fn import_metadata_from_url_priv(
|
||||||
url: String,
|
url: String,
|
||||||
|
source: Source,
|
||||||
this_connection: Arc<Mutex<SqliteConnection>>,
|
this_connection: Arc<Mutex<SqliteConnection>>,
|
||||||
sender: &async_channel::Sender<ProcessMsg>,
|
sender: &async_channel::Sender<ProcessMsg>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
|
@ -223,20 +244,18 @@ fn import_metadata_from_url_priv(
|
||||||
formatx!(gettext("Downloading {}"), &url).unwrap(),
|
formatx!(gettext("Downloading {}"), &url).unwrap(),
|
||||||
));
|
));
|
||||||
|
|
||||||
match runtime.block_on(download_tmp_file(&url, &sender)) {
|
match runtime.block_on(download_tmp_file(&url, sender)) {
|
||||||
Ok(db_file) => {
|
Ok(db_file) => {
|
||||||
let _ = sender.send_blocking(ProcessMsg::Message(
|
let _ = sender.send_blocking(ProcessMsg::Message(
|
||||||
formatx!(gettext("Importing downloaded library"), &url).unwrap(),
|
formatx!(gettext("Importing downloaded library"), &url).unwrap(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let _ = sender.send_blocking(ProcessMsg::Result(
|
let _ = sender.send_blocking(ProcessMsg::Result(
|
||||||
import_metadata_from_file(db_file.path(), this_connection, true).and_then(
|
import_metadata_from_file(db_file.path(), source, this_connection, true).map(
|
||||||
|tracks| {
|
|tracks| {
|
||||||
if !tracks.is_empty() {
|
if !tracks.is_empty() {
|
||||||
log::warn!("The metadata file at {url} contains tracks.");
|
log::warn!("The metadata file at {url} contains tracks.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
@ -252,6 +271,7 @@ fn import_metadata_from_url_priv(
|
||||||
fn import_library_from_url_priv(
|
fn import_library_from_url_priv(
|
||||||
url: String,
|
url: String,
|
||||||
library_folder: impl AsRef<Path>,
|
library_folder: impl AsRef<Path>,
|
||||||
|
source: Source,
|
||||||
this_connection: Arc<Mutex<SqliteConnection>>,
|
this_connection: Arc<Mutex<SqliteConnection>>,
|
||||||
sender: &async_channel::Sender<ProcessMsg>,
|
sender: &async_channel::Sender<ProcessMsg>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
|
@ -263,7 +283,7 @@ fn import_library_from_url_priv(
|
||||||
formatx!(gettext("Downloading {}"), &url).unwrap(),
|
formatx!(gettext("Downloading {}"), &url).unwrap(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let archive_file = runtime.block_on(download_tmp_file(&url, &sender));
|
let archive_file = runtime.block_on(download_tmp_file(&url, sender));
|
||||||
|
|
||||||
match archive_file {
|
match archive_file {
|
||||||
Ok(archive_file) => {
|
Ok(archive_file) => {
|
||||||
|
|
@ -274,8 +294,9 @@ fn import_library_from_url_priv(
|
||||||
let _ = sender.send_blocking(ProcessMsg::Result(import_library_from_zip_priv(
|
let _ = sender.send_blocking(ProcessMsg::Result(import_library_from_zip_priv(
|
||||||
archive_file.path(),
|
archive_file.path(),
|
||||||
library_folder,
|
library_folder,
|
||||||
|
source,
|
||||||
this_connection,
|
this_connection,
|
||||||
&sender,
|
sender,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -293,6 +314,7 @@ fn import_library_from_url_priv(
|
||||||
/// In any case, tracks are returned.
|
/// In any case, tracks are returned.
|
||||||
fn import_metadata_from_file(
|
fn import_metadata_from_file(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
|
source: Source,
|
||||||
this_connection: Arc<Mutex<SqliteConnection>>,
|
this_connection: Arc<Mutex<SqliteConnection>>,
|
||||||
ignore_tracks: bool,
|
ignore_tracks: bool,
|
||||||
) -> Result<Vec<tables::Track>> {
|
) -> Result<Vec<tables::Track>> {
|
||||||
|
|
@ -327,6 +349,7 @@ fn import_metadata_from_file(
|
||||||
// Import metadata that is not already present.
|
// Import metadata that is not already present.
|
||||||
|
|
||||||
for mut person in persons {
|
for mut person in persons {
|
||||||
|
person.source = source;
|
||||||
person.created_at = now;
|
person.created_at = now;
|
||||||
person.edited_at = now;
|
person.edited_at = now;
|
||||||
person.last_used_at = now;
|
person.last_used_at = now;
|
||||||
|
|
@ -339,6 +362,7 @@ fn import_metadata_from_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut role in roles {
|
for mut role in roles {
|
||||||
|
role.source = source;
|
||||||
role.created_at = now;
|
role.created_at = now;
|
||||||
role.edited_at = now;
|
role.edited_at = now;
|
||||||
role.last_used_at = now;
|
role.last_used_at = now;
|
||||||
|
|
@ -350,6 +374,7 @@ fn import_metadata_from_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut instrument in instruments {
|
for mut instrument in instruments {
|
||||||
|
instrument.source = source;
|
||||||
instrument.created_at = now;
|
instrument.created_at = now;
|
||||||
instrument.edited_at = now;
|
instrument.edited_at = now;
|
||||||
instrument.last_used_at = now;
|
instrument.last_used_at = now;
|
||||||
|
|
@ -362,6 +387,7 @@ fn import_metadata_from_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut work in works {
|
for mut work in works {
|
||||||
|
work.source = source;
|
||||||
work.created_at = now;
|
work.created_at = now;
|
||||||
work.edited_at = now;
|
work.edited_at = now;
|
||||||
work.last_used_at = now;
|
work.last_used_at = now;
|
||||||
|
|
@ -388,6 +414,7 @@ fn import_metadata_from_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut ensemble in ensembles {
|
for mut ensemble in ensembles {
|
||||||
|
ensemble.source = source;
|
||||||
ensemble.created_at = now;
|
ensemble.created_at = now;
|
||||||
ensemble.edited_at = now;
|
ensemble.edited_at = now;
|
||||||
ensemble.last_used_at = now;
|
ensemble.last_used_at = now;
|
||||||
|
|
@ -407,6 +434,7 @@ fn import_metadata_from_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut recording in recordings {
|
for mut recording in recordings {
|
||||||
|
recording.source = source;
|
||||||
recording.created_at = now;
|
recording.created_at = now;
|
||||||
recording.edited_at = now;
|
recording.edited_at = now;
|
||||||
recording.last_used_at = now;
|
recording.last_used_at = now;
|
||||||
|
|
@ -466,6 +494,7 @@ fn import_metadata_from_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut album in albums {
|
for mut album in albums {
|
||||||
|
album.source = source;
|
||||||
album.created_at = now;
|
album.created_at = now;
|
||||||
album.edited_at = now;
|
album.edited_at = now;
|
||||||
album.last_used_at = now;
|
album.last_used_at = now;
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,6 @@ impl Library {
|
||||||
works,
|
works,
|
||||||
recordings,
|
recordings,
|
||||||
albums,
|
albums,
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LibraryQuery {
|
LibraryQuery {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ use gtk::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config, library::Library, process::Process, process_manager::ProcessManager,
|
config, db::tables::Source, library::Library, process::Process,
|
||||||
process_row::ProcessRow, window::Window,
|
process_manager::ProcessManager, process_row::ProcessRow, window::Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
|
|
@ -128,7 +128,13 @@ impl LibraryManager {
|
||||||
}
|
}
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
if let Some(path) = path.path() {
|
if let Some(path) = path.path() {
|
||||||
match self.imp().library.get().unwrap().import_library_from_zip(&path) {
|
match self
|
||||||
|
.imp()
|
||||||
|
.library
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.import_library_from_zip(&path, Source::Import)
|
||||||
|
{
|
||||||
Ok(receiver) => {
|
Ok(receiver) => {
|
||||||
let process = Process::new(
|
let process = Process::new(
|
||||||
&formatx!(
|
&formatx!(
|
||||||
|
|
@ -186,7 +192,13 @@ impl LibraryManager {
|
||||||
}
|
}
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
if let Some(path) = path.path() {
|
if let Some(path) = path.path() {
|
||||||
match self.imp().library.get().unwrap().export_library_to_zip(&path) {
|
match self
|
||||||
|
.imp()
|
||||||
|
.library
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.export_library_to_zip(&path)
|
||||||
|
{
|
||||||
Ok(receiver) => {
|
Ok(receiver) => {
|
||||||
let process = Process::new(
|
let process = Process::new(
|
||||||
&formatx!(
|
&formatx!(
|
||||||
|
|
@ -228,7 +240,7 @@ impl LibraryManager {
|
||||||
.library
|
.library
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.import_metadata_from_url(&url)
|
.import_metadata_from_url(&url, Source::Metadata)
|
||||||
{
|
{
|
||||||
Ok(receiver) => {
|
Ok(receiver) => {
|
||||||
let process = Process::new(&gettext("Updating metadata"), receiver);
|
let process = Process::new(&gettext("Updating metadata"), receiver);
|
||||||
|
|
@ -259,7 +271,7 @@ impl LibraryManager {
|
||||||
.library
|
.library
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.import_library_from_url(&url)
|
.import_library_from_url(&url, Source::Metadata)
|
||||||
{
|
{
|
||||||
Ok(receiver) => {
|
Ok(receiver) => {
|
||||||
let process = Process::new(&gettext("Updating music library"), receiver);
|
let process = Process::new(&gettext("Updating music library"), receiver);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ mod program;
|
||||||
mod program_tile;
|
mod program_tile;
|
||||||
mod recording_tile;
|
mod recording_tile;
|
||||||
mod search_page;
|
mod search_page;
|
||||||
mod search_tag;
|
|
||||||
mod selector;
|
mod selector;
|
||||||
mod slider_row;
|
mod slider_row;
|
||||||
mod tag_tile;
|
mod tag_tile;
|
||||||
|
|
@ -47,7 +46,7 @@ fn main() -> glib::ExitCode {
|
||||||
gettextrs::textdomain(config::PKGNAME).unwrap();
|
gettextrs::textdomain(config::PKGNAME).unwrap();
|
||||||
|
|
||||||
gio::resources_register(
|
gio::resources_register(
|
||||||
&gio::Resource::load(&format!(
|
&gio::Resource::load(format!(
|
||||||
"{}/{}/{}.gresource",
|
"{}/{}/{}.gresource",
|
||||||
config::DATADIR,
|
config::DATADIR,
|
||||||
config::PKGNAME,
|
config::PKGNAME,
|
||||||
|
|
|
||||||
|
|
@ -221,14 +221,14 @@ impl Player {
|
||||||
items.push(PlaylistItem::new(
|
items.push(PlaylistItem::new(
|
||||||
true,
|
true,
|
||||||
recording.work.composers_string(),
|
recording.work.composers_string(),
|
||||||
&recording.work.name.get(),
|
recording.work.name.get(),
|
||||||
Some(&performances),
|
Some(&performances),
|
||||||
None,
|
None,
|
||||||
&self.library_path_to_file_path(&tracks[0].path),
|
self.library_path_to_file_path(&tracks[0].path),
|
||||||
&tracks[0].track_id,
|
&tracks[0].track_id,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
let mut tracks = tracks.into_iter();
|
let mut tracks = tracks.iter();
|
||||||
let first_track = tracks.next().unwrap();
|
let first_track = tracks.next().unwrap();
|
||||||
|
|
||||||
let track_title = |track: &Track, number: usize| -> String {
|
let track_title = |track: &Track, number: usize| -> String {
|
||||||
|
|
@ -249,10 +249,10 @@ impl Player {
|
||||||
items.push(PlaylistItem::new(
|
items.push(PlaylistItem::new(
|
||||||
true,
|
true,
|
||||||
recording.work.composers_string(),
|
recording.work.composers_string(),
|
||||||
&recording.work.name.get(),
|
recording.work.name.get(),
|
||||||
Some(&performances),
|
Some(&performances),
|
||||||
Some(&track_title(&first_track, 1)),
|
Some(&track_title(first_track, 1)),
|
||||||
&self.library_path_to_file_path(&first_track.path),
|
self.library_path_to_file_path(&first_track.path),
|
||||||
&first_track.track_id,
|
&first_track.track_id,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -260,11 +260,11 @@ impl Player {
|
||||||
items.push(PlaylistItem::new(
|
items.push(PlaylistItem::new(
|
||||||
false,
|
false,
|
||||||
recording.work.composers_string(),
|
recording.work.composers_string(),
|
||||||
&recording.work.name.get(),
|
recording.work.name.get(),
|
||||||
Some(&performances),
|
Some(&performances),
|
||||||
// track number = track index + 1 (first track) + 1 (zero based)
|
// track number = track index + 1 (first track) + 1 (zero based)
|
||||||
Some(&track_title(&track, index + 2)),
|
Some(&track_title(track, index + 2)),
|
||||||
&self.library_path_to_file_path(&track.path),
|
self.library_path_to_file_path(&track.path),
|
||||||
&track.track_id,
|
&track.track_id,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ impl Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_query(query: LibraryQuery) -> Self {
|
pub fn from_query(query: LibraryQuery) -> Self {
|
||||||
let settings = gio::Settings::new(&config::APP_ID);
|
let settings = gio::Settings::new(config::APP_ID);
|
||||||
|
|
||||||
glib::Object::builder()
|
glib::Object::builder()
|
||||||
.property(
|
.property(
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ mod imp {
|
||||||
self.set_program_from_settings(&settings);
|
self.set_program_from_settings(&settings);
|
||||||
|
|
||||||
let obj = self.obj().to_owned();
|
let obj = self.obj().to_owned();
|
||||||
settings.connect_changed(Some(&self.key.get().unwrap()), move |settings, _| {
|
settings.connect_changed(Some(self.key.get().unwrap()), move |settings, _| {
|
||||||
obj.imp().set_program_from_settings(settings);
|
obj.imp().set_program_from_settings(settings);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ mod imp {
|
||||||
.push(&RecordingEditor::new(
|
.push(&RecordingEditor::new(
|
||||||
obj.imp().navigation.get().unwrap(),
|
obj.imp().navigation.get().unwrap(),
|
||||||
obj.imp().library.get().unwrap(),
|
obj.imp().library.get().unwrap(),
|
||||||
Some(&obj.imp().recording.get().unwrap()),
|
Some(obj.imp().recording.get().unwrap()),
|
||||||
));
|
));
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -90,8 +90,8 @@ mod imp {
|
||||||
let delete_action = gio::ActionEntry::builder("delete")
|
let delete_action = gio::ActionEntry::builder("delete")
|
||||||
.activate(move |_, _, _| {
|
.activate(move |_, _, _| {
|
||||||
let dialog = adw::AlertDialog::builder()
|
let dialog = adw::AlertDialog::builder()
|
||||||
.heading(&gettext("Delete recording?"))
|
.heading(gettext("Delete recording?"))
|
||||||
.body(&gettext("The recording will be removed from your music library and the corresponding audio files will be deleted. This action cannot be undone."))
|
.body(gettext("The recording will be removed from your music library and the corresponding audio files will be deleted. This action cannot be undone."))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
dialog.add_response("delete", &gettext("Delete"));
|
dialog.add_response("delete", &gettext("Delete"));
|
||||||
|
|
@ -142,7 +142,7 @@ impl RecordingTile {
|
||||||
let obj: Self = glib::Object::new();
|
let obj: Self = glib::Object::new();
|
||||||
let imp = obj.imp();
|
let imp = obj.imp();
|
||||||
|
|
||||||
imp.work_label.set_label(&recording.work.name.get());
|
imp.work_label.set_label(recording.work.name.get());
|
||||||
imp.composer_label.set_label(
|
imp.composer_label.set_label(
|
||||||
&recording
|
&recording
|
||||||
.work
|
.work
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ use crate::{
|
||||||
program::Program,
|
program::Program,
|
||||||
program_tile::ProgramTile,
|
program_tile::ProgramTile,
|
||||||
recording_tile::RecordingTile,
|
recording_tile::RecordingTile,
|
||||||
search_tag::Tag,
|
tag_tile::{Tag, TagTile},
|
||||||
tag_tile::TagTile,
|
|
||||||
util,
|
util,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -392,7 +391,7 @@ impl SearchPage {
|
||||||
imp.header_box.set_visible(!query.is_empty());
|
imp.header_box.set_visible(!query.is_empty());
|
||||||
|
|
||||||
let highlight = if let Some(work) = &query.work {
|
let highlight = if let Some(work) = &query.work {
|
||||||
imp.title_label.set_text(&work.name.get());
|
imp.title_label.set_text(work.name.get());
|
||||||
if let Some(composers) = work.composers_string() {
|
if let Some(composers) = work.composers_string() {
|
||||||
imp.subtitle_label.set_text(&composers);
|
imp.subtitle_label.set_text(&composers);
|
||||||
imp.subtitle_label.set_visible(true);
|
imp.subtitle_label.set_visible(true);
|
||||||
|
|
@ -401,15 +400,15 @@ impl SearchPage {
|
||||||
}
|
}
|
||||||
Some(Tag::Work(work.to_owned()))
|
Some(Tag::Work(work.to_owned()))
|
||||||
} else if let Some(person) = &query.composer {
|
} else if let Some(person) = &query.composer {
|
||||||
imp.title_label.set_text(&person.name.get());
|
imp.title_label.set_text(person.name.get());
|
||||||
imp.subtitle_label.set_visible(false);
|
imp.subtitle_label.set_visible(false);
|
||||||
Some(Tag::Composer(person.to_owned()))
|
Some(Tag::Composer(person.to_owned()))
|
||||||
} else if let Some(person) = &query.performer {
|
} else if let Some(person) = &query.performer {
|
||||||
imp.title_label.set_text(&person.name.get());
|
imp.title_label.set_text(person.name.get());
|
||||||
imp.subtitle_label.set_visible(false);
|
imp.subtitle_label.set_visible(false);
|
||||||
Some(Tag::Performer(person.to_owned()))
|
Some(Tag::Performer(person.to_owned()))
|
||||||
} else if let Some(ensemble) = &query.ensemble {
|
} else if let Some(ensemble) = &query.ensemble {
|
||||||
imp.title_label.set_text(&ensemble.name.get());
|
imp.title_label.set_text(ensemble.name.get());
|
||||||
imp.subtitle_label.set_visible(false);
|
imp.subtitle_label.set_visible(false);
|
||||||
Some(Tag::Ensemble(ensemble.to_owned()))
|
Some(Tag::Ensemble(ensemble.to_owned()))
|
||||||
} else if let Some(instrument) = &query.instrument {
|
} else if let Some(instrument) = &query.instrument {
|
||||||
|
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
use std::cell::OnceCell;
|
|
||||||
|
|
||||||
use adw::{glib, glib::subclass::Signal, prelude::*, subclass::prelude::*};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
|
|
||||||
use crate::db::models::{Ensemble, Instrument, Person, Work};
|
|
||||||
|
|
||||||
mod imp {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
|
||||||
#[template(file = "data/ui/search_tag.blp")]
|
|
||||||
pub struct SearchTag {
|
|
||||||
#[template_child]
|
|
||||||
pub label: TemplateChild<gtk::Label>,
|
|
||||||
pub tag: OnceCell<Tag>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[glib::object_subclass]
|
|
||||||
impl ObjectSubclass for SearchTag {
|
|
||||||
const NAME: &'static str = "MusicusSearchTag";
|
|
||||||
type Type = super::SearchTag;
|
|
||||||
type ParentType = gtk::Box;
|
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.bind_template();
|
|
||||||
klass.bind_template_instance_callbacks();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
|
||||||
obj.init_template();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for SearchTag {
|
|
||||||
fn signals() -> &'static [Signal] {
|
|
||||||
static SIGNALS: Lazy<Vec<Signal>> =
|
|
||||||
Lazy::new(|| vec![Signal::builder("remove").build()]);
|
|
||||||
|
|
||||||
SIGNALS.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WidgetImpl for SearchTag {}
|
|
||||||
impl BoxImpl for SearchTag {}
|
|
||||||
}
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct SearchTag(ObjectSubclass<imp::SearchTag>)
|
|
||||||
@extends gtk::Widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gtk::template_callbacks]
|
|
||||||
impl SearchTag {
|
|
||||||
pub fn new(tag: Tag) -> Self {
|
|
||||||
let obj: SearchTag = glib::Object::new();
|
|
||||||
|
|
||||||
let label = match &tag {
|
|
||||||
Tag::Composer(person) => person.name.get(),
|
|
||||||
Tag::Performer(person) => person.name.get(),
|
|
||||||
Tag::Ensemble(ensemble) => ensemble.name.get(),
|
|
||||||
Tag::Instrument(instrument) => instrument.name.get(),
|
|
||||||
Tag::Work(work) => work.name.get(),
|
|
||||||
};
|
|
||||||
|
|
||||||
obj.imp().label.set_label(label);
|
|
||||||
obj.set_tooltip_text(Some(label));
|
|
||||||
obj.imp().tag.set(tag).unwrap();
|
|
||||||
|
|
||||||
obj
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
|
||||||
self.connect_local("remove", true, move |values| {
|
|
||||||
let obj = values[0].get::<Self>().unwrap();
|
|
||||||
f(&obj);
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tag(&self) -> &Tag {
|
|
||||||
self.imp().tag.get().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
|
||||||
fn remove(&self) {
|
|
||||||
self.emit_by_name::<()>("remove", &[]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Tag {
|
|
||||||
Composer(Person),
|
|
||||||
Performer(Person),
|
|
||||||
Ensemble(Ensemble),
|
|
||||||
Instrument(Instrument),
|
|
||||||
Work(Work),
|
|
||||||
}
|
|
||||||
|
|
@ -294,7 +294,7 @@ impl RecordingSelectorPopover {
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
|
|
||||||
row.set_tooltip_text(Some(&work.name.get()));
|
row.set_tooltip_text(Some(work.name.get()));
|
||||||
|
|
||||||
let work = work.clone();
|
let work = work.clone();
|
||||||
let obj = self.clone();
|
let obj = self.clone();
|
||||||
|
|
|
||||||
|
|
@ -256,7 +256,7 @@ impl WorkSelectorPopover {
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
|
|
||||||
row.set_tooltip_text(Some(&work.name.get()));
|
row.set_tooltip_text(Some(work.name.get()));
|
||||||
|
|
||||||
let work = work.clone();
|
let work = work.clone();
|
||||||
let obj = self.clone();
|
let obj = self.clone();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::cell::OnceCell;
|
||||||
|
|
||||||
use gtk::{glib, prelude::*, subclass::prelude::*};
|
use gtk::{glib, prelude::*, subclass::prelude::*};
|
||||||
|
|
||||||
use crate::search_tag::Tag;
|
use crate::db::models::{Ensemble, Instrument, Person, Work};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -78,3 +78,12 @@ impl TagTile {
|
||||||
self.imp().tag.get().unwrap()
|
self.imp().tag.get().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Tag {
|
||||||
|
Composer(Person),
|
||||||
|
Performer(Person),
|
||||||
|
Ensemble(Ensemble),
|
||||||
|
Instrument(Instrument),
|
||||||
|
Work(Work),
|
||||||
|
}
|
||||||
|
|
|
||||||
13
src/util.rs
13
src/util.rs
|
|
@ -2,15 +2,15 @@ pub mod activatable_row;
|
||||||
pub mod drag_widget;
|
pub mod drag_widget;
|
||||||
pub mod error_dialog;
|
pub mod error_dialog;
|
||||||
|
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use gettextrs::gettext;
|
use gettextrs::gettext;
|
||||||
use gtk::glib::{self, clone};
|
use gtk::glib::{self, clone};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use error_dialog::ErrorDialog;
|
use error_dialog::ErrorDialog;
|
||||||
|
|
||||||
lazy_static! {
|
/// The user's language code.
|
||||||
/// The user's language code.
|
pub static LANG: LazyLock<String> = LazyLock::new(|| {
|
||||||
pub static ref LANG: String = {
|
|
||||||
let lang = match glib::language_names().first() {
|
let lang = match glib::language_names().first() {
|
||||||
Some(language_name) => match language_name.split('_').next() {
|
Some(language_name) => match language_name.split('_').next() {
|
||||||
Some(lang) => lang.to_string(),
|
Some(lang) => lang.to_string(),
|
||||||
|
|
@ -21,15 +21,14 @@ lazy_static! {
|
||||||
|
|
||||||
log::info!("Intialized user language to '{lang}'.");
|
log::info!("Intialized user language to '{lang}'.");
|
||||||
lang
|
lang
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
/// Create and show an error toast. This will also log the error to the console.
|
/// Create and show an error toast. This will also log the error to the console.
|
||||||
pub fn error_toast(msgid: &str, err: anyhow::Error, toast_overlay: &adw::ToastOverlay) {
|
pub fn error_toast(msgid: &str, err: anyhow::Error, toast_overlay: &adw::ToastOverlay) {
|
||||||
log::error!("{msgid}: {err:?}");
|
log::error!("{msgid}: {err:?}");
|
||||||
|
|
||||||
let toast = adw::Toast::builder()
|
let toast = adw::Toast::builder()
|
||||||
.title(&gettext(msgid))
|
.title(gettext(msgid))
|
||||||
.button_label("Details")
|
.button_label("Details")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ glib::wrapper! {
|
||||||
impl ErrorDialog {
|
impl ErrorDialog {
|
||||||
pub fn present(err: &anyhow::Error, parent: &impl IsA<gtk::Widget>) {
|
pub fn present(err: &anyhow::Error, parent: &impl IsA<gtk::Widget>) {
|
||||||
let obj: Self = glib::Object::builder()
|
let obj: Self = glib::Object::builder()
|
||||||
.property("error-text", &format!("{err:?}"))
|
.property("error-text", format!("{err:?}"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
obj.present(Some(parent));
|
obj.present(Some(parent));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
use std::{cell::RefCell, path::Path};
|
use std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
use adw::{prelude::*, subclass::prelude::*};
|
use adw::{prelude::*, subclass::prelude::*};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
@ -6,8 +9,10 @@ use gettextrs::gettext;
|
||||||
use gtk::{gio, glib, glib::clone};
|
use gtk::{gio, glib, glib::clone};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
album_page::AlbumPage,
|
||||||
config,
|
config,
|
||||||
editor::tracks::TracksEditor,
|
db::tables::Source,
|
||||||
|
editor::{album::AlbumEditor, tracks::TracksEditor},
|
||||||
empty_page::EmptyPage,
|
empty_page::EmptyPage,
|
||||||
library::{Library, LibraryQuery},
|
library::{Library, LibraryQuery},
|
||||||
library_manager::LibraryManager,
|
library_manager::LibraryManager,
|
||||||
|
|
@ -31,6 +36,7 @@ mod imp {
|
||||||
pub library: RefCell<Option<Library>>,
|
pub library: RefCell<Option<Library>>,
|
||||||
pub player: Player,
|
pub player: Player,
|
||||||
pub process_manager: ProcessManager,
|
pub process_manager: ProcessManager,
|
||||||
|
pub inhibitor_cookie: Cell<Option<u32>>,
|
||||||
|
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub toast_overlay: TemplateChild<adw::ToastOverlay>,
|
pub toast_overlay: TemplateChild<adw::ToastOverlay>,
|
||||||
|
|
@ -83,6 +89,16 @@ mod imp {
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
let obj = self.obj().to_owned();
|
||||||
|
let create_album_action = gio::ActionEntry::builder("create-album")
|
||||||
|
.activate(move |_, _, _| {
|
||||||
|
if let Some(library) = &*obj.imp().library.borrow() {
|
||||||
|
let editor = AlbumEditor::new(&obj.imp().navigation_view, library, None);
|
||||||
|
obj.imp().navigation_view.push(&editor);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
let obj = self.obj().to_owned();
|
let obj = self.obj().to_owned();
|
||||||
let library_action = gio::ActionEntry::builder("library")
|
let library_action = gio::ActionEntry::builder("library")
|
||||||
.activate(move |_, _, _| {
|
.activate(move |_, _, _| {
|
||||||
|
|
@ -104,8 +120,12 @@ mod imp {
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
self.obj()
|
self.obj().add_action_entries([
|
||||||
.add_action_entries([import_action, library_action, preferences_action]);
|
import_action,
|
||||||
|
create_album_action,
|
||||||
|
library_action,
|
||||||
|
preferences_action,
|
||||||
|
]);
|
||||||
|
|
||||||
let player_bar = PlayerBar::new(&self.player);
|
let player_bar = PlayerBar::new(&self.player);
|
||||||
self.player_bar_revealer.set_child(Some(&player_bar));
|
self.player_bar_revealer.set_child(Some(&player_bar));
|
||||||
|
|
@ -148,6 +168,25 @@ mod imp {
|
||||||
let obj = self.obj().to_owned();
|
let obj = self.obj().to_owned();
|
||||||
self.player.connect_raise(move |_| obj.present());
|
self.player.connect_raise(move |_| obj.present());
|
||||||
|
|
||||||
|
let obj = self.obj().to_owned();
|
||||||
|
self.player.connect_playing_notify(move |player| {
|
||||||
|
if let Some(app) = obj.application() {
|
||||||
|
if let Some(cookie) = obj.imp().inhibitor_cookie.take() {
|
||||||
|
app.uninhibit(cookie);
|
||||||
|
};
|
||||||
|
|
||||||
|
if player.playing() {
|
||||||
|
let cookie = app.inhibit(
|
||||||
|
Some(&obj),
|
||||||
|
gtk::ApplicationInhibitFlags::SUSPEND,
|
||||||
|
Some(&gettext("Currently playing music")),
|
||||||
|
);
|
||||||
|
|
||||||
|
obj.imp().inhibitor_cookie.set(Some(cookie));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let settings = gio::Settings::new(config::APP_ID);
|
let settings = gio::Settings::new(config::APP_ID);
|
||||||
let library_path = settings.string("library-path").to_string();
|
let library_path = settings.string("library-path").to_string();
|
||||||
if !library_path.is_empty() {
|
if !library_path.is_empty() {
|
||||||
|
|
@ -164,8 +203,8 @@ mod imp {
|
||||||
fn close_request(&self) -> glib::signal::Propagation {
|
fn close_request(&self) -> glib::signal::Propagation {
|
||||||
if self.process_manager.any_ongoing() {
|
if self.process_manager.any_ongoing() {
|
||||||
let dialog = adw::AlertDialog::builder()
|
let dialog = adw::AlertDialog::builder()
|
||||||
.heading(&gettext("Close window?"))
|
.heading(gettext("Close window?"))
|
||||||
.body(&gettext(
|
.body(gettext(
|
||||||
"There are ongoing processes that will be canceled.",
|
"There are ongoing processes that will be canceled.",
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -277,7 +316,7 @@ impl Window {
|
||||||
config::METADATA_URL.to_string()
|
config::METADATA_URL.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
match library.import_metadata_from_url(&url) {
|
match library.import_metadata_from_url(&url, Source::Metadata) {
|
||||||
Ok(receiver) => {
|
Ok(receiver) => {
|
||||||
let process = Process::new(&gettext("Updating metadata"), receiver);
|
let process = Process::new(&gettext("Updating metadata"), receiver);
|
||||||
self.imp().process_manager.add_process(&process);
|
self.imp().process_manager.add_process(&process);
|
||||||
|
|
@ -339,17 +378,12 @@ impl Window {
|
||||||
fn reset_view(&self) {
|
fn reset_view(&self) {
|
||||||
let navigation = self.imp().navigation_view.get();
|
let navigation = self.imp().navigation_view.get();
|
||||||
|
|
||||||
// Get all pages that are not instances of SearchPage.
|
// Get all pages that are not instances of SearchPage or AlbumPage.
|
||||||
let mut navigation_stack = navigation
|
let mut navigation_stack = navigation
|
||||||
.navigation_stack()
|
.navigation_stack()
|
||||||
.iter::<adw::NavigationPage>()
|
.iter::<adw::NavigationPage>()
|
||||||
.filter_map(|page| match page {
|
.filter_map(|page| page.ok())
|
||||||
Ok(page) => match page.downcast_ref::<SearchPage>() {
|
.filter(|page| !page.is::<SearchPage>() && !page.is::<AlbumPage>())
|
||||||
Some(_) => None,
|
|
||||||
None => Some(page),
|
|
||||||
},
|
|
||||||
Err(_) => None,
|
|
||||||
})
|
|
||||||
.collect::<Vec<adw::NavigationPage>>();
|
.collect::<Vec<adw::NavigationPage>>();
|
||||||
|
|
||||||
navigation_stack.insert(
|
navigation_stack.insert(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue