sonor/
track.rs

1use crate::{utils, Result};
2use roxmltree::Node;
3
4/// A [Track](struct.Track.html) with some metadata like the track number, its duration and the
5/// elapsed time.
6#[derive(Debug)]
7pub struct TrackInfo {
8    track: Track,
9    metadata: String,
10    track_no: u32,
11    duration: u32,
12    elapsed: u32,
13}
14
15#[allow(missing_docs)]
16impl TrackInfo {
17    pub(crate) fn new(
18        track: Track,
19        metadata: String,
20        track_no: u32,
21        duration: u32,
22        elapsed: u32,
23    ) -> Self {
24        Self {
25            track,
26            metadata,
27            track_no,
28            duration,
29            elapsed,
30        }
31    }
32
33    pub fn track(&self) -> &Track {
34        &self.track
35    }
36    pub fn metadata(&self) -> &str {
37        &self.metadata
38    }
39    pub fn track_no(&self) -> u32 {
40        self.track_no
41    }
42    pub fn duration(&self) -> u32 {
43        self.duration
44    }
45    pub fn elapsed(&self) -> u32 {
46        self.elapsed
47    }
48}
49
50/// The track struct contains information about the music in UPnP music players.
51/// It always has a title and an URI, but sometimes there is a creator, album or duration specified
52/// too.
53#[derive(Debug)]
54pub struct Track {
55    title: String,
56    creator: Option<String>,
57    album: Option<String>,
58    duration: Option<u32>,
59    uri: String,
60}
61
62#[allow(missing_docs)]
63impl Track {
64    pub fn title(&self) -> &str {
65        &self.title
66    }
67    pub fn creator(&self) -> Option<&str> {
68        self.creator.as_deref()
69    }
70    pub fn album(&self) -> Option<&str> {
71        self.album.as_deref()
72    }
73    pub fn duration(&self) -> Option<u32> {
74        self.duration
75    }
76    pub fn uri(&self) -> &str {
77        &self.uri
78    }
79}
80
81impl std::fmt::Display for Track {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        write!(f, "{}", &self.title)?;
84        if let Some(creator) = &self.creator {
85            write!(f, " - {}", creator)?;
86        }
87        if let Some(album) = &self.album {
88            write!(f, " ({})", album)?;
89        }
90        Ok(())
91    }
92}
93
94impl Track {
95    pub(crate) fn from_xml(node: Node<'_, '_>) -> Result<Self> {
96        let mut title = None;
97        let mut creator = None;
98        let mut album = None;
99        let mut res = None;
100
101        for child in node.children() {
102            match child.tag_name().name() {
103                "title" => title = Some(child.text().unwrap_or_default().to_string()),
104                "creator" => creator = Some(child.text().unwrap_or_default().to_string()),
105                "album" => album = Some(child.text().unwrap_or_default().to_string()),
106                "res" => res = Some(child),
107                _ => (),
108            }
109        }
110
111        let title = title.ok_or_else(|| {
112            rupnp::Error::XmlMissingElement(node.tag_name().name().to_string(), "title".to_string())
113        })?;
114        let res = res.ok_or_else(|| {
115            rupnp::Error::XmlMissingElement(node.tag_name().name().to_string(), "res".to_string())
116        })?;
117        let duration = res
118            .attributes()
119            .find(|a| a.name().eq_ignore_ascii_case("duration"))
120            .map(|a| utils::seconds_from_str(a.value()))
121            .transpose()?;
122
123        let uri = res.text().unwrap_or_default().to_string();
124
125        Ok(Self {
126            title,
127            creator,
128            album,
129            duration,
130            uri,
131        })
132    }
133}