diff --git a/lektor.code-workspace b/lektor.code-workspace
index 92cad9d639e75851c03645ed9c8f0d43153f9748..33dae83601bbc6cb7e9c9a5844a5b6a2f6588984 100644
--- a/lektor.code-workspace
+++ b/lektor.code-workspace
@@ -16,10 +16,6 @@
             "path": "src/rust/liblektor-rs",
             "name": "Lektor Rust Lib Sources"
         },
-        {
-            "path": "src/rust/amadeus-rs",
-            "name": "Amadeus RS Sources"
-        },
         {
             "path": "src/rust/amadeus-next",
             "name": "Amadeus Next RS Sources"
@@ -36,4 +32,4 @@
         "rust-analyzer.procMacro.enable": true,
         "rust-analyzer.procMacro.attributes.enable": true,
     }
-}
\ No newline at end of file
+}
diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json
index 96d2140d5d00fb4f8a21c32a47e8be69c798088a..0d6f1e06aad7b1025de7f83f0b02ac6c57165b65 100644
--- a/src/.vscode/settings.json
+++ b/src/.vscode/settings.json
@@ -1,6 +1,7 @@
 {
     "files.exclude": {
+        "/rust": true,
+        "rust/": true,
         "**/rust": true,
-        "rust": true,
     },
-}
+}
\ No newline at end of file
diff --git a/src/base/commands.c b/src/base/commands.c
index 7cad7dbcfc98a83f79d1ec6075d67ebb6482e41f..4bae615ca6047a9695921176120c9507522065a7 100644
--- a/src/base/commands.c
+++ b/src/base/commands.c
@@ -784,7 +784,7 @@ command_find(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX],
  * continuation will be for tags. This limits informations to `UINT64_MAX / 2`
  * the number of informations for a kara, this limit should be fine.
  */
-static const int64_t BASE_TAGS_CONTINUATION = UINT64_MAX / 2;
+static const int64_t UNUSED BASE_TAGS_CONTINUATION = UINT64_MAX / 2;
 
 bool
 command_kara_info(struct lkt_state UNUSED *srv, size_t UNUSED c,
@@ -803,7 +803,8 @@ command_kara_tags(struct lkt_state UNUSED *srv, size_t UNUSED c,
 }
 
 bool
-command_kara_infotags(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_MAX], int cont)
+command_kara_infotags(struct lkt_state UNUSED *srv, size_t UNUSED c,
+                      char UNUSED *args[LKT_MESSAGE_ARGS_MAX], int UNUSED cont)
 {
     LOG_ERROR("COMMAND", "The command kara infotags is not implemented for now");
     return false;
diff --git a/src/rust/amadeus-next/amalib/src/response.rs b/src/rust/amadeus-next/amalib/src/response.rs
index e732571626e47873af97968b34dbc5c09fb9ad71..eaa22f679e1f556e6d10a2026002ce3ae74c61af 100644
--- a/src/rust/amadeus-next/amalib/src/response.rs
+++ b/src/rust/amadeus-next/amalib/src/response.rs
@@ -226,6 +226,12 @@ impl LektorPlaybackStatusResponse {
     }
 }
 
