diff --git a/lektord/src/c_wrapper/abort.rs b/lektord/src/c_wrapper/abort.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2dd284464ae030e52ee4e156c754128284d9ba2d
--- /dev/null
+++ b/lektord/src/c_wrapper/abort.rs
@@ -0,0 +1,6 @@
+/// So the C/C++ part of the code can call the Rust panic and unwind things. With that tokio can
+/// catch an abort and stay alive.
+#[export_name = "___lkt_abort"]
+pub(super) extern "C" fn lkt_abort() {
+    panic!()
+}
diff --git a/lektord/src/c_wrapper/commands.rs b/lektord/src/c_wrapper/commands.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ca218d47b84b0ea2da68c5f641e38a43a2893f86
--- /dev/null
+++ b/lektord/src/c_wrapper/commands.rs
@@ -0,0 +1,142 @@
+use super::{PlayerEvent, STATE};
+use anyhow::{bail, Error, Result};
+use lektor_nkdb::PlayState;
+use lektor_utils::*;
+use std::{ffi::*, path::Path, ptr::NonNull};
+
+/// Safe wrapper around `mod_stop_playback`. Stops the playback. Same as [player_toggle_pause], don't update
+/// the database yourself, it will be done by a callback chain.
+pub(crate) fn player_stop() -> Result<()> {
+    extern "C" {
+        fn mod_stop_playback() -> c_int;
+    }
+    if 0 != unsafe { mod_stop_playback() } {
+        bail!("failed to stop playback")
+    }
+    Ok(())
+}
+
+/// Safe wrapper around `mod_set_paused`. Set the pause state of the player. Same remark as
+/// [player_toggle_pause], don't update the database yourself, it will be done by a callback chain
+/// latter.
+pub(crate) fn player_set_paused(state: PlayState) -> Result<()> {
+    extern "C" {
+        fn mod_set_paused(_: c_int) -> c_int;
+    }
+    let paused = match state {
+        PlayState::Stop => bail!("can't convert stop into a paused flag"),
+        PlayState::Play => 0 as c_int,
+        PlayState::Pause => 1 as c_int,
+    };
+    if 0 != unsafe { mod_set_paused(paused) } {
+        bail!("failed to set paused state to {state:?}")
+    }
+    Ok(())
+}
+
+/// Safe wrapper around `mod_toggle_pause`. Send the toggle signal to the player. No need to update
+/// the database in addition to this call, the database will be updated when the change will be
+/// made in the mpv player.
+pub(crate) fn player_toggle_pause() -> Result<()> {
+    extern "C" {
+        fn mod_toggle_pause() -> c_int;
+    }
+    if 0 != unsafe { mod_toggle_pause() } {
+        anyhow::bail!("failed to toggle pause state")
+    }
+    Ok(())
+}
+
+/// Safe wrapper around `mod_set_position` Set the position in the player module.
+#[allow(dead_code)]
+pub(crate) fn player_set_position(position: i64) -> Result<()> {
+    extern "C" {
+        fn mod_set_position(_: c_int) -> c_int;
+    }
+    let position = c_int::try_from(position)?;
+    if 0 != unsafe { mod_set_position(position) } {
+        anyhow::bail!("failed to set player position to {position}s")
+    }
+    Ok(())
+}
+
+/// Safe wrapper around `mod_get_duration` Get the duration of the current playing file.
+pub(crate) fn player_get_duration() -> Result<i64> {
+    extern "C" {
+        fn mod_get_duration(_: NonNull<c_int>) -> c_int;
+    }
+    let mut duration: c_int = 0;
+    if 0 != unsafe { mod_get_duration(NonNull::new_unchecked((&mut duration) as *mut _)) } {
+        anyhow::bail!("failed to get current kara duration")
+    }
+    Ok(duration.into())
+}
+
+/// Safe wrapper around `mod_get_elapsed` Get the duration since the begin of the kara.
+pub(crate) fn player_get_elapsed() -> Result<i64> {
+    extern "C" {
+        fn mod_get_elapsed(_: NonNull<c_int>) -> c_int;
+    }
+    let mut elapsed: c_int = 0;
+    if 0 != unsafe { mod_get_elapsed(NonNull::new_unchecked((&mut elapsed) as *mut _)) } {
+        anyhow::bail!("failed to get current kara elapsed time since start of file")
+    }
+    Ok(elapsed.into())
+}
+
+/// Safe wrapper around `mod_load_file` Tell the player to load a file by its path.
+pub(crate) fn player_load_file(path: impl AsRef<Path>, id: u64) -> Result<()> {
+    extern "C" {
+        fn mod_load_file(_: NonNull<c_char>, _: u64) -> c_int;
+    }
+    let path = path
+        .as_ref()
+        .to_str()
+        .ok_or_else(|| Error::msg("path contained non-utf8 characters"))?;
+    let cstr = CString::new(path)?;
+    if 0 != unsafe { mod_load_file(NonNull::new_unchecked(cstr.as_ptr() as *mut _), id) } {
+        anyhow::bail!("failed load file {path}")
+    }
+    Ok(())
+}
+
+/// Safe wrapper around `mod_set_volume` Set the volume of the player.
+#[allow(dead_code)]
+pub(crate) fn player_set_playback_volume(vol: i64) -> Result<()> {
+    extern "C" {
+        fn mod_set_volume(_: c_int) -> c_int;
+    }
+    let vol = c_int::try_from(vol)?.clamp(0, 100);
+    if 0 != unsafe { mod_set_volume(vol) } {
+        anyhow::bail!("failed to set player volume to {vol}%")
+    }
+    Ok(())
+}
+
+fn send_msg(msg: PlayerEvent) {
+    if let Some((sender, _)) = unsafe { STATE.get() } {
+        if let Err(err) = sender.blocking_send(msg) {
+            log::error!("failed to send msg {msg:?}: {err}");
+        }
+    } else {
+        log::error!("no lektord state was set for the player module")
+    }
+}
+
+/// Set the play state in the database, the signal is send from the player.
+#[no_mangle]
+pub(super) extern "C" fn lkt_toggle_play_state(state: c_int) {
+    send_msg(PlayerEvent::SetPlayState(state))
+}
+
+/// The player needs to play the next kara in the queue.
+#[no_mangle]
+pub(super) extern "C" fn lkt_play_next() {
+    send_msg(PlayerEvent::PlayNext)
+}
+
+/// The player needs to play the previous kara in the queue or in the history.
+#[no_mangle]
+pub(super) extern "C" fn lkt_play_prev() {
+    send_msg(PlayerEvent::PlayPrev)
+}
diff --git a/lektord/src/c_wrapper/mod.rs b/lektord/src/c_wrapper/mod.rs
index e5be5087b8ab9f6bf6f36dd0d8051053b0ad6da4..83386bb3793f6f7fd236180771f1e691ab112872 100644
--- a/lektord/src/c_wrapper/mod.rs
+++ b/lektord/src/c_wrapper/mod.rs
@@ -1,19 +1,22 @@
 //! Interface with the C/C++ part of the code. The only place we allow unsafe code in this crate.
 
 use crate::LektorStatePtr;
