Commit Graph

220 Commits

Author SHA1 Message Date
Claude
f8e09939ba refactor: Clean up dead code and simplify pmoserver REST API
Changes:
1. Removed dead code from paradise/worker.rs:
   - Unused process_song() method
   - Unused DecodedBlock struct
   - Unused helper functions: song_duration_ms, ms_to_frames, decode_block_audio

2. Simplified pmoserver_ext.rs (840 → 383 lines):
   - Removed complex orchestration endpoints (status, playlist, history, streaming)
   - Kept only simple API access endpoints:
     * /now-playing
     * /block/current
     * /block/{event_id}
     * /channels
   - Removed dependencies on RadioParadiseSource and ParadiseChannel

3. Created channels.rs:
   - Extracted channel definitions from paradise/channel.rs
   - Pure data module with no orchestration logic
   - Contains: ParadiseChannelKind, ChannelDescriptor, ALL_CHANNELS

Note: This is work in progress. Still need to update lib.rs and remove
unused modules once dependencies are fully resolved.
2025-11-05 07:15:16 +00:00
Claude
1817d1becc feat: Add I32 support to RadioParadiseStreamSource
Add support for 32-bit integer audio samples to match FileSource and
HttpSource capabilities, ensuring complete bit depth coverage.

Changes:
- Add I32 case to pcm_to_audio_segment() for 32-bit stereo samples
- Update output_type() comment to document 16/24/32-bit support
- Note that bit depth is auto-detected from FLAC header via pmoflac

The implementation now supports the full range of FLAC bit depths:
- 16-bit: AudioChunk::I16 (most common)
- 24-bit: AudioChunk::I24 (high quality)
- 32-bit: AudioChunk::I32 (maximum precision)

pmoflac reads bits_per_sample from the FLAC STREAMINFO header
(decoder.rs:97), so the actual bit depth is determined by the source
stream, not hardcoded.

Verified: cargo check passes successfully.
2025-11-05 06:40:51 +00:00
Claude
77469d5ee8 fix: Correct I24 conversion in RadioParadiseStreamSource
Fix three critical bugs in pcm_to_audio_segment():

1. **Incorrect sign extension for I24**
   - Before: i32::from_le_bytes([b0, b1, b2, 0]) >> 8
     Always produces positive values for negative samples
   - After: Proper sign extension using bit 7 of MSB
     buf[3] = 0xFF if (b2 & 0x80) != 0

2. **Silent clamping instead of error handling**
   - Before: I24::new_clamped() - silently clamps invalid values
   - After: I24::new().ok_or_else() - returns error for invalid values
     Consistent with FileSource/HttpSource behavior

3. **Missing validation before chunks_exact()**
   - Before: chunks_exact() panics if size not multiple of frame_bytes
   - After: Explicit validation with descriptive error message

Implementation now matches the reference pattern from pmoaudio's
FileSource and HttpSource (file_source.rs:326-357, http_source.rs:440-470).

Verified: cargo check passes successfully.
2025-11-05 06:34:05 +00:00
Claude
ce63cbffb3 refactor: Remove dead FFmpeg code from pmoparadise
Remove unused FFmpeg-based progressive streaming implementation that was
never completed and is not used anywhere in the codebase.

Changes:
- Delete src/ffmpeg_streaming.rs (173 lines of unfinished code with TODOs)
- Remove ffmpeg module import from lib.rs
- Remove ffmpeg feature from Cargo.toml
- Remove ffmpeg-next dependency from Cargo.toml

The current implementation uses claxon (StreamingPCMDecoder) and symphonia
(decode_block_audio) for FLAC decoding, which are fully functional.

Verified: cargo check passes successfully after removal.
2025-11-05 06:24:57 +00:00
Claude
28e33dc26f fix: Properly await async metadata setters in song_to_metadata
Problem:
- Used `let _ = metadata.set_title(...)` which creates unawaited Future
- Futures were never executed → metadata fields never set!
- Ignored Result<(), MetadataError> which could contain errors

Solution:
- Spawn tokio task to configure metadata asynchronously
- Properly await all set_*() calls
- Handle errors with eprintln! warnings instead of silent ignore
- Clone all data upfront for the async task

Type info:
- metadata: MemoryTrackMetadata (concrete type)
- Returns: Arc<RwLock<dyn TrackMetadata>> (trait object)
- Methods: async fn set_*(&mut self) -> MetadataResult<()>