+impl LektorKaraInfoResponse {
+    pub fn into_inner(self) -> Vec<(String, String)> {
+        self.infos
+    }
+}
+
 impl LektorIntegerResponse {
     pub fn into_inner(self) -> usize {
         self.content
diff --git a/src/rust/amadeus-rs/rsc/AmadeusLogo.jpg b/src/rust/amadeus-next/rsc/AmadeusLogo.jpg
similarity index 100%
rename from src/rust/amadeus-rs/rsc/AmadeusLogo.jpg
rename to src/rust/amadeus-next/rsc/AmadeusLogo.jpg
diff --git a/src/rust/amadeus-rs/rsc/IPAMincho.ttf b/src/rust/amadeus-next/rsc/IPAMincho.ttf
similarity index 100%
rename from src/rust/amadeus-rs/rsc/IPAMincho.ttf
rename to src/rust/amadeus-next/rsc/IPAMincho.ttf
diff --git a/src/rust/amadeus-rs/rsc/UbuntuMono-Regular.ttf b/src/rust/amadeus-next/rsc/UbuntuMono-Regular.ttf
similarity index 100%
rename from src/rust/amadeus-rs/rsc/UbuntuMono-Regular.ttf
rename to src/rust/amadeus-next/rsc/UbuntuMono-Regular.ttf
diff --git a/src/rust/amadeus-rs/rsc/UbuntuMono-UFL.txt b/src/rust/amadeus-next/rsc/UbuntuMono-UFL.txt
similarity index 100%
rename from src/rust/amadeus-rs/rsc/UbuntuMono-UFL.txt
rename to src/rust/amadeus-next/rsc/UbuntuMono-UFL.txt
diff --git a/src/rust/amadeus-rs/.gitignore b/src/rust/amadeus-rs/.gitignore
deleted file mode 100644
index 27aff38a798f227cca94d0093cb29736ad803cf5..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/target
-*.lock
diff --git a/src/rust/amadeus-rs/.gitlab-ci.yml b/src/rust/amadeus-rs/.gitlab-ci.yml
deleted file mode 100644
index 1d9669e1ad5d4dc7c17b7a827ec4dc0f04b0dd16..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/.gitlab-ci.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-image: "rust:latest"
-
-before_script:
-  - apt-get update -yqq
-  - apt-get install -yqq --no-install-recommends build-essential libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev libxcb-xfixes0-dev 
-
-test:cargo:
-  script:
-    - rustc --version && cargo --version
-    - cargo test --verbose
diff --git a/src/rust/amadeus-rs/Cargo.toml b/src/rust/amadeus-rs/Cargo.toml
deleted file mode 100644
index 99a44cbce3f207cf1c86f5138e1fdb53609381c3..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/Cargo.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[profile.release]
-strip         = true
-lto           = true
-opt-level     = "s"
-codegen-units = 1
-
-[workspace]
-resolver = "2"
-members = [
-    "amadeus",
-    "amadeus-lib",
-    "amadeus-macro",
-    "lkt-rs",
-    "lkt-lib"
-]
diff --git a/src/rust/amadeus-rs/LICENSE b/src/rust/amadeus-rs/LICENSE
deleted file mode 100644
index 14d093867d2df165a8eb9060c47071f471d171e6..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2022 Maël MARTIN
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/src/rust/amadeus-rs/README.md b/src/rust/amadeus-rs/README.md
deleted file mode 100644
index 8544a75dcf2ebfa29a438bc2a3475b68ee01e49a..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# Amadeus RS
-
-Amadeus, the rust version.
-
-## Build
-
-You need a rust toolchain. When cloned just enter `cargo build` to build the
-application. You will need some XCB libraries on Linux. On Ubuntu you will need
-the following packets : `libxcb-render0-dev`, `libxcb-render-util0-dev`,
-`libxcb-shape0-dev`, `libxcb-xfixes0-dev`
diff --git a/src/rust/amadeus-rs/amadeus-lib/Cargo.toml b/src/rust/amadeus-rs/amadeus-lib/Cargo.toml
deleted file mode 100644
index 60061d423543db5397159d64ca3a0a03ea767d52..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-lib/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name    = "amadeus_lib"
-version = "0.1.0"
-edition = "2021"
-license = "MIT"
-
-[dependencies]
-lkt_lib       = { path = "../lkt-lib" }
-amadeus_macro = { path = "../amadeus-macro" }
-log           = { version = "0.4" }
-lazy_static   = "1"
diff --git a/src/rust/amadeus-rs/amadeus-lib/src/actions.rs b/src/rust/amadeus-rs/amadeus-lib/src/actions.rs
deleted file mode 100644
index 00743374e7532fc96385bff8827cd4a44950ae49..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-lib/src/actions.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-#[derive(Clone)]
-pub enum Action {
-    /// Resume playback from that kara.
-    PlayFromKara,
-
-    /// Pop the kara out of the queue.
-    DeleteKaraFromQueue,
-
-    /// Insert the kara in the queue. The kara will be inserted at the top of
-    /// the queue, but after all the other inserted karas.
-    InsertKaraInQueue,
-
-    /// Add the kara at the end of the playlist.
-    AddKaraToQueue,
-
-    /// Add the kara to a playlist. The playlist will be selected by the user in
-    /// a pop-up or something like that.
-    AddKaraToPlaylist,
-
-    /// Action to delete a kara from a playlist. The passed `u64` is the unique
-    /// id of the playlist, the internal one.
-    DeleteKaraFromPlaylist(u64),
-
-    /// Open the playlist in a window for the user to browse.
-    OpenPlaylist,
-
-    /// Add all the content of a playlist to the queue.
-    AddPlaylistToQueue,
-
-    /// Insert all the content of a playlist in the queue.
-    InsertPlaylistToQueue,
-
-    /// Clear the content of the playlist, delete all the contained karas.
-    ClearPlaylistContent,
-
-    /// Connect to lektord.
-    ConnectToLektord,
-
-    /// Disconnect from lektord.
-    DisconnectFromLektord,
-
-    /// Refresh the list of playlists
-    RefreshPlaylists,
-
-    PlaybackPrevious,
-    PlaybackPlay,
-    PlaybackPause,
-    PlaybackNext,
-}
-
-impl std::fmt::Debug for Action {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        use Action::*;
-        match self {
-            PlayFromKara => write!(f, "PlayFromKara"),
-            DeleteKaraFromQueue => write!(f, "DeleteKaraFromQueue"),
-            InsertKaraInQueue => write!(f, "InsertKaraInQueue"),
-            AddKaraToQueue => write!(f, "AddKaraToQueue"),
-            AddKaraToPlaylist => write!(f, "AddKaraToPlaylist"),
-            DeleteKaraFromPlaylist(arg0) => {
-                f.debug_tuple("DeleteKaraFromPlaylist").field(arg0).finish()
-            }
-            OpenPlaylist => write!(f, "OpenPlaylist"),
-            AddPlaylistToQueue => write!(f, "AddPlaylistToQueue"),
-            InsertPlaylistToQueue => write!(f, "InsertPlaylistToQueue"),
-            ClearPlaylistContent => write!(f, "ClearPlaylistContent"),
-            ConnectToLektord => write!(f, "ConnectToLektord"),
-            DisconnectFromLektord => write!(f, "DisconnectFromLektord"),
-            PlaybackPrevious => write!(f, "PlaybackPrevious"),
-            PlaybackPlay => write!(f, "PlaybackPlay"),
-            PlaybackPause => write!(f, "PlaybackPause"),
-            PlaybackNext => write!(f, "PlaybackNext"),
-            RefreshPlaylists => write!(f, "RefreshPlaylists"),
-        }
-    }
-}
-
-pub fn get_card_action_name(act: &Action) -> &'static str {
-    use Action::*;
-    match act {
-        PlayFromKara => "Play from that kara",
-        DeleteKaraFromQueue => "Delete from the queue",
-
-        InsertKaraInQueue => "Insert in the queue",
-        AddKaraToQueue => "Add to the queue",
-        AddKaraToPlaylist => "Add to playlist",
-
-        DeleteKaraFromPlaylist(_) => "Delete from playlist",
-        OpenPlaylist => "Open playlist",
-        AddPlaylistToQueue => "Add playlist to the queue",
-        InsertPlaylistToQueue => "Insert the playlist in the queue",
-        ClearPlaylistContent => "Clear the playlist content",
-
-        ConnectToLektord => "Connect to lektord",
-        DisconnectFromLektord => "Disconnect from lektord",
-
-        PlaybackPrevious => "Previous",
-        PlaybackPlay => "Play",
-        PlaybackPause => "Pause",
-        PlaybackNext => "Next",
-
-        RefreshPlaylists => "Refresh playlists",
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus-lib/src/deamon/command_deamon.rs b/src/rust/amadeus-rs/amadeus-lib/src/deamon/command_deamon.rs
deleted file mode 100644
index d02a88feb3235619beaf432b80f4412c63e31cef..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-lib/src/deamon/command_deamon.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use super::*;
-
-pub struct CommandDeamon {
-    thread: Arc<Mutex<Cell<Option<thread::JoinHandle<()>>>>>,
-    quit: Arc<AtomicBool>,
-    joined: Arc<AtomicBool>,
-}
-
-impl Deamon for CommandDeamon {
-    type Channels = (Sender<LektorQuery>, Receiver<LektorResponse>);
-    implement_deamon_quit!();
-    implement_deamon_joined!();
-
-    fn spawn(hostname: String, port: i16) -> io::Result<(Self::Channels, Self)> {
-        let mut connexion = LektorConnexion::new(hostname, port)?;
-
-        let (responses_send, responses_recv) = channel::<LektorResponse>();
-        let (commands_send, commands_recv) = channel::<LektorQuery>();
-        let quit = Arc::<AtomicBool>::new(AtomicBool::default());
-        let joined = Arc::<AtomicBool>::new(AtomicBool::default());
-        quit.store(false, atomic::Ordering::SeqCst);
-        joined.store(false, atomic::Ordering::SeqCst);
-        let quit_deamon = quit.clone();
-
-        let thread = thread::spawn(move || {
-            loop {
-                break_when_flagged!(quit_deamon);
-                match commands_recv.recv_timeout(Duration::from_secs(1)) {
-                    Ok(command) => {
-                        let res = match connexion.send_query(command) {
-                            Ok(ok) => ok,
-                            Err(e) => {
-                                error!("failed to send query to lektor: {e}");
-                                info!("quiting the status deamon");
-                                quit_deamon.store(false, atomic::Ordering::SeqCst);
-                                break;
-                            }
-                        };
-                        if let Err(e) = responses_send.send(res) {
-                            error!("failed to send response to amadeus: {e}");
-                            info!("quiting the status deamon");
-                            quit_deamon.store(false, atomic::Ordering::SeqCst);
-                            break;
-                        }
-                    }
-                    Err(RecvTimeoutError::Timeout) => {}
-                    Err(e) => {
-                        error!("failed to get command from amadeus: {e}");
-                        info!("quiting the command deamon");
-                        quit_deamon.store(false, atomic::Ordering::SeqCst);
-                        break;
-                    }
-                };
-            }
-            info!("quiting the command deamon!");
-        });
-
-        let ret = Self {
-            thread: Arc::new(Mutex::new(Cell::new(Some(thread)))),
-            quit,
-            joined,
-        };
-        Ok(((commands_send, responses_recv), ret))
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus-lib/src/deamon/mod.rs b/src/rust/amadeus-rs/amadeus-lib/src/deamon/mod.rs
deleted file mode 100644
index 77e56f24f979c843bf48429c9e9c3f7fc1744305..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-lib/src/deamon/mod.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-mod command_deamon;
-mod status_deamon;
-pub use command_deamon::*;
-pub use status_deamon::*;
-
-use lkt_lib::*;
-use log::*;
-use std::{
-    cell::Cell,
-    io,
-    sync::{
-        atomic::{self, AtomicBool},
-        mpsc::{channel, Receiver, RecvTimeoutError, Sender},
-        Arc, Mutex,
-    },
-    thread,
-    time::Duration,
-};
-
-/// A common interface for all deamons.
-pub trait Deamon: Sized {
-    type Channels;
-
-    /// Quit the deamon
-    fn quit(&self);
-
-    /// Tel whever the quit has been issued.
-    fn should_quit(&self) -> bool;
-
-    /// Spawn a deamon
-    fn spawn(hostname: String, port: i16) -> io::Result<(Self::Channels, Self)>;
-
-    /// Returns true when the thread has terminated.
-    fn joined(&self) -> bool;
-
-    /// Join the deamon.
-    fn join(&self);
-}
-
-#[macro_export]
-macro_rules! break_when_flagged {
-    ($arc_atomic: expr) => {
-        if $arc_atomic.load(atomic::Ordering::SeqCst) {
-            break;
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! implement_deamon_quit {
-    () => {
-        fn quit(&self) {
-            debug!("asked to quit a deamon!");
-            self.quit.store(true, atomic::Ordering::SeqCst);
-        }
-
-        fn should_quit(&self) -> bool {
-            self.quit.load(atomic::Ordering::SeqCst)
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! implement_deamon_joined {
-    () => {
-        fn joined(&self) -> bool {
-            return self.joined.load(atomic::Ordering::SeqCst);
-        }
-
-        fn join(&self) {
-            let locked = self.thread.lock();
-            if locked.is_err() {
-                error!("Failed to lock the mutex that has the join handler",)
-            }
-            let locked = locked.unwrap();
-            let thread = locked.replace(None);
-            match thread {
-                Some(thread) => {
-                    let _ = thread.join();
-                    self.joined.store(true, atomic::Ordering::SeqCst);
-                }
-                None => error!("Nothing to join!"),
-            }
-        }
-    };
-}
-
-pub(self) use break_when_flagged;
-pub(self) use implement_deamon_joined;
-pub(self) use implement_deamon_quit;
diff --git a/src/rust/amadeus-rs/amadeus-lib/src/deamon/status_deamon.rs b/src/rust/amadeus-rs/amadeus-lib/src/deamon/status_deamon.rs
deleted file mode 100644
index b90b13087f373c60a37ca6c341915535a145aae1..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-lib/src/deamon/status_deamon.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use super::*;
-
-pub type StatusDeamonMessageType = (
-    LektorPlaybackStatusResponse,
-    Option<LektorCurrentKaraResponse>,
-);
-
-pub struct StatusDeamon {
-    thread: Arc<Mutex<Cell<Option<thread::JoinHandle<()>>>>>,
-    quit: Arc<AtomicBool>,
-    joined: Arc<AtomicBool>,
-}
-
-impl Deamon for StatusDeamon {
-    type Channels = (Receiver<StatusDeamonMessageType>,);
-    implement_deamon_quit!();
-    implement_deamon_joined!();
-
-    fn spawn(hostname: String, port: i16) -> io::Result<(Self::Channels, StatusDeamon)> {
-        let mut connexion = LektorConnexion::new(hostname, port)?;
-
-        let (responses_send, responses_recv) = channel();
-        let quit = Arc::<AtomicBool>::new(AtomicBool::default());
-        let joined = Arc::<AtomicBool>::new(AtomicBool::default());
-        quit.store(false, atomic::Ordering::SeqCst);
-        joined.store(false, atomic::Ordering::SeqCst);
-        let quit_deamon = quit.clone();
-
-        let thread = thread::spawn(move || {
-            loop {
-                break_when_flagged!(quit_deamon);
-                thread::sleep(Duration::from_secs(1));
-                break_when_flagged!(quit_deamon);
-
-                let status = match connexion.send_query(LektorQuery::PlaybackStatus) {
-                    Ok(LektorResponse::PlaybackStatus(res)) => res,
-                    Ok(_) => {
-                        error!("got invalid response from lektor, not a status...");
-                        info!("quiting the status deamon");
-                        quit_deamon.store(false, atomic::Ordering::SeqCst);
-                        break;
-                    }
-                    Err(e) => {
-                        error!("failed to send the playback status command to lektor: {e}");
-                        info!("quiting the status deamon");
-                        quit_deamon.store(false, atomic::Ordering::SeqCst);
-                        break;
-                    }
-                };
-
-                let current = match status.state() {
-                    LektorState::Stopped => None,
-                    LektorState::Play(_) | LektorState::Pause(_) => {
-                        match connexion.send_query(LektorQuery::CurrentKara) {
-                            Ok(LektorResponse::CurrentKara(res)) => Some(res),
-                            Ok(_) => {
-                                error!("got invalid response from lektor, not a current kara...");
-                                None
-                            }
-                            Err(e) => {
-                                error!("failed to send the current kara command to lektor: {e}");
-                                None
-                            }
-                        }
-                    }
-                };
-
-                info!("send {status:?} and {current:?}");
-                if let Err(e) = responses_send.send((status, current)) {
-                    error!("Failed to send a status response to amadeus: {e}");
-                    info!("quiting the status deamon");
-                    quit_deamon.store(false, atomic::Ordering::SeqCst);
-                    break;
-                }
-            }
-            info!("quiting the status deamon!");
-        });
-
-        let ret = Self {
-            thread: Arc::new(Mutex::new(Cell::new(Some(thread)))),
-            quit,
-            joined,
-        };
-        Ok(((responses_recv,), ret))
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus-lib/src/lib.rs b/src/rust/amadeus-rs/amadeus-lib/src/lib.rs
deleted file mode 100644
index 719c05fa492ad45e7bb0ea9bc612ffaeeb9961bd..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-lib/src/lib.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod actions;
-pub mod deamon;
diff --git a/src/rust/amadeus-rs/amadeus-macro/Cargo.toml b/src/rust/amadeus-rs/amadeus-macro/Cargo.toml
deleted file mode 100644
index 7671b6f7e25463f99d7aea99467a1542185a926e..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-macro/Cargo.toml
+++ /dev/null
@@ -1,5 +0,0 @@
-[package]
-name    = "amadeus_macro"
-version = "0.1.0"
-edition = "2021"
-license = "MIT"
\ No newline at end of file
diff --git a/src/rust/amadeus-rs/amadeus-macro/src/lib.rs b/src/rust/amadeus-rs/amadeus-macro/src/lib.rs
deleted file mode 100644
index 3be4abf86039feae4d636763113449e2336d058e..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus-macro/src/lib.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-#[macro_export]
-macro_rules! either {
-    ($test:expr => $true_expr:expr; $false_expr:expr) => {
-        if $test {
-            $true_expr
-        } else {
-            $false_expr
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! lkt_command_from_str {
-    ($lit:literal) => {
-        concat!($lit, '\n').to_owned()
-    };
-}
-
-#[macro_export]
-macro_rules! then_some {
-    ($cond: expr => $some: expr) => {
-        if $cond {
-            Some($some)
-        } else {
-            None
-        }
-    };
-}
diff --git a/src/rust/amadeus-rs/amadeus/Cargo.toml b/src/rust/amadeus-rs/amadeus/Cargo.toml
deleted file mode 100644
index e60c07ea07810f86b22699518c5dc6fe437ed2e3..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name    = "amadeus"
-version = "0.1.0"
-edition = "2021"
-license = "MIT"
-
-[dependencies]
-lkt_lib       = { path = "../lkt-lib" }
-amadeus_lib   = { path = "../amadeus-lib" }
-amadeus_macro = { path = "../amadeus-macro" }
-serde         = { version = "1", default-features = false,    features = [ "derive", "std" ] }
-serde_json    = { version = "1", default-features = false,    features = [ "std" ] }
-image         = { version = "0.24", default-features = false, features = [ "jpeg", "ico", "png" ] }
-eframe        = { version = "0",                              features = [ "persistence" ] }
-egui          = { version = "0",                              features = [ "extra_debug_asserts", "extra_asserts", "serde", "persistence" ] }
-epi           = { version = "0",                              features = [ "persistence" ] }
-log           = { version = "0.4" }
-lazy_static   = "1"
diff --git a/src/rust/amadeus-rs/amadeus/src/amadeus.rs b/src/rust/amadeus-rs/amadeus/src/amadeus.rs
deleted file mode 100644
index 0ac96a3a9ea51c16d634b253b98d193a79781097..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/amadeus.rs
+++ /dev/null
@@ -1,560 +0,0 @@
-use crate::{cards::*, constants, utils, widgets};
-use amadeus_lib::{
-    actions,
-    deamon::{self, CommandDeamon, Deamon, StatusDeamon},
-};
-use amadeus_macro::*;
-use eframe::{egui, App};
-use lkt_lib::{
-    Kara, LektorCurrentKaraInnerResponse, LektorPlaybackStatusResponse, LektorQuery,
-    LektorResponse, LektorState, Playlist,
-};
-use log::*;
-use std::{
-    sync::mpsc::{Receiver, SendError, Sender},
-    time,
-};
-
-type CommandDeamonData = (
-    (Sender<LektorQuery>, Receiver<LektorResponse>),
-    deamon::CommandDeamon,
-);
-
-type StatusDeamonData = (
-    (Receiver<deamon::StatusDeamonMessageType>,),
-    deamon::StatusDeamon,
-);
-
-pub struct Amadeus<'a> {
-    config: utils::AmadeusConfig,
-    has_config_changed: bool,
-    need_about_window: bool,
-    need_settings_window: bool,
-    actions: Vec<actions::Action>,
-
-    last_render_instant: time::SystemTime,
-    begin_render_instant: time::SystemTime,
-
-    deamon: Option<CommandDeamonData>,
-    status_deamon: Option<StatusDeamonData>,
-
-    lektord_status: Option<LektorPlaybackStatusResponse>,
-    lektord_current: Option<LektorCurrentKaraInnerResponse>,
-
-    lektord_queue: KaraCardCollection<'a>,
-    lektord_historic: KaraCardCollection<'a>,
-
-    lektord_search_results: KaraCardCollection<'a>,
-    lektord_search_query: String,
-    lektord_updated_query: bool,
-
-    playlist_store: widgets::PlaylistsStore,
-
-    lektord_state: LektorState,
-
-    amadeus_logo_texture: Option<egui::TextureHandle>,
-}
-
-fn handle_sender_error(e: Result<(), SendError<LektorQuery>>) {
-    if let Err(e) = e {
-        let query = e.0;
-        error!("error while sending commands {query:?} to command deamon")
-    }
-}
-
-impl Default for Amadeus<'_> {
-    fn default() -> Self {
-        Self {
-            last_render_instant: time::SystemTime::now(),
-            begin_render_instant: time::SystemTime::UNIX_EPOCH,
-
-            lektord_queue: KaraCardCollection::new("Queue".to_owned())
-                .add_action(actions::Action::PlayFromKara)
-                .add_action(actions::Action::DeleteKaraFromQueue)
-                .add_action(actions::Action::AddKaraToPlaylist),
-            lektord_historic: KaraCardCollection::new("Historic".to_owned())
-                .add_action(actions::Action::AddKaraToQueue)
-                .add_action(actions::Action::InsertKaraInQueue)
-                .add_action(actions::Action::DeleteKaraFromQueue)
-                .add_action(actions::Action::AddKaraToPlaylist),
-            lektord_search_results: KaraCardCollection::new("Search results".to_owned())
-                .add_action(actions::Action::AddKaraToQueue)
-                .add_action(actions::Action::InsertKaraInQueue)
-                .add_action(actions::Action::DeleteKaraFromQueue)
-                .add_action(actions::Action::AddKaraToPlaylist),
-            lektord_search_query: String::new(),
-
-            actions: Vec::with_capacity(10),
-            playlist_store: Default::default(),
-            config: Default::default(),
-            has_config_changed: Default::default(),
-            need_about_window: Default::default(),
-            need_settings_window: Default::default(),
-            deamon: Default::default(),
-            status_deamon: Default::default(),
-            lektord_updated_query: Default::default(),
-            lektord_state: Default::default(),
-            amadeus_logo_texture: Default::default(),
-
-            lektord_status: None,
-            lektord_current: None,
-        }
-    }
-}
-
-impl Amadeus<'_> {
-    pub fn create(cc: &eframe::CreationContext<'_>) -> Box<Self> {
-        let mut ret = Amadeus::default();
-        let ctx = &cc.egui_ctx;
-        if let Some(storage) = cc.storage {
-            ret.config = eframe::get_value(storage, "amadeus-rs-config").unwrap_or_default();
-        }
-
-        ctx.set_fonts(utils::font::get_font_definitions());
-        ret.load_amadeus_logo(ctx);
-
-        let mut style = (*ctx.style()).clone();
-        style.text_styles = utils::font::get_font_styles();
-        ctx.set_style(style);
-
-        ret.apply_settings();
-
-        Box::new(ret)
-    }
-
-    fn load_amadeus_logo(&mut self, ctx: &egui::Context) {
-        let (logo_buffer, [size_x, size_y]) = utils::get_icon_as_dynamic_image();
-        let pixels = logo_buffer.as_flat_samples();
-        let logo_texture = egui::ColorImage::from_rgba_unmultiplied(
-            [size_x as usize, size_y as usize],
-            pixels.as_slice(),
-        );
-        self.amadeus_logo_texture =
-            Some(ctx.load_texture("amadeus-logo", logo_texture, egui::TextureOptions::LINEAR));
-    }
-
-    fn collect_fulfilled_actions(&mut self) -> Vec<(u64, actions::Action)> {
-        let mut ret = self.lektord_queue.fulfilled_actions();
-        ret.extend(self.lektord_historic.fulfilled_actions());
-        ret.extend(self.lektord_search_results.fulfilled_actions());
-        ret.extend(self.playlist_store.fulfilled_actions());
-        ret
-    }
-
-    fn render_side_panel_main_view_button(
-        &mut self,
-        ui: &mut egui::Ui,
-        title: &str,
-        view: utils::AmadeusMainView,
-    ) {
-        ui.add_space(constants::PADDING);
-        let selected = view == self.config.main_panel_view;
-        if selected {
-            ui.colored_label(constants::get_text_color(self.config.dark_mode), title);
-        } else {
-            let the_btn = egui::Button::new(title).frame(false);
-            if ui.add(the_btn).clicked() {
-                self.config.main_panel_view = view;
-            }
-        }
-    }
-
-    fn render_side_panel(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
-        if self.config.side_panel_show {
-            egui::SidePanel::left("LEFT_PANEL")
-                .resizable(true)
-                .show(ctx, |ui| {
-                    ui.vertical(|ui| {
-                        use utils::AmadeusMainView::*;
-                        ui.style_mut().override_text_style = Some(utils::font::heading1());
-                        self.render_side_panel_main_view_button(ui, "🎵 Queue", Queue);
-                        self.render_side_panel_main_view_button(ui, "📚 Historic", Historic);
-                        self.render_side_panel_main_view_button(ui, "🔍 Search", SearchResults);
-                        ui.add_space(constants::PADDING);
-                        ui.style_mut().override_text_style = Some(utils::font::body());
-                        ui.separator();
-                    });
-                    self.playlist_store.render(ui, self.config.dark_mode);
-                });
-        }
-        if self.playlist_store.refresh_playlists() {
-            self.actions.push(actions::Action::RefreshPlaylists);
-        }
-    }
-
-    fn render_central_panel(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
-        egui::CentralPanel::default().show(ctx, |ui| {
-            use utils::AmadeusMainView::*;
-            match self.config.main_panel_view {
-                Queue => self.lektord_queue.render(ui, self.config.dark_mode),
-                Historic => self.lektord_historic.render(ui, self.config.dark_mode),
-                SearchResults => {
-                    ui.style_mut().override_text_style = Some(utils::font::heading1());
-                    ui.label("Database search query");
-                    ui.add_space(constants::PADDING);
-
-                    let response = ui.add(
-                        egui::TextEdit::singleline(&mut self.lektord_search_query)
-                            .desired_width(f32::INFINITY),
-                    );
-                    self.lektord_updated_query |=
-                        response.lost_focus() || ui.input().key_pressed(egui::Key::Enter);
-                    ui.add_space(constants::PADDING * 2.);
-
-                    self.lektord_search_results
-                        .render(ui, self.config.dark_mode);
-                }
-            }
-        });
-    }
-
-    fn render_bottom_panel(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
-        if !(self.lektord_status.is_some() && self.lektord_current.is_some()) {
-            return;
-        }
-
-        let status = self.lektord_status.as_ref().unwrap();
-        let current = self.lektord_current.as_ref().unwrap();
-
-        let progress = (status.elapsed() as f64 / status.duration() as f64) as f32;
-        let text_duration = format!(
-            "{:02}:{:02}",
-            status.duration() / 60,
-            status.duration() % 60
-        );
-
-        egui::TopBottomPanel::bottom("FOOTER")
-            .max_height(constants::BOTTOM_PANEL_MAX_SIZE)
-            .min_height(constants::BOTTOM_PANEL_MAX_SIZE)
-            .resizable(false)
-            .show(ctx, |ui| {
-                // The text...
-                ui.horizontal(|ui| {
-                    KaraCard::new(Kara {
-                        id: status.songid().unwrap() as u32,
-                        source_name: current.source().to_string(),
-                        song_type: current.song_type().to_string(),
-                        language: current.language().to_string(),
-                        category: current.category().to_string(),
-                        title: current.title().to_string(),
-                        song_number: current.song_number().map(|x| x as u32),
-                        author: current.author().to_string(),
-                        is_available: true,
-                    })
-                    .render_compact(ui, self.config.dark_mode);
-                    ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| {
-                        ui.style_mut().override_text_style = Some(utils::font::heading3());
-                        ui.add_space(constants::PADDING * 2.);
-                        ui.label(text_duration);
-                    });
-                });
-                // The progress bar...
-                let style = ui.style().clone();
-                ui.style_mut().override_text_style = Some(utils::font::small());
-                ui.style_mut().visuals.override_text_color =
-                    Some(constants::get_text_color(self.config.dark_mode));
-                ui.add(widgets::progress_bar(self.config.dark_mode, progress));
-                ui.set_style(style);
-            });
-    }
-
-    fn render_top_panel(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
-        egui::TopBottomPanel::top("MENU").show(ctx, |ui| {
-            ui.add_space(constants::TOP_PANEL_PADDING * 2.);
-            egui::menu::bar(ui, |ui| {
-                ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| {
-                    ui.style_mut().override_text_style = Some(utils::font::heading1());
-                    if ui.add(egui::Button::new("Amadeus").frame(false)).clicked() {
-                        self.config.side_panel_show = !self.config.side_panel_show;
-                    }
-
-                    ui.add(egui::Separator::default());
-                    egui::menu::menu_button(ui, "⚡", |ui| {
-                        ui.style_mut().override_text_style = Some(utils::font::body());
-                        ui.add(egui::Button::new("Connect lektord").wrap(false))
-                            .clicked()
-                            .then(|| {
-                                self.actions.push(actions::Action::ConnectToLektord);
-                                ui.close_menu();
-                            });
-                        ui.add(egui::Button::new("Disconnect lektord").wrap(false))
-                            .clicked()
-                            .then(|| {
-                                self.actions.push(actions::Action::DisconnectFromLektord);
-                                ui.close_menu();
-                            });
-                    })
-                    .response
-                    .on_hover_text("Action menu");
-                    ui.add(egui::Separator::default());
-
-                    ui.button("⚙")
-                        .on_hover_text("Settings window")
-                        .clicked()
-                        .then(|| self.need_settings_window = true);
-
-                    ui.button("☕")
-                        .on_hover_text("About window")
-                        .clicked()
-                        .then(|| self.need_about_window = true);
-
-                    ui.add(egui::Separator::default());
-                    ui.button("⏪")
-                        .on_hover_text("Previous")
-                        .clicked()
-                        .then(|| self.actions.push(actions::Action::PlaybackPrevious));
-                    match self.lektord_state {
-                        LektorState::Stopped | LektorState::Pause(_) => ui
-                            .button("▶")
-                            .on_hover_text("Play")
-                            .clicked()
-                            .then(|| self.actions.push(actions::Action::PlaybackPlay)),
-                        LektorState::Play(_) => ui
-                            .button("⏸")
-                            .on_hover_text("Pause")
-                            .clicked()
-                            .then(|| self.actions.push(actions::Action::PlaybackPause)),
-                    };
-                    ui.button("⏩")
-                        .on_hover_text("Next")
-                        .clicked()
-                        .then(|| self.actions.push(actions::Action::PlaybackNext));
-                });
-
-                ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| {
-                    ui.style_mut().override_text_style = Some(utils::font::body());
-                    if ui.add(egui::Button::new("❌")).clicked() {
-                        frame.close();
-                    }
-
-                    ui.style_mut().override_text_style = Some(utils::font::small_body());
-                    let frame_duration = self
-                        .begin_render_instant
-                        .duration_since(self.last_render_instant)
-                        .unwrap_or_default()
-                        .as_millis();
-                    let fps = 1000. / frame_duration as f64;
-                    ui.label(format!("{fps:02.1}fps | {frame_duration:02}ms"));
-                    let (drag_id, drag_space) = ui.allocate_space(ui.available_size());
-                    ui.interact(drag_space, drag_id, egui::Sense::drag())
-                        .dragged()
-                        .then(|| frame.drag_window());
-                });
-            });
-            ui.add_space(constants::TOP_PANEL_PADDING);
-        });
-    }
-
-    fn set_visuals(&mut self, ctx: &egui::Context) {
-        ctx.request_repaint();
-        let mut visuals =
-            either!(self.config.dark_mode => egui::Visuals::dark(); egui::Visuals::light());
-
-        visuals.window_rounding = egui::Rounding::none();
-        visuals.widgets.active.rounding = egui::Rounding::none();
-        visuals.widgets.noninteractive.rounding = egui::Rounding::none();
-        visuals.widgets.inactive.rounding = egui::Rounding::none();
-        visuals.widgets.hovered.rounding = egui::Rounding::none();
-        visuals.widgets.open.rounding = egui::Rounding::none();
-
-        visuals.hyperlink_color = constants::get_accent_color(self.config.dark_mode);
-
-        ctx.set_visuals(visuals);
-    }
-
-    /// Handle the events recieved from the deamons that connect to lektord.
-    fn handle_deamons_events(&mut self) {
-        if let Some(((_, deamon), _)) = self.deamon.as_mut() {
-            while let Ok(event) = deamon.try_recv() {
-                use LektorResponse::*;
-                match event {
-                    PlaybackStatus(_) => todo!(),
-                    CurrentKara(_) => todo!(),
-                    PlaylistSet(set) => {
-                        self.playlist_store.clear_playlists();
-                        set.into_iter()
-                            .for_each(|plt| self.playlist_store.create(Playlist { name: plt }))
-                    }
-                    EmptyResponse(_) => todo!(),
-                    KaraSet(_) => todo!(),
-                }
-            }
-        }
-
-        if let Some(((deamon,), _)) = self.status_deamon.as_mut() {
-            while let Ok((status, current)) = deamon.try_recv() {
-                // Sanitize some values, if the time/elapsed/duration is not
-                // valid, we set everything to None/zero.
-                self.lektord_status = then_some!(status.verify() => status);
-                self.lektord_current =
-                    then_some!(current.is_some() => current.unwrap().maybe_into_inner()).flatten();
-            }
-        }
-    }
-
-    /// Handle quits/exits of the deamons
-    fn handle_deamons_exit(&mut self) {
-        // Handle the deamon closing process.
-        if let Some((_, status_deamon)) = &mut self.status_deamon {
-            if status_deamon.should_quit() {
-                self.lektord_status = None;
-                self.lektord_current = None;
-                status_deamon.join();
-                self.status_deamon = None;
-            }
-        };
-        if let Some((_, deamon)) = &mut self.deamon {
-            if deamon.should_quit() {
-                deamon.join();
-                self.deamon = None;
-            }
-        };
-    }
-
-    /// Handle actions recieved from the user. May communicate to the deamons
-    /// and send infos/commands to lektord if they are available.
-    fn handle_action(&mut self) {
-        use actions::Action::*;
-
-        // Handle actions on lektor items
-        for (id, act) in self.collect_fulfilled_actions() {
-            match act {
-                OpenPlaylist => self.playlist_store.show_playlist(id),
-                ClearPlaylistContent => self.playlist_store.clear_playlist(id),
-                PlayFromKara
-                | DeleteKaraFromQueue
-                | InsertKaraInQueue
-                | AddKaraToQueue
-                | AddKaraToPlaylist
-                | DeleteKaraFromPlaylist(_)
-                | AddPlaylistToQueue
-                | InsertPlaylistToQueue => {
-                    debug!("execute action {act:?} on lektor item with id {id}")
-                }
-
-                // This should not occure on items because they are global
-                // actions.
-                RefreshPlaylists
-                | PlaybackPrevious
-                | PlaybackPlay
-                | PlaybackPause
-                | PlaybackNext
-                | ConnectToLektord
-                | DisconnectFromLektord => unreachable!(),
-            }
-        }
-
-        // Handle top level actions
-        for act in self.actions.drain(..) {
-            match act {
-                ConnectToLektord => {
-                    if self.status_deamon.is_none() {
-                        let connexion = StatusDeamon::spawn(
-                            self.config.lektord_hostname.clone(),
-                            self.config.lektord_port.as_integer() as i16,
-                        );
-                        if let Ok(connexion) = connexion {
-                            self.status_deamon = Some(connexion);
-                        }
-                    }
-                    if self.deamon.is_none() {
-                        let connexion = CommandDeamon::spawn(
-                            self.config.lektord_hostname.clone(),
-                            self.config.lektord_port.as_integer() as i16,
-                        );
-                        if let Ok(connexion) = connexion {
-                            self.deamon = Some(connexion);
-                        }
-                    }
-                }
-                DisconnectFromLektord => {
-                    debug!("asked to close the deamons");
-                    if let Some((_, status_deamon)) = &self.status_deamon {
-                        status_deamon.quit();
-                    }
-                    if let Some((_, deamon)) = &self.deamon {
-                        deamon.quit();
-                    }
-                    break;
-                }
-
-                PlaybackPrevious | PlaybackPlay | PlaybackPause | PlaybackNext => {
-                    debug!("Execute action {act:?} on lektor")
-                }
-
-                RefreshPlaylists => {
-                    if let Some(((sender, _), _)) = &self.deamon {
-                        handle_sender_error(sender.send(LektorQuery::ListAllPlaylists))
-                    }
-                }
-
-                _ => unreachable!(),
-            }
-        }
-    }
-
-    fn apply_settings(&mut self) {
-        self.lektord_queue
-            .with_max_content(self.config.ui_max_queue_items.as_integer());
-        self.lektord_historic
-            .with_max_content(self.config.ui_max_history_items.as_integer());
-        self.lektord_search_results
-            .with_max_content(self.config.ui_max_search_items.as_integer());
-    }
-}
-
-impl App for Amadeus<'_> {
-    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
-        self.begin_render_instant = time::SystemTime::now();
-        ctx.request_repaint();
-        self.set_visuals(ctx);
-
-        self.render_top_panel(ctx, frame);
-        self.render_side_panel(ctx, frame);
-        self.render_central_panel(ctx, frame);
-        self.render_bottom_panel(ctx, frame);
-
-        self.playlist_store
-            .render_playlist_contents(ctx, self.config.dark_mode);
-        widgets::render_about_window(
-            &mut self.need_about_window,
-            self.config.dark_mode,
-            ctx,
-            self.amadeus_logo_texture.as_ref().unwrap(),
-        );
-        self.config.render_settings_window(
-            ctx,
-            &mut self.need_settings_window,
-            &mut self.has_config_changed,
-        );
-
-        self.last_render_instant = self.begin_render_instant;
-
-        self.handle_deamons_events();
-        self.handle_deamons_exit();
-        self.handle_action();
-
-        if self.has_config_changed {
-            self.apply_settings();
-        }
-    }
-
-    fn save(&mut self, storage: &mut dyn eframe::Storage) {
-        if self.has_config_changed {
-            eframe::set_value(storage, "amadeus-rs-config", &self.config);
-            self.has_config_changed = false;
-        }
-    }
-
-    fn persist_egui_memory(&self) -> bool {
-        true
-    }
-
-    fn persist_native_window(&self) -> bool {
-        true
-    }
-
-    fn auto_save_interval(&self) -> time::Duration {
-        time::Duration::from_secs(1)
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/cards/card.rs b/src/rust/amadeus-rs/amadeus/src/cards/card.rs
deleted file mode 100644
index ee9ea5e4b788f1a973b17d76fd95d49a77cfd744..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/cards/card.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-use crate::{constants, utils, widgets::render_action_menu};
-use amadeus_lib::actions;
-use amadeus_macro::either;
-use lkt_lib::*;
-
-/// A simple card trait
-pub trait Card<'a, LktType: LektorType<'a>>: ToString + Clone {
-    /// Create an instance of the card from the lektor type
-    fn new(lkt_type: LktType) -> Self;
-
-    /// Render the card to the screen
-    fn render(&mut self, ui: &mut egui::Ui, dark_mode: bool, actions: &[actions::Action]);
-
-    /// Render the card to the screen. Variant to try to render the element in a
-    /// more compact way.
-    fn render_compact(&self, ui: &mut egui::Ui, dark_mode: bool);
-
-    /// Returns the Ids of the activated actions.
-    fn fulfilled_actions(&mut self) -> Vec<actions::Action>;
-
-    /// Get the unique id from the inner lkt type.
-    fn unique_id(&self) -> u64;
-
-    /// The card can indicate if it need to be separator by separators
-    const NEED_SEPARATOR: bool;
-
-    /// Indicate if there is a need to have a separator between the header and
-    /// the first row.
-    const NEED_HEADER_SEPARATOR: bool;
-
-    /// Set the spacing to insert after the last card is rendered.
-    const BOTTOM_SPACE: Option<f32>;
-}
-
-#[derive(Clone)]
-pub struct KaraCard {
-    pub inner: Kara,
-    actions: Vec<actions::Action>,
-}
-
-#[derive(Clone)]
-pub struct PlaylistCard {
-    pub inner: Playlist,
-    actions: Vec<actions::Action>,
-}
-
-impl ToString for KaraCard {
-    fn to_string(&self) -> String {
-        format!(
-            "{} - {} / {} - {}{} - {} [{}]{}",
-            self.inner.category,
-            self.inner.language,
-            self.inner.source_name,
-            self.inner.song_type,
-            match self.inner.song_number {
-                Some(num) => num.to_string(),
-                None => "".to_string(),
-            },
-            self.inner.title,
-            self.inner.author,
-            either!(self.inner.is_available => ""; " (unavailable)")
-        )
-    }
-}
-
-impl ToString for PlaylistCard {
-    fn to_string(&self) -> String {
-        self.inner.name.clone()
-    }
-}
-
-impl AsRef<str> for PlaylistCard {
-    fn as_ref(&self) -> &str {
-        &self.inner.name
-    }
-}
-
-impl Card<'_, Kara> for KaraCard {
-    const NEED_SEPARATOR: bool = true;
-    const NEED_HEADER_SEPARATOR: bool = true;
-    const BOTTOM_SPACE: Option<f32> = Some(constants::BOTTOM_PANEL_MAX_SIZE);
-
-    fn new(inner: Kara) -> Self {
-        Self {
-            inner,
-            actions: Vec::with_capacity(5),
-        }
-    }
-
-    fn render(&mut self, ui: &mut egui::Ui, dark_mode: bool, actions: &[actions::Action]) {
-        ui.add_space(constants::PADDING);
-        static MIN_WIDTH_FOR_ADDITIONAL_INFOS: f32 = 1024.;
-        let song = format!(
-            "{} - {}{} - {}",
-            self.inner.source_name,
-            self.inner.song_type,
-            match self.inner.song_number {
-                Some(num) => num.to_string(),
-                None => "".to_string(),
-            },
-            self.inner.title
-        );
-        ui.horizontal(|ui| {
-            ui.add_space(constants::PADDING * 3.);
-            let left_space = ui.available_width();
-            ui.vertical(|ui| {
-                ui.horizontal(|ui| {
-                    ui.style_mut().override_text_style = Some(utils::font::body());
-                    ui.colored_label(
-                        constants::get_text_color(dark_mode),
-                        format!("{} - {}", self.inner.category, self.inner.language),
-                    );
-                    if left_space >= MIN_WIDTH_FOR_ADDITIONAL_INFOS {
-                        ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| {
-                            ui.label(format!("kara by {}", self.inner.author));
-                        });
-                    }
-                });
-                ui.horizontal(|ui| {
-                    ui.style_mut().override_text_style = Some(utils::font::heading2());
-                    render_action_menu(ui, actions, "▶", &mut self.actions);
-                    ui.colored_label(constants::get_text_color(dark_mode), song);
-                    ui.style_mut().override_text_style = Some(utils::font::body());
-                    if left_space >= MIN_WIDTH_FOR_ADDITIONAL_INFOS {
-                        ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| {
-                            if !self.inner.is_available {
-                                ui.colored_label(
-                                    constants::get_accent_color(dark_mode),
-                                    " (unavailable)",
-                                );
-                            }
-                            ui.label(format!(" #{}", self.inner.id));
-                        });
-                    }
-                });
-            });
-        });
-        ui.add_space(constants::PADDING);
-    }
-
-    fn render_compact(&self, ui: &mut egui::Ui, dark_mode: bool) {
-        let header = format!(
-            "{} - {} by {}",
-            self.inner.category, self.inner.language, self.inner.author
-        );
-        let song = format!(
-            "{} - {}{} - {}",
-            self.inner.source_name,
-            self.inner.song_type,
-            match self.inner.song_number {
-                Some(num) => num.to_string(),
-                None => "".to_string(),
-            },
-            self.inner.title
-        );
-        ui.horizontal(|ui| {
-            ui.add_space(constants::PADDING * 3.);
-            ui.vertical(|ui| {
-                ui.add_space(constants::PADDING * 2.);
-                ui.horizontal(|ui| {
-                    ui.style_mut().override_text_style = Some(utils::font::heading3());
-                    ui.colored_label(constants::get_text_color(dark_mode), header);
-                    ui.style_mut().override_text_style = Some(utils::font::body());
-                    ui.label(egui::RichText::new(format!("#{}", self.inner.id)));
-                });
-                ui.horizontal(|ui| {
-                    ui.style_mut().override_text_style = Some(utils::font::heading1());
-                    ui.colored_label(constants::get_text_color(dark_mode), song);
-                    if !self.inner.is_available {
-                        ui.colored_label(constants::get_accent_color(dark_mode), " (unavailable)");
-                    }
-                });
-            });
-        });
-    }
-
-    fn fulfilled_actions(&mut self) -> Vec<actions::Action> {
-        let ret = self.actions.clone();
-        self.actions.clear();
-        ret
-    }
-
-    fn unique_id(&self) -> u64 {
-        self.inner.unique_id()
-    }
-}
-
-impl Card<'_, Playlist> for PlaylistCard {
-    const NEED_SEPARATOR: bool = false;
-    const NEED_HEADER_SEPARATOR: bool = false;
-    const BOTTOM_SPACE: Option<f32> = None;
-
-    fn new(inner: Playlist) -> Self {
-        Self {
-            inner,
-            actions: Vec::with_capacity(5),
-        }
-    }
-
-    fn render(&mut self, ui: &mut egui::Ui, dark_mode: bool, actions: &[actions::Action]) {
-        ui.horizontal(|ui| {
-            ui.add_space(constants::PADDING * 2.);
-            ui.style_mut().override_text_style = Some(utils::font::body());
-            render_action_menu(ui, actions, "▶", &mut self.actions);
-            ui.colored_label(
-                constants::get_text_color(dark_mode),
-                self.inner.name.to_string(),
-            );
-        });
-        ui.add_space(constants::PADDING);
-    }
-
-    fn render_compact(&self, ui: &mut egui::Ui, dark_mode: bool) {
-        ui.horizontal(|ui| {
-            ui.colored_label(
-                constants::get_text_color(dark_mode),
-                self.inner.name.to_string(),
-            );
-        });
-    }
-
-    fn fulfilled_actions(&mut self) -> Vec<actions::Action> {
-        let ret = self.actions.clone();
-        self.actions.clear();
-        ret
-    }
-
-    fn unique_id(&self) -> u64 {
-        self.inner.unique_id()
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/cards/collection.rs b/src/rust/amadeus-rs/amadeus/src/cards/collection.rs
deleted file mode 100644
index 8dc93ee9be04ff1fa2145fc431eb21bb66ef7aee..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/cards/collection.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-use crate::{cards::card::*, constants, utils};
-use amadeus_lib::actions;
-use amadeus_macro::either;
-use lkt_lib::{Kara, LektorType};
-use std::{borrow::Borrow, cmp::min, marker::PhantomData};
-
-pub type KaraCardCollection<'a> = CardCollection<'a, KaraCard, Kara>;
-
-/// Struct to hold a collection of cards
-pub struct CardCollection<'a, CardType, LktType>
-where
-    CardType: Card<'a, LktType>,
-    LktType: LektorType<'a>,
-{
-    name: String,
-    content: Vec<CardType>,
-    actions: Vec<actions::Action>,
-    max_content: Option<usize>,
-    phantom: PhantomData<&'a LktType>,
-}
-
-impl<'a, CardType: Card<'a, LktType>, LktType: LektorType<'a>> ToString
-    for CardCollection<'a, CardType, LktType>
-{
-    fn to_string(&self) -> String {
-        format!(
-            "Collection {}: {}",
-            self.name,
-            self.content
-                .iter()
-                .map(|card| card.to_string())
-                .fold(String::new(), |a, b| a + ", " + b.borrow())
-        )
-    }
-}
-
-impl<'a, CardType: Card<'a, LktType>, LktType: LektorType<'a>>
-    CardCollection<'a, CardType, LktType>
-{
-    pub fn new(name: String) -> Self {
-        Self {
-            name,
-            max_content: None,
-            content: vec![],
-            actions: vec![],
-            phantom: PhantomData,
-        }
-    }
-
-    pub fn with_max_content(&mut self, max_content: usize) -> &mut Self {
-        self.max_content = either!(max_content != 0 => Some(max_content); None);
-        self
-    }
-
-    pub fn add_card(&mut self, card: CardType) -> &mut Self {
-        self.content.push(card);
-        self
-    }
-
-    pub fn add_action(mut self, act: actions::Action) -> Self {
-        self.actions.push(act);
-        self
-    }
-
-    pub fn empty(&self) -> bool {
-        self.content.is_empty()
-    }
-
-    pub fn fulfilled_actions(&mut self) -> Vec<(u64, actions::Action)> {
-        let mut ret = Vec::new();
-        for card in &mut self.content {
-            let actions = card.fulfilled_actions();
-            let actions_count = actions.len();
-            if actions_count != 0 {
-                let id = card.unique_id();
-                ret.reserve(ret.len() + actions_count);
-                for action in actions {
-                    ret.push((id, action));
-                }
-            }
-        }
-        ret
-    }
-
-    pub fn render(&mut self, ui: &mut egui::Ui, dark_mode: bool) {
-        ui.vertical(|ui| {
-            ui.add_space(constants::PADDING);
-            ui.horizontal(|ui| {
-                ui.style_mut().override_text_style = Some(utils::font::heading1());
-                ui.colored_label(constants::get_text_color(dark_mode), self.name.to_string());
-                ui.style_mut().override_text_style = Some(utils::font::small_body());
-                ui.label(format!(" ({} elements)", self.content.len()));
-            });
-            if CardType::NEED_HEADER_SEPARATOR {
-                ui.add_space(constants::PADDING);
-                ui.add(egui::Separator::default());
-            }
-            if self.empty() {
-                ui.vertical_centered_justified(|ui| {
-                    ui.add_space(constants::PADDING * 2.);
-                    ui.add(egui::Spinner::new());
-                    ui.allocate_space(ui.available_size())
-                });
-            } else {
-                egui::ScrollArea::vertical()
-                    .hscroll(false)
-                    .always_show_scroll(false)
-                    .max_width(f32::INFINITY)
-                    .show(ui, |ui| {
-                        ui.horizontal(|ui| ui.add_space(ui.available_width()));
-                        let upper_bound = match self.max_content {
-                            None => self.content.len(),
-                            Some(x) => min(self.content.len(), x),
-                        };
-                        for card in &mut self.content[0..upper_bound] {
-                            card.render(ui, dark_mode, &self.actions);
-                            if CardType::NEED_SEPARATOR {
-                                ui.add(egui::Separator::default());
-                            }
-                        }
-                        if let Some(space) = CardType::BOTTOM_SPACE {
-                            ui.add_space(space);
-                        }
-                    });
-                ui.allocate_space(ui.available_size());
-            }
-        });
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/cards/mod.rs b/src/rust/amadeus-rs/amadeus/src/cards/mod.rs
deleted file mode 100644
index 46282020633d111cf7b9702b37782456608d4027..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/cards/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-mod card;
-mod collection;
-
-pub use card::Card;
-pub use card::KaraCard;
-pub use card::PlaylistCard;
-
-pub use collection::CardCollection;
-pub use collection::KaraCardCollection;
diff --git a/src/rust/amadeus-rs/amadeus/src/constants.rs b/src/rust/amadeus-rs/amadeus/src/constants.rs
deleted file mode 100644
index 41a90eefb0d5579509bb7a4f90438d81e41bf129..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/constants.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use amadeus_macro::either;
-use eframe::egui::Color32;
-
-pub const PADDING: f32 = 6.0;
-pub const TOP_PANEL_PADDING: f32 = 0.5;
-pub const BOTTOM_PANEL_MAX_SIZE: f32 = 90.;
-const WHITE: Color32 = Color32::from_rgb(255, 255, 255);
-const BLACK: Color32 = Color32::from_rgb(0, 0, 0);
-const CYAN: Color32 = Color32::from_rgb(0, 255, 255);
-const RED: Color32 = Color32::from_rgb(255, 0, 0);
-
-pub fn get_fill_color(dark_mode: bool) -> Color32 {
-    let base_color = get_accent_color(dark_mode);
-    if dark_mode {
-        base_color
-    } else {
-        let factor = 0.69_f32;
-        Color32::from_rgb(
-            (base_color.r() as f32 * factor) as u8,
-            (base_color.g() as f32 * factor) as u8,
-            (base_color.b() as f32 * factor) as u8,
-        )
-    }
-}
-
-pub fn get_text_color(dark_mode: bool) -> Color32 {
-    either!(dark_mode => WHITE; BLACK)
-}
-
-pub fn get_accent_color(dark_mode: bool) -> Color32 {
-    either!(dark_mode => get_darker_color(CYAN); RED)
-}
-
-fn get_darker_color(color: Color32) -> Color32 {
-    let factor = 0.69_f32;
-    Color32::from_rgb(
-        (color.r() as f32 * factor) as u8,
-        (color.g() as f32 * factor) as u8,
-        (color.b() as f32 * factor) as u8,
-    )
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/logger.rs b/src/rust/amadeus-rs/amadeus/src/logger.rs
deleted file mode 100644
index dc9bc4e5eb6d075ea04fe23e6ac86b96e0001a66..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/logger.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-use lazy_static::lazy_static;
-use log::{Level, Metadata, Record, SetLoggerError};
-use std::sync::{Arc, Mutex};
-
-struct SimpleLogger {
-    level: Arc<Mutex<Level>>,
-}
-
-#[repr(transparent)]
-struct SimpleLoggerRef {
-    pub inner: SimpleLogger,
-}
-
-lazy_static! {
-    static ref LOGGER: SimpleLoggerRef = SimpleLoggerRef {
-        inner: SimpleLogger {
-            level: Arc::new(Mutex::new(Level::Debug)),
-        }
-    };
-}
-
-impl log::Log for SimpleLogger {
-    fn enabled(&self, metadata: &Metadata) -> bool {
-        match self.level.lock() {
-            Ok(level) => metadata.level() <= *level,
-            Err(e) => panic!("Failed to lock the log level mutex: {e}"),
-        }
-    }
-
-    fn log(&self, record: &Record) {
-        if self.enabled(record.metadata()) {
-            eprintln!(
-                "{} - {} - {}",
-                record.target(),
-                record.level(),
-                record.args()
-            );
-        }
-    }
-
-    fn flush(&self) {}
-}
-
-pub fn init() -> Result<(), SetLoggerError> {
-    let default_level = match LOGGER.inner.level.lock() {
-        Ok(lvl) => lvl,
-        Err(e) => panic!("Failed to lock the log level mutex: {e}"),
-    };
-
-    log::set_logger(&LOGGER.inner).map(|()| {
-        log::set_max_level(default_level.to_level_filter());
-    })
-}
-
-pub fn set_level(level: Level) {
-    match LOGGER.inner.level.lock() {
-        Ok(mut inner_level) => {
-            *inner_level = level;
-            log::set_max_level(level.to_level_filter());
-        }
-        Err(e) => panic!("Failed to lock the log level mutex: {e}"),
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/main.rs b/src/rust/amadeus-rs/amadeus/src/main.rs
deleted file mode 100644
index a29cb0d438445fd1179774a3454831e9fba7e064..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/main.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-mod amadeus;
-mod cards;
-mod constants;
-mod logger;
-mod utils;
-mod widgets;
-
-use eframe::egui::Vec2;
-
-fn main() {
-    logger::set_level(log::Level::Debug);
-    if let Err(e) = logger::init() {
-        panic!("Failed to install logger: {e}")
-    }
-
-    eframe::run_native(
-        "amadeus-rs",
-        eframe::NativeOptions {
-            maximized: false,
-            decorated: true,
-            drag_and_drop_support: true,
-            resizable: true,
-            initial_window_size: Some(Vec2::new(800., 600.)),
-            icon_data: Some(utils::get_icon_data()),
-            ..Default::default()
-        },
-        Box::new(|cc| amadeus::Amadeus::create(cc)),
-    );
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/utils/config.rs b/src/rust/amadeus-rs/amadeus/src/utils/config.rs
deleted file mode 100644
index 6f8189dd1760d03e73ee714b7515f465a4552fd7..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/utils/config.rs
+++ /dev/null
@@ -1,130 +0,0 @@
-use crate::widgets;
-
-/// Indicate which view should be displayed in the central panel of Amadeus'
-/// main window.
-#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq)]
-pub enum AmadeusMainView {
-    /// The main panel should show the queue of lektord, i.e. the kara that will
-    /// be played.
-    Queue,
-
-    /// The main panel should show the kara that where played by lektord.
-    Historic,
-
-    /// The main panel should show the search view.
-    SearchResults,
-}
-
-#[derive(serde::Serialize, serde::Deserialize)]
-pub struct AmadeusConfig {
-    pub dark_mode: bool,
-    pub admin_password: String,
-    pub lektord_hostname: String,
-    pub lektord_port: super::NetworkPort,
-    pub lektord_auto_reconnect: bool,
-    pub main_panel_view: AmadeusMainView,
-    pub side_panel_show: bool,
-    pub ui_max_queue_items: super::IntegerTextBuffer<usize>,
-    pub ui_max_search_items: super::IntegerTextBuffer<usize>,
-    pub ui_max_history_items: super::IntegerTextBuffer<usize>,
-}
-
-impl Default for AmadeusMainView {
-    fn default() -> Self {
-        Self::Queue
-    }
-}
-
-impl Default for AmadeusConfig {
-    fn default() -> Self {
-        Self {
-            dark_mode: true,
-            admin_password: "".to_owned(),
-            lektord_hostname: "localhost".to_owned(),
-            lektord_port: super::NetworkPort::from("6600"),
-            lektord_auto_reconnect: false,
-            main_panel_view: Default::default(),
-            side_panel_show: true,
-            ui_max_queue_items: super::IntegerTextBuffer::from("100"),
-            ui_max_search_items: super::IntegerTextBuffer::from("100"),
-            ui_max_history_items: super::IntegerTextBuffer::from("100"),
-        }
-    }
-}
-
-impl AmadeusConfig {
-    pub fn render_settings_window(
-        &mut self,
-        ctx: &egui::Context,
-        show: &mut bool,
-        changed: &mut bool,
-    ) {
-        widgets::WindowBuilder::new("Settings", self.dark_mode).show(show, ctx, |ui| {
-            ui.separator();
-            widgets::add_heading2_label(ui, "☰  Lektord deamon");
-            widgets::add_labelled_edit_line(
-                ui,
-                "Hostname         ",
-                &mut self.lektord_hostname,
-                changed,
-            );
-            widgets::add_labelled_edit_line(
-                ui,
-                "Port             ",
-                &mut self.lektord_port,
-                changed,
-            );
-            widgets::add_labelled_toggle_switch(
-                ui,
-                self.dark_mode,
-                "Auto-reconnect",
-                &mut self.lektord_auto_reconnect,
-                changed,
-            );
-
-            ui.separator();
-            widgets::add_heading2_label(ui, "☰  Admin user");
-            widgets::add_labelled_password(
-                ui,
-                "Admin pwd        ",
-                &mut self.admin_password,
-                changed,
-            );
-
-            ui.separator();
-            widgets::add_heading2_label(ui, "☰  UI");
-            widgets::add_labelled_toggle_switch(
-                ui,
-                self.dark_mode,
-                "Dark theme",
-                &mut self.dark_mode,
-                changed,
-            );
-            widgets::add_labelled_toggle_switch(
-                ui,
-                self.dark_mode,
-                "Show side panel",
-                &mut self.side_panel_show,
-                changed,
-            );
-            widgets::add_labelled_edit_line(
-                ui,
-                "Max queue size   ",
-                &mut self.ui_max_queue_items,
-                changed,
-            );
-            widgets::add_labelled_edit_line(
-                ui,
-                "Max search size  ",
-                &mut self.ui_max_search_items,
-                changed,
-            );
-            widgets::add_labelled_edit_line(
-                ui,
-                "Max history size ",
-                &mut self.ui_max_history_items,
-                changed,
-            );
-        });
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/utils/font.rs b/src/rust/amadeus-rs/amadeus/src/utils/font.rs
deleted file mode 100644
index c75eeebf28ad1f83e08dc6d04ea95467704ae80b..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/utils/font.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-use std::collections::BTreeMap;
-
-use egui::{
-    FontData, FontDefinitions,
-    FontFamily::{Monospace, Proportional},
-    FontId, TextStyle,
-};
-
-#[inline]
-pub fn heading1() -> TextStyle {
-    TextStyle::Heading
-}
-
-#[inline]
-pub fn heading2() -> TextStyle {
-    TextStyle::Name("Heading2".into())
-}
-
-#[inline]
-pub fn heading3() -> TextStyle {
-    TextStyle::Name("ContextHeading".into())
-}
-
-#[inline]
-pub fn body() -> TextStyle {
-    TextStyle::Body
-}
-
-#[inline]
-pub fn small_body() -> TextStyle {
-    TextStyle::Name("SmallBody".into())
-}
-
-#[inline]
-pub fn monospace() -> TextStyle {
-    TextStyle::Monospace
-}
-
-#[inline]
-pub fn button() -> TextStyle {
-    TextStyle::Button
-}
-
-#[inline]
-pub fn small() -> TextStyle {
-    TextStyle::Small
-}
-
-pub fn get_font_definitions() -> FontDefinitions {
-    static FONT_NAME: &str = "UbuntuMono";
-    static FONT_DATA: &[u8] = include_bytes!("../../../rsc/UbuntuMono-Regular.ttf");
-    static CJK_NAME: &str = "IPAMincho";
-    static CJK_DATA: &[u8] = include_bytes!("../../../rsc/IPAMincho.ttf");
-    let mut fonts = FontDefinitions::default();
-
-    fonts
-        .font_data
-        .insert(FONT_NAME.to_owned(), FontData::from_static(FONT_DATA));
-    fonts
-        .font_data
-        .insert(CJK_NAME.to_owned(), FontData::from_static(CJK_DATA));
-
-    fonts
-        .families
-        .get_mut(&Proportional)
-        .unwrap()
-        .insert(0, FONT_NAME.to_owned());
-    fonts
-        .families
-        .get_mut(&Proportional)
-        .unwrap()
-        .push(CJK_NAME.to_owned());
-
-    fonts
-        .families
-        .get_mut(&Monospace)
-        .unwrap()
-        .insert(0, FONT_NAME.to_owned());
-    fonts
-        .families
-        .get_mut(&Monospace)
-        .unwrap()
-        .push(CJK_NAME.to_owned());
-
-    fonts
-}
-
-pub fn get_font_styles() -> BTreeMap<TextStyle, FontId> {
-    let styles = [
-        (heading1(), FontId::new(30.0, Proportional)),
-        (heading2(), FontId::new(25.0, Proportional)),
-        (heading3(), FontId::new(23.0, Proportional)),
-        (body(), FontId::new(18.0, Proportional)),
-        (monospace(), FontId::new(14.0, Proportional)),
-        (small_body(), FontId::new(14.0, Proportional)),
-        (button(), FontId::new(14.0, Proportional)),
-        (small(), FontId::new(10.0, Proportional)),
-    ];
-    styles.into()
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/utils/int_text_buffer.rs b/src/rust/amadeus-rs/amadeus/src/utils/int_text_buffer.rs
deleted file mode 100644
index c53348eaed65002162508b2d128050c95bb1fbee..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/utils/int_text_buffer.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-use egui::TextBuffer;
-use serde::{Deserialize, Serialize};
-use std::{fmt, marker::PhantomData, str::FromStr};
-
-#[derive(Serialize, Deserialize)]
-pub struct IntegerTextBuffer<T: private::Unsigned + private::Zero<T>> {
-    buffer: String,
-    phantom: PhantomData<T>,
-}
-
-impl<T: private::Unsigned + private::Zero<T>> IntegerTextBuffer<T> {
-    pub fn as_integer(&self) -> T
-    where
-        <T as FromStr>::Err: std::fmt::Display,
-    {
-        if self.buffer.is_empty() {
-            T::ZERO
-        } else {
-            match self.buffer.parse::<T>() {
-                Ok(int) => int,
-                Err(e) => panic!("Failed to parse integer: {}", e),
-            }
-        }
-    }
-
-    fn remove_all_non_digit_chars(&mut self) {
-        self.buffer = self.buffer.chars().filter(|c| c.is_ascii_digit()).collect();
-    }
-}
-
-impl<T: private::Unsigned + private::Zero<T>> fmt::Display for IntegerTextBuffer<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.buffer)
-    }
-}
-
-impl<T: private::Unsigned + private::Zero<T>> From<&str> for IntegerTextBuffer<T> {
-    fn from(text: &str) -> Self {
-        Self {
-            buffer: text.to_string(),
-            phantom: PhantomData,
-        }
-    }
-}
-
-impl<T: private::Unsigned + private::Zero<T>> AsRef<str> for IntegerTextBuffer<T> {
-    fn as_ref(&self) -> &str {
-        self.buffer.as_str()
-    }
-}
-
-impl<T: private::Unsigned + private::Zero<T>> TextBuffer for IntegerTextBuffer<T> {
-    fn is_mutable(&self) -> bool {
-        true
-    }
-
-    fn as_str(&self) -> &str {
-        self.as_ref()
-    }
-
-    fn char_range(&self, char_range: std::ops::Range<usize>) -> &str {
-        assert!(char_range.start <= char_range.end);
-        let start_byte = self.byte_index_from_char_index(char_range.start);
-        let end_byte = self.byte_index_from_char_index(char_range.end);
-        &self.as_str()[start_byte..end_byte]
-    }
-
-    fn insert_text(&mut self, text: &str, char_index: usize) -> usize {
-        let ret = self.buffer.insert_text(text, char_index);
-        self.remove_all_non_digit_chars();
-        ret
-    }
-
-    fn delete_char_range(&mut self, char_range: std::ops::Range<usize>) {
-        self.buffer.delete_char_range(char_range);
-    }
-
-    fn clear(&mut self) {
-        self.delete_char_range(0..self.as_ref().len());
-    }
-
-    fn replace(&mut self, text: &str) {
-        self.clear();
-        self.insert_text(text, 0);
-    }
-
-    fn take(&mut self) -> String {
-        let s = self.as_ref().to_owned();
-        self.clear();
-        s
-    }
-}
-
-mod private {
-    use std::str::FromStr;
-
-    pub trait Unsigned: FromStr {}
-    impl Unsigned for u8 {}
-    impl Unsigned for u16 {}
-    impl Unsigned for u32 {}
-    impl Unsigned for u64 {}
-    impl Unsigned for u128 {}
-    impl Unsigned for usize {}
-
-    pub trait Zero<T: Unsigned> {
-        const ZERO: T;
-    }
-
-    impl Zero<u8> for u8 {
-        const ZERO: u8 = 0u8;
-    }
-
-    impl Zero<u16> for u16 {
-        const ZERO: u16 = 0u16;
-    }
-
-    impl Zero<u32> for u32 {
-        const ZERO: u32 = 0u32;
-    }
-
-    impl Zero<u64> for u64 {
-        const ZERO: u64 = 0u64;
-    }
-
-    impl Zero<u128> for u128 {
-        const ZERO: u128 = 0u128;
-    }
-
-    impl Zero<usize> for usize {
-        const ZERO: usize = 0usize;
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/utils/mod.rs b/src/rust/amadeus-rs/amadeus/src/utils/mod.rs
deleted file mode 100644
index a7606af3abbee4305788870df6ce2e5d31de1571..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/utils/mod.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-pub(crate) mod font;
-
-mod config;
-pub(crate) use config::AmadeusConfig;
-pub(crate) use config::AmadeusMainView;
-
-mod int_text_buffer;
-pub(crate) use int_text_buffer::IntegerTextBuffer;
-pub(crate) type NetworkPort = IntegerTextBuffer<u16>;
-
-pub(crate) fn get_icon_as_dynamic_image() -> (image::RgbaImage, [u32; 2]) {
-    static LOGO_SIDE: u32 = 48u32;
-    let logo_data = image::load_from_memory(include_bytes!("../../../rsc/AmadeusLogo.jpg"))
-        .expect("Failed to load Amadeus Logo")
-        .thumbnail(LOGO_SIDE, LOGO_SIDE);
-    let size = [logo_data.width(), logo_data.height()];
-    let logo_buffer = logo_data.to_rgba8();
-    (logo_buffer, size)
-}
-
-pub(crate) fn get_icon_data() -> eframe::IconData {
-    let (logo_buffer, [size_x, size_y]) = get_icon_as_dynamic_image();
-    eframe::IconData {
-        rgba: logo_buffer.to_vec(),
-        height: size_y,
-        width: size_x,
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/widgets/about_window.rs b/src/rust/amadeus-rs/amadeus/src/widgets/about_window.rs
deleted file mode 100644
index 1a8d18a22776c57e06905f67caf75238fe7bfbc1..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/widgets/about_window.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-use crate::{constants, widgets};
-
-static THIRD_PARTIE_FONTS: [(&str, &str, Option<&str>); 2] = [
-    (
-        "UbuntuMono",
-        "https://design.ubuntu.com/font/",
-        Some("Dalton Maag (under the Ubuntu font licence)"),
-    ),
-    (
-        "IPAMincho",
-        "http://ossipedia.ipa.go.jp/ipafont/",
-        Some("Information-technology Promotion Agency, Japan (under IPA licence)"),
-    ),
-];
-
-static THIRD_PARTIE_LIBS: [(&str, &str, Option<&str>); 6] = [
-    (
-        "serde",
-        "https://github.com/serde-rs/serde",
-        Some("(under MIT or APACHE-2.0)"),
-    ),
-    (
-        "serde_json",
-        "https://github.com/serde-rs/json",
-        Some("(under MIT or APACHE-2.0)"),
-    ),
-    (
-        "egui",
-        "https://github.com/emilk/egui",
-        Some("(under MIT or APACHE-2.0)"),
-    ),
-    (
-        "lazy_static",
-        "https://github.com/rust-lang-nursery/lazy-static.rs",
-        Some("(under MIT or APACHE-2.0)"),
-    ),
-    (
-        "regex",
-        "https://github.com/rust-lang/regex",
-        Some("(under MIT or APACHE-2.0)"),
-    ),
-    (
-        "image",
-        "https://github.com/image-rs/image",
-        Some("(under MIT)"),
-    ),
-];
-
-pub fn render_about_window(
-    show: &mut bool,
-    dark_mode: bool,
-    ctx: &egui::Context,
-    logo: &egui::TextureHandle,
-) {
-    widgets::WindowBuilder::new("About", dark_mode)
-        .with_resizable(true)
-        .show(show, ctx, |ui| {
-            {
-                ui.separator();
-                widgets::add_heading2_label(ui, "☰  Amadeus RS");
-                ui.style_mut().override_text_style = Some(crate::utils::font::body());
-                ui.horizontal_top(|ui| {
-                    ui.image(logo, logo.size_vec2());
-                    ui.add_space(constants::PADDING);
-                    ui.add(
-                        egui::Label::new(concat!(
-                            "Amadeus RS is a rewrite of the Amadeus front end to lektord.\n",
-                            "Amadeus RS is under the MIT licence."
-                        ))
-                        .wrap(true),
-                    );
-                });
-                ui.add_space(constants::PADDING);
-            }
-
-            {
-                ui.separator();
-                widgets::add_heading2_label(ui, "☰  Third party libraries and fonts");
-                ui.style_mut().override_text_style = Some(crate::utils::font::body());
-                ui.label("The followinf thrid party libraries where used:");
-                for (label, url, after) in THIRD_PARTIE_LIBS {
-                    widgets::add_bullet_hyperlink(ui, label, url, after);
-                }
-                ui.add_space(constants::PADDING);
-                ui.label("The following third party fonts where used:");
-                for (label, url, after) in THIRD_PARTIE_FONTS {
-                    widgets::add_bullet_hyperlink(ui, label, url, after);
-                }
-                ui.add_space(constants::PADDING);
-            }
-        });
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/widgets/action_menu.rs b/src/rust/amadeus-rs/amadeus/src/widgets/action_menu.rs
deleted file mode 100644
index 20a8cab1458aff11367809599ee68f486e63d630..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/widgets/action_menu.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use crate::utils;
-use amadeus_lib::actions::*;
-
-pub fn render_action_menu(
-    ui: &mut egui::Ui,
-    actions: &[Action],
-    menu_name: impl Into<egui::WidgetText>,
-    fullfilled: &mut Vec<Action>,
-) {
-    ui.menu_button(menu_name, |ui| {
-        ui.style_mut().override_text_style = Some(utils::font::body());
-        for act in actions {
-            let button = egui::Button::new(get_card_action_name(act))
-                .wrap(false)
-                .frame(false);
-            if ui.add(button).clicked() {
-                fullfilled.push(act.clone());
-                ui.close_menu();
-            }
-        }
-    });
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/widgets/mod.rs b/src/rust/amadeus-rs/amadeus/src/widgets/mod.rs
deleted file mode 100644
index 07935c24d1eac8c48ddce66d71f19c7bace73b13..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/widgets/mod.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-mod about_window;
-mod action_menu;
-mod playlists;
-mod progress_bar;
-mod toggle_switch;
-mod window;
-
-pub use about_window::render_about_window;
-pub use action_menu::render_action_menu;
-use lkt_lib::Kara;
-pub use playlists::PlaylistsStore;
-pub use progress_bar::progress_bar;
-pub use toggle_switch::toggle_switch;
-pub use window::WindowBuilder;
-
-use crate::{constants, utils};
-
-pub fn add_bullet_hyperlink(ui: &mut egui::Ui, label: &str, url: &str, after: Option<&str>) {
-    ui.horizontal(|ui| {
-        ui.label("    -");
-        ui.hyperlink_to(label, url);
-        if let Some(text) = after {
-            ui.add(egui::Label::new(text).wrap(true));
-        }
-    });
-}
-
-pub fn add_heading2_label(ui: &mut egui::Ui, text: &str) {
-    ui.style_mut().override_text_style = Some(utils::font::heading2());
-    ui.add(egui::Label::new(text).wrap(false));
-    ui.add_space(constants::PADDING);
-}
-
-pub fn add_labelled_edit_line(
-    ui: &mut egui::Ui,
-    before: &str,
-    text: &mut dyn egui::TextBuffer,
-    changed: &mut bool,
-) {
-    ui.horizontal(|ui| {
-        ui.style_mut().override_text_style = Some(utils::font::body());
-        ui.add(egui::Label::new(before));
-        let response = ui.add(egui::TextEdit::singleline(text));
-        *changed |= response.lost_focus();
-    });
-}
-
-pub fn add_labelled_toggle_switch(
-    ui: &mut egui::Ui,
-    dark_mode: bool,
-    before: &str,
-    switch: &mut bool,
-    changed: &mut bool,
-) {
-    ui.horizontal(|ui| {
-        ui.style_mut().override_text_style = Some(utils::font::body());
-        ui.add(egui::Label::new(before));
-        ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| {
-            let response = ui.add(toggle_switch(dark_mode, switch));
-            *changed |= response.changed();
-        });
-    });
-}
-
-pub fn add_labelled_password(
-    ui: &mut egui::Ui,
-    before: &str,
-    text: &mut dyn egui::TextBuffer,
-    changed: &mut bool,
-) {
-    ui.horizontal(|ui| {
-        ui.style_mut().override_text_style = Some(utils::font::body());
-        ui.add(egui::Label::new(before));
-        let response = ui.add(egui::TextEdit::singleline(text).password(true));
-        *changed |= response.lost_focus();
-    });
-}
-
-pub fn get_label_text_for_kara(kara: &Kara) -> String {
-    format!(
-        "{} - {} / {} - {} - {} [{}]",
-        kara.category, kara.language, kara.source_name, kara.song_type, kara.title, kara.author
-    )
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/widgets/playlists.rs b/src/rust/amadeus-rs/amadeus/src/widgets/playlists.rs
deleted file mode 100644
index 0376bb772395011a732f1cb6b45a81d303fe38ff..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/widgets/playlists.rs
+++ /dev/null
@@ -1,247 +0,0 @@
-use crate::{
-    constants, utils,
-    widgets::{self, render_action_menu},
-};
-use amadeus_lib::actions;
-use lkt_lib::{Kara, LektorType, Playlist};
-use std::collections::{HashMap, HashSet};
-
-static DEFAULT_HASHSET_SIZE: usize = 10;
-
-pub struct PlaylistsStore {
-    /// The list of all playlists
-    playlists: HashMap<String, u64>,
-
-    /// The kara that are contained in the playlists. They are indexed by the
-    /// playlist's unique id. The not ordered and unique kara per playlist
-    /// aspects of playlists are enforced by the HashSet.
-    contents: HashMap<u64, HashSet<Kara>>,
-
-    /// Store the playlists to show.
-    playlists_to_show: HashMap<u64, bool>,
-
-    /// The fullfilled actions in this frame.
-    actions: Vec<(u64, actions::Action)>,
-
-    /// A temp buffer for handling actions on a specific item, where we know the
-    /// id to put but not the render function.
-    temp_actions: Vec<actions::Action>,
-
-    /// Whever to refresh all the playlists or not
-    refresh_playlists: bool,
-}
-
-impl Default for PlaylistsStore {
-    fn default() -> Self {
-        Self {
-            playlists: HashMap::with_capacity(DEFAULT_HASHSET_SIZE),
-            contents: HashMap::with_capacity(DEFAULT_HASHSET_SIZE),
-            playlists_to_show: HashMap::with_capacity(DEFAULT_HASHSET_SIZE),
-            actions: Vec::new(),
-            temp_actions: Vec::new(),
-            refresh_playlists: false,
-        }
-    }
-}
-
-impl PlaylistsStore {
-    pub fn fulfilled_actions(&mut self) -> Vec<(u64, actions::Action)> {
-        if !self.actions.is_empty() {
-            let ret = self.actions.clone();
-            self.actions.clear();
-            ret
-        } else {
-            vec![]
-        }
-    }
-
-    /// Render the opened playlists.
-    pub fn render_playlist_contents(&mut self, ctx: &egui::Context, dark_mode: bool) {
-        let to_show = self
-            .playlists_to_show
-            .iter()
-            .filter(|(_, flag)| **flag)
-            .map(|(id, _)| *id)
-            .collect::<Vec<u64>>();
-        for id in to_show {
-            let name = self
-                .playlists
-                .iter()
-                .filter(|(_, plt_id)| **plt_id == id)
-                .map(|(name, _)| name.as_str())
-                .next()
-                .unwrap();
-            let flag = self.playlists_to_show.get_mut(&id).unwrap();
-            let karas = self.contents.get(&id).unwrap();
-            let actions = widgets::WindowBuilder::new(name, dark_mode)
-                .with_resizable(false)
-                .with_default_size(egui::vec2(200., 500.))
-                .with_actions(vec![
-                    actions::Action::AddPlaylistToQueue,
-                    actions::Action::InsertPlaylistToQueue,
-                ])
-                .with_warning_actions(vec![actions::Action::ClearPlaylistContent])
-                .show(flag, ctx, |ui| {
-                    egui::ScrollArea::vertical()
-                        .hscroll(false)
-                        .always_show_scroll(false)
-                        .max_width(f32::INFINITY)
-                        .show(ui, |ui| {
-                            if karas.is_empty() {
-                                ui.horizontal(|ui| {
-                                    ui.label("Empty playlist");
-                                    ui.add_space(constants::PADDING);
-                                    ui.add(egui::Spinner::new());
-                                });
-                            } else {
-                                for kara in karas {
-                                    ui.horizontal(|ui| {
-                                        ui.style_mut().override_text_style =
-                                            Some(utils::font::body());
-                                        render_action_menu(
-                                            ui,
-                                            &[
-                                                actions::Action::AddKaraToQueue,
-                                                actions::Action::InsertKaraInQueue,
-                                                actions::Action::AddKaraToPlaylist,
-                                                actions::Action::DeleteKaraFromPlaylist(id),
-                                                actions::Action::DeleteKaraFromQueue,
-                                            ],
-                                            widgets::get_label_text_for_kara(kara),
-                                            &mut self.temp_actions,
-                                        );
-                                        self.temp_actions
-                                            .iter()
-                                            .map(|act| (id, act.clone()))
-                                            .for_each(|fullfilled| self.actions.push(fullfilled));
-                                        self.temp_actions.clear();
-                                    });
-                                }
-                            }
-                        });
-                });
-            if let Some(actions) = actions {
-                actions
-                    .iter()
-                    .map(|act| (id, act.clone()))
-                    .for_each(|fullfilled| self.actions.push(fullfilled));
-            }
-        }
-    }
-
-    /// Render the list of playlists. Also register some of the user's actions
-    /// like opening a playlist, etc.
-    pub fn render(&mut self, ui: &mut egui::Ui, dark_mode: bool) {
-        ui.vertical(|ui| {
-            ui.add_space(constants::PADDING);
-            ui.horizontal(|ui| {
-                ui.style_mut().override_text_style = Some(utils::font::heading1());
-                ui.add(egui::widgets::Button::new("🗀 Playlists").frame(false))
-                    .clicked()
-                    .then(|| self.refresh_playlists = true);
-                ui.style_mut().override_text_style = Some(utils::font::small_body());
-            });
-            ui.style_mut().override_text_style = Some(utils::font::body());
-            if self.playlists.is_empty() {
-                ui.vertical_centered_justified(|ui| {
-                    ui.add(egui::Spinner::new());
-                    ui.allocate_space(ui.available_size())
-                });
-            } else {
-                egui::ScrollArea::vertical()
-                    .hscroll(false)
-                    .always_show_scroll(false)
-                    .max_width(f32::INFINITY)
-                    .show(ui, |ui| {
-                        ui.horizontal(|ui| ui.add_space(ui.available_width()));
-                        for (name, id) in &self.playlists {
-                            let plt_size = self.playlist_size(name).unwrap();
-                            ui.horizontal(|ui| {
-                                ui.add_space(constants::PADDING);
-                                render_action_menu(
-                                    ui,
-                                    &[
-                                        actions::Action::OpenPlaylist,
-                                        actions::Action::AddPlaylistToQueue,
-                                        actions::Action::InsertPlaylistToQueue,
-                                    ],
-                                    "▶",
-                                    &mut self.temp_actions,
-                                );
-                                self.temp_actions
-                                    .iter()
-                                    .map(|act| (*id, act.clone()))
-                                    .for_each(|fullfilled| self.actions.push(fullfilled));
-                                self.temp_actions.clear();
-                                ui.colored_label(constants::get_text_color(dark_mode), name);
-                                ui.style_mut().override_text_style =
-                                    Some(utils::font::small_body());
-                                ui.label(format!("{plt_size} karas"));
-                            });
-                        }
-                    });
-                ui.allocate_space(ui.available_size());
-            }
-        });
-    }
-
-    fn get_playlist_by_name(&self, name: &str) -> Option<(&str, u64)> {
-        return self
-            .playlists
-            .iter()
-            .filter(|(plt_name, _)| *plt_name == name)
-            .map(|(name, id)| (name.as_str(), *id))
-            .next();
-    }
-
-    pub fn show_playlist(&mut self, id: u64) {
-        if let Some(flag) = self.playlists_to_show.get_mut(&id) {
-            *flag = true;
-        }
-    }
-
-    pub fn clear_playlist(&mut self, id: u64) {
-        if let Some(content) = self.contents.get_mut(&id) {
-            content.clear();
-        }
-    }
-
-    pub fn refresh_playlists(&mut self) -> bool {
-        let refresh = self.refresh_playlists;
-        self.refresh_playlists = false;
-        refresh
-    }
-
-    pub fn clear_playlists(&mut self) {
-        self.contents.clear();
-        self.playlists_to_show.clear();
-        self.actions.clear();
-        self.playlists.clear();
-    }
-
-    pub fn create(&mut self, plt: Playlist) {
-        self.contents.insert(
-            plt.unique_id(),
-            HashSet::with_capacity(DEFAULT_HASHSET_SIZE),
-        );
-        let id = plt.unique_id();
-        self.playlists.insert(plt.name, id);
-        self.playlists_to_show.insert(id, false);
-    }
-
-    pub fn add(&mut self, name: &str, kara: Kara) {
-        if let Some((_, id)) = self.get_playlist_by_name(name) {
-            if let Some(plt_content) = self.contents.get_mut(&id) {
-                plt_content.insert(kara);
-            } else {
-                panic!("The playlist {name} has no content set, it should have been created when the playlist was created")
-            }
-        }
-    }
-
-    pub fn playlist_size(&self, name: &str) -> Option<usize> {
-        let (_, id) = self.get_playlist_by_name(name)?;
-        let content = self.contents.get(&id)?;
-        Some(content.len())
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/widgets/progress_bar.rs b/src/rust/amadeus-rs/amadeus/src/widgets/progress_bar.rs
deleted file mode 100644
index 91b721748c56fe25be6ed234aeabdae5b5bcce47..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/widgets/progress_bar.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-pub fn progress_bar(dark_mode: bool, progress: f32) -> impl egui::Widget + 'static {
-    move |ui: &mut egui::Ui| {
-        let progress = progress.clamp(0.0, 1.0);
-        let fill_color = crate::constants::get_fill_color(dark_mode);
-        let desired_width = egui::NumExt::at_least(ui.available_size_before_wrap().x, 96.0);
-        let height = ui.spacing().interact_size.y;
-        let (outer_rect, response) =
-            ui.allocate_exact_size(egui::vec2(desired_width, height), egui::Sense::hover());
-
-        if ui.is_rect_visible(response.rect) {
-            let rect = egui::Rect::from_min_size(
-                outer_rect.min,
-                egui::vec2(
-                    egui::NumExt::at_least(outer_rect.width() * progress, outer_rect.height()),
-                    outer_rect.height(),
-                ),
-            );
-            ui.painter().rect(rect, 0.0, fill_color, egui::Stroke::NONE);
-        }
-
-        response
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/widgets/toggle_switch.rs b/src/rust/amadeus-rs/amadeus/src/widgets/toggle_switch.rs
deleted file mode 100644
index 16d5b93ade1a9106d34ab5416b78a6af80ccb5b3..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/widgets/toggle_switch.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use crate::constants;
-use amadeus_macro::either;
-
-pub fn toggle_switch(dark_mode: bool, on: &mut bool) -> impl egui::Widget + '_ {
-    move |ui: &mut egui::Ui| {
-        let desired_size = ui.spacing().interact_size.y * egui::vec2(2.0, 1.0);
-        let (rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
-        if response.clicked() {
-            *on = !*on;
-            response.mark_changed();
-        }
-        response.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::Checkbox, *on, ""));
-
-        if ui.is_rect_visible(rect) {
-            let (bg_fill, bg_stroke, fg_stroke, expansion) = {
-                let visuals = ui.style().interact_selectable(&response, *on);
-                (
-                    visuals.bg_fill,
-                    visuals.bg_stroke,
-                    visuals.fg_stroke,
-                    visuals.expansion,
-                )
-            };
-            let fill_fore = constants::get_accent_color(dark_mode);
-            let fill_back = constants::get_fill_color(dark_mode);
-            let rect = rect.expand(expansion);
-            let radius = 0.5 * rect.height();
-
-            ui.painter()
-                .rect(rect, radius, either!(*on => fill_back; bg_fill), bg_stroke);
-
-            let circle_x = egui::lerp(
-                (rect.left() + radius)..=(rect.right() - radius),
-                ui.ctx().animate_bool(response.id, *on),
-            );
-            let center = egui::pos2(circle_x, rect.center().y);
-
-            ui.painter()
-                .circle(center, 0.75 * radius, fill_fore, fg_stroke);
-        }
-
-        response
-    }
-}
diff --git a/src/rust/amadeus-rs/amadeus/src/widgets/window.rs b/src/rust/amadeus-rs/amadeus/src/widgets/window.rs
deleted file mode 100644
index afa682f70c25c5c771a6125e235d78e8d02059ef..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/amadeus/src/widgets/window.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use crate::constants;
-use amadeus_lib::actions::Action;
-use amadeus_macro::*;
-
-use super::render_action_menu;
-
-/// Structure used to hole informations about the window to create, we use a
-/// builder pattern here.
-pub struct WindowBuilder<'a> {
-    name: &'a str,
-    dark_mode: bool,
-    resizable: Option<bool>,
-    default_size: Option<egui::Vec2>,
-    actions: Option<Vec<Action>>,
-    warning_actions: Option<Vec<Action>>,
-}
-
-impl<'a> WindowBuilder<'a> {
-    pub fn new(name: &'a str, dark_mode: bool) -> Self {
-        Self {
-            name,
-            dark_mode,
-            resizable: None,
-            default_size: None,
-            actions: None,
-            warning_actions: None,
-        }
-    }
-
-    pub fn with_resizable(self, resizable: bool) -> Self {
-        Self {
-            name: self.name,
-            dark_mode: self.dark_mode,
-            resizable: Some(resizable),
-            default_size: self.default_size,
-            actions: self.actions,
-            warning_actions: self.warning_actions,
-        }
-    }
-
-    pub fn with_default_size(self, default_size: egui::Vec2) -> Self {
-        Self {
-            name: self.name,
-            dark_mode: self.dark_mode,
-            resizable: self.resizable,
-            default_size: Some(default_size),
-            actions: self.actions,
-            warning_actions: self.warning_actions,
-        }
-    }
-
-    pub fn with_actions(self, actions: Vec<Action>) -> Self {
-        Self {
-            name: self.name,
-            dark_mode: self.dark_mode,
-            resizable: self.resizable,
-            default_size: self.default_size,
-            actions: Some(actions),
-            warning_actions: self.warning_actions,
-        }
-    }
-
-    pub fn with_warning_actions(self, actions: Vec<Action>) -> Self {
-        Self {
-            name: self.name,
-            dark_mode: self.dark_mode,
-            resizable: self.resizable,
-            default_size: self.default_size,
-            actions: self.actions,
-            warning_actions: Some(actions),
-        }
-    }
-
-    pub fn show<R>(
-        self,
-        flag: &mut bool,
-        ctx: &egui::Context,
-        add_contents: impl FnOnce(&mut egui::Ui) -> R,
-    ) -> Option<Vec<Action>> {
-        let mut flag_copy = *flag;
-        let mut temp_actions = Vec::<Action>::new();
-
-        // Force the mut borrow of the flag to be dropped
-        {
-            let win = egui::Window::new(self.name).open(flag).title_bar(false);
-            let (win, resizable) = match self.resizable {
-                Some(resizable) => (win.resizable(resizable), resizable),
-                None => (win, false),
-            };
-            let win = match self.default_size {
-                None => win,
-                Some(sizes) => {
-                    either!(resizable => win.default_size(sizes).fixed_size(sizes); win.default_size(sizes))
-                }
-            };
-            win.show(ctx, |ui| {
-                ui.horizontal(|ui| {
-                    ui.style_mut().override_text_style = Some(super::utils::font::heading3());
-                    ui.add(egui::Button::new("❌"))
-                        .on_hover_text("Close the window")
-                        .clicked()
-                        .then(|| flag_copy = false);
-
-                    if let Some(actions) = self.actions {
-                        render_action_menu(ui, &actions, "▶", &mut temp_actions)
-                    }
-                    if let Some(actions) = self.warning_actions {
-                        render_action_menu(ui, &actions, "⚠", &mut temp_actions)
-                    }
-
-                    ui.colored_label(constants::get_text_color(self.dark_mode), self.name);
-                });
-                ui.add_space(constants::PADDING / 2.);
-                add_contents(ui)
-            });
-        }
-
-        // See if we need to close the window
-        *flag = flag_copy;
-
-        // Return the actions if needed
-        if temp_actions.is_empty() {
-            None
-        } else {
-            Some(temp_actions)
-        }
-    }
-}
diff --git a/src/rust/amadeus-rs/lkt-lib/Cargo.toml b/src/rust/amadeus-rs/lkt-lib/Cargo.toml
deleted file mode 100644
index d28a58181390b0bbe7207150be48431dfcef0619..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name    = "lkt_lib"
-version = "0.1.0"
-edition = "2021"
-license = "MIT"
-
-[dependencies]
-amadeus_macro   = { path = "../amadeus-macro" }
-serde           = { version = "1", default-features = false, features = [ "derive", "std" ] }
-serde_json      = { version = "1", default-features = false, features = [ "std" ] }
-log             = { version = "0.4" }
-regex           = "1"
-lazy_static     = "1"
\ No newline at end of file
diff --git a/src/rust/amadeus-rs/lkt-lib/src/connexion.rs b/src/rust/amadeus-rs/lkt-lib/src/connexion.rs
deleted file mode 100644
index 243155f155925ca69769bbd6aba175023955127a..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/src/connexion.rs
+++ /dev/null
@@ -1,164 +0,0 @@
-//! Create a connexion to a lektord instande then send commands and recieve data
-//! from the server.
-
-use crate::{
-    constants, query::LektorQuery, query::LektorQueryLineType, response::LektorFormatedResponse,
-    LektorResponse,
-};
-use log::*;
-use std::{
-    io::{self, BufRead, BufReader, Write},
-    net::TcpStream,
-    str::FromStr,
-};
-
-pub enum LektorQueryError {
-    IO(io::Error),
-    Query(String),
-    Other(String),
-    ToTyped(String),
-}
-
-impl std::fmt::Display for LektorQueryError {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        use LektorQueryError::*;
-        match self {
-            IO(io) => write!(f, "lektor query error io: {io}"),
-            Query(err) => write!(f, "lektor query logic error: {err}"),
-            Other(other) => write!(f, "lektor query error: {other}"),
-            ToTyped(msg) => write!(f, "lektor formated to typed error: {msg}"),
-        }
-    }
-}
-
-pub struct LektorConnexion {
-    pub version: String,
-    stream: TcpStream,
-    reader: BufReader<TcpStream>,
-}
-
-impl std::fmt::Debug for LektorConnexion {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("LektorConnexion")
-            .field("stream", &self.stream)
-            .field("version", &self.version)
-            .field("reader", &self.reader)
-            .finish()
-    }
-}
-
-impl LektorConnexion {
-    pub fn new(hostname: impl AsRef<str>, port: i16) -> io::Result<Self> {
-        match TcpStream::connect(format!("{}:{}", hostname.as_ref(), port)) {
-            Ok(lektord) => {
-                let mut ret: Self = Self {
-                    stream: lektord.try_clone().unwrap(),
-                    version: constants::MPD_VERSION.to_owned(),
-                    reader: BufReader::new(lektord),
-                };
-                let mut daemon_version: String = String::new();
-                ret.reader.read_line(&mut daemon_version).unwrap();
-                assert_eq!(
-                    daemon_version.trim(),
-                    format!("OK MPD {}", ret.version),
-                    "Expected MPD server version {}, but got version {}, abort!",
-                    ret.version,
-                    daemon_version
-                );
-                Ok(ret)
-            }
-            Err(e) => {
-                error!("Failed to connect to lektor: {e}");
-                Err(e)
-            }
-        }
-    }
-
-    pub fn send_query(&mut self, query: LektorQuery) -> Result<LektorResponse, LektorQueryError> {
-        let mut res: Vec<String> = Vec::new();
-        query.verify().map_err(LektorQueryError::Query)?;
-        let builder = query.get_response_type();
-        self.send_query_inner(query, &mut res)
-            .map_err(LektorQueryError::IO)?;
-        let formated = LektorFormatedResponse::try_from(res).map_err(LektorQueryError::Other)?;
-        builder(formated).map_err(LektorQueryError::ToTyped)
-    }
-
-    fn send_query_inner(
-        &mut self,
-        query: LektorQuery,
-        previous_ret: &mut Vec<String>,
-    ) -> io::Result<()> {
-        self.write_string(query.to_string())?;
-        loop {
-            match self.read_replies() {
-                Err(e) => return Err(e),
-                Ok((res, _)) if res.is_empty() => return Ok(()),
-                Ok((res, None)) => {
-                    return {
-                        previous_ret.extend(res);
-                        Ok(())
-                    }
-                }
-                Ok((res, Some(cont))) => {
-                    previous_ret.extend(res);
-                    self.write_string(
-                        LektorQuery::create_continuation(query.clone(), cont).to_string(),
-                    )?;
-                }
-            }
-        }
-    }
-
-    fn read_replies(&mut self) -> io::Result<(Vec<String>, Option<usize>)> {
-        let error_return_value = io::Result::Err(io::Error::from(io::ErrorKind::Other));
-        let mut ret: Vec<String> = Vec::new();
-        let mut reply_line: String = String::new();
-        let mut continuation = None;
-        loop {
-            reply_line.clear();
-            match self.reader.read_line(&mut reply_line) {
-                Err(e) => return io::Result::Err(e),
-                Ok(size) => {
-                    if size == 0 {
-                        return io::Result::Err(io::Error::new(
-                            io::ErrorKind::Other,
-                            "recieved empty line",
-                        ));
-                    }
-                    let msg = reply_line.trim();
-                    match LektorQueryLineType::from_str(msg) {
-                        Ok(LektorQueryLineType::Ok) => return Ok((ret, continuation)),
-                        Ok(LektorQueryLineType::Ack) => return error_return_value,
-                        Ok(LektorQueryLineType::Data) => ret.push(msg.to_string()),
-                        Ok(LektorQueryLineType::ListOk) => continue,
-                        Ok(LektorQueryLineType::Continuation(cont)) => continuation = Some(cont),
-                        Err(_) => {
-                            return Err(io::Error::new(
-                                io::ErrorKind::Other,
-                                "unknown query line type",
-                            ))
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    fn write_string(&mut self, buffer: impl AsRef<str>) -> io::Result<()> {
-        let buffer = buffer.as_ref();
-        if buffer.len() >= constants::LKT_MESSAGE_MAX {
-            Err(io::Error::new(
-                io::ErrorKind::InvalidData,
-                "buffer to send is to big!",
-            ))
-        } else if !buffer.ends_with('\n') {
-            Err(io::Error::new(
-                io::ErrorKind::InvalidData,
-                "buffer dosen't end with a new line",
-            ))
-        } else {
-            self.stream.write_all(buffer.as_bytes())
-        }
-    }
-}
diff --git a/src/rust/amadeus-rs/lkt-lib/src/constants.rs b/src/rust/amadeus-rs/lkt-lib/src/constants.rs
deleted file mode 100644
index 505f0af8e8e8bc5773341ca8fc6496988cb76508..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/src/constants.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-//! Contains standard constants from lektord, must be updated manually if they
-//! change in lektord...
-
-#![allow(dead_code)]
-
-/// MPD commands are at most 32 character long.
-pub(crate) const LKT_COMMAND_LEN_MAX: usize = 32;
-
-/// At most 32 words in a command are supported.
-pub(crate) const LKT_MESSAGE_ARGS_MAX: usize = 32;
-
-/// A message is at most <defined> chars long
-pub(crate) const LKT_MESSAGE_MAX: usize = 2048;
-
-/// At most 64 commands per client.
-pub(crate) const COMMAND_LIST_MAX: usize = 64;
-
-/// At most 16 messages per client.
-pub(crate) const BUFFER_OUT_MAX: usize = 16;
-
-/// Expected version from the lektord daemin.
-pub(crate) const MPD_VERSION: &str = "0.21.16";
diff --git a/src/rust/amadeus-rs/lkt-lib/src/lib.rs b/src/rust/amadeus-rs/lkt-lib/src/lib.rs
deleted file mode 100644
index 331d354dd87a2a3762e3e6686bab89c9f66dcaaf..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/src/lib.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//! Contains structurs and mechanisms to build, send and recieve commands
-//! to/from lektord.
-
-mod connexion;
-mod constants;
-mod query;
-mod response;
-mod types;
-mod uri;
-
-pub use connexion::*;
-pub use query::*;
-pub use response::*;
-pub use types::*;
-pub use uri::*;
diff --git a/src/rust/amadeus-rs/lkt-lib/src/query.rs b/src/rust/amadeus-rs/lkt-lib/src/query.rs
deleted file mode 100644
index 9abc3e277784cdab5719a19a02552a6b0ccf688a..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/src/query.rs
+++ /dev/null
@@ -1,180 +0,0 @@
-//! Contains files to create and build queries to send latter to lektord.
-
-use crate::*;
-use amadeus_macro::*;
-use std::string::ToString;
-
-pub(crate) enum LektorQueryLineType {
-    Ok,
-    ListOk,
-    Ack,
-    Continuation(usize),
-    Data,
-}
-
-#[derive(Debug, Clone)]
-pub enum LektorQuery {
-    Ping,
-    Close,
-    KillServer,
-    ConnectAsUser(String, Box<LektorQuery>),
-
-    CurrentKara,
-    PlaybackStatus,
-
-    PlayNext,
-    PlayPrevious,
-    ShuffleQueue,
-
-    ListAllPlaylists,
-    ListPlaylist(String),
-    SearchKara(LektorUri),
-
-    FindAddKara(LektorUri),
-    InsertKara(LektorUri),
-    AddKara(LektorUri),
-
-    Continuation(usize, Box<LektorQuery>),
-}
-
-impl std::str::FromStr for LektorQueryLineType {
-    type Err = ();
-
-    fn from_str(line: &str) -> Result<Self, Self::Err> {
-        if Self::is_line_ok(line) {
-            Ok(Self::Ok)
-        } else if Self::is_line_ack(line) {
-            Ok(Self::Ack)
-        } else if Self::is_line_list_ok(line) {
-            Ok(Self::ListOk)
-        } else if let Some(cont) = Self::is_line_continuation(line) {
-            Ok(Self::Continuation(cont))
-        } else {
-            Ok(Self::Data)
-        }
-    }
-}
-
-impl LektorQueryLineType {
-    fn is_line_continuation(line: &str) -> Option<usize> {
-        if !line.starts_with("continue:") {
-            return None;
-        }
-        match line.trim_start_matches("continue:").trim().parse::<usize>() {
-            Ok(cont) => Some(cont),
-            Err(_) => None,
-        }
-    }
-
-    fn is_line_list_ok(line: &str) -> bool {
-        (line == "list_OK\n") || (line == "list_OK")
-    }
-
-    fn is_line_ok(line: &str) -> bool {
-        (line == "OK\n") || (line == "OK")
-    }
-
-    fn is_line_ack(line: &str) -> bool {
-        line.starts_with("ACK: ")
-    }
-}
-
-/// The type of the function to use to produce a typed response from the
-/// formated response.
-type QueryToTypeResponseBuilder = fn(LektorFormatedResponse) -> Result<LektorResponse, String>;
-
-impl LektorQuery {
-    /// Get the function to use to produce the typed response from the formated
-    /// response, to automatically create the correct thing.
-    pub fn get_response_type(&self) -> QueryToTypeResponseBuilder {
-        use LektorQuery::*;
-        match self {
-            Ping | Close | KillServer | PlayNext | PlayPrevious | ShuffleQueue | InsertKara(_)
-            | AddKara(_) => LektorEmptyResponse::from_formated,
-
-            ListAllPlaylists => LektorPlaylistSetResponse::from_formated,
-            PlaybackStatus => LektorPlaybackStatusResponse::from_formated,
-            CurrentKara => LektorCurrentKaraResponse::from_formated,
-
-            ConnectAsUser(_, cmd) | Continuation(_, cmd) => cmd.get_response_type(),
-
-            ListPlaylist(_) | SearchKara(_) | FindAddKara(_) => {
-                LektorKaraSetResponse::from_formated
-            }
-        }
-    }
-
-    /// Create a continued query out of another one. If the query is already a
-    /// continuation query then the underlying query is reused.
-    pub fn create_continuation(query: Self, cont: usize) -> Self {
-        match query {
-            Self::Continuation(_, query) => Self::Continuation(cont, query),
-            _ => Self::Continuation(cont, Box::new(query)),
-        }
-    }
-
-    /// Verify that a query is Ok.
-    pub fn verify(&self) -> Result<(), String> {
-        use LektorQuery::*;
-        match self {
-            // User commands
-            SearchKara(_) | FindAddKara(_) | InsertKara(_) | AddKara(_) | PlaybackStatus
-            | PlayNext | PlayPrevious | ShuffleQueue | ListAllPlaylists | ListPlaylist(_)
-            | CurrentKara | Ping => Ok(()),
-
-            // Should be admin commands
-            Close => Err("close is an admin command".to_string()),
-            KillServer => Err("kill server is an admin command".to_string()),
-
-            // Admin commands
-            ConnectAsUser(_, cmd) => match cmd.as_ref() {
-                Close | KillServer => Ok(()),
-                _ => Err(format!("not an admin command: {cmd:?}")),
-            },
-
-            // Continuation commands
-            Continuation(_, cmd) => match cmd.as_ref() {
-                ListAllPlaylists | FindAddKara(_) | SearchKara(_) | ListPlaylist(_) => Ok(()),
-                _ => Err(format!("not a continuable command: {cmd:?}")),
-            },
-        }
-    }
-}
-
-impl ToString for LektorQuery {
-    fn to_string(&self) -> String {
-        use LektorQuery::*;
-        match self {
-            Ping => lkt_command_from_str!("ping"),
-            Close => lkt_command_from_str!("close"),
-            KillServer => lkt_command_from_str!("kill"),
-            ConnectAsUser(password, cmd) => format!(
-                concat!(
-                    "command_list_ok_begin\n",
-                    "password {}\n",
-                    "{}\n",
-                    "command_list_end\n",
-                ),
-                password,
-                cmd.to_string()
-            ),
-
-            CurrentKara => lkt_command_from_str!("currentsong"),
-            PlaybackStatus => lkt_command_from_str!("status"),
-
-            PlayNext => lkt_command_from_str!("next"),
-            PlayPrevious => lkt_command_from_str!("previous"),
-            ShuffleQueue => lkt_command_from_str!("shuffle"),
-
-            ListAllPlaylists => lkt_command_from_str!("listplaylists"),
-            ListPlaylist(plt_name) => format!("listplaylist {}\n", plt_name),
-            SearchKara(uri) => format!("find {}\n", uri.to_string()),
-
-            FindAddKara(uri) => format!("findadd {}\n", uri.to_string()),
-            InsertKara(uri) => format!("__insert {}\n", uri.to_string()),
-            AddKara(uri) => format!("add {}\n", uri.to_string()),
-
-            Continuation(cont, query) => format!("{cont} {}", query.to_owned().to_string()),
-        }
-    }
-}
diff --git a/src/rust/amadeus-rs/lkt-lib/src/response.rs b/src/rust/amadeus-rs/lkt-lib/src/response.rs
deleted file mode 100644
index 127fa3037ba6c8782723b4ded5ec76d6af7e6513..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/src/response.rs
+++ /dev/null
@@ -1,341 +0,0 @@
-//! Contains types for typed response.
-
-use crate::types::LektorState;
-use amadeus_macro::*;
-
-/// A formated response is just a list of key/pairs. We get every line that is
-/// not Ok/Ack/Continue (i.e. data lines) and split on the first ':' and trim
-/// spaces from the keys and the values. The keys are always in lowercase.
-#[derive(Debug)]
-pub struct LektorFormatedResponse {
-    content: Vec<(String, String)>,
-    raw_content: Vec<String>,
-}
-
-impl LektorFormatedResponse {
-    /// Pop the first key found in the response, get an error if the key is not
-    /// found. If multiple keys are found, only the first found is returned.
-    pub fn pop(&mut self, key: &str) -> Result<String, String> {
-        match self
-            .content
-            .iter()
-            .enumerate()
-            .find_map(|(index, (what, _))| then_some!(what == key => index))
-        {
-            Some(index) => Ok(self.content.remove(index).1),
-            None => Err(format!("no key {key} was found in formated response")),
-        }
-    }
-
-    /// Pop all the entries with the said key. If no key is found then the empty
-    /// vector is returned. This function can't fail.
-    ///
-    /// FIXME: Get ride of the clone in this function...
-    pub fn pop_all(&mut self, key: &str) -> Vec<String> {
-        let mut ret = Vec::new();
-        self.content.retain(|(what, field)| {
-            if *what == key {
-                ret.push(field.clone());
-                false
-            } else {
-                true
-            }
-        });
-        ret
-    }
-
-    /// Get the raw content of the response.
-    pub fn pop_raw(self) -> Vec<String> {
-        self.raw_content
-    }
-}
-
-impl IntoIterator for LektorFormatedResponse {
-    type Item = (String, String);
-    type IntoIter =
-        <std::vec::Vec<(std::string::String, std::string::String)> as IntoIterator>::IntoIter;
-
-    fn into_iter(self) -> Self::IntoIter {
-        self.content.into_iter()
-    }
-}
-
-impl TryFrom<Vec<String>> for LektorFormatedResponse {
-    type Error = String;
-
-    fn try_from(vec: Vec<String>) -> Result<Self, Self::Error> {
-        let (mut content, mut raw_content) = (Vec::new(), Vec::new());
-        for line in vec {
-            match line.splitn(2, ':').collect::<Vec<&str>>()[..] {
-                [key, value] => content.push((key.trim().to_lowercase(), value.trim().to_string())),
-                _ => raw_content.push(line),
-            }
-        }
-        Ok(Self {
-            content,
-            raw_content,
-        })
-    }
-}
-
-#[derive(Debug)]
-pub enum LektorResponse {
-    PlaybackStatus(LektorPlaybackStatusResponse),
-    CurrentKara(LektorCurrentKaraResponse),
-    PlaylistSet(LektorPlaylistSetResponse),
-    EmptyResponse(LektorEmptyResponse),
-    KaraSet(LektorKaraSetResponse),
-}
-
-/// A trait for typed lektor responses. Such responses must be built by
-/// consuming a formated response. We also protect from implemeting this trait
-/// outside of this crate.
-pub trait FromLektorResponse: std::fmt::Debug + private::Sealed {
-    /// Consume a formated response to produce the correctly typed response. May
-    /// got an error as a string that describes the problem.
-    fn from_formated(response: LektorFormatedResponse) -> Result<LektorResponse, String>
-    where
-        Self: Sized;
-}
-
-mod private {
-    use super::*;
-    pub trait Sealed {}
-    impl Sealed for LektorPlaybackStatusResponse {}
-    impl Sealed for LektorCurrentKaraResponse {}
-    impl Sealed for LektorPlaylistSetResponse {}
-    impl Sealed for LektorKaraSetResponse {}
-    impl Sealed for LektorEmptyResponse {}
-}
-
-macro_rules! getter {
-    ($name: ident: ref $type: ty) => {
-        pub fn $name(&self) -> &$type {
-            &self.$name
-        }
-    };
-
-    ($name: ident: $type: ty) => {
-        pub fn $name(&self) -> $type {
-            self.$name
-        }
-    };
-}
-
-#[derive(Debug)]
-pub struct LektorPlaybackStatusResponse {
-    elapsed: usize,
-    songid: Option<usize>,
-    song: Option<usize>,
-    volume: usize,
-    state: LektorState,
-    duration: usize,
-    updating_db: usize,
-    playlistlength: usize,
-    random: bool,
-    consume: bool,
-    single: bool,
-    repeat: bool,
-}
-
-#[derive(Debug)]
-pub struct LektorPlaylistSetResponse {
-    playlists: Vec<String>,
-}
-
-#[derive(Debug)]
-pub struct LektorCurrentKaraInnerResponse {
-    title: String,
-    author: String,
-    source: String,
-    song_type: String,
-    song_number: Option<usize>,
-    category: String,
-    language: String,
-}
-
-#[derive(Debug)]
-pub struct LektorKaraSetResponse {
-    karas: Vec<String>,
-}
-
-#[derive(Debug)]
-pub struct LektorCurrentKaraResponse {
-    content: Option<LektorCurrentKaraInnerResponse>,
-}
-
-#[derive(Debug)]
-pub struct LektorEmptyResponse;
-
-impl LektorCurrentKaraResponse {
-    getter!(content: ref Option<LektorCurrentKaraInnerResponse>);
-
-    pub fn maybe_into_inner(self) -> Option<LektorCurrentKaraInnerResponse> {
-        self.content
-    }
-
-    pub fn into_inner(self) -> LektorCurrentKaraInnerResponse {
-        self.content.unwrap()
-    }
-}
-
-impl LektorCurrentKaraInnerResponse {
-    getter!(title: ref String);
-    getter!(author: ref String);
-    getter!(source: ref String);
-    getter!(song_type: ref String);
-    getter!(song_number: Option<usize>);
-    getter!(category: ref String);
-    getter!(language: ref String);
-}
-
-impl LektorPlaybackStatusResponse {
-    getter!(elapsed: usize);
-    getter!(songid: Option<usize>);
-    getter!(song: Option<usize>);
-    getter!(volume: usize);
-    getter!(state: LektorState);
-    getter!(duration: usize);
-    getter!(updating_db: usize);
-    getter!(playlistlength: usize);
-    getter!(random: bool);
-    getter!(consume: bool);
-    getter!(single: bool);
-    getter!(repeat: bool);
-
-    pub fn verify(&self) -> bool {
-        self.elapsed() <= self.duration()
-    }
-}
-
-impl LektorKaraSetResponse {
-    pub fn iter(&self) -> &[String] {
-        &self.karas[..]
-    }
-}
-
-impl LektorPlaylistSetResponse {
-    pub fn iter(&self) -> &[String] {
-        &self.playlists[..]
-    }
-}
-
-impl IntoIterator for LektorKaraSetResponse {
-    type Item = String;
-    type IntoIter = <std::vec::Vec<std::string::String> as IntoIterator>::IntoIter;
-    fn into_iter(self) -> Self::IntoIter {
-        self.karas.into_iter()
-    }
-}
-
-impl IntoIterator for LektorPlaylistSetResponse {
-    type Item = String;
-    type IntoIter = <std::vec::Vec<std::string::String> as IntoIterator>::IntoIter;
-    fn into_iter(self) -> Self::IntoIter {
-        self.playlists.into_iter()
-    }
-}
-
-impl LektorCurrentKaraInnerResponse {
-    /// If the response is partial we might want to return none (if we are not
-    /// playing anything for example...)
-    pub(self) fn is_partial(&self) -> bool {
-        self.title.is_empty()
-            || self.author.is_empty()
-            || self.source.is_empty()
-            || self.song_type.is_empty()
-            || self.category.is_empty()
-            || self.language.is_empty()
-    }
-}
-
-impl FromLektorResponse for LektorEmptyResponse {
-    fn from_formated(_: LektorFormatedResponse) -> Result<LektorResponse, String> {
-        Ok(LektorResponse::EmptyResponse(Self {}))
-    }
-}
-
-impl FromLektorResponse for LektorPlaylistSetResponse {
-    fn from_formated(mut response: LektorFormatedResponse) -> Result<LektorResponse, String> {
-        Ok(LektorResponse::PlaylistSet(Self {
-            playlists: response.pop_all("name"),
-        }))
-    }
-}
-
-impl FromLektorResponse for LektorKaraSetResponse {
-    fn from_formated(response: LektorFormatedResponse) -> Result<LektorResponse, String>
-    where
-        Self: Sized,
-    {
-        Ok(LektorResponse::KaraSet(Self {
-            karas: response.pop_raw(),
-        }))
-    }
-}
-
-impl FromLektorResponse for LektorPlaybackStatusResponse {
-    fn from_formated(mut response: LektorFormatedResponse) -> Result<LektorResponse, String> {
-        let mut ret = Self {
-            elapsed: response.pop("elapsed")?.parse::<usize>().unwrap_or(0),
-            songid: match response.pop("songid")?.parse::<isize>() {
-                Ok(x) if x <= 0 => None,
-                Ok(x) => Some(x as usize),
-                Err(_) => None,
-            },
-            song: match response.pop("song")?.parse::<usize>() {
-                Ok(x) => Some(x),
-                Err(_) => None,
-            },
-            volume: response
-                .pop("volume")?
-                .parse::<usize>()
-                .unwrap()
-                .clamp(0, 100),
-            duration: response.pop("duration")?.parse::<usize>().unwrap(),
-            updating_db: response.pop("updating_db")?.parse::<usize>().unwrap(),
-            playlistlength: response.pop("playlistlength")?.parse::<usize>().unwrap(),
-            random: response.pop("random")?.parse::<usize>().unwrap() != 0,
-            consume: response.pop("consume")?.parse::<usize>().unwrap() != 0,
-            single: response.pop("single")?.parse::<usize>().unwrap() != 0,
-            repeat: response.pop("repeat")?.parse::<usize>().unwrap() != 0,
-            state: LektorState::Stopped,
-        };
-        ret.state = match &response.pop("state")?[..] {
-            "play" => LektorState::Play(ret.songid.unwrap()),
-            "pause" => LektorState::Pause(ret.songid.unwrap()),
-            _ => LektorState::Stopped,
-        };
-        Ok(LektorResponse::PlaybackStatus(ret))
-    }
-}
-
-impl FromLektorResponse for LektorCurrentKaraResponse {
-    fn from_formated(mut response: LektorFormatedResponse) -> Result<LektorResponse, String> {
-        let song_type_number = response.pop("type")?;
-        let (song_type, song_number) = match song_type_number.find(char::is_numeric) {
-            Some(index) => (
-                song_type_number[..index].to_owned(),
-                match song_type_number[index..].parse::<usize>() {
-                    Ok(x) => Some(x),
-                    Err(_) => None,
-                },
-            ),
-            None => panic!("Oupsy"),
-        };
-
-        let inner = LektorCurrentKaraInnerResponse {
-            title: response.pop("title")?,
-            author: response.pop("author")?,
-            source: response.pop("source")?,
-            category: response.pop("category")?,
-            language: response.pop("language")?,
-            song_type,
-            song_number,
-        };
-
-        Ok(LektorResponse::CurrentKara(Self {
-            content: then_some!(!inner.is_partial() => inner),
-        }))
-    }
-}
diff --git a/src/rust/amadeus-rs/lkt-lib/src/types.rs b/src/rust/amadeus-rs/lkt-lib/src/types.rs
deleted file mode 100644
index cab41589de820bd5f50b231de7888480848eacf2..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/src/types.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-//! Common types for lektord.
-
-use std::hash::{Hash, Hasher};
-
-/// Lektor Types are the Kara and the Playlist types. You should not implement
-/// this type yourself.
-pub trait LektorType<'a>:
-    Clone + serde::Serialize + serde::Deserialize<'a> + Hash + private::Sealed
-{
-    fn unique_id(&self) -> u64;
-}
-
-#[derive(Clone, Eq, serde::Serialize, serde::Deserialize)]
-pub struct Kara {
-    pub id: u32,
-    pub source_name: String,
-
-    #[serde(rename = "type")]
-    pub song_type: String,
-
-    pub language: String,
-    pub category: String,
-    pub title: String,
-    pub song_number: Option<u32>,
-    pub author: String,
-    pub is_available: bool,
-}
-
-#[derive(Clone, Eq, serde::Serialize, serde::Deserialize, PartialEq, Hash)]
-pub struct Playlist {
-    pub name: String,
-}
-
-impl PartialEq for Kara {
-    fn eq(&self, other: &Self) -> bool {
-        self.id == other.id
-    }
-}
-
-impl LektorType<'_> for Kara {
-    fn unique_id(&self) -> u64 {
-        private::unique_id(self)
-    }
-}
-
-impl LektorType<'_> for Playlist {
-    fn unique_id(&self) -> u64 {
-        private::unique_id(self)
-    }
-}
-
-#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq, Copy)]
-pub enum LektorState {
-    Stopped,
-    Play(usize),
-    Pause(usize),
-}
-
-impl Default for LektorState {
-    fn default() -> Self {
-        Self::Stopped
-    }
-}
-
-/// We seal the implementation only for supported types.
-mod private {
-    use super::{Kara, Playlist};
-    use std::{
-        collections::hash_map::DefaultHasher,
-        hash::{Hash, Hasher},
-    };
-
-    pub trait Sealed {}
-
-    impl Sealed for Kara {}
-    impl Sealed for Playlist {}
-
-    pub(crate) fn unique_id<T: Hash + Sealed>(object: &T) -> u64 {
-        let mut s = DefaultHasher::new();
-        object.hash(&mut s);
-        s.finish()
-    }
-}
-
-/// Sealed implementation of Hash for Kara.
-impl Hash for Kara {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        ((self.id as usize) * 2).hash(state);
-    }
-}
diff --git a/src/rust/amadeus-rs/lkt-lib/src/uri.rs b/src/rust/amadeus-rs/lkt-lib/src/uri.rs
deleted file mode 100644
index 7c1e8308540a95ae32976828bfb6483a9e664e1f..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-lib/src/uri.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-//! Build lektord queries.
-
-#[derive(Debug, Clone)]
-pub enum LektorUri {
-    Id(i32),
-    Author(String),
-    Playlist(String),
-    Query(String),
-}
-
-impl ToString for LektorUri {
-    fn to_string(&self) -> String {
-        match self {
-            Self::Id(id) => format!("id://{}", id),
-            Self::Author(author) => format!("author://{}", author),
-            Self::Playlist(plt_name) => format!("playlist://{}", plt_name),
-            Self::Query(sql_query) => format!("query://%{}%", sql_query),
-        }
-    }
-}
diff --git a/src/rust/amadeus-rs/lkt-rs/Cargo.toml b/src/rust/amadeus-rs/lkt-rs/Cargo.toml
deleted file mode 100644
index 9909296afe0d25253ac3135d17e9fa2fdbff75af..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-rs/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name    = "lkt"
-version = "0.1.0"
-edition = "2021"
-license = "MIT"
-
-[dependencies]
-lkt_lib       = { path = "../lkt-lib" }
-amadeus_macro = { path = "../amadeus-macro" }
-serde         = { version = "1",                           features = [ "derive" ] }
-serde_json    = { version = "1", default-features = false, features = [ "std" ] }
-log           = { version = "0.4" }
-lazy_static   = "1"
diff --git a/src/rust/amadeus-rs/lkt-rs/src/main.rs b/src/rust/amadeus-rs/lkt-rs/src/main.rs
deleted file mode 100644
index f50969b3de81a74ab32d1247b7aca91ed3f0ee14..0000000000000000000000000000000000000000
--- a/src/rust/amadeus-rs/lkt-rs/src/main.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use lkt_lib::*;
-
-fn main() {
-    let mut lektor = LektorConnexion::new("localhost", 6600).unwrap();
-    if lektor.send_query(LektorQuery::Ping).is_ok() {}
-
-    if let Ok(LektorResponse::CurrentKara(current)) = lektor.send_query(LektorQuery::CurrentKara) {
-        println!("CURRENT {current:?}");
-    }
-
-    if let Ok(LektorResponse::PlaybackStatus(status)) =
-        lektor.send_query(LektorQuery::PlaybackStatus)
-    {
-        println!("STATUS {status:?}");
-    }
-
-    if let Ok(LektorResponse::PlaylistSet(plts)) = lektor.send_query(LektorQuery::ListAllPlaylists)
-    {
-        println!("PLTS {plts:?}");
-    }
-}
diff --git a/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/uri.rs b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/uri.rs
index d58c370c2bf216e895d52e615f6d9ef2a9ed0592..9c77af5f18a1f4dfdb3e4d882745f12db951cc0f 100644
--- a/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/uri.rs
+++ b/src/rust/liblektor-rs/lektor_c_compat/src/rs_types/uri.rs
@@ -34,7 +34,7 @@ pub enum LktUriField {
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub enum LktUriValue<'a> {
     String(&'a str),
-    Integer(i32),
+    Integer(i64),
 }
 
 impl Default for LktUriField {
@@ -82,11 +82,11 @@ impl LktCUri {
 
     /// Get the URI value as an int. If the contained thing is not an integer
     /// returns None.
-    fn get_value_as_int(&self) -> Option<i32> {
+    fn get_value_as_int(&self) -> Option<i64> {
         use LktUriValueType::*;
         match self.get_value_type() {
             Null | String => None,
-            Integer => Some(unsafe { lkt_uri_get_value_as_int(self.ptr) }),
+            Integer => Some(unsafe { lkt_uri_get_value_as_int(self.ptr) } as i64),
         }
     }
 
diff --git a/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql b/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql
index dcd18261383281758e597fdb21df33ba3ac4f844..b75c9f725b1df1e12fe17457a66bfdbb6b00b326 100644
--- a/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql
+++ b/src/rust/liblektor-rs/lektor_db/migrations/2022-09-30-204512_initial/up.sql
@@ -1,23 +1,23 @@
 -- A list of repos karas where downloaded from. All repos should have different
 -- names! Their IDs are local to the client.
 CREATE TABLE repo
-  ( id   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
-  , name TEXT    NOT NULL UNIQUE
+  ( id   BIGINT NOT NULL PRIMARY KEY
+  , name TEXT   NOT NULL UNIQUE
   );
 
 -- Link a kara in the local referencial to its reference in the distant repo.
 -- Local IDs are all uniques and every client should be expected to have
 -- different ones.
 CREATE TABLE repo_kara
-  ( repo_id       INTEGER NOT NULL REFERENCES repo(id)
-  , repo_kara_id  INTEGER NOT NULL
-  , local_kara_id INTEGER NOT NULL REFERENCES kara(id)
+  ( repo_id       BIGINT NOT NULL REFERENCES repo(id)
+  , repo_kara_id  BIGINT NOT NULL
+  , local_kara_id BIGINT NOT NULL REFERENCES kara(id)
   , PRIMARY KEY (repo_id, repo_kara_id, local_kara_id)
   );
 
 -- A kara entry in the databse.
 CREATE TABLE kara
-  ( id          INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
+  ( id          BIGINT  NOT NULL PRIMARY KEY
   , is_dl       BOOLEAN NOT NULL DEFAULT false
   , song_title  TEXT    NOT NULL
   , song_type   TEXT    NOT NULL
@@ -27,23 +27,23 @@ CREATE TABLE kara
   );
 
 -- We can have multiple kara makers for one kara.
-CREATE TABLE kara_makers
-  ( id   INTEGER NOT NULL REFERENCES kara ON DELETE CASCADE
-  , name TEXT    NOT NULL
+CREATE TABLE kara_maker
+  ( id   BIGINT NOT NULL REFERENCES kara ON DELETE CASCADE
+  , name TEXT   NOT NULL
   , PRIMARY KEY (id, name)
   );
 
 -- Tags are informations used to be able to make queries in a easier way.
 CREATE TABLE tag
-  ( id   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
-  , name TEXT    NOT NULL UNIQUE
+  ( id   BIGINT NOT NULL PRIMARY KEY
+  , name TEXT   NOT NULL UNIQUE
   );
 
 -- The content of a tag for kara. Multiple tags for a single kara with the same
 -- tag id means that there a list of values.
-CREATE TABLE kara_tags
-  ( kara_id INTEGER NOT NULL REFERENCES kara(id) ON DELETE CASCADE
-  , tag_id  INTEGER NOT NULL REFERENCES tag(id)  ON DELETE CASCADE
+CREATE TABLE kara_tag
+  ( kara_id BIGINT NOT NULL REFERENCES kara(id) ON DELETE CASCADE
+  , tag_id  BIGINT NOT NULL REFERENCES tag(id)  ON DELETE CASCADE
   , value   TEXT
   , PRIMARY KEY (kara_id, tag_id, value)
   );
@@ -51,8 +51,8 @@ CREATE TABLE kara_tags
 -- Store the history of played karas from the queue. For now we allow a kara to
 -- appear multiple times in the history.
 CREATE TABLE history
-  ( id      INTEGER NOT NULL REFERENCES kara(id) ON DELETE CASCADE
-  , epoch   INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
+  ( id      BIGINT NOT NULL REFERENCES kara(id) ON DELETE CASCADE
+  , epoch   BIGINT NOT NULL PRIMARY KEY
   );
 
 -- The list of ISO 639-1 languages with their associated codes.
@@ -64,43 +64,8 @@ CREATE TABLE iso_639_1
   , is_macro BOOLEAN NOT NULL DEFAULT false
   );
 
-CREATE TABLE kara_langs
-  ( id   INTEGER NOT NULL REFERENCES kara      ON DELETE CASCADE
-  , code TEXT    NOT NULL REFERENCES iso_639_1 ON DELETE CASCADE
+CREATE TABLE kara_lang
+  ( id   BIGINT NOT NULL REFERENCES kara      ON DELETE CASCADE
+  , code TEXT   NOT NULL REFERENCES iso_639_1 ON DELETE CASCADE
   , PRIMARY KEY (id, code)
   );
-
--- As defined in ISO 639-1:
--- https://archive.wikiwix.com/cache/index2.php?url=http%3A%2F%2Fwww.sil.org%2Fiso639-3%2Fcodes.asp%3Forder%3D639_1%26letter%3D%2525#federation=archive.wikiwix.com&tab=url
-INSERT OR REPLACE INTO iso_639_1 (is_macro, is_iso, code, name_en) VALUES
-  ( true,  true, "ar", "Arabic"         ),
-  ( false, true, "br", "Breton"         ),
-  ( false, true, "ca", "Catalan"        ),
-  ( false, true, "de", "German"         ),
-  ( false, true, "el", "Greek"          ),
-  ( false, true, "en", "English"        ),
-  ( false, true, "eo", "Esperanto"      ),
-  ( false, true, "es", "Spanish"        ),
-  ( false, true, "eu", "Basque"         ),
-  ( true,  true, "fa", "Persian"        ),
-  ( false, true, "fr", "French"         ),
-  ( false, true, "he", "Hebrew"         ),
-  ( true,  true, "ie", "Interlingue"    ),
-  ( false, true, "it", "Italian"        ),
-  ( false, true, "ja", "Japanese"       ),
-  ( false, true, "ko", "Korean"         ),
-  ( false, true, "la", "Latin"          ),
-  ( false, true, "nl", "Dutch"          ),
-  ( false, true, "no", "Norwegian"      ),
-  ( false, true, "oc", "Occitan"        ),
-  ( false, true, "pl", "Polish"         ),
-  ( false, true, "pt", "Portuguese"     ),
-  ( false, true, "ru", "Russian"        ),
-  ( false, true, "sv", "Swedish"        ),
-  ( false, true, "vi", "Vietnamese"     ),
-  ( false, true, "zh", "Chinese"        ),
-  ( false, true, "zu", "Zulu"           ),
-  -- The things we added because they were not defined in the ISO 639-1 and
-  -- because we need them to distinguish some karas.
-  ( true,  false, "fx", "Fictional"     ),
-  ( true,  false, "ot", "Joker"         );
diff --git a/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093114_iso-639-1/down.sql b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093114_iso-639-1/down.sql
new file mode 100644
index 0000000000000000000000000000000000000000..7cb99c19129b81d7dd7db57b0b68533dbb2abace
--- /dev/null
+++ b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093114_iso-639-1/down.sql
@@ -0,0 +1 @@
+DELETE FROM iso_639_1;
\ No newline at end of file
diff --git a/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093114_iso-639-1/up.sql b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093114_iso-639-1/up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..775b44c089840d483155887c1dfb113419f55a21
--- /dev/null
+++ b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093114_iso-639-1/up.sql
@@ -0,0 +1,34 @@
+-- As defined in ISO 639-1:
+-- https://archive.wikiwix.com/cache/index2.php?url=http%3A%2F%2Fwww.sil.org%2Fiso639-3%2Fcodes.asp%3Forder%3D639_1%26letter%3D%2525#federation=archive.wikiwix.com&tab=url
+INSERT OR REPLACE INTO iso_639_1 (is_macro, is_iso, code, name_en) VALUES
+  ( true,  true, "ar", "Arabic"         ),
+  ( false, true, "br", "Breton"         ),
+  ( false, true, "ca", "Catalan"        ),
+  ( false, true, "de", "German"         ),
+  ( false, true, "el", "Greek"          ),
+  ( false, true, "en", "English"        ),
+  ( false, true, "eo", "Esperanto"      ),
+  ( false, true, "es", "Spanish"        ),
+  ( false, true, "eu", "Basque"         ),
+  ( true,  true, "fa", "Persian"        ),
+  ( false, true, "fr", "French"         ),
+  ( false, true, "he", "Hebrew"         ),
+  ( true,  true, "ie", "Interlingue"    ),
+  ( false, true, "it", "Italian"        ),
+  ( false, true, "ja", "Japanese"       ),
+  ( false, true, "ko", "Korean"         ),
+  ( false, true, "la", "Latin"          ),
+  ( false, true, "nl", "Dutch"          ),
+  ( false, true, "no", "Norwegian"      ),
+  ( false, true, "oc", "Occitan"        ),
+  ( false, true, "pl", "Polish"         ),
+  ( false, true, "pt", "Portuguese"     ),
+  ( false, true, "ru", "Russian"        ),
+  ( false, true, "sv", "Swedish"        ),
+  ( false, true, "vi", "Vietnamese"     ),
+  ( false, true, "zh", "Chinese"        ),
+  ( false, true, "zu", "Zulu"           ),
+  -- The things we added because they were not defined in the ISO 639-1 and
+  -- because we need them to distinguish some karas.
+  ( true,  false, "fx", "Fictional"     ),
+  ( true,  false, "ot", "Joker"         );
\ No newline at end of file
diff --git a/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093422_playlists/down.sql b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093422_playlists/down.sql
new file mode 100644
index 0000000000000000000000000000000000000000..837d192c96d1822cf0e640aa052b9d1805b70d48
--- /dev/null
+++ b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093422_playlists/down.sql
@@ -0,0 +1,2 @@
+DELETE TABLE playlist;
+DELETE TABLE playlist_kara;
\ No newline at end of file
diff --git a/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093422_playlists/up.sql b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093422_playlists/up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..b9a1a4a064ddac86deb5493912da8a240724b90b
--- /dev/null
+++ b/src/rust/liblektor-rs/lektor_db/migrations/2023-01-24-093422_playlists/up.sql
@@ -0,0 +1,18 @@
+-- A record of all the playlists. They should have all different names. We also
+-- store the creation time and the name of the creator for statistics.
+CREATE TABLE playlist
+  ( id      BIGINT NOT NULL PRIMARY KEY
+  , name    TEXT   NOT NULL UNIQUE
+  , creator TEXT   NOT NULL
+  , ctime   BIGINT NOT NULL DEFAULT (unixepoch())
+  );
+
+-- Create a link between the karas and the playlists. Here we store the date at
+-- which the kara was added to the playlist. Without taking into account the
+-- deletions in playlists the mtime of a playlist should be the `MAX(atime)`.
+CREATE TABLE playlist_kara
+  ( playlist_id BIGINT NOT NULL REFERENCES playlist(id) ON DELETE CASCADE
+  , kara_id     BIGINT NOT NULL REFERENCES kara(id)     ON DELETE CASCADE
+  , atime       BIGINT NOT NULL DEFAULT (unixepoch())
+  , PRIMARY KEY (playlist_id, kara_id)
+  );
diff --git a/src/rust/liblektor-rs/lektor_db/src/connexion.rs b/src/rust/liblektor-rs/lektor_db/src/connexion.rs
index 493584ebb0d8d8d9c4f20c12007c65dcb047f501..fdd7a0c719d28caca482078692b14e25af4308ad 100644
--- a/src/rust/liblektor-rs/lektor_db/src/connexion.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/connexion.rs
@@ -57,10 +57,10 @@ macro_rules! uri_as_int {
         match uri_val!($what :/ $uri) {
             LktUriValue::Integer(int) => int,
             LktUriValue::String(str) => {
-                let str = str.parse::<u64>().map_err(|err| {
+                let str = str.parse::<i64>().map_err(|err| {
                     format!("the {} `{str}` is not a correct integer: {err}", $what)
                 })?;
-                i32::try_from(str).map_err(|err| format!("the {} `{str}` {err}", $what))?
+                i64::try_from(str).map_err(|err| format!("the {} `{str}` {err}", $what))?
             }
         }
     };
@@ -92,32 +92,38 @@ impl LktDatabaseConnection {
         })
     }
 
-    /// Get a tag id by its name.
-    fn get_tag_id_by_name(&mut self, tag_name: impl AsRef<str>) -> LktDatabaseResult<i32> {
-        Ok(with_dsl!(tag => tag.filter(name.is(tag_name.as_ref()))
-            .first::<Tag>(&mut self.sqlite)?.id
-        ))
+    /// Get a tag id by its name. If the tag doesn't exists, create it.
+    fn get_tag_id_by_name(&mut self, tag_name: impl AsRef<str>) -> LktDatabaseResult<i64> {
+        with_dsl!(tag => self.sqlite.exclusive_transaction(|conn| {
+            let getter = tag.filter(name.is(tag_name.as_ref()));
+            match getter.first::<Tag>(conn) {
+                Ok(Tag { id: tag_id, .. }) => Ok(tag_id),
+                Err(diesel::result::Error::NotFound) => {
+                    let new_id = tag.select(max(id)).first::<Option<i64>>(conn)?.unwrap_or_default() + 1;
+                    diesel::insert_into(tag).values(NewTag { id: new_id, name: tag_name.as_ref() }).execute(conn)?;
+                    Ok(getter.first::<Tag>(conn)?.id)
+                }
+                Err(err) => Err(LktDatabaseError::DieselResult(err)),
+        }}))
     }
 
     /// Get a free local id for all karas. Note that using this function might
     /// be unsafe because there is no guarenties that the returned ID will be
     /// free by the time a kara is inserted with the said id...
-    pub fn get_kara_new_local_id(&mut self) -> LktDatabaseResult<i32> {
-        let max = with_dsl!(kara => kara.select(max(id))
-            .first::<Option<i32>>(&mut self.sqlite)?
-            .unwrap_or(0)
-        );
-        Ok(max + 1)
+    #[deprecated(note = "We must create a new id in a given database to avoid race conditions")]
+    fn get_kara_new_local_id(&mut self) -> LktDatabaseResult<i64> {
+        Ok(with_dsl!(kara => kara.select(max(id))
+            .first::<Option<i64>>(&mut self.sqlite)?
+            .unwrap_or_default() + 1
+        ))
     }
 
     /// Delete a kara with its id in a repo.
     pub fn delete_kara_by_repo(
         &mut self,
-        arg_repo_id: u64,
-        arg_kara_id: u64,
+        arg_repo_id: i64,
+        arg_kara_id: i64,
     ) -> LktDatabaseResult<()> {
-        let arg_repo_id = i32::try_from(arg_repo_id)?;
-        let arg_kara_id = i32::try_from(arg_kara_id)?;
         self.sqlite.exclusive_transaction(|c| {
             let local_id = with_dsl!(repo_kara => repo_kara
                 .filter(repo_id.eq(arg_repo_id))
@@ -132,8 +138,7 @@ impl LktDatabaseConnection {
     }
 
     /// Delete a kara by its local ID.
-    pub fn delete_kara_by_local_id(&mut self, kara_id: u64) -> LktDatabaseResult<()> {
-        let local_id = i32::try_from(kara_id)?;
+    pub fn delete_kara_by_local_id(&mut self, local_id: i64) -> LktDatabaseResult<()> {
         self.sqlite.exclusive_transaction(|c| {
             with_dsl!(repo_kara => diesel::delete(repo_kara.filter(local_kara_id.eq(local_id))).execute(c)?);
             with_dsl!(kara      => diesel::delete(kara.filter(id.eq(local_id))).execute(c)?);
@@ -143,7 +148,10 @@ impl LktDatabaseConnection {
 
     /// Ensure that a given language is present in the database. If it's not
     /// insert it. Existence test is done on the code of the language.
-    pub fn ensure_languages_exist<'a>(&mut self, langs: &[Language<'a>]) -> LktDatabaseResult<()> {
+    pub fn ensure_languages_exist<'a>(
+        &mut self,
+        langs: &[NewLanguage<'a>],
+    ) -> LktDatabaseResult<()> {
         self.sqlite.exclusive_transaction(|c| {
             for lang in langs {
                 with_dsl!(iso_639_1 => match iso_639_1.filter(code.eq(lang.code)).count().get_result(c)? {
@@ -161,18 +169,17 @@ impl LktDatabaseConnection {
         let (id, new_kara, karamakers, langs, tags) = kara;
         self.ensure_languages_exist(&langs)?;
         self.sqlite.exclusive_transaction(|c| {
-            with_dsl!(kara        => diesel::insert_into(kara).values(&new_kara).execute(c)?);
-            with_dsl!(repo_kara   => diesel::insert_into(repo_kara).values(id).execute(c)?);
-            with_dsl!(kara_makers => diesel::insert_or_ignore_into(kara_makers).values(karamakers).execute(c)?);
-            with_dsl!(kara_langs  => {
-                use models::{Language, KaraLanguage};
-                let langs = langs.into_iter().map(|Language { code: lang, .. }| KaraLanguage { id: new_kara.id, code: lang } );
-                diesel::delete(kara_langs.filter(id.eq(new_kara.id))).execute(c)?;
-                diesel::insert_into(kara_langs).values(langs.collect::<Vec<_>>()).execute(c)?;
+            with_dsl!(kara       => diesel::insert_into(kara).values(&new_kara).execute(c)?);
+            with_dsl!(repo_kara  => diesel::insert_into(repo_kara).values(id).execute(c)?);
+            with_dsl!(kara_maker => diesel::insert_or_ignore_into(kara_maker).values(karamakers).execute(c)?);
+            with_dsl!(kara_lang  => {
+                let langs = langs.into_iter().map(|NewLanguage { code: lang, .. }| NewKaraLanguage { id: new_kara.id, code: lang } );
+                diesel::delete(kara_lang.filter(id.eq(new_kara.id))).execute(c)?;
+                diesel::insert_into(kara_lang).values(langs.collect::<Vec<_>>()).execute(c)?;
             });
-            with_dsl!(kara_tags   => {
-                diesel::delete(kara_tags.filter(kara_id.eq(new_kara.id))).execute(c)?;
-                diesel::insert_into(kara_tags).values(tags).execute(c)?;
+            with_dsl!(kara_tag => {
+                diesel::delete(kara_tag.filter(kara_id.eq(new_kara.id))).execute(c)?;
+                diesel::insert_into(kara_tag).values(tags).execute(c)?;
             });
             Ok(())
         })
@@ -181,25 +188,24 @@ impl LktDatabaseConnection {
     /// Create a series of models from a kara signature from Kurisu's V1 API.
     pub fn new_kara_v1<'a>(
         &mut self,
-        repo_id: u64,
+        repo_id: i64,
         kara: api_v1::Kara<'a>,
     ) -> LktDatabaseResult<NewKaraRequest<'a>> {
         let local_id = self.get_kara_new_local_id()?;
-        let repo_id = i32::try_from(repo_id)?;
         let id = KaraId {
             repo_id,
             local_kara_id: local_id,
-            repo_kara_id: i32::try_from(kara.id)?,
+            repo_kara_id: i64::try_from(kara.id)?,
         };
-        let lang = Language::from(kara.get_language());
-        let kara_makers = vec![KaraMaker {
+        let lang = NewLanguage::from(kara.get_language());
+        let kara_maker = vec![NewKaraMaker {
             id: local_id,
             name: kara.author_name,
         }];
         let tags = vec![AddKaraTag {
             kara_id: local_id,
             tag_id: self.get_tag_id_by_name("number")?,
-            value: Some(format!("{}", kara.song_number)),
+            value: KaraTagValue::Integer(i64::try_from(kara.song_number)?),
         }];
         let kara = NewKara {
             id: local_id,
@@ -209,16 +215,16 @@ impl LktDatabaseConnection {
             source_name: kara.source_name,
             file_hash: format!("{}", kara.unix_timestamp),
         };
-        Ok((id, kara, kara_makers, vec![lang], tags))
+        Ok((id, kara, kara_maker, vec![lang], tags))
     }
 
     /// Peek the next kara to play
-    pub fn peek_next_kara_in_queue(&self) -> Option<u64> {
+    pub fn peek_next_kara_in_queue(&self) -> Option<i64> {
         self.queue.peek_next().copied()
     }
 
     /// Pop the next kara to play
-    pub fn pop_next_kara_in_queue(&mut self) -> Option<u64> {
+    pub fn pop_next_kara_in_queue(&mut self) -> Option<i64> {
         self.queue.pop_next()
     }
 
@@ -228,27 +234,41 @@ impl LktDatabaseConnection {
     }
 
     /// Add a kara at the end of the queue.
-    pub fn enqueue_kara(&mut self, local_id: u64) {
+    pub fn enqueue_kara(&mut self, local_id: i64) {
         self.queue.enqueue_kara(local_id)
     }
 
     /// Add a kara at the end of a priority in the queue.
-    pub fn enqueue_kara_with_priority(&mut self, local_id: u64, priority: LktDatabasePriority) {
+    pub fn enqueue_kara_with_priority(&mut self, local_id: i64, priority: LktDatabasePriority) {
         self.queue.enqueue_kara_with_priority(local_id, priority)
     }
 
     /// Add a kara at the top of the queue.
-    pub fn insert_kara(&mut self, local_id: u64) {
+    pub fn insert_kara(&mut self, local_id: i64) {
         self.queue.insert_kara(local_id)
     }
 
     /// Add a kara at the top of a priority in the queue.
-    pub fn insert_kara_with_priority(&mut self, local_id: u64, priority: LktDatabasePriority) {
+    pub fn insert_kara_with_priority(&mut self, local_id: i64, priority: LktDatabasePriority) {
         self.queue.insert_kara_with_priority(local_id, priority)
     }
 
-    /// Search karas by URIs. We returns the local ids.
-    pub fn search(&mut self, uri: LktCUri) -> Result<Vec<i32>, String> {
+    /// Search the queue by URIs. We return the local ids.
+    pub fn queue_search(&mut self, _uri: LktCUri) -> Result<Vec<i64>, String> {
+        todo!()
+    }
+
+    /// Search the given playlist by URIs. We return the local ids.
+    pub fn playlist_search<S: AsRef<str>>(
+        &mut self,
+        _playlist: S,
+        _uri: LktCUri,
+    ) -> Result<Vec<i64>, String> {
+        todo!()
+    }
+
+    /// Search karas by URIs. We return the local ids.
+    pub fn database_search(&mut self, uri: LktCUri) -> Result<Vec<i64>, String> {
         use lektor_c_compat::rs::{LktUriField, LktUriValue};
         let uri_type = uri
             .get_type()
@@ -258,35 +278,35 @@ impl LktDatabaseConnection {
             LktUriField::Id => Ok(vec![with_dsl!(kara => kara
                 .filter(id.is(uri_as_int!("id" :/ uri)))
                 .select(id)
-                .first::<i32>(&mut self.sqlite)
+                .first::<i64>(&mut self.sqlite)
                 .map_err(|err| format!("{err}"))?
             )]),
 
-            LktUriField::KaraMaker => Ok(with_dsl!(kara_makers => kara_makers
+            LktUriField::KaraMaker => Ok(with_dsl!(kara_maker => kara_maker
                 .filter(name.like(uri_str!("author" :/ uri)))
-                .inner_join(with_dsl!(kara => kara))
+                .inner_join(schema::kara::table)
                 .select(id)
-                .load::<i32>(&mut self.sqlite)
+                .load::<i64>(&mut self.sqlite)
                 .map_err(|err| format!("{err}"))?
             )),
 
             LktUriField::Origin => Ok(with_dsl!(kara => kara
                 .filter(song_origin.like(uri_str!("origin" :/ uri)))
-                .select(id).load::<i32>(&mut self.sqlite)
+                .select(id).load::<i64>(&mut self.sqlite)
                 .map_err(|err| format!("{err}"))?
             )),
 
             LktUriField::Type => Ok(with_dsl!(kara => kara
                 .filter(song_type.is(uri_str!("type" :/ uri)))
-                .select(id).load::<i32>(&mut self.sqlite)
+                .select(id).load::<i64>(&mut self.sqlite)
                 .map_err(|err| format!("{err}"))?
             )),
 
-            LktUriField::Language => Ok(with_dsl!(kara_langs => kara_langs
+            LktUriField::Language => Ok(with_dsl!(kara_lang => kara_lang
                 .filter(code.like(uri_str!("language" :/ uri)))
-                .inner_join(with_dsl!(kara => kara))
+                .inner_join(schema::kara::table)
                 .select(id)
-                .load::<i32>(&mut self.sqlite)
+                .load::<i64>(&mut self.sqlite)
                 .map_err(|err| format!("{err}"))?
             )),
 
@@ -296,28 +316,28 @@ impl LktDatabaseConnection {
             }
 
             LktUriField::Playlist => {
-                let _ = uri_str!("playlist" :/ uri);
-                todo!()
+                let playlist = uri_str!("playlist" :/ uri);
+                Err(format!(
+                    "need to implement playlists, can't filter for {playlist}"
+                ))
             }
         }
     }
 
     /// Get all infos about a kara, its metadata, its tags, repo, repo id,
     /// languages, karamakers...
-    pub fn get_info(&mut self, local_id: u64) -> Result<(), String> {
-        let local_id = i32::try_from(local_id)
-            .map_err(|err| format!("the id {local_id} is too big to be an i32: {err}"))?;
+    pub fn get_info(&mut self, local_id: i64) -> Result<(), String> {
         let _repo: String = with_dsl!(repo_kara => repo_kara
             .filter(local_kara_id.is(local_id))
-            .inner_join(with_dsl!(repo => repo.on(id.is(repo_id))))
+            .inner_join(schema::repo::table)
             .select(schema::repo::name)
             .first::<String>(&mut self.sqlite)
             .map_err(|err| format!("failed to get parent repo of kara {local_id}: {err}"))?
         );
-        let _kara_makers: Vec<String> = with_dsl!(kara => kara
+        let _kara_maker: Vec<String> = with_dsl!(kara => kara
             .filter(id.is(local_id))
-            .inner_join(schema::kara_makers::table)
-            .select(schema::kara_makers::name)
+            .inner_join(schema::kara_maker::table)
+            .select(schema::kara_maker::name)
             .load::<String>(&mut self.sqlite)
             .map_err(|err| format!("failed to get makers for kara {local_id}: {err}"))?
         );
diff --git a/src/rust/liblektor-rs/lektor_db/src/lib.rs b/src/rust/liblektor-rs/lektor_db/src/lib.rs
index 785bdea0603eb0488abdda0960840643f4c5631d..b29f7c4f2e3a1620c1feea45c53915f7ec755ceb 100644
--- a/src/rust/liblektor-rs/lektor_db/src/lib.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/lib.rs
@@ -16,7 +16,7 @@ pub(self) use std::{collections::VecDeque, ops::Range, path::Path};
 pub type NewKaraRequest<'a> = (
     models::KaraId,
     models::NewKara<'a>,
-    Vec<models::KaraMaker<'a>>,
-    Vec<models::Language<'a>>,
-    Vec<models::AddKaraTag>,
+    Vec<models::NewKaraMaker<'a>>,
+    Vec<models::NewLanguage<'a>>,
+    Vec<models::AddKaraTag<'a>>,
 );
diff --git a/src/rust/liblektor-rs/lektor_db/src/models.rs b/src/rust/liblektor-rs/lektor_db/src/models.rs
index 9cbf5321b415bbf74946b222bb83e470278981fd..fb58d92f8ad99a3c14984ca8da55acd358aefd1a 100644
--- a/src/rust/liblektor-rs/lektor_db/src/models.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/models.rs
@@ -1,6 +1,7 @@
 //! Models used for querying, inserting or updating the database.
 
 use crate::{schema::*, *};
+use diesel::{deserialize::FromSqlRow, expression::AsExpression, sql_types};
 use kurisu_api::v1 as api_v1;
 
 // First the insertable things
@@ -8,22 +9,22 @@ use kurisu_api::v1 as api_v1;
 #[derive(Insertable)]
 #[diesel(table_name = repo)]
 pub struct NewRepo<'a> {
-    pub id: i32,
+    pub id: i64,
     pub name: &'a str,
 }
 
 #[derive(Debug, Insertable, Queryable, Selectable)]
 #[diesel(table_name = repo_kara)]
 pub struct KaraId {
-    pub repo_id: i32,
-    pub repo_kara_id: i32,
-    pub local_kara_id: i32,
+    pub repo_id: i64,
+    pub repo_kara_id: i64,
+    pub local_kara_id: i64,
 }
 
 #[derive(Debug, Insertable)]
 #[diesel(table_name = kara)]
 pub struct NewKara<'a> {
-    pub id: i32,
+    pub id: i64,
     pub song_title: &'a str,
     pub song_type: &'a str,
     pub song_origin: &'a str,
@@ -31,38 +32,81 @@ pub struct NewKara<'a> {
     pub file_hash: String,
 }
 
-#[derive(Debug, Insertable, Queryable, Selectable)]
-#[diesel(table_name = kara_makers)]
-pub struct KaraMaker<'a> {
-    pub id: i32,
+#[derive(Debug, Insertable)]
+#[diesel(table_name = kara_maker)]
+pub struct NewKaraMaker<'a> {
+    pub id: i64,
     pub name: &'a str,
 }
 
-#[derive(Debug, Insertable, Queryable, Selectable)]
-#[diesel(table_name = kara_langs)]
-pub struct KaraLanguage<'a> {
-    pub id: i32,
+/// Add a new language to a kara. The language must exists in the database.
+#[derive(Debug, Insertable)]
+#[diesel(table_name = kara_lang)]
+pub struct NewKaraLanguage<'a> {
+    pub id: i64,
     pub code: &'a str,
 }
 
-#[derive(Debug, Insertable, Queryable, Selectable)]
+/// Add a new language to the database. Will be deprecated.
+#[derive(Debug, Insertable)]
 #[diesel(table_name = iso_639_1)]
-pub struct Language<'a> {
+pub struct NewLanguage<'a> {
     pub code: &'a str,
     pub name_en: &'a str,
     pub is_iso: bool,
     pub is_macro: bool,
 }
 
-#[derive(Insertable)]
-#[diesel(table_name = kara_tags)]
-pub struct AddKaraTag {
-    pub kara_id: i32,
-    pub tag_id: i32,
-    pub value: Option<String>,
+/// Represent possible values for a tag.
+#[derive(Debug, FromSqlRow, AsExpression)]
+#[diesel(sql_type = sql_types::Nullable<sql_types::Text>)]
+pub enum KaraTagValue<'a> {
+    /// The tag has no value, it's juste present.?
+    None,
+
+    /// The tag has a string slice value.
+    Str(&'a str),
+
+    /// The tag has an owned string value.
+    String(String),
+
+    /// The tag has an integer value.
+    Integer(i64),
+}
+
+#[derive(Debug, Insertable)]
+#[diesel(table_name = kara_tag)]
+pub struct AddKaraTag<'a> {
+    pub kara_id: i64,
+    pub tag_id: i64,
+    pub value: KaraTagValue<'a>,
+}
+
+#[derive(Debug, Insertable)]
+#[diesel(table_name = tag)]
+pub struct NewTag<'a> {
+    pub id: i64,
+    pub name: &'a str,
+}
+
+impl<'a> diesel::serialize::ToSql<sql_types::Nullable<sql_types::Text>, diesel::sqlite::Sqlite>
+    for KaraTagValue<'a>
+{
+    fn to_sql<'b>(
+        &'b self,
+        out: &mut diesel::serialize::Output<'b, '_, diesel::sqlite::Sqlite>,
+    ) -> diesel::serialize::Result {
+        match self {
+            KaraTagValue::None => return Ok(diesel::serialize::IsNull::Yes),
+            KaraTagValue::Str(str) => out.set_value(*str),
+            KaraTagValue::String(str) => out.set_value(str.as_str()),
+            KaraTagValue::Integer(int) => out.set_value(format!("{int}")),
+        };
+        Ok(diesel::serialize::IsNull::No)
+    }
 }
 
-impl<'a> From<api_v1::Language<'a>> for Language<'a> {
+impl<'a> From<api_v1::Language<'a>> for NewLanguage<'a> {
     fn from(lang: api_v1::Language<'a>) -> Self {
         Self {
             code: lang.code,
@@ -78,14 +122,14 @@ impl<'a> From<api_v1::Language<'a>> for Language<'a> {
 #[derive(Queryable, Selectable)]
 #[diesel(table_name = tag)]
 pub struct Tag {
-    pub id: i32,
+    pub id: i64,
     pub name: String,
 }
 
 #[derive(Queryable, Selectable)]
 #[diesel(table_name = kara)]
 pub struct Kara {
-    pub id: i32,
+    pub id: i64,
     pub is_dl: bool,
     pub song_title: String,
     pub song_type: String,
diff --git a/src/rust/liblektor-rs/lektor_db/src/queue.rs b/src/rust/liblektor-rs/lektor_db/src/queue.rs
index f41050a281fa2c54e77adbbab7ebaa6054457c69..54e3f1434ddec7bafabb3ecb26071711f9b72bd4 100644
--- a/src/rust/liblektor-rs/lektor_db/src/queue.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/queue.rs
@@ -43,7 +43,6 @@ impl_from_for_proprity!(i16);
 impl_from_for_proprity!(i32);
 impl_from_for_proprity!(i64);
 
-impl_into_for_priority!(i32);
 impl_into_for_priority!(usize);
 
 /// The iterator for the database queue. The iterator returns references to
@@ -64,7 +63,7 @@ pub struct LktDatabaseQueueRangeIter<'a> {
 }
 
 impl<'a> Iterator for LktDatabaseQueueIter<'a> {
-    type Item = &'a u64;
+    type Item = &'a i64;
 
     fn next(&mut self) -> Option<Self::Item> {
         let priority = self.priority?.clamp(0, LKT_DATABASE_QUEUES_COUNT - 1);
@@ -84,7 +83,7 @@ impl<'a> Iterator for LktDatabaseQueueIter<'a> {
 }
 
 impl<'a> Iterator for LktDatabaseQueueRangeIter<'a> {
-    type Item = &'a u64;
+    type Item = &'a i64;
 
     fn next(&mut self) -> Option<Self::Item> {
         let new_remaining = self.remaining.saturating_sub(1);
@@ -100,24 +99,24 @@ impl<'a> Iterator for LktDatabaseQueueRangeIter<'a> {
 /// The queue datastructure used for storing karas in the queue.
 #[derive(Debug, Default)]
 pub struct LktDatabaseQueue {
-    levels: [VecDeque<u64>; LKT_DATABASE_QUEUES_COUNT],
+    levels: [VecDeque<i64>; LKT_DATABASE_QUEUES_COUNT],
 }
 
 impl LktDatabaseQueue {
-    pub fn enqueue_kara(&mut self, local_id: u64) {
+    pub fn enqueue_kara(&mut self, local_id: i64) {
         self.levels[0].push_back(local_id);
     }
 
-    pub fn enqueue_kara_with_priority(&mut self, local_id: u64, priority: LktDatabasePriority) {
+    pub fn enqueue_kara_with_priority(&mut self, local_id: i64, priority: LktDatabasePriority) {
         let priority: usize = priority.into();
         self.levels[priority].push_back(local_id);
     }
 
-    pub fn insert_kara(&mut self, local_id: u64) {
+    pub fn insert_kara(&mut self, local_id: i64) {
         self.levels[LKT_DATABASE_QUEUES_COUNT - 1].push_front(local_id);
     }
 
-    pub fn insert_kara_with_priority(&mut self, local_id: u64, priority: LktDatabasePriority) {
+    pub fn insert_kara_with_priority(&mut self, local_id: i64, priority: LktDatabasePriority) {
         let priority: usize = priority.into();
         self.levels[priority].push_front(local_id);
     }
@@ -136,11 +135,11 @@ impl LktDatabaseQueue {
         }
     }
 
-    pub fn peek_next(&self) -> Option<&u64> {
+    pub fn peek_next(&self) -> Option<&i64> {
         self.iter().next()
     }
 
-    pub fn pop_next(&mut self) -> Option<u64> {
+    pub fn pop_next(&mut self) -> Option<i64> {
         let level = self
             .levels
             .iter_mut()
diff --git a/src/rust/liblektor-rs/lektor_db/src/schema.rs b/src/rust/liblektor-rs/lektor_db/src/schema.rs
index 48a94b8b58a6fbb8635865c97b91e6a3fbe91e65..977a01e8f5327e4c0fa83e3cb619d2c4663b39ba 100644
--- a/src/rust/liblektor-rs/lektor_db/src/schema.rs
+++ b/src/rust/liblektor-rs/lektor_db/src/schema.rs
@@ -2,8 +2,8 @@
 
 diesel::table! {
     history (epoch) {
-        id -> Integer,
-        epoch -> Integer,
+        id -> BigInt,
+        epoch -> BigInt,
     }
 }
 
@@ -18,7 +18,7 @@ diesel::table! {
 
 diesel::table! {
     kara (id) {
-        id -> Integer,
+        id -> BigInt,
         is_dl -> Bool,
         song_title -> Text,
         song_type -> Text,
@@ -29,55 +29,74 @@ diesel::table! {
 }
 
 diesel::table! {
-    kara_langs (id, code) {
-        id -> Integer,
+    kara_lang (id, code) {
+        id -> BigInt,
         code -> Text,
     }
 }
 
 diesel::table! {
-    kara_makers (id, name) {
-        id -> Integer,
+    kara_maker (id, name) {
+        id -> BigInt,
         name -> Text,
     }
 }
 
 diesel::table! {
-    kara_tags (kara_id, tag_id, value) {
-        kara_id -> Integer,
-        tag_id -> Integer,
+    kara_tag (kara_id, tag_id, value) {
+        kara_id -> BigInt,
+        tag_id -> BigInt,
         value -> Nullable<Text>,
     }
 }
 
+diesel::table! {
+    playlist (id) {
+        id -> BigInt,
+        name -> Text,
+        creator -> Text,
+        ctime -> BigInt,
+    }
+}
+
+diesel::table! {
+    playlist_kara (playlist_id, kara_id) {
+        playlist_id -> BigInt,
+        kara_id -> BigInt,
+        atime -> BigInt,
+    }
+}
+
 diesel::table! {
     repo (id) {
-        id -> Integer,
+        id -> BigInt,
         name -> Text,
     }
 }
 
 diesel::table! {
     repo_kara (repo_id, repo_kara_id, local_kara_id) {
-        repo_id -> Integer,
-        repo_kara_id -> Integer,
-        local_kara_id -> Integer,
+        repo_id -> BigInt,
+        repo_kara_id -> BigInt,
+        local_kara_id -> BigInt,
     }
 }
 
 diesel::table! {
     tag (id) {
-        id -> Integer,
+        id -> BigInt,
         name -> Text,
     }
 }
 
 diesel::joinable!(history -> kara (id));
-diesel::joinable!(kara_langs -> iso_639_1 (code));
-diesel::joinable!(kara_langs -> kara (id));
-diesel::joinable!(kara_makers -> kara (id));
-diesel::joinable!(kara_tags -> kara (kara_id));
-diesel::joinable!(kara_tags -> tag (tag_id));
+diesel::joinable!(kara_lang -> iso_639_1 (code));
+diesel::joinable!(kara_lang -> kara (id));
+diesel::joinable!(kara_maker -> kara (id));
+diesel::joinable!(kara_tag -> kara (kara_id));
+diesel::joinable!(kara_tag -> tag (tag_id));
+diesel::joinable!(playlist_kara -> kara (kara_id));
+diesel::joinable!(playlist_kara -> playlist (playlist_id));
 diesel::joinable!(repo_kara -> kara (local_kara_id));
 diesel::joinable!(repo_kara -> repo (repo_id));
 
@@ -85,9 +104,11 @@ diesel::allow_tables_to_appear_in_same_query!(
     history,
     iso_639_1,
     kara,
-    kara_langs,
-    kara_makers,
-    kara_tags,
+    kara_lang,
+    kara_maker,
+    kara_tag,
+    playlist,
+    playlist_kara,
     repo,
     repo_kara,
     tag,
diff --git a/src/rust/liblektor-rs/lektor_unsafe/src/db.rs b/src/rust/liblektor-rs/lektor_unsafe/src/db.rs
index 2468c6b55b5aa3a38dea03a2bff10032c4139a8f..a17a4fe57014f00724296483cd1e700182d5a663 100644
--- a/src/rust/liblektor-rs/lektor_unsafe/src/db.rs
+++ b/src/rust/liblektor-rs/lektor_unsafe/src/db.rs
@@ -57,8 +57,8 @@ pub unsafe extern "C" fn lkt_database_close_connection(db: *mut LktDatabaseConne
 #[no_mangle]
 pub unsafe extern "C" fn lkt_database_delete_kara_by_repo(
     db: *mut LktDatabaseConnection,
-    repo: u64,
-    kara: u64,
+    repo: i64,
+    kara: i64,
 ) -> bool {
     let db = db.as_mut().expect("passing a nullptr as db handle");
     db.delete_kara_by_repo(repo, kara)
@@ -75,7 +75,7 @@ pub unsafe extern "C" fn lkt_database_delete_kara_by_repo(
 #[no_mangle]
 pub unsafe extern "C" fn lkt_database_delete_kara_by_local_id(
     db: *mut LktDatabaseConnection,
-    local_id: u64,
+    local_id: i64,
 ) -> bool {
     let db = db.as_mut().expect("passing a nullptr as db handle");
     db.delete_kara_by_local_id(local_id)
@@ -90,7 +90,7 @@ pub unsafe extern "C" fn lkt_database_delete_kara_by_local_id(
 #[no_mangle]
 pub unsafe extern "C" fn lkt_database_get_kara_info(
     db: *mut LktDatabaseConnection,
-    local_id: u64,
+    local_id: i64,
     cb: extern "C" fn(*const c_char, *const c_char, *mut c_void),
     user: *mut c_void,
 ) -> bool {
@@ -104,7 +104,7 @@ pub unsafe extern "C" fn lkt_database_get_kara_info(
 #[no_mangle]
 pub unsafe extern "C" fn lkt_database_get_kara_tags(
     db: *mut LktDatabaseConnection,
-    local_id: u64,
+    local_id: i64,
     cb: extern "C" fn(*const c_char, *const c_char, *mut c_void),
     user: *mut c_void,
 ) -> bool {