metadonnée technique dans le cache audio
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3065,6 +3065,7 @@ dependencies = [
|
|||||||
"pmoplaylist",
|
"pmoplaylist",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|||||||
@@ -29,10 +29,11 @@ rand = "0.8"
|
|||||||
# HTTP streaming dependencies
|
# HTTP streaming dependencies
|
||||||
bytes = { version = "1.0", optional = true }
|
bytes = { version = "1.0", optional = true }
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||||
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
cache-sink = ["dep:pmoaudiocache", "dep:pmoflac", "dep:pmometadata"]
|
cache-sink = ["dep:pmoaudiocache", "dep:pmoflac", "dep:pmometadata", "dep:serde_json"]
|
||||||
playlist = ["cache-sink", "dep:pmoplaylist", "dep:pmocache"]
|
playlist = ["cache-sink", "dep:pmoplaylist", "dep:pmocache"]
|
||||||
http-stream = ["dep:pmoflac", "dep:pmometadata", "dep:bytes", "dep:serde"]
|
http-stream = ["dep:pmoflac", "dep:pmometadata", "dep:bytes", "dep:serde"]
|
||||||
all = ["cache-sink", "playlist", "http-stream"]
|
all = ["cache-sink", "playlist", "http-stream"]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use pmoaudio::{
|
|||||||
};
|
};
|
||||||
use pmoaudiocache::AudioTrackMetadataExt;
|
use pmoaudiocache::AudioTrackMetadataExt;
|
||||||
use pmoflac::{encode_flac_stream, EncoderOptions, PcmFormat};
|
use pmoflac::{encode_flac_stream, EncoderOptions, PcmFormat};
|
||||||
|
use serde_json::{Number, Value};
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
@@ -289,6 +290,10 @@ impl NodeLogic for FlacCacheSinkLogic {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(transform) = self.cache.transform_metadata(&pk).await {
|
||||||
|
persist_transform_streaminfo(&self.cache, &pk, &transform);
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 2: Prebuffer terminé! Copier les métadonnées et pusher à la playlist
|
// Phase 2: Prebuffer terminé! Copier les métadonnées et pusher à la playlist
|
||||||
// Copier les métadonnées du TrackBoundary dans le cache
|
// Copier les métadonnées du TrackBoundary dans le cache
|
||||||
// IMPORTANT: Faire ceci AVANT d'ajouter à la playlist pour que les métadonnées soient disponibles
|
// IMPORTANT: Faire ceci AVANT d'ajouter à la playlist pour que les métadonnées soient disponibles
|
||||||
@@ -910,6 +915,41 @@ async fn pump_track_segments_from_channel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn persist_transform_streaminfo(
|
||||||
|
cache: &pmoaudiocache::Cache,
|
||||||
|
pk: &str,
|
||||||
|
tmeta: &pmocache::download::TransformMetadata,
|
||||||
|
) {
|
||||||
|
if let Some(sr) = tmeta.sample_rate {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "sample_rate", Value::Number(Number::from(sr)));
|
||||||
|
}
|
||||||
|
if let Some(bps) = tmeta.bits_per_sample {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "bits_per_sample", Value::Number(Number::from(bps)));
|
||||||
|
}
|
||||||
|
if let Some(ch) = tmeta.channels {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "channels", Value::Number(Number::from(ch)));
|
||||||
|
}
|
||||||
|
if let Some(ts) = tmeta.total_samples {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "total_samples", Value::Number(Number::from(ts)));
|
||||||
|
if let Some(sr) = tmeta.sample_rate {
|
||||||
|
if sr > 0 {
|
||||||
|
let secs = (ts as f64 / sr as f64).round() as u64;
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "duration_secs", Value::Number(Number::from(secs)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Détermine la profondeur de bit d'un chunk audio
|
/// Détermine la profondeur de bit d'un chunk audio
|
||||||
fn get_chunk_bit_depth(chunk: &AudioChunk) -> u8 {
|
fn get_chunk_bit_depth(chunk: &AudioChunk) -> u8 {
|
||||||
match chunk {
|
match chunk {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use anyhow::Result;
|
|||||||
use pmocache::CacheConfig;
|
use pmocache::CacheConfig;
|
||||||
use serde_json::{Number, Value};
|
use serde_json::{Number, Value};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use pmocache::download::TransformMetadata;
|
||||||
|
|
||||||
/// Configuration pour le cache audio
|
/// Configuration pour le cache audio
|
||||||
pub struct AudioConfig;
|
pub struct AudioConfig;
|
||||||
@@ -56,6 +57,37 @@ pub fn new_cache(dir: &str, limit: usize) -> Result<Cache> {
|
|||||||
Cache::with_transformer(dir, limit, Some(transformer_factory))
|
Cache::with_transformer(dir, limit, Some(transformer_factory))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn persist_transform_streaminfo(cache: &Cache, pk: &str, tmeta: &TransformMetadata) {
|
||||||
|
if let Some(sr) = tmeta.sample_rate {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "sample_rate", Value::Number(Number::from(sr)));
|
||||||
|
}
|
||||||
|
if let Some(bps) = tmeta.bits_per_sample {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "bits_per_sample", Value::Number(Number::from(bps)));
|
||||||
|
}
|
||||||
|
if let Some(ch) = tmeta.channels {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "channels", Value::Number(Number::from(ch)));
|
||||||
|
}
|
||||||
|
if let Some(ts) = tmeta.total_samples {
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "total_samples", Value::Number(Number::from(ts)));
|
||||||
|
if let Some(sr) = tmeta.sample_rate {
|
||||||
|
if sr > 0 {
|
||||||
|
let secs = (ts as f64 / sr as f64).round() as u64;
|
||||||
|
let _ = cache
|
||||||
|
.db
|
||||||
|
.set_a_metadata(pk, "duration_secs", Value::Number(Number::from(secs)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Crée un cache audio et lance la consolidation en arrière-plan
|
/// Crée un cache audio et lance la consolidation en arrière-plan
|
||||||
///
|
///
|
||||||
/// Cette fonction crée le cache et lance immédiatement une consolidation
|
/// Cette fonction crée le cache et lance immédiatement une consolidation
|
||||||
@@ -138,6 +170,10 @@ pub async fn add_with_metadata_extraction(
|
|||||||
// Attendre que le fichier soit téléchargé et converti
|
// Attendre que le fichier soit téléchargé et converti
|
||||||
cache.wait_until_finished(&pk).await?;
|
cache.wait_until_finished(&pk).await?;
|
||||||
|
|
||||||
|
if let Some(transform) = cache.transform_metadata(&pk).await {
|
||||||
|
persist_transform_streaminfo(cache, &pk, &transform);
|
||||||
|
}
|
||||||
|
|
||||||
// Lire le fichier FLAC pour extraire les métadonnées
|
// Lire le fichier FLAC pour extraire les métadonnées
|
||||||
let file_path = cache.get_file_path(&pk);
|
let file_path = cache.get_file_path(&pk);
|
||||||
let flac_bytes = tokio::fs::read(&file_path).await?;
|
let flac_bytes = tokio::fs::read(&file_path).await?;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use bytes::Bytes;
|
|||||||
use pmocache::download::TransformMetadata;
|
use pmocache::download::TransformMetadata;
|
||||||
use pmocache::StreamTransformer;
|
use pmocache::StreamTransformer;
|
||||||
use pmoflac::{transcode_to_flac_stream, AudioCodec, TranscodeOptions};
|
use pmoflac::{transcode_to_flac_stream, AudioCodec, TranscodeOptions};
|
||||||
|
use serde_json::json;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
/// Creates the transformer consumed by the audio cache.
|
/// Creates the transformer consumed by the audio cache.
|
||||||
@@ -36,7 +37,19 @@ pub fn create_streaming_flac_transformer() -> StreamTransformer {
|
|||||||
.set_metadata(TransformMetadata {
|
.set_metadata(TransformMetadata {
|
||||||
mode: Some(mode.to_string()),
|
mode: Some(mode.to_string()),
|
||||||
input_codec: Some(codec_to_string(codec)),
|
input_codec: Some(codec_to_string(codec)),
|
||||||
details: None,
|
details: Some(
|
||||||
|
json!({
|
||||||
|
"sample_rate": info.sample_rate,
|
||||||
|
"bits_per_sample": info.bits_per_sample,
|
||||||
|
"channels": info.channels,
|
||||||
|
"total_samples": info.total_samples,
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
sample_rate: Some(info.sample_rate),
|
||||||
|
bits_per_sample: Some(info.bits_per_sample),
|
||||||
|
channels: Some(info.channels),
|
||||||
|
total_samples: info.total_samples,
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@@ -290,6 +290,10 @@ pub struct TransformMetadata {
|
|||||||
pub mode: Option<String>,
|
pub mode: Option<String>,
|
||||||
pub input_codec: Option<String>,
|
pub input_codec: Option<String>,
|
||||||
pub details: Option<String>,
|
pub details: Option<String>,
|
||||||
|
pub sample_rate: Option<u32>,
|
||||||
|
pub bits_per_sample: Option<u8>,
|
||||||
|
pub channels: Option<u8>,
|
||||||
|
pub total_samples: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TransformContext {
|
pub struct TransformContext {
|
||||||
|
|||||||
Reference in New Issue
Block a user