-use anyhow::{bail, Error, Result};
+use anyhow::{bail, Result};
 use lektor_nkdb::PlayState;
 use lektor_utils::{config::LektorPlayerConfig, *};
 use std::{
     ffi::*,
-    path::Path,
     ptr::NonNull,
     sync::{Arc, OnceLock},
 };
 
+mod abort;
+mod commands;
 mod loging;
 mod playstate;
-use playstate::*;
+
+use self::{abort::*, playstate::*};
+pub(crate) use commands::*;
 
 /// The lektord state pointer to pass to the player module. Should be better to pass to its
 /// constructor...
@@ -159,147 +162,3 @@ pub async fn close_player_module() -> Result<()> {
 
     Ok(())
 }
-
-/// Safe wrapper around `mod_stop_playback`. Stops the playback. Same as [player_toggle_pause], don't update
-/// the database yourself, it will be done by a callback chain.
-pub(crate) fn player_stop() -> Result<()> {
-    extern "C" {
-        fn mod_stop_playback() -> c_int;
-    }
-    if 0 != unsafe { mod_stop_playback() } {
-        bail!("failed to stop playback")
-    }
-    Ok(())
-}
-
-/// Safe wrapper around `mod_set_paused`. Set the pause state of the player. Same remark as
-/// [player_toggle_pause], don't update the database yourself, it will be done by a callback chain
-/// latter.
-pub(crate) fn player_set_paused(state: PlayState) -> Result<()> {
-    extern "C" {
-        fn mod_set_paused(_: c_int) -> c_int;
-    }
-    let paused = match state {
-        PlayState::Stop => bail!("can't convert stop into a paused flag"),
-        PlayState::Play => 0 as c_int,
-        PlayState::Pause => 1 as c_int,
-    };
-    if 0 != unsafe { mod_set_paused(paused) } {
-        bail!("failed to set paused state to {state:?}")
-    }
-    Ok(())
-}
-
-/// Safe wrapper around `mod_toggle_pause`. Send the toggle signal to the player. No need to update
-/// the database in addition to this call, the database will be updated when the change will be
-/// made in the mpv player.
-pub(crate) fn player_toggle_pause() -> Result<()> {
-    extern "C" {
-        fn mod_toggle_pause() -> c_int;
-    }
-    if 0 != unsafe { mod_toggle_pause() } {
-        anyhow::bail!("failed to toggle pause state")
-    }
-    Ok(())
-}
-
-/// Safe wrapper around `mod_set_position` Set the position in the player module.
-#[allow(dead_code)]
-pub(crate) fn player_set_position(position: i64) -> Result<()> {
-    extern "C" {
-        fn mod_set_position(_: c_int) -> c_int;
-    }
-    let position = c_int::try_from(position)?;
-    if 0 != unsafe { mod_set_position(position) } {
-        anyhow::bail!("failed to set player position to {position}s")
-    }
-    Ok(())
-}
-
-/// Safe wrapper around `mod_get_duration` Get the duration of the current playing file.
-pub(crate) fn player_get_duration() -> Result<i64> {
-    extern "C" {
-        fn mod_get_duration(_: NonNull<c_int>) -> c_int;
-    }
-    let mut duration: c_int = 0;
-    if 0 != unsafe { mod_get_duration(NonNull::new_unchecked((&mut duration) as *mut _)) } {
-        anyhow::bail!("failed to get current kara duration")
-    }
-    Ok(duration.into())
-}
-
-/// Safe wrapper around `mod_get_elapsed` Get the duration since the begin of the kara.
-pub(crate) fn player_get_elapsed() -> Result<i64> {
-    extern "C" {
-        fn mod_get_elapsed(_: NonNull<c_int>) -> c_int;
-    }
-    let mut elapsed: c_int = 0;
-    if 0 != unsafe { mod_get_elapsed(NonNull::new_unchecked((&mut elapsed) as *mut _)) } {
-        anyhow::bail!("failed to get current kara elapsed time since start of file")
-    }
-    Ok(elapsed.into())
-}
-
-/// Safe wrapper around `mod_load_file` Tell the player to load a file by its path.
-pub(crate) fn player_load_file(path: impl AsRef<Path>, id: u64) -> Result<()> {
-    extern "C" {
-        fn mod_load_file(_: NonNull<c_char>, _: u64) -> c_int;
-    }
-    let path = path
-        .as_ref()
-        .to_str()
-        .ok_or_else(|| Error::msg("path contained non-utf8 characters"))?;
-    let cstr = CString::new(path)?;
-    if 0 != unsafe { mod_load_file(NonNull::new_unchecked(cstr.as_ptr() as *mut _), id) } {
-        anyhow::bail!("failed load file {path}")
-    }
-    Ok(())
-}
-
-/// Safe wrapper around `mod_set_volume` Set the volume of the player.
-#[allow(dead_code)]
-pub(crate) fn player_set_playback_volume(vol: i64) -> Result<()> {
-    extern "C" {
-        fn mod_set_volume(_: c_int) -> c_int;
-    }
-    let vol = c_int::try_from(vol)?.clamp(0, 100);
-    if 0 != unsafe { mod_set_volume(vol) } {
-        anyhow::bail!("failed to set player volume to {vol}%")
-    }
-    Ok(())
-}
-
-fn send_msg(msg: PlayerEvent) {
-    if let Some((sender, _)) = unsafe { STATE.get() } {
-        if let Err(err) = sender.blocking_send(msg) {
-            log::error!("failed to send msg {msg:?}: {err}");
-        }
-    } else {
-        log::error!("no lektord state was set for the player module")
-    }
-}
-
-/// Set the play state in the database, the signal is send from the player.
-#[no_mangle]
-extern "C" fn lkt_toggle_play_state(state: c_int) {
-    send_msg(PlayerEvent::SetPlayState(state))
-}
-
-/// The player needs to play the next kara in the queue.
-#[no_mangle]
-extern "C" fn lkt_play_next() {
-    send_msg(PlayerEvent::PlayNext)
-}
-
-/// The player needs to play the previous kara in the queue or in the history.
-#[no_mangle]
-extern "C" fn lkt_play_prev() {
-    send_msg(PlayerEvent::PlayPrev)
-}
-
-/// So the C/C++ part of the code can call the Rust panic and unwind things. With that tokio can
-/// catch an abort and stay alive.
-#[export_name = "___lkt_abort"]
-extern "C" fn lkt_abort() {
-    panic!()
-}