diff --git a/Cargo.lock b/Cargo.lock index 2c4e493..b78e6d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1880,6 +1880,7 @@ dependencies = [ "glib", "gstreamer-play", "gtk4", + "lazy_static", "libadwaita", "log", "mpris-server", diff --git a/Cargo.toml b/Cargo.toml index ac6b612..4f0b267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ gettext-rs = { version = "0.7", features = ["gettext-system"] } glib = { version = "0.20", features = ["v2_84"] } gstreamer-play = "0.23" gtk = { package = "gtk4", version = "0.9", features = ["v4_18", "blueprint"] } +lazy_static = "1" log = "0.4" mpris-server = "0.8" once_cell = "1" diff --git a/data/res/style.css b/data/res/style.css index 4e55c11..316dcd4 100644 --- a/data/res/style.css +++ b/data/res/style.css @@ -6,12 +6,15 @@ font-size: smaller; } -.rounded-entry { - border-radius: 999px; - padding-left: 12px; - padding-right: 12px; - padding-top: 3px; - padding-bottom: 3px; +.searchbar .searchtag { + background-color: alpha(currentColor, 0.1); + border-radius: 100px; +} + +.searchbar .searchtag>button { + min-width: 24px; + min-height: 24px; + margin: 0px; } .tile { diff --git a/data/ui/editor/album.blp b/data/ui/editor/album.blp index 9811472..d89d79b 100644 --- a/data/ui/editor/album.blp +++ b/data/ui/editor/album.blp @@ -65,15 +65,9 @@ template $MusicusAlbumEditor: Adw.NavigationPage { margin-top: 24; styles [ - "boxed-list-separate", + "boxed-list", ] - 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 { title: _("_Create album"); use-underline: true; diff --git a/data/ui/empty_page.blp b/data/ui/empty_page.blp index 1be754d..bee614a 100644 --- a/data/ui/empty_page.blp +++ b/data/ui/empty_page.blp @@ -50,32 +50,23 @@ template $MusicusEmptyPage: Adw.NavigationPage { } menu primary_menu { - section { - item { - label: _("_Import music"); - action: "win.import"; - } - - item { - label: _("_Create album"); - action: "win.create-album"; - } - - item { - label: _("_Library manager"); - action: "win.library"; - } + item { + label: _("_Import music"); + action: "win.import"; } - section { - item { - label: _("_Preferences"); - action: "win.preferences"; - } + item { + label: _("_Library manager"); + action: "win.library"; + } - item { - label: _("_About Musicus"); - action: "app.about"; - } + item { + label: _("_Preferences"); + action: "win.preferences"; + } + + item { + label: _("_About Musicus"); + action: "app.about"; } } diff --git a/data/ui/search_page.blp b/data/ui/search_page.blp index 1cae41a..9c42770 100644 --- a/data/ui/search_page.blp +++ b/data/ui/search_page.blp @@ -78,10 +78,6 @@ template $MusicusSearchPage: Adw.NavigationPage { placeholder-text: _("Enter composers, performers, works…"); margin-top: 24; activate => $select() swapped; - - styles [ - "rounded-entry", - ] } Gtk.Stack stack { @@ -264,33 +260,24 @@ template $MusicusSearchPage: Adw.NavigationPage { } menu primary_menu { - section { - item { - label: _("_Import music"); - action: "win.import"; - } - - item { - label: _("_Create album"); - action: "win.create-album"; - } - - item { - label: _("_Library manager"); - action: "win.library"; - } + item { + label: _("_Import music"); + action: "win.import"; } - section { - item { - label: _("_Preferences"); - action: "win.preferences"; - } + item { + label: _("_Library manager"); + action: "win.library"; + } - item { - label: _("_About Musicus"); - action: "app.about"; - } + item { + label: _("_Preferences"); + action: "win.preferences"; + } + + item { + label: _("_About Musicus"); + action: "app.about"; } } diff --git a/data/ui/search_tag.blp b/data/ui/search_tag.blp new file mode 100644 index 0000000..be66cf5 --- /dev/null +++ b/data/ui/search_tag.blp @@ -0,0 +1,22 @@ +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; + } +} \ No newline at end of file diff --git a/flatpak/de.johrpan.Musicus.Devel.json b/flatpak/de.johrpan.Musicus.Devel.json index 09a2fd1..07cabd6 100644 --- a/flatpak/de.johrpan.Musicus.Devel.json +++ b/flatpak/de.johrpan.Musicus.Devel.json @@ -5,7 +5,7 @@ "sdk": "org.gnome.Sdk", "sdk-extensions": [ "org.freedesktop.Sdk.Extension.rust-stable", - "org.freedesktop.Sdk.Extension.llvm20" + "org.freedesktop.Sdk.Extension.llvm18" ], "command": "musicus", "finish-args": [ @@ -20,7 +20,7 @@ "--env=G_MESSAGES_DEBUG=none" ], "build-options": { - "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin", + "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm18/bin", "build-args": [ "--share=network" ], @@ -31,6 +31,17 @@ } }, "modules": [ + { + "name": "blueprint-compiler", + "buildsystem": "meson", + "sources": [ + { + "type": "git", + "url": "https://gitlab.gnome.org/jwestman/blueprint-compiler.git", + "tag": "v0.16.0" + } + ] + }, { "name": "musicus", "buildsystem": "meson", diff --git a/flatpak/de.johrpan.Musicus.json b/flatpak/de.johrpan.Musicus.json index 7b12404..3ddfaaa 100644 --- a/flatpak/de.johrpan.Musicus.json +++ b/flatpak/de.johrpan.Musicus.json @@ -5,7 +5,7 @@ "sdk": "org.gnome.Sdk", "sdk-extensions": [ "org.freedesktop.Sdk.Extension.rust-stable", - "org.freedesktop.Sdk.Extension.llvm20" + "org.freedesktop.Sdk.Extension.llvm18" ], "command": "musicus", "finish-args": [ @@ -20,7 +20,7 @@ "--env=G_MESSAGES_DEBUG=none" ], "build-options": { - "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin", + "append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm18/bin", "build-args": [ "--share=network" ], diff --git a/migrations/2025-08-10-104302_source/down.sql b/migrations/2025-08-10-104302_source/down.sql deleted file mode 100644 index a286ae8..0000000 --- a/migrations/2025-08-10-104302_source/down.sql +++ /dev/null @@ -1,248 +0,0 @@ -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; \ No newline at end of file diff --git a/migrations/2025-08-10-104302_source/up.sql b/migrations/2025-08-10-104302_source/up.sql deleted file mode 100644 index 68a3356..0000000 --- a/migrations/2025-08-10-104302_source/up.sql +++ /dev/null @@ -1,245 +0,0 @@ -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; \ No newline at end of file diff --git a/po/de.po b/po/de.po index f95d221..57c58a9 100644 --- a/po/de.po +++ b/po/de.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-05-30 15:27+0200\n" +"POT-Creation-Date: 2025-04-27 17:54+0200\n" "PO-Revision-Date: 2025-04-27 18:23+0200\n" "Last-Translator: elias@johrpan.de\n" "Language-Team: German \n" @@ -818,7 +818,7 @@ msgstr "Bibliothek exportieren" msgid "Exporting music library to {}" msgstr "Bibliothek wird nach {} exportiert" -#: src/library_manager.rs:234 src/window.rs:305 +#: src/library_manager.rs:234 src/window.rs:282 msgid "Updating metadata" msgstr "Metadaten werden aktualisiert" @@ -826,23 +826,19 @@ msgstr "Metadaten werden aktualisiert" msgid "Updating music library" msgstr "Musikbibliothek wird aktualisiert" -#: src/window.rs:166 -msgid "Currently playing music" -msgstr "Musik wird abgespielt" - -#: src/window.rs:190 +#: src/window.rs:167 msgid "Close window?" msgstr "Fenster schließen?" -#: src/window.rs:192 +#: src/window.rs:169 msgid "There are ongoing processes that will be canceled." msgstr "Es gibt laufende Prozesse, die abgebrochen werden." -#: src/window.rs:197 +#: src/window.rs:174 msgid "Keep open" msgstr "Nicht schließen" -#: src/window.rs:198 +#: src/window.rs:175 msgid "Close window" msgstr "Fenster schließen" diff --git a/po/template.pot b/po/template.pot index e47fe6b..03c8554 100644 --- a/po/template.pot +++ b/po/template.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-05-30 15:27+0200\n" +"POT-Creation-Date: 2025-04-27 17:54+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -784,7 +784,7 @@ msgstr "" msgid "Exporting music library to {}" msgstr "" -#: src/library_manager.rs:234 src/window.rs:305 +#: src/library_manager.rs:234 src/window.rs:282 msgid "Updating metadata" msgstr "" @@ -792,22 +792,18 @@ msgstr "" msgid "Updating music library" msgstr "" -#: src/window.rs:166 -msgid "Currently playing music" -msgstr "" - -#: src/window.rs:190 +#: src/window.rs:167 msgid "Close window?" msgstr "" -#: src/window.rs:192 +#: src/window.rs:169 msgid "There are ongoing processes that will be canceled." msgstr "" -#: src/window.rs:197 +#: src/window.rs:174 msgid "Keep open" msgstr "" -#: src/window.rs:198 +#: src/window.rs:175 msgid "Close window" msgstr "" diff --git a/src/album_page.rs b/src/album_page.rs index 2eb51e3..dfab603 100644 --- a/src/album_page.rs +++ b/src/album_page.rs @@ -72,7 +72,8 @@ mod imp { .unwrap() .recordings .iter() - .flat_map(|r| obj.player().recording_to_playlist(r)) + .map(|r| obj.player().recording_to_playlist(r)) + .flatten() .collect::>(); if let Err(err) = obj.player().append(playlist) { @@ -164,7 +165,8 @@ impl AlbumPage { .unwrap() .recordings .iter() - .flat_map(|r| self.player().recording_to_playlist(r)) + .map(|r| self.player().recording_to_playlist(r)) + .flatten() .collect::>(); self.player().append_and_play(playlist); diff --git a/src/album_tile.rs b/src/album_tile.rs index 464b98e..73667cb 100644 --- a/src/album_tile.rs +++ b/src/album_tile.rs @@ -45,7 +45,7 @@ impl AlbumTile { pub fn new(album: &Album) -> Self { 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 diff --git a/src/db/models.rs b/src/db/models.rs index 7682ffb..729801c 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -74,7 +74,6 @@ pub struct Album { pub album_id: String, pub name: TranslatedString, pub recordings: Vec, - pub enable_updates: bool, } impl Eq for Person {} @@ -434,7 +433,6 @@ impl Album { album_id: data.album_id, name: data.name, recordings, - enable_updates: data.enable_updates, }) } diff --git a/src/db/schema.rs b/src/db/schema.rs index c267a7b..eace9ab 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -20,8 +20,6 @@ diesel::table! { albums (album_id) { album_id -> Text, name -> Text, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, @@ -42,12 +40,11 @@ diesel::table! { ensembles (ensemble_id) { ensemble_id -> Text, name -> Text, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, last_played_at -> Nullable, + enable_updates -> Bool, } } @@ -55,12 +52,11 @@ diesel::table! { instruments (instrument_id) { instrument_id -> Text, name -> Text, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, last_played_at -> Nullable, + enable_updates -> Bool, } } @@ -68,8 +64,6 @@ diesel::table! { mediums (medium_id) { medium_id -> Text, discid -> Text, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, @@ -81,17 +75,16 @@ diesel::table! { persons (person_id) { person_id -> Text, name -> Text, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, last_played_at -> Nullable, + enable_updates -> Bool, } } diesel::table! { - recording_ensembles (recording_id, ensemble_id, sequence_number) { + recording_ensembles (recording_id, ensemble_id) { recording_id -> Text, ensemble_id -> Text, role_id -> Nullable, @@ -100,7 +93,7 @@ diesel::table! { } diesel::table! { - recording_persons (recording_id, person_id, sequence_number) { + recording_persons (recording_id, person_id) { recording_id -> Text, person_id -> Text, role_id -> Nullable, @@ -114,12 +107,11 @@ diesel::table! { recording_id -> Text, work_id -> Text, year -> Nullable, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, last_played_at -> Nullable, + enable_updates -> Bool, } } @@ -127,11 +119,10 @@ diesel::table! { roles (role_id) { role_id -> Text, name -> Text, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, + enable_updates -> Bool, } } @@ -167,7 +158,7 @@ diesel::table! { } diesel::table! { - work_persons (work_id, person_id, sequence_number) { + work_persons (work_id, person_id) { work_id -> Text, person_id -> Text, role_id -> Nullable, @@ -181,12 +172,11 @@ diesel::table! { parent_work_id -> Nullable, sequence_number -> Nullable, name -> Text, - source -> Text, - enable_updates -> Bool, created_at -> Timestamp, edited_at -> Timestamp, last_used_at -> Timestamp, last_played_at -> Nullable, + enable_updates -> Bool, } } diff --git a/src/db/tables.rs b/src/db/tables.rs index 19ac04c..223b24b 100644 --- a/src/db/tables.rs +++ b/src/db/tables.rs @@ -24,12 +24,11 @@ use super::{schema::*, TranslatedString}; pub struct Person { pub person_id: String, pub name: TranslatedString, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, pub last_played_at: Option, + pub enable_updates: bool, } #[derive(Boxed, Insertable, Queryable, Selectable, Clone, Debug)] @@ -38,11 +37,10 @@ pub struct Person { pub struct Role { pub role_id: String, pub name: TranslatedString, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, + pub enable_updates: bool, } #[derive(Boxed, Insertable, Queryable, Selectable, Clone, Debug)] @@ -51,12 +49,11 @@ pub struct Role { pub struct Instrument { pub instrument_id: String, pub name: TranslatedString, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, pub last_played_at: Option, + pub enable_updates: bool, } #[derive(Insertable, Queryable, Selectable, Clone, Debug)] @@ -66,12 +63,11 @@ pub struct Work { pub parent_work_id: Option, pub sequence_number: Option, pub name: TranslatedString, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, pub last_played_at: Option, + pub enable_updates: bool, } #[derive(Insertable, Queryable, Selectable, Clone, Debug)] @@ -96,12 +92,11 @@ pub struct WorkInstrument { pub struct Ensemble { pub ensemble_id: String, pub name: TranslatedString, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, pub last_played_at: Option, + pub enable_updates: bool, } #[derive(Insertable, Queryable, Selectable, Clone, Debug)] @@ -119,12 +114,11 @@ pub struct Recording { pub recording_id: String, pub work_id: String, pub year: Option, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, pub last_played_at: Option, + pub enable_updates: bool, } #[derive(Insertable, Queryable, Selectable, Clone, Debug)] @@ -174,8 +168,6 @@ pub struct TrackWork { pub struct Medium { pub medium_id: String, pub discid: String, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, @@ -187,8 +179,6 @@ pub struct Medium { pub struct Album { pub album_id: String, pub name: TranslatedString, - pub source: Source, - pub enable_updates: bool, pub created_at: NaiveDateTime, pub edited_at: NaiveDateTime, pub last_used_at: NaiveDateTime, @@ -266,40 +256,3 @@ impl AsRef for PathBufWrapper { self.0.as_ref() } } - -#[derive(AsExpression, FromSqlRow, Copy, Clone, Debug)] -#[diesel(sql_type = Text)] -pub enum Source { - Metadata, - User, - Import, - Unknown, -} - -impl ToSql 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 FromSql for Source -where - DB: Backend, - String: FromSql, -{ - fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result { - Ok(match String::from_sql(bytes)?.as_str() { - "metadata" => Source::Metadata, - "user" => Source::User, - "import" => Source::Import, - _ => Source::Unknown, - }) - } -} diff --git a/src/editor/album.rs b/src/editor/album.rs index 4c08614..1a5cff1 100644 --- a/src/editor/album.rs +++ b/src/editor/album.rs @@ -39,8 +39,6 @@ mod imp { #[template_child] pub recordings_list: TemplateChild, #[template_child] - pub enable_updates_row: TemplateChild, - #[template_child] pub save_row: TemplateChild, } @@ -128,10 +126,6 @@ impl AlbumEditor { for recording in &album.recordings { obj.add_recording(recording.to_owned()); } - - obj.imp() - .enable_updates_row - .set_active(album.enable_updates); } obj @@ -197,16 +191,10 @@ impl AlbumEditor { .map(|r| r.recording()) .collect::>(); - let enable_updates = self.imp().enable_updates_row.is_active(); - if let Some(album_id) = self.imp().album_id.get() { - library - .update_album(album_id, name, recordings, enable_updates) - .unwrap(); + library.update_album(album_id, name, recordings).unwrap(); } else { - let album = library - .create_album(name, recordings, enable_updates) - .unwrap(); + let album = library.create_album(name, recordings).unwrap(); self.emit_by_name::<()>("created", &[&album]); } diff --git a/src/editor/program.rs b/src/editor/program.rs index 1ae1cca..708c7b0 100644 --- a/src/editor/program.rs +++ b/src/editor/program.rs @@ -69,7 +69,7 @@ mod imp { self.parent_constructed(); 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")) .build(); diff --git a/src/editor/recording.rs b/src/editor/recording.rs index a8029e7..4d60e66 100644 --- a/src/editor/recording.rs +++ b/src/editor/recording.rs @@ -246,7 +246,7 @@ impl RecordingEditor { } 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( &work .composers_string() diff --git a/src/editor/tracks.rs b/src/editor/tracks.rs index caf356c..4c05e47 100644 --- a/src/editor/tracks.rs +++ b/src/editor/tracks.rs @@ -245,7 +245,8 @@ impl TracksEditor { .track_rows .borrow() .iter() - .flat_map(|t| t.track_data().parts.clone()) + .map(|t| t.track_data().parts.clone()) + .flatten() .collect::>() }; diff --git a/src/editor/work.rs b/src/editor/work.rs index 77f9d17..18191c7 100644 --- a/src/editor/work.rs +++ b/src/editor/work.rs @@ -390,15 +390,17 @@ impl WorkEditor { }; self.emit_by_name::<()>("created", &[&part]); - } else if let Some(work_id) = self.imp().work_id.get() { - library - .update_work(work_id, name, parts, composers, instruments, enable_updates) - .unwrap(); } else { - let work = library - .create_work(name, parts, composers, instruments, enable_updates) - .unwrap(); - self.emit_by_name::<()>("created", &[&work]); + if let Some(work_id) = self.imp().work_id.get() { + library + .update_work(work_id, name, parts, composers, instruments, enable_updates) + .unwrap(); + } else { + let work = library + .create_work(name, parts, composers, instruments, enable_updates) + .unwrap(); + self.emit_by_name::<()>("created", &[&work]); + } } self.imp().navigation.get().unwrap().pop(); diff --git a/src/editor/work/part_row.rs b/src/editor/work/part_row.rs index ca5ae1d..b7d3e42 100644 --- a/src/editor/work/part_row.rs +++ b/src/editor/work/part_row.rs @@ -145,7 +145,7 @@ impl WorkEditorPartRow { } fn set_part(&self, part: Work) { - self.set_title(part.name.get()); + self.set_title(&part.name.get()); if !part.parts.is_empty() { self.set_subtitle( diff --git a/src/empty_page.rs b/src/empty_page.rs index d9bd011..1cf2ffb 100644 --- a/src/empty_page.rs +++ b/src/empty_page.rs @@ -10,8 +10,8 @@ use gtk::{gio, glib, glib::subclass::Signal}; use once_cell::sync::Lazy; use crate::{ - config, db::tables::Source, library::Library, process::Process, - process_manager::ProcessManager, process_row::ProcessRow, + config, library::Library, process::Process, process_manager::ProcessManager, + process_row::ProcessRow, }; mod imp { @@ -92,8 +92,8 @@ impl EmptyPage { #[template_callback] async fn download_library(&self) { let dialog = adw::AlertDialog::builder() - .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.")) + .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.")) .build(); dialog.add_response("continue", &gettext("Continue")); @@ -119,7 +119,7 @@ impl EmptyPage { .library .get() .unwrap() - .import_library_from_url(&url, Source::Metadata) + .import_library_from_url(&url) { Ok(receiver) => { let process = Process::new(&gettext("Downloading music library"), receiver); diff --git a/src/library/edit.rs b/src/library/edit.rs index 66ceba7..1e71da9 100644 --- a/src/library/edit.rs +++ b/src/library/edit.rs @@ -10,13 +10,7 @@ use chrono::prelude::*; use diesel::{prelude::*, QueryDsl, SqliteConnection}; use super::Library; -use crate::db::{ - self, - models::*, - schema::*, - tables::{self, Source}, - TranslatedString, -}; +use crate::db::{self, models::*, schema::*, tables, TranslatedString}; impl Library { pub fn create_person(&self, name: TranslatedString, enable_updates: bool) -> Result { @@ -27,7 +21,6 @@ impl Library { let person = Person { person_id: db::generate_id(), name, - source: Source::User, created_at: now, edited_at: now, last_used_at: now, @@ -93,7 +86,6 @@ impl Library { let instrument = Instrument { instrument_id: db::generate_id(), name, - source: Source::User, created_at: now, edited_at: now, last_used_at: now, @@ -155,7 +147,6 @@ impl Library { let role = Role { role_id: db::generate_id(), name, - source: Source::User, created_at: now, edited_at: now, last_used_at: now, @@ -218,7 +209,7 @@ impl Library { ) -> Result { let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap(); - let work = Self::create_work_priv( + let work = self.create_work_priv( connection, name, parts, @@ -235,6 +226,7 @@ impl Library { } fn create_work_priv( + &self, connection: &mut SqliteConnection, name: TranslatedString, parts: Vec, @@ -250,9 +242,8 @@ impl Library { let work_data = tables::Work { work_id: work_id.clone(), parent_work_id: parent_work_id.map(|w| w.to_string()), - sequence_number, + sequence_number: sequence_number, name, - source: Source::User, created_at: now, edited_at: now, last_used_at: now, @@ -265,7 +256,7 @@ impl Library { .execute(connection)?; for (index, part) in parts.into_iter().enumerate() { - Self::create_work_priv( + self.create_work_priv( connection, part.name, part.parts, @@ -318,7 +309,7 @@ impl Library { ) -> Result<()> { let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap(); - Self::update_work_priv( + self.update_work_priv( connection, work_id, name, @@ -336,6 +327,7 @@ impl Library { } fn update_work_priv( + &self, connection: &mut SqliteConnection, work_id: &str, name: TranslatedString, @@ -375,7 +367,7 @@ impl Library { .optional()? .is_some() { - Self::update_work_priv( + self.update_work_priv( connection, &part.work_id, part.name, @@ -389,7 +381,7 @@ impl Library { } else { // Note: The previously used ID is discarded. This should be OK, because // at this point, the part ID should not have been used anywhere. - Self::create_work_priv( + self.create_work_priv( connection, part.name, part.parts, @@ -462,7 +454,6 @@ impl Library { let ensemble_data = tables::Ensemble { ensemble_id: db::generate_id(), name, - source: Source::User, created_at: now, edited_at: now, last_used_at: now, @@ -539,7 +530,6 @@ impl Library { recording_id: recording_id.clone(), work_id: work.work_id.clone(), year, - source: Source::User, created_at: now, edited_at: now, last_used_at: now, @@ -703,7 +693,6 @@ impl Library { &self, name: TranslatedString, recordings: Vec, - enable_updates: bool, ) -> Result { let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap(); @@ -713,8 +702,6 @@ impl Library { let album_data = tables::Album { album_id: album_id.clone(), name, - source: Source::User, - enable_updates, created_at: now, edited_at: now, last_used_at: now, @@ -749,7 +736,6 @@ impl Library { album_id: &str, name: TranslatedString, recordings: Vec, - enable_updates: bool, ) -> Result<()> { let connection = &mut *self.imp().connection.get().unwrap().lock().unwrap(); @@ -759,7 +745,6 @@ impl Library { .filter(albums::album_id.eq(album_id)) .set(( albums::name.eq(name), - albums::enable_updates.eq(enable_updates), albums::edited_at.eq(now), albums::last_used_at.eq(now), )) diff --git a/src/library/exchange.rs b/src/library/exchange.rs index cdd453f..5e512ae 100644 --- a/src/library/exchange.rs +++ b/src/library/exchange.rs @@ -19,11 +19,7 @@ use zip::{write::SimpleFileOptions, ZipWriter}; use super::Library; use crate::{ - db::{ - self, - schema::*, - tables::{self, Source}, - }, + db::{self, schema::*, tables}, process::ProcessMsg, }; @@ -32,27 +28,17 @@ impl Library { pub fn import_library_from_zip( &self, path: impl AsRef, - source: Source, ) -> Result> { - 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 library_folder = PathBuf::from(&self.folder()); let this_connection = self.imp().connection.get().unwrap().clone(); let (sender, receiver) = async_channel::unbounded::(); thread::spawn(move || { - if let Err(err) = - sender.send_blocking(ProcessMsg::Result(import_library_from_zip_priv( - path, - library_folder, - source, - this_connection, - &sender, - ))) - { + if let Err(err) = sender.send_blocking(ProcessMsg::Result( + import_library_from_zip_priv(path, library_folder, this_connection, &sender), + )) { log::error!("Failed to send library action result: {err:?}"); } }); @@ -66,10 +52,7 @@ impl Library { &self, path: impl AsRef, ) -> Result> { - 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 path = path.as_ref().to_owned(); @@ -95,7 +78,6 @@ impl Library { pub fn import_library_from_url( &self, url: &str, - source: Source, ) -> Result> { log::info!("Importing library from URL {url}"); let url = url.to_owned(); @@ -106,7 +88,7 @@ impl Library { thread::spawn(move || { if let Err(err) = sender.send_blocking(ProcessMsg::Result( - import_library_from_url_priv(url, library_folder, source, this_connection, &sender), + import_library_from_url_priv(url, library_folder, this_connection, &sender), )) { log::error!("Failed to send library action result: {err:?}"); } @@ -119,7 +101,6 @@ impl Library { pub fn import_metadata_from_url( &self, url: &str, - source: Source, ) -> Result> { log::info!("Importing metadata from URL {url}"); @@ -130,7 +111,7 @@ impl Library { thread::spawn(move || { if let Err(err) = sender.send_blocking(ProcessMsg::Result( - import_metadata_from_url_priv(url, source, this_connection, &sender), + import_metadata_from_url_priv(url, this_connection, &sender), )) { log::error!("Failed to send library action result: {err:?}"); } @@ -144,7 +125,6 @@ impl Library { fn import_library_from_zip_priv( zip_path: impl AsRef, library_folder: impl AsRef, - source: Source, this_connection: Arc>, sender: &async_channel::Sender, ) -> Result<()> { @@ -158,7 +138,7 @@ fn import_library_from_zip_priv( )?; // Import metadata. - let tracks = import_metadata_from_file(tmp_db_file.path(), source, this_connection, false)?; + let tracks = import_metadata_from_file(tmp_db_file.path(), this_connection, false)?; // Import audio files. let n_tracks = tracks.len(); @@ -232,7 +212,6 @@ fn add_file_to_zip( fn import_metadata_from_url_priv( url: String, - source: Source, this_connection: Arc>, sender: &async_channel::Sender, ) -> Result<()> { @@ -244,18 +223,20 @@ fn import_metadata_from_url_priv( 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) => { let _ = sender.send_blocking(ProcessMsg::Message( formatx!(gettext("Importing downloaded library"), &url).unwrap(), )); let _ = sender.send_blocking(ProcessMsg::Result( - import_metadata_from_file(db_file.path(), source, this_connection, true).map( + import_metadata_from_file(db_file.path(), this_connection, true).and_then( |tracks| { if !tracks.is_empty() { log::warn!("The metadata file at {url} contains tracks."); } + + Ok(()) }, ), )); @@ -271,7 +252,6 @@ fn import_metadata_from_url_priv( fn import_library_from_url_priv( url: String, library_folder: impl AsRef, - source: Source, this_connection: Arc>, sender: &async_channel::Sender, ) -> Result<()> { @@ -283,7 +263,7 @@ fn import_library_from_url_priv( 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 { Ok(archive_file) => { @@ -294,9 +274,8 @@ fn import_library_from_url_priv( let _ = sender.send_blocking(ProcessMsg::Result(import_library_from_zip_priv( archive_file.path(), library_folder, - source, this_connection, - sender, + &sender, ))); } Err(err) => { @@ -314,7 +293,6 @@ fn import_library_from_url_priv( /// In any case, tracks are returned. fn import_metadata_from_file( path: impl AsRef, - source: Source, this_connection: Arc>, ignore_tracks: bool, ) -> Result> { @@ -349,7 +327,6 @@ fn import_metadata_from_file( // Import metadata that is not already present. for mut person in persons { - person.source = source; person.created_at = now; person.edited_at = now; person.last_used_at = now; @@ -362,7 +339,6 @@ fn import_metadata_from_file( } for mut role in roles { - role.source = source; role.created_at = now; role.edited_at = now; role.last_used_at = now; @@ -374,7 +350,6 @@ fn import_metadata_from_file( } for mut instrument in instruments { - instrument.source = source; instrument.created_at = now; instrument.edited_at = now; instrument.last_used_at = now; @@ -387,7 +362,6 @@ fn import_metadata_from_file( } for mut work in works { - work.source = source; work.created_at = now; work.edited_at = now; work.last_used_at = now; @@ -414,7 +388,6 @@ fn import_metadata_from_file( } for mut ensemble in ensembles { - ensemble.source = source; ensemble.created_at = now; ensemble.edited_at = now; ensemble.last_used_at = now; @@ -434,7 +407,6 @@ fn import_metadata_from_file( } for mut recording in recordings { - recording.source = source; recording.created_at = now; recording.edited_at = now; recording.last_used_at = now; @@ -494,7 +466,6 @@ fn import_metadata_from_file( } for mut album in albums { - album.source = source; album.created_at = now; album.edited_at = now; album.last_used_at = now; diff --git a/src/library/query.rs b/src/library/query.rs index 502d354..43b685f 100644 --- a/src/library/query.rs +++ b/src/library/query.rs @@ -414,6 +414,7 @@ impl Library { works, recordings, albums, + ..Default::default() } } LibraryQuery { diff --git a/src/library_manager.rs b/src/library_manager.rs index be07c1a..5c494f3 100644 --- a/src/library_manager.rs +++ b/src/library_manager.rs @@ -9,8 +9,8 @@ use gtk::{ }; use crate::{ - config, db::tables::Source, library::Library, process::Process, - process_manager::ProcessManager, process_row::ProcessRow, window::Window, + config, library::Library, process::Process, process_manager::ProcessManager, + process_row::ProcessRow, window::Window, }; mod imp { @@ -128,13 +128,7 @@ impl LibraryManager { } Ok(path) => { if let Some(path) = path.path() { - match self - .imp() - .library - .get() - .unwrap() - .import_library_from_zip(&path, Source::Import) - { + match self.imp().library.get().unwrap().import_library_from_zip(&path) { Ok(receiver) => { let process = Process::new( &formatx!( @@ -192,13 +186,7 @@ impl LibraryManager { } Ok(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) => { let process = Process::new( &formatx!( @@ -240,7 +228,7 @@ impl LibraryManager { .library .get() .unwrap() - .import_metadata_from_url(&url, Source::Metadata) + .import_metadata_from_url(&url) { Ok(receiver) => { let process = Process::new(&gettext("Updating metadata"), receiver); @@ -271,7 +259,7 @@ impl LibraryManager { .library .get() .unwrap() - .import_library_from_url(&url, Source::Metadata) + .import_library_from_url(&url) { Ok(receiver) => { let process = Process::new(&gettext("Updating music library"), receiver); diff --git a/src/main.rs b/src/main.rs index 2a9821e..35e5a4c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ mod program; mod program_tile; mod recording_tile; mod search_page; +mod search_tag; mod selector; mod slider_row; mod tag_tile; @@ -46,7 +47,7 @@ fn main() -> glib::ExitCode { gettextrs::textdomain(config::PKGNAME).unwrap(); gio::resources_register( - &gio::Resource::load(format!( + &gio::Resource::load(&format!( "{}/{}/{}.gresource", config::DATADIR, config::PKGNAME, diff --git a/src/player.rs b/src/player.rs index da1c1bc..8f75d9a 100644 --- a/src/player.rs +++ b/src/player.rs @@ -221,14 +221,14 @@ impl Player { items.push(PlaylistItem::new( true, recording.work.composers_string(), - recording.work.name.get(), + &recording.work.name.get(), Some(&performances), None, - self.library_path_to_file_path(&tracks[0].path), + &self.library_path_to_file_path(&tracks[0].path), &tracks[0].track_id, )); } else { - let mut tracks = tracks.iter(); + let mut tracks = tracks.into_iter(); let first_track = tracks.next().unwrap(); let track_title = |track: &Track, number: usize| -> String { @@ -249,10 +249,10 @@ impl Player { items.push(PlaylistItem::new( true, recording.work.composers_string(), - recording.work.name.get(), + &recording.work.name.get(), Some(&performances), - Some(&track_title(first_track, 1)), - self.library_path_to_file_path(&first_track.path), + Some(&track_title(&first_track, 1)), + &self.library_path_to_file_path(&first_track.path), &first_track.track_id, )); @@ -260,11 +260,11 @@ impl Player { items.push(PlaylistItem::new( false, recording.work.composers_string(), - recording.work.name.get(), + &recording.work.name.get(), Some(&performances), // track number = track index + 1 (first track) + 1 (zero based) - Some(&track_title(track, index + 2)), - self.library_path_to_file_path(&track.path), + Some(&track_title(&track, index + 2)), + &self.library_path_to_file_path(&track.path), &track.track_id, )); } diff --git a/src/program.rs b/src/program.rs index 435cd38..803dbe5 100644 --- a/src/program.rs +++ b/src/program.rs @@ -83,7 +83,7 @@ impl Program { } 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() .property( diff --git a/src/program_tile.rs b/src/program_tile.rs index d776864..78f5718 100644 --- a/src/program_tile.rs +++ b/src/program_tile.rs @@ -56,7 +56,7 @@ mod imp { self.set_program_from_settings(&settings); 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); }); } diff --git a/src/recording_tile.rs b/src/recording_tile.rs index f7afd6c..8ff1373 100644 --- a/src/recording_tile.rs +++ b/src/recording_tile.rs @@ -69,7 +69,7 @@ mod imp { .push(&RecordingEditor::new( obj.imp().navigation.get().unwrap(), obj.imp().library.get().unwrap(), - Some(obj.imp().recording.get().unwrap()), + Some(&obj.imp().recording.get().unwrap()), )); }) .build(); @@ -90,8 +90,8 @@ mod imp { let delete_action = gio::ActionEntry::builder("delete") .activate(move |_, _, _| { let dialog = adw::AlertDialog::builder() - .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.")) + .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.")) .build(); dialog.add_response("delete", &gettext("Delete")); @@ -142,7 +142,7 @@ impl RecordingTile { let obj: Self = glib::Object::new(); 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( &recording .work diff --git a/src/search_page.rs b/src/search_page.rs index 21dc00f..b9c6ff7 100644 --- a/src/search_page.rs +++ b/src/search_page.rs @@ -22,7 +22,8 @@ use crate::{ program::Program, program_tile::ProgramTile, recording_tile::RecordingTile, - tag_tile::{Tag, TagTile}, + search_tag::Tag, + tag_tile::TagTile, util, }; @@ -391,7 +392,7 @@ impl SearchPage { imp.header_box.set_visible(!query.is_empty()); 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() { imp.subtitle_label.set_text(&composers); imp.subtitle_label.set_visible(true); @@ -400,15 +401,15 @@ impl SearchPage { } Some(Tag::Work(work.to_owned())) } 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); Some(Tag::Composer(person.to_owned())) } 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); Some(Tag::Performer(person.to_owned())) } 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); Some(Tag::Ensemble(ensemble.to_owned())) } else if let Some(instrument) = &query.instrument { diff --git a/src/search_tag.rs b/src/search_tag.rs new file mode 100644 index 0000000..e2cdb8b --- /dev/null +++ b/src/search_tag.rs @@ -0,0 +1,98 @@ +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, + pub tag: OnceCell, + } + + #[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) { + obj.init_template(); + } + } + + impl ObjectImpl for SearchTag { + fn signals() -> &'static [Signal] { + static SIGNALS: Lazy> = + Lazy::new(|| vec![Signal::builder("remove").build()]); + + SIGNALS.as_ref() + } + } + + impl WidgetImpl for SearchTag {} + impl BoxImpl for SearchTag {} +} + +glib::wrapper! { + pub struct SearchTag(ObjectSubclass) + @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(&self, f: F) -> glib::SignalHandlerId { + self.connect_local("remove", true, move |values| { + let obj = values[0].get::().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), +} diff --git a/src/selector/recording.rs b/src/selector/recording.rs index 1a256d8..eb16c67 100644 --- a/src/selector/recording.rs +++ b/src/selector/recording.rs @@ -294,7 +294,7 @@ impl RecordingSelectorPopover { .build(), ); - row.set_tooltip_text(Some(work.name.get())); + row.set_tooltip_text(Some(&work.name.get())); let work = work.clone(); let obj = self.clone(); diff --git a/src/selector/work.rs b/src/selector/work.rs index c636d3a..46e84f1 100644 --- a/src/selector/work.rs +++ b/src/selector/work.rs @@ -256,7 +256,7 @@ impl WorkSelectorPopover { .build(), ); - row.set_tooltip_text(Some(work.name.get())); + row.set_tooltip_text(Some(&work.name.get())); let work = work.clone(); let obj = self.clone(); diff --git a/src/tag_tile.rs b/src/tag_tile.rs index 7b92ea5..2f2f642 100644 --- a/src/tag_tile.rs +++ b/src/tag_tile.rs @@ -2,7 +2,7 @@ use std::cell::OnceCell; use gtk::{glib, prelude::*, subclass::prelude::*}; -use crate::db::models::{Ensemble, Instrument, Person, Work}; +use crate::search_tag::Tag; mod imp { use super::*; @@ -78,12 +78,3 @@ impl TagTile { self.imp().tag.get().unwrap() } } - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Tag { - Composer(Person), - Performer(Person), - Ensemble(Ensemble), - Instrument(Instrument), - Work(Work), -} diff --git a/src/util.rs b/src/util.rs index f1c1322..6358704 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,33 +2,34 @@ pub mod activatable_row; pub mod drag_widget; pub mod error_dialog; -use std::sync::LazyLock; - use gettextrs::gettext; use gtk::glib::{self, clone}; +use lazy_static::lazy_static; use error_dialog::ErrorDialog; -/// The user's language code. -pub static LANG: LazyLock = LazyLock::new(|| { - let lang = match glib::language_names().first() { - Some(language_name) => match language_name.split('_').next() { - Some(lang) => lang.to_string(), +lazy_static! { + /// The user's language code. + pub static ref LANG: String = { + let lang = match glib::language_names().first() { + Some(language_name) => match language_name.split('_').next() { + Some(lang) => lang.to_string(), + None => "generic".to_string(), + }, None => "generic".to_string(), - }, - None => "generic".to_string(), - }; + }; - log::info!("Intialized user language to '{lang}'."); - lang -}); + log::info!("Intialized user language to '{lang}'."); + lang + }; +} /// 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) { log::error!("{msgid}: {err:?}"); let toast = adw::Toast::builder() - .title(gettext(msgid)) + .title(&gettext(msgid)) .button_label("Details") .build(); diff --git a/src/util/error_dialog.rs b/src/util/error_dialog.rs index 280386d..50d8977 100644 --- a/src/util/error_dialog.rs +++ b/src/util/error_dialog.rs @@ -61,7 +61,7 @@ glib::wrapper! { impl ErrorDialog { pub fn present(err: &anyhow::Error, parent: &impl IsA) { let obj: Self = glib::Object::builder() - .property("error-text", format!("{err:?}")) + .property("error-text", &format!("{err:?}")) .build(); obj.present(Some(parent)); diff --git a/src/window.rs b/src/window.rs index 73ed189..ce2f4ab 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,7 +1,4 @@ -use std::{ - cell::{Cell, RefCell}, - path::Path, -}; +use std::{cell::RefCell, path::Path}; use adw::{prelude::*, subclass::prelude::*}; use anyhow::{anyhow, Result}; @@ -9,10 +6,8 @@ use gettextrs::gettext; use gtk::{gio, glib, glib::clone}; use crate::{ - album_page::AlbumPage, config, - db::tables::Source, - editor::{album::AlbumEditor, tracks::TracksEditor}, + editor::tracks::TracksEditor, empty_page::EmptyPage, library::{Library, LibraryQuery}, library_manager::LibraryManager, @@ -36,7 +31,6 @@ mod imp { pub library: RefCell>, pub player: Player, pub process_manager: ProcessManager, - pub inhibitor_cookie: Cell>, #[template_child] pub toast_overlay: TemplateChild, @@ -89,16 +83,6 @@ mod imp { }) .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 library_action = gio::ActionEntry::builder("library") .activate(move |_, _, _| { @@ -120,12 +104,8 @@ mod imp { }) .build(); - self.obj().add_action_entries([ - import_action, - create_album_action, - library_action, - preferences_action, - ]); + self.obj() + .add_action_entries([import_action, library_action, preferences_action]); let player_bar = PlayerBar::new(&self.player); self.player_bar_revealer.set_child(Some(&player_bar)); @@ -168,25 +148,6 @@ mod imp { let obj = self.obj().to_owned(); 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 library_path = settings.string("library-path").to_string(); if !library_path.is_empty() { @@ -203,8 +164,8 @@ mod imp { fn close_request(&self) -> glib::signal::Propagation { if self.process_manager.any_ongoing() { let dialog = adw::AlertDialog::builder() - .heading(gettext("Close window?")) - .body(gettext( + .heading(&gettext("Close window?")) + .body(&gettext( "There are ongoing processes that will be canceled.", )) .build(); @@ -316,7 +277,7 @@ impl Window { config::METADATA_URL.to_string() }; - match library.import_metadata_from_url(&url, Source::Metadata) { + match library.import_metadata_from_url(&url) { Ok(receiver) => { let process = Process::new(&gettext("Updating metadata"), receiver); self.imp().process_manager.add_process(&process); @@ -378,12 +339,17 @@ impl Window { fn reset_view(&self) { let navigation = self.imp().navigation_view.get(); - // Get all pages that are not instances of SearchPage or AlbumPage. + // Get all pages that are not instances of SearchPage. let mut navigation_stack = navigation .navigation_stack() .iter::() - .filter_map(|page| page.ok()) - .filter(|page| !page.is::() && !page.is::()) + .filter_map(|page| match page { + Ok(page) => match page.downcast_ref::() { + Some(_) => None, + None => Some(page), + }, + Err(_) => None, + }) .collect::>(); navigation_stack.insert(