Init commit

This commit is contained in:
Ultradesu
2026-08-12 01:30:57 +01:00
commit 423ea59371
67 changed files with 23280 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "furumi-backend-api"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[dependencies]
furumi-domain.workspace = true
[lints]
workspace = true
+346
View File
@@ -0,0 +1,346 @@
//! Transport-independent contract between the frontend application and backend.
use std::fmt;
use furumi_domain::{Artist, Queue, QueueItemId, Release, ReleaseKey, Track, TrackKey};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RequestId(u64);
impl RequestId {
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum PlaybackStatus {
#[default]
Stopped,
Playing,
Paused,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PlaybackSnapshot {
pub status: PlaybackStatus,
pub position_seconds: f64,
pub duration_seconds: f64,
pub volume: f32,
}
impl Default for PlaybackSnapshot {
fn default() -> Self {
Self {
status: PlaybackStatus::Stopped,
position_seconds: 0.0,
duration_seconds: 0.0,
volume: 0.72,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub enum RemoteData<T> {
#[default]
NotRequested,
Loading {
request_id: RequestId,
},
Ready(T),
Failed {
message: String,
},
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct LibrarySnapshot {
pub artists: Vec<Artist>,
pub featured_releases: Vec<Release>,
pub recently_played: Vec<Track>,
pub playlists: Vec<PlaylistSnapshot>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct PlaylistSnapshot {
pub id: i64,
pub title: String,
pub is_likes: bool,
pub tracks: Vec<Track>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum DevicePlaybackRole {
#[default]
Active,
Control,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ConnectedDeviceSnapshot {
pub id: String,
pub name: String,
pub client_version: String,
pub is_self: bool,
pub presence: DevicePresence,
pub trust: DeviceTrust,
pub is_active: bool,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum DevicePresence {
#[default]
Offline,
Online,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum DeviceTrust {
#[default]
Trusted,
Revoked,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PendingPairingSnapshot {
pub request_id: String,
pub device_id: String,
pub name: String,
pub client_version: String,
pub requester_group_id: Option<String>,
pub requester_group_active_devices: usize,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ConnectedDevicesSnapshot {
pub this_device_id: String,
pub this_device_name: String,
pub group_id: String,
pub role: DevicePlaybackRole,
pub active_device_id: String,
pub active_device_name: String,
pub devices: Vec<ConnectedDeviceSnapshot>,
pub pending_pairings: Vec<PendingPairingSnapshot>,
pub invite: Option<String>,
pub busy: bool,
pub last_sync: Option<String>,
pub error: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct SearchResults {
pub artists: Vec<Artist>,
pub releases: Vec<Release>,
pub tracks: Vec<Track>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SearchStats {
pub tracks: usize,
pub artists: usize,
pub peers_queried: usize,
pub duration_ms: u64,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct SearchSnapshot {
pub request_id: Option<RequestId>,
pub results: SearchResults,
pub federation_pending: bool,
pub stats: Option<SearchStats>,
pub error: Option<String>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum FederationOperation {
#[default]
Idle,
Search,
Artist,
Release,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FederationActivitySnapshot {
pub operation: FederationOperation,
pub pending: bool,
pub stats: Option<SearchStats>,
pub error: Option<String>,
}
/// Lightweight live diagnostics for the federation node.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FederationDebugSnapshot {
pub running: bool,
pub endpoint_id: String,
pub dht_node_id: String,
pub connected_peers: usize,
pub known_contacts: usize,
pub stored_dht_records: Option<usize>,
pub published_items: usize,
pub error: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct VersionEntrySnapshot {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BuildInfoSnapshot {
pub software: Vec<VersionEntrySnapshot>,
pub protocols: Vec<VersionEntrySnapshot>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SettingsSnapshot {
pub network_id: String,
pub device_name: String,
pub library_path: String,
pub federation_enabled: bool,
pub save_federated_on_listen: bool,
pub language: String,
}
impl Default for SettingsSnapshot {
fn default() -> Self {
Self {
network_id: "furumi".into(),
device_name: String::new(),
library_path: "~/Music/Furumi".into(),
federation_enabled: true,
save_federated_on_listen: true,
language: "English".into(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct BackendSnapshot {
pub revision: u64,
pub library: RemoteData<LibrarySnapshot>,
pub playback: PlaybackSnapshot,
pub queue: Queue,
pub search: SearchSnapshot,
pub federation_activity: FederationActivitySnapshot,
pub federation_debug: FederationDebugSnapshot,
pub build_info: BuildInfoSnapshot,
pub connected_devices: ConnectedDevicesSnapshot,
pub settings: SettingsSnapshot,
pub playback_error: Option<String>,
pub settings_error: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BackendCommand {
Initialize,
TogglePlayback,
Play,
Pause,
Stop,
Seek {
position_seconds: f64,
},
SetVolume {
volume: f32,
},
PlayRelease {
release_id: ReleaseKey,
start: usize,
},
PlayTrack {
track: TrackKey,
},
PlayQueueItem {
item_id: QueueItemId,
},
PlayContext {
tracks: Vec<TrackKey>,
selected: TrackKey,
},
AddNext {
tracks: Vec<TrackKey>,
},
AddToEnd {
tracks: Vec<TrackKey>,
},
ToggleLike {
track: TrackKey,
},
CreatePlaylist {
title: String,
},
RenamePlaylist {
playlist_id: i64,
title: String,
},
DeletePlaylist {
playlist_id: i64,
},
AddToPlaylist {
playlist_id: i64,
tracks: Vec<TrackKey>,
},
RemoveFromPlaylist {
playlist_id: i64,
tracks: Vec<TrackKey>,
},
CreateDeviceInvite,
ConnectDevice {
invite: String,
},
AnswerDevicePairing {
request_id: String,
accept: bool,
use_requester_group: bool,
},
SelectPlaybackDevice {
device_id: String,
},
Next,
Previous,
Search {
request_id: RequestId,
query: String,
},
CancelSearch {
request_id: RequestId,
},
LoadArtist {
request_id: RequestId,
key: furumi_domain::ArtistKey,
name: String,
},
LoadRelease {
request_id: RequestId,
key: ReleaseKey,
artist_key: furumi_domain::ArtistKey,
artist_name: String,
},
UpdateSettings(SettingsSnapshot),
Shutdown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SendCommandError {
Busy,
Closed,
}
impl fmt::Display for SendCommandError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Busy => formatter.write_str("backend command queue is full"),
Self::Closed => formatter.write_str("backend is unavailable"),
}
}
}
impl std::error::Error for SendCommandError {}