Fix progressive cache: distinguish temporary EOF from real EOF
Problem:
- PlaylistSource reads cached files faster than FlacCacheSink writes them
- FLAC decoder encounters EOF and stops playback prematurely
- First track doesn't play completely (stops at prebuffer point ~600ms)
- Needed to differentiate:
* Temporary EOF: file still being written (wait and retry)
* Real EOF: file completely written (stop decoding)
Solution:
1. Added Cache::is_download_complete() method (pmocache/src/cache.rs:735)
- Checks for existence of completion marker (.complete file)
- Marker created only when file is fully written and closed
- Fast synchronous check (no async overhead)
2. Modified decode_and_emit_track() (playlist_source.rs:337)
- On EOF: check if completion marker exists
- If no marker: file still being written → wait 50ms and retry read
- If marker exists: file complete → finish decoding
- Reduced wait from 100ms to 50ms for better responsiveness
Benefits:
✅ First track now plays completely (not just prebuffer portion)
✅ Progressive caching still works (playback starts at ~600ms)
✅ Proper EOF handling (no premature stops)
✅ Efficient polling (50ms retry interval)
✅ Works for both fresh downloads and cached files
Tested:
- Fresh download: EOF retries visible in logs every ~50ms
- File plays until completion marker created
- No premature track termination
Related to previous optimization (commit d8594e7) that made
prebuffer→playlist push immediate (76ms instead of 19s).
This commit is contained in:
@@ -332,15 +332,17 @@ async fn decode_and_emit_track(
|
||||
|
||||
// Si EOF atteint (read == 0)
|
||||
if read == 0 {
|
||||
// Vérifier si le download est toujours en cours (cache progressif)
|
||||
if cache.get_download(cache_pk).await.is_some() {
|
||||
// Download en cours - attendre un peu et réessayer
|
||||
tracing::trace!("decode_and_emit_track: EOF reached but download ongoing, waiting...");
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
// Vérifier si le fichier est complètement écrit (completion marker existe)
|
||||
// Si pas de marker, le fichier est encore en cours d'écriture (cache progressif)
|
||||
if !cache.is_download_complete(cache_pk) {
|
||||
// Fichier encore en cours d'écriture - attendre un peu et réessayer
|
||||
tracing::trace!("decode_and_emit_track: EOF reached but file not complete (no marker), waiting 50ms...");
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
continue; // Retry la lecture
|
||||
}
|
||||
|
||||
// Download terminé - c'est vraiment la fin du fichier
|
||||
// Completion marker existe - c'est vraiment la fin du fichier
|
||||
tracing::trace!("decode_and_emit_track: EOF reached and file is complete (marker exists)");
|
||||
if pending.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -716,6 +716,27 @@ impl<C: CacheConfig> Cache<C> {
|
||||
downloads.get(pk).cloned()
|
||||
}
|
||||
|
||||
/// Vérifie si le téléchargement/ingestion d'un fichier est complètement terminé
|
||||
///
|
||||
/// Cette méthode vérifie l'existence du fichier marker de complétion (.complete)
|
||||
/// qui est créé uniquement quand le fichier est complètement écrit et fermé.
|
||||
///
|
||||
/// Utile pour différencier:
|
||||
/// - EOF temporaire : fichier encore en cours d'écriture (retourne false)
|
||||
/// - EOF réel : fichier complètement écrit (retourne true)
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `pk` - Clé primaire du fichier
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` si le fichier est complètement écrit (marker existe), `false` sinon
|
||||
pub fn is_download_complete(&self, pk: &str) -> bool {
|
||||
let completion_marker = self.get_completion_marker_path(pk);
|
||||
completion_marker.exists()
|
||||
}
|
||||
|
||||
/// Retourne la taille actuelle téléchargée (source)
|
||||
///
|
||||
/// Si le download est en cours, retourne la taille téléchargée.
|
||||
|
||||
Reference in New Issue
Block a user