Elabore une crate pmosource

This commit is contained in:
2025-10-16 22:00:35 +02:00
parent 68b0c89130
commit e0fbb11475
16 changed files with 561 additions and 1 deletions

17
pmosource/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "pmosource"
version = "0.1.0"
edition = "2021"
authors = ["PMOMusic Contributors"]
description = "Common traits and types for PMOMusic sources"
license = "MIT OR Apache-2.0"
repository = "https://github.com/yourusername/pmomusic"
keywords = ["music", "source", "streaming"]
categories = ["multimedia"]
[dependencies]
# Gestion des erreurs
thiserror = "1.0"
# Image format support
image = { version = "0.25", default-features = false, features = ["webp"] }

113
pmosource/README.md Normal file
View File

@@ -0,0 +1,113 @@
# pmosource
Common traits and types for PMOMusic sources.
## Overview
`pmosource` provides the foundational abstractions for different music sources in the PMOMusic ecosystem, such as Radio Paradise, Qobuz, and potentially others in the future.
## Features
- **`MusicSource` trait**: Common interface for all music sources
- **Default images**: Standardized 300x300px WebP images embedded in binaries
- **Source identification**: Consistent naming and ID scheme
## Usage
### Implementing the trait
```rust
use pmosource::MusicSource;
const DEFAULT_IMAGE: &[u8] = include_bytes!("../assets/default.webp");
#[derive(Debug)]
pub struct MyMusicSource;
impl MusicSource for MyMusicSource {
fn name(&self) -> &str {
"My Music Service"
}
fn id(&self) -> &str {
"my-music-service"
}
fn default_image(&self) -> &[u8] {
DEFAULT_IMAGE
}
}
```
### Using a music source
```rust
use pmosource::MusicSource;
use pmoparadise::RadioParadiseSource;
use pmoqobuz::QobuzSource;
let rp = RadioParadiseSource;
let qobuz = QobuzSource;
println!("Source: {} ({})", rp.name(), rp.id());
println!("Image size: {} bytes", rp.default_image().len());
```
## Image Format
All default images should be:
- **Format**: WebP
- **Dimensions**: 300x300 pixels (square)
- **Quality**: 85 (good balance between size and quality)
- **Location**: `<crate>/assets/default.webp`
### Converting images
Use the provided Python script or similar tool:
```python
from PIL import Image
def convert_to_webp(input_path, output_path, size=300):
img = Image.open(input_path)
# Convert to RGB if necessary
if img.mode not in ('RGB', 'RGBA'):
img = img.convert('RGB')
# Make it square (center crop)
width, height = img.size
if width != height:
min_dim = min(width, height)
left = (width - min_dim) // 2
top = (height - min_dim) // 2
right = left + min_dim
bottom = top + min_dim
img = img.crop((left, top, right, bottom))
# Resize to target size
img = img.resize((size, size), Image.Resampling.LANCZOS)
# Save as WebP
img.save(output_path, 'WEBP', quality=85, method=6)
```
## Current Implementations
- **pmoparadise**: Radio Paradise
- **pmoqobuz**: Qobuz
## Future Enhancements
The `MusicSource` trait can be extended with additional methods such as:
- Authentication status
- Available quality levels
- Streaming capabilities
- Search functionality
- Playlist management
- And more...
## License
MIT OR Apache-2.0

View File

