1use crate::{utils, Result};
2use roxmltree::Node;
3
4#[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#[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}