code: adding mp3 duration to lock buttons client side

This commit is contained in:
Gabor Körber 2024-01-20 22:36:30 +01:00
parent a7da277977
commit 69ec048bc9
4 changed files with 95 additions and 5 deletions

14
Cargo.lock generated
View File

@ -560,9 +560,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -689,6 +689,15 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "mp3-duration"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348bdc7300502f0801e5b57c448815713cd843b744ef9bda252a2698fdf90a0f"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.7.0" version = "0.7.0"
@ -1145,6 +1154,7 @@ dependencies = [
"log", "log",
"minijinja", "minijinja",
"minimp3", "minimp3",
"mp3-duration",
"regex", "regex",
"rodio", "rodio",
"serde", "serde",

View File

@ -11,8 +11,10 @@ axum = "0.6.18"
axum-template = { version = "0.19.0", features = ["minijinja"] } axum-template = { version = "0.19.0", features = ["minijinja"] }
cpal = "0.15.2" cpal = "0.15.2"
log = "0.4.20" log = "0.4.20"
# metadata = "0.1.8"
minijinja = { version = "1.0.3", features = ["loader"] } minijinja = { version = "1.0.3", features = ["loader"] }
minimp3 = "0.5.1" minimp3 = "0.5.1"
mp3-duration = "0.1.10"
regex = "1.9.0" regex = "1.9.0"
rodio = "0.17.1" rodio = "0.17.1"
serde = { version = "1.0.171", features = ["derive"] } serde = { version = "1.0.171", features = ["derive"] }

View File

@ -6,9 +6,11 @@ use std::fs;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct SoundClip { pub struct SoundClip {
title: String,
hash: String, hash: String,
file_name: String, file_name: String,
directory: String, directory: String,
length: Option<u64>,
} }
impl fmt::Display for SoundClip { impl fmt::Display for SoundClip {
@ -23,8 +25,11 @@ impl fmt::Display for SoundClip {
impl SoundClip { impl SoundClip {
pub fn new(file_name: String, directory: String) -> Self { pub fn new(file_name: String, directory: String) -> Self {
let full_file_name = format!("{}/{}", directory, file_name);
Self { Self {
title: normalize_filename(&file_name),
hash: encode_filename(&file_name), hash: encode_filename(&file_name),
length: get_sound_clip_length(&full_file_name),
file_name: file_name, file_name: file_name,
directory: directory, directory: directory,
} }
@ -49,6 +54,44 @@ fn encode_filename(file_name: &str) -> String {
hash_hex hash_hex
} }
/* // needs metadata to install ffmpeg.
fn get_sound_clip_length(file_path: &str) -> Option<u64> {
if let Ok(meta) = metadata::media_file::MediaFileMetadata::new(&std::path::Path::new(file_path))
{
return Some(meta._duration);
}
return None;
}
*/
fn get_sound_clip_length(file_path: &str) -> Option<u64> {
let path = std::path::Path::new(file_path);
if let Ok(duration) = mp3_duration::from_path(&path) {
if let Ok(millis) = duration.as_millis().try_into() {
return Some(millis);
}
return Some(duration.as_secs() * 1000 + 1);
}
return None;
}
fn normalize_filename(input: &str) -> String {
// List of extensions to be removed
let extensions = [".mp3", ".wav", ".flac", ".ogg"];
// Remove the extension if it matches any in the list
let without_extension = extensions.iter().fold(input.to_string(), |acc, &ext| {
if acc.ends_with(ext) {
acc.trim_end_matches(ext).to_string()
} else {
acc
}
});
// Replace all underscores with spaces
without_extension.replace("_", " ")
}
pub fn scan_directory_for_clips(directory: &str, extensions: &[&str]) -> Option<Vec<SoundClip>> { pub fn scan_directory_for_clips(directory: &str, extensions: &[&str]) -> Option<Vec<SoundClip>> {
let mut sound_clips = Vec::new(); let mut sound_clips = Vec::new();

View File

@ -47,12 +47,42 @@
} }
} }
function play(hash) { var disableTimeout;
var none = null;
function play(hash, timeout) {
if (timeout && timeout != "none") {
// Add the "disabled" class to all clip buttons
$('.clip').addClass('disabled');
// Add "primary" and "loading" classes to the clicked button
var clickedButton = $('#clip-' + hash);
clickedButton.addClass('double loading positive');
// Clear any existing timeout to avoid conflicts
clearTimeout(disableTimeout);
// Set a new timeout
disableTimeout = setTimeout(function () {
$('.clip').removeClass('disabled');
clickedButton.removeClass('double loading positive');
}, timeout);
}
fetch("/play/" + hash); fetch("/play/" + hash);
} }
function stop() { function stop() {
fetch("/stop"); fetch("/stop");
// Clear the timeout
clearTimeout(disableTimeout);
// Remove the "disabled" class from all clip buttons
// and the "primary" and "loading" classes from any button that might have them
$('.clip').removeClass('disabled');
$('.clip').removeClass('double loading positive');
} }
</script> </script>
<style type="text/css"> <style type="text/css">
@ -61,6 +91,10 @@
/* Define a CSS variable for the width */ /* Define a CSS variable for the width */
} }
.ui.ui.ui.ui.ui.ui.loading.button {
color: black !important;
}
.left-column { .left-column {
width: var(--left-column-width) !important; width: var(--left-column-width) !important;
height: 100%; height: 100%;
@ -110,8 +144,9 @@
<div class="ui wrapped compact wrapping spaced /**buttons"> <div class="ui wrapped compact wrapping spaced /**buttons">
{% for clip in clips %} {% for clip in clips %}
<button class="ui {{ loop.cycle('red', 'blue', 'green', 'violet', 'orange') }} basic button" <button id="clip-{{clip.hash}}"
onclick="play('{{clip.hash}}')">{{clip.file_name}}</button> class="ui {{ loop.cycle('red', 'blue', 'green', 'violet', 'orange') }} basic button clip"
onclick="play('{{clip.hash}}', {{clip.length}})">{{clip.title}}</button>
{% endfor %} {% endfor %}
</div> </div>