@@ -0,0 +1,66 @@
//! Example showing how to use the MusicSource trait
//!
//! This example demonstrates accessing source information and images
//! from different music sources (requires pmoparadise and pmoqobuz to be compiled).
use pmosource::{MusicSource, DEFAULT_IMAGE_SIZE};
// Mock implementations for demonstration
#[derive(Debug)]
struct RadioParadiseSource;
impl MusicSource for RadioParadiseSource {
fn name(&self) -> &str {
"Radio Paradise"
}
fn id(&self) -> &str {
"radio-paradise"
}
fn default_image(&self) -> &[u8] {
// This would normally be: include_bytes!("../../pmoparadise/assets/default.webp")
// For this example, we return an empty slice
&[]
}
}
#[derive(Debug)]
struct QobuzSource;
impl MusicSource for QobuzSource {
fn name(&self) -> &str {
"Qobuz"
}
fn id(&self) -> &str {
"qobuz"
}
fn default_image(&self) -> &[u8] {
// This would normally be: include_bytes!("../../pmoqobuz/assets/default.webp")
// For this example, we return an empty slice
&[]
}
}
fn main() {
println!("PMOMusic Sources\n");
println!("Standard image size: {}x{} pixels\n", DEFAULT_IMAGE_SIZE, DEFAULT_IMAGE_SIZE);
let sources: Vec<Box<dyn MusicSource>> = vec![
Box::new(RadioParadiseSource),
Box::new(QobuzSource),
];
for source in sources {
println!("Source: {}", source.name());
println!(" ID: {}", source.id());
println!(" Image MIME: {}", source.default_image_mime_type());
println!(" Image size: {} bytes", source.default_image().len());
println!();
}
println!("Note: In a real implementation, the images would be embedded in the binary");
println!(" and would be approximately 3-10 KB each in WebP format.");
}

112
pmosource/src/lib.rs Normal file
View File

@@ -0,0 +1,112 @@
//! # PMOSource
//!
//! Common traits and types for PMOMusic sources.
//!
//! This crate provides the foundational abstractions for different music sources
//! in the PMOMusic ecosystem, such as Radio Paradise, Qobuz, etc.
use std::fmt::Debug;
/// Standard size for default images (300x300 pixels)
pub const DEFAULT_IMAGE_SIZE: u32 = 300;
/// Error types for music source operations
#[derive(Debug, thiserror::Error)]
pub enum MusicSourceError {
#[error("Failed to load default image: {0}")]
ImageLoadError(String),
#[error("Invalid image format: {0}")]
InvalidImageFormat(String),
#[error("Source not available: {0}")]
SourceUnavailable(String),
}
/// Result type for music source operations
pub type Result<T> = std::result::Result<T, MusicSourceError>;
/// Main trait for music sources
///
/// This trait defines the common interface that all music sources must implement.
/// It provides methods for:
/// - Getting the source name and identification
/// - Retrieving default images/logos
/// - Other common operations (to be extended)
pub trait MusicSource: Debug + Send + Sync {
/// Returns the human-readable name of the music source
///
/// # Examples
///
/// ```ignore
/// assert_eq!(source.name(), "Radio Paradise");
/// ```
fn name(&self) -> &str;
/// Returns a unique identifier for the music source
///
/// This is typically a lowercase, hyphenated version of the name
/// suitable for use in URLs, file names, etc.
///
/// # Examples
///
/// ```ignore
/// assert_eq!(source.id(), "radio-paradise");
/// ```
fn id(&self) -> &str;
/// Returns the default image/logo for this source as WebP bytes
///
/// The image should be square (300x300 pixels) and in WebP format.
/// This is embedded in the binary for offline availability.
///
/// # Returns
///
/// A byte slice containing the WebP-encoded image data
///
/// # Examples
///
/// ```ignore
/// let image_data = source.default_image();
/// assert!(image_data.len() > 0);
/// ```
fn default_image(&self) -> &[u8];
/// Returns the MIME type of the default image
///
/// By default, this returns "image/webp" since all default images
/// should be in WebP format.
fn default_image_mime_type(&self) -> &str {
"image/webp"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct TestSource;
impl MusicSource for TestSource {
fn name(&self) -> &str {
"Test Source"
}
fn id(&self) -> &str {
"test-source"
}
fn default_image(&self) -> &[u8] {
&[]
}
}
#[test]
fn test_music_source_trait() {
let source = TestSource;
assert_eq!(source.name(), "Test Source");
assert_eq!(source.id(), "test-source");
assert_eq!(source.default_image_mime_type(), "image/webp");
}
}