All 7 tests still pass 
2025-11-05 06:01:51 +00:00
Claude
094c4af082 fix: Complete RadioParadiseStreamSource refactoring for current pmoaudio API
Refactored RadioParadiseStreamSource to use current pmoaudio API:

Audio Segment Creation:
- Replaced AudioSegment::new_audio() with manual construction using _AudioSegment
- Convert PCM to Vec<[i16; 2]> or Vec<[I24; 2]> stereo pairs
- Use AudioChunkData::new(stereo, sample_rate, gain_db) → Arc
- Wrap in AudioChunk::I16() or AudioChunk::I24()
- Create AudioSegment with order, timestamp_sec, and _AudioSegment::Chunk()

Sync Markers:
- Replaced AudioSegment::new_sync() with AudioSegment::new_track_boundary()
- Created TopZeroSync manually with _AudioSegment::Sync()
- Use AudioSegment::new_end_of_stream() for EOF

Stream Handling:
- Changed from decoder.next() (doesn't exist) to decoder.read()
- Added AsyncReadExt import
- Use buffered read approach like http_source
- Changed decoder.stream_info() to decoder.info()

I24 Construction:
- Changed I24::from_i32() to I24::new_clamped()
- Properly handles 24-bit PCM conversion with sign extension

Metadata:
- Fixed RwLock usage - write().await returns guard directly, no Result
- Added `let _` for Future return values

Testing:
 All 7 unit tests pass (cache FIFO behavior)
 RadioParadiseStreamSource compiles successfully with pmoaudio feature
2025-11-05 05:57:52 +00:00
Claude
dbfa392429 fix: Partial test corrections for RadioParadiseStreamSource
Fixed test issues:
- Changed EventId(i) to plain i (EventId is type alias for u64)
- Added create_test_client() helper using RadioParadiseClient::with_client()
- Cast DEFAULT_CHUNK_DURATION_MS to u32 as expected by constructor

Outstanding API incompatibility issues:
- AudioSegment API has evolved (new_audio/new_sync no longer exist)
- AudioChunkData::from_interleaved() doesn't exist
- I24::from_i32() should be I24::new() or I24::new_clamped()
- Need to understand current pmoaudio API for creating audio segments

Tests compile but RadioParadiseStreamSource implementation needs
significant refactoring to match current pmoaudio API.
2025-11-05 05:50:29 +00:00
Claude
fc5b0288b7 test: Add comprehensive unit tests for RadioParadiseStreamSource cache
Added 8 unit tests covering cache FIFO behavior:

1. test_cache_fifo_basic - Verify basic cache operation with 5 elements
2. test_cache_fifo_exactly_10_elements - Verify behavior at capacity limit
3. test_cache_fifo_eviction_oldest - Verify oldest element evicted on overflow
4. test_cache_fifo_multiple_evictions - Verify multiple sequential evictions
5. test_cache_never_exceeds_capacity - Critical test: 100 insertions, never exceeds 10
6. test_cache_fifo_order_preserved - Verify FIFO order (front=oldest, back=newest)
7. test_block_queue_push - Verify block queue management

Tests validate:
- VecDeque capacity never exceeded (while loop correctness)
- Oldest elements evicted first (FIFO ordering)
- Cache maintains exactly ≤10 elements at all times
- Pre-allocated capacity of 10 is respected

Note: Tests require 'pmoaudio' feature which depends on libsoxr system library
2025-11-05 05:44:53 +00:00
Claude
17dec3351e fix: Use while loop instead of if for robust cache size guarantee
Problem:
- With `if >= CACHE_SIZE`, only ONE element removed per call
- If cache ever had >10 elements (abnormal state), would stay oversized
- Example: 12 elements → if removes 1 → 11 elements → add 1 → 12 elements 

Solution:
- Use `while >= CACHE_SIZE` to remove ALL excess elements
- Example: 12 elements → while removes 2 → 10 elements → add 1 → 10 elements 
- Guarantees exactly ≤10 elements regardless of initial state

Changes:
- mark_block_downloaded(): changed `if` to `while`
- Updated comment to reflect "tous les éléments excédentaires"
- Documentation updated with robustness guarantee
2025-11-05 05:35:09 +00:00
Claude
ec61af7b71 fix: Prevent cache from exceeding pre-allocated capacity
Problem:
- Previous logic: push_back() first, then pop_front() if len > 10
- This temporarily creates 11 elements, exceeding VecDeque capacity of 10
- Wastes the benefit of with_capacity() pre-allocation

Solution:
- Check capacity BEFORE adding: if len >= 10, pop_front() first
- Then push_back() new element
- Guarantees never exceeding 10 elements at any time

Changes:
- mark_block_downloaded(): inverted order (pop before push)
- Changed condition from `> CACHE_SIZE` to `>= CACHE_SIZE`
- Documentation updated with correct logic and benefits
2025-11-05 05:33:36 +00:00
Claude
c2b78fa040 fix: Replace HashSet with VecDeque for recent blocks cache
Problem:
- HashSet doesn't maintain insertion order
- iter().next() returns arbitrary element, not the oldest
- Cache eviction was unpredictable

Solution:
- Use VecDeque for FIFO ordering
- push_back() adds new block
- pop_front() removes oldest block when cache exceeds 10 elements
- contains() is O(n) but performant for 10 elements

Changes:
- RadioParadiseStreamSourceLogic: recent_blocks now VecDeque<EventId>
- mark_block_downloaded(): simplified with guaranteed FIFO eviction
- Documentation updated with VecDeque usage and advantages
2025-11-05 05:31:43 +00:00
Claude
1d8bdfa30c docs: Add RadioParadiseStreamSource documentation and usage example
- Add comprehensive technical documentation (RADIO_PARADISE_STREAM_SOURCE.md)
- Add practical usage example (examples/radio_paradise_stream.rs)
- Document architecture, timing algorithm, and API
- Include both basic and advanced usage patterns with nowplaying stream
2025-11-05 05:16:51 +00:00
Claude
e084e75faa feat: Add RadioParadiseStreamSource - pmoaudio node for Radio Paradise
Implement a new pmoaudio source node that streams Radio Paradise blocks
with automatic TrackBoundary insertion at the correct timing.

Features:
- Downloads and decodes FLAC blocks from Radio Paradise API
- Queue management for block IDs via push_block_id()
- Recent blocks cache (10 blocks) to avoid re-downloads
- Automatic TrackBoundary insertion based on sample count timing
- Converts Song metadata to TrackMetadata with cover URLs
- Timeout of 3 seconds for new block IDs (radio real-time)
- Support for 16-bit and 24-bit FLAC audio

Architecture:
- RadioParadiseStreamSourceLogic: Pure business logic implementing NodeLogic
- RadioParadiseStreamSource: Wrapper using Node<> pattern
- Uses logic_mut() for push_block_id() configuration

The node emits:
- TopZeroSync at block start
- TrackBoundary before each song (with same order as next chunk)
- Audio chunks (I16 or I24)
- EndOfStream on timeout or completion

New pmoaudio feature gate with dependencies on:
- pmoaudio, pmoflac, pmometadata, futures-util
2025-11-04 23:28:13 +00:00
Claude
8b0317f88d refactor: Simplify FlacCacheSink playlist registration using logic_mut()
Improve the architecture by configuring the playlist handle directly
in register_playlist() instead of deferring it to run().

Changes:
- Remove playlist_handle_pending field (no longer needed)
- register_playlist() now calls logic_mut() to configure immediately
- run() becomes a simple delegation with no configuration logic
- Follows proper pattern: configuration before run(), not during run()

This is cleaner than the previous approach which used a pending field
and transferred it during run(). The new approach:
1. User calls register_playlist() → directly configures logic
2. User calls run() → simple delegation to inner.run()

Architecture now properly separates configuration from execution.
2025-11-04 22:33:32 +00:00
Claude
92c53ed3a5 feat: Add logic_mut() to Node and fix playlist registration in FlacCacheSink
- Add Node::logic_mut() method to allow post-construction configuration
  of node logic before run() is called
- Fix FlacCacheSink to properly transfer playlist_handle_pending to
  the inner logic using logic_mut()
- Resolves FIXME at flac_cache_sink.rs:656 about missing logic_mut()

This enables the playlist registration mechanism to work correctly:
1. User calls register_playlist() on FlacCacheSink
2. Handle is stored in playlist_handle_pending
3. During run(), handle is transferred to FlacCacheSinkLogic
4. Tracks are automatically added to playlist after caching
2025-11-04 22:29:32 +00:00
Claude
5d88d3a25e refactor: Fix compilation warnings across multiple crates
- Remove unused imports (Filter, Digest, Resource, etc.)
- Prefix unused variables with underscore (_pk, _size)
- Fix snake_case naming for local variables
- Remove unused ArgumentError enum in pmoupnp
- Apply cargo fix suggestions for unused imports

Remaining warnings are async_fn_in_trait style warnings which
would require API breaking changes to address.
2025-11-04 22:20:35 +00:00
Claude
701decfc55 fix: Update netstat2 from 0.9.1 to 0.11.2 to resolve libc compatibility
Resolved compilation error where netstat2 v0.9.1 was accessing
tcp_info.state instead of tcp_info.tcpi_state on Linux systems.
The latest version 0.11.2 includes the fix for this field name change.
2025-11-04 22:14:40 +00:00
Claude
3b1b81a893 security: Remove sensitive credentials from repository
- Remove .pmomusic.yml from git tracking (contains passwords)
- Add .pmomusic.yml.example as template without sensitive data
- Add SECURITY_CONFIG.md with setup instructions
- Credentials should be configured locally or via environment variables

This change prevents accidental exposure of Qobuz credentials.
Users must copy .pmomusic.yml.example to .pmomusic.yml and
configure their own credentials.
2025-11-04 20:38:10 +00:00
134d769374 Merge pull request 'push-mqttpywkpspw' (#19) from push-mqttpywkpspw into main
Reviewed-on: #19
2025-11-04 21:15:53 +01:00
8389d1a78e Récupération de l'erreur git cleaning 2025-11-04 21:13:06 +01:00
0603da2998 Ajout de la gestion des couvertures d'albums par le FlacCacheSink 2025-11-03 20:48:58 +01:00
157baadcfd Ajout d'un noeud puis vers le cache audio 2025-11-03 14:45:37 +01:00
7d907e7429 Implémentation des pmometadata dans pmocacheaudio 2025-11-03 14:45:37 +01:00
5f29496d79 Retour sur pmoaudio 2025-11-03 14:45:37 +01:00
e743c8affe Corrections mineurs sur pmoflac 2025-11-03 14:45:37 +01:00
0cd2b6a64a Refactoring PMOMetadata 2025-11-03 14:45:37 +01:00
a14210345c Restructuration de pmoaudio avec ajout des messages de synchro 2025-11-03 14:45:37 +01:00
56b3ec8285 Création de la crate pmometadata 2025-11-03 14:45:37 +01:00
0351d17fee Passons en stéreo
reprise du module DSP pmoaudio
2025-11-03 14:45:37 +01:00
a6ed30e0c7 Update la web app pour tirer partie du nouveau systeme de cache 2025-10-29 22:36:36 +01:00
b4a8925281 intégration de pmoflac dans pmoaudiocache. nétoyage du code audio obsolete 2025-10-29 22:36:36 +01:00
b004c8a8bf Ajoute une fonction de transcodage xxx-> flac en stream à pmoflac 2025-10-29 22:36:36 +01:00
a1170a8689 Debug suite à revue de code 2025-10-29 22:36:36 +01:00
f77376e07c unification des decodeurs 2025-10-29 22:36:36 +01:00
61b15f290d Refactoring pmoflac - factorisation des erreurs 2025-10-29 22:36:36 +01:00
5b60fdbe1d Refactoring de pmoflac -factorisation de code ogg et opus 2025-10-29 22:36:36 +01:00
e82979561a Création d'un décodeur générique 2025-10-29 22:36:36 +01:00
7c1485ae72 Ajout d'un lecteur AIFF 2025-10-29 22:36:36 +01:00
107e657666 Ajout d'un lecteur wav 2025-10-29 22:36:36 +01:00
bc1d0ef275 Ajout d'un decodeur ogg et opus vers pcm 2025-10-29 22:36:36 +01:00
162265c661 Ajout d'un decodeur ogg-vobis 2025-10-29 22:36:36 +01:00
da2d45c12d Ajout du decodage mp3 à pmoflac 2025-10-29 22:36:36 +01:00
36949a1da8 Cacheaudio en tream 2025-10-29 22:36:36 +01:00
dd5ed5e890 pmoflac corrections 2025-10-29 22:36:36 +01:00
fe984edb7b Crate pmoflac 2025-10-29 22:36:36 +01:00
93db9c25e7 la crate des playlists 2025-10-29 22:36:36 +01:00
09cdd3f516 stream radio paradise 2025-10-29 22:36:36 +01:00
24502ff53b ok encore l'inconnu... 2025-10-29 22:36:36 +01:00
dea7062038 Je ne sais pas trop 2025-10-29 22:35:53 +01:00
aae941b7cb passage à de l'encodage rééelement en flux 2025-10-29 22:35:53 +01:00