1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct PlaybackPosition {
15 pub updated_at: Option<u8>,
16 pub position: Option<u64>,
17}
18
19#[derive(Debug, serde::Serialize)]
20pub struct TrackAttributes {
21 pub language_code: Option<String>,
22 pub characteristics: Option<Vec<u8>>,
23 pub display_name: Option<String>,
24}
25
26#[derive(Debug, serde::Serialize)]
27pub struct Track {
28 pub id: Option<String>,
29 pub track_attributes: Option<TrackAttributes>,
30}
31
32pub fn encode_rewind(audio_advance_unmuted: bool) -> anyhow::Result<Vec<u8>> {
36 let tlv = tlv::TlvItemEnc {
37 tag: 0,
38 value: tlv::TlvItemValueEnc::StructInvisible(vec![
39 (0, tlv::TlvItemValueEnc::Bool(audio_advance_unmuted)).into(),
40 ]),
41 };
42 Ok(tlv.encode()?)
43}
44
45pub fn encode_fast_forward(audio_advance_unmuted: bool) -> anyhow::Result<Vec<u8>> {
47 let tlv = tlv::TlvItemEnc {
48 tag: 0,
49 value: tlv::TlvItemValueEnc::StructInvisible(vec![
50 (0, tlv::TlvItemValueEnc::Bool(audio_advance_unmuted)).into(),
51 ]),
52 };
53 Ok(tlv.encode()?)
54}
55
56pub fn encode_skip_forward(delta_position_milliseconds: u64) -> anyhow::Result<Vec<u8>> {
58 let tlv = tlv::TlvItemEnc {
59 tag: 0,
60 value: tlv::TlvItemValueEnc::StructInvisible(vec![
61 (0, tlv::TlvItemValueEnc::UInt64(delta_position_milliseconds)).into(),
62 ]),
63 };
64 Ok(tlv.encode()?)
65}
66
67pub fn encode_skip_backward(delta_position_milliseconds: u64) -> anyhow::Result<Vec<u8>> {
69 let tlv = tlv::TlvItemEnc {
70 tag: 0,
71 value: tlv::TlvItemValueEnc::StructInvisible(vec![
72 (0, tlv::TlvItemValueEnc::UInt64(delta_position_milliseconds)).into(),
73 ]),
74 };
75 Ok(tlv.encode()?)
76}
77
78pub fn encode_seek(position: u64) -> anyhow::Result<Vec<u8>> {
80 let tlv = tlv::TlvItemEnc {
81 tag: 0,
82 value: tlv::TlvItemValueEnc::StructInvisible(vec![
83 (0, tlv::TlvItemValueEnc::UInt64(position)).into(),
84 ]),
85 };
86 Ok(tlv.encode()?)
87}
88
89pub fn encode_activate_audio_track(track_id: String, audio_output_index: Option<u8>) -> anyhow::Result<Vec<u8>> {
91 let tlv = tlv::TlvItemEnc {
92 tag: 0,
93 value: tlv::TlvItemValueEnc::StructInvisible(vec![
94 (0, tlv::TlvItemValueEnc::String(track_id)).into(),
95 (1, tlv::TlvItemValueEnc::UInt8(audio_output_index.unwrap_or(0))).into(),
96 ]),
97 };
98 Ok(tlv.encode()?)
99}
100
101pub fn encode_activate_text_track(track_id: String) -> anyhow::Result<Vec<u8>> {
103 let tlv = tlv::TlvItemEnc {
104 tag: 0,
105 value: tlv::TlvItemValueEnc::StructInvisible(vec![
106 (0, tlv::TlvItemValueEnc::String(track_id)).into(),
107 ]),
108 };
109 Ok(tlv.encode()?)
110}
111
112pub fn decode_current_state(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
116 if let tlv::TlvItemValue::Int(v) = inp {
117 Ok(*v as u8)
118 } else {
119 Err(anyhow::anyhow!("Expected Integer"))
120 }
121}
122
123pub fn decode_start_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
125 if let tlv::TlvItemValue::Int(v) = inp {
126 Ok(Some(*v as u8))
127 } else {
128 Ok(None)
129 }
130}
131
132pub fn decode_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
134 if let tlv::TlvItemValue::Int(v) = inp {
135 Ok(Some(*v))
136 } else {
137 Ok(None)
138 }
139}
140
141pub fn decode_sampled_position(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<PlaybackPosition>> {
143 if let tlv::TlvItemValue::List(_fields) = inp {
144 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
146 Ok(Some(PlaybackPosition {
147 updated_at: item.get_int(&[0]).map(|v| v as u8),
148 position: item.get_int(&[1]),
149 }))
150 } else {
154 Ok(None)
155 }
157}
158
159pub fn decode_playback_speed(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
161 if let tlv::TlvItemValue::Int(v) = inp {
162 Ok(*v as u8)
163 } else {
164 Err(anyhow::anyhow!("Expected Integer"))
165 }
166}
167
168pub fn decode_seek_range_end(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
170 if let tlv::TlvItemValue::Int(v) = inp {
171 Ok(Some(*v))
172 } else {
173 Ok(None)
174 }
175}
176
177pub fn decode_seek_range_start(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
179 if let tlv::TlvItemValue::Int(v) = inp {
180 Ok(Some(*v))
181 } else {
182 Ok(None)
183 }
184}
185
186pub fn decode_active_audio_track(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Track>> {
188 if let tlv::TlvItemValue::List(_fields) = inp {
189 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
191 Ok(Some(Track {
192 id: item.get_string_owned(&[0]),
193 track_attributes: {
194 if let Some(nested_tlv) = item.get(&[1]) {
195 if let tlv::TlvItemValue::List(_) = nested_tlv {
196 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
197 Some(TrackAttributes {
198 language_code: nested_item.get_string_owned(&[0]),
199 characteristics: {
200 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
201 let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
202 Some(items)
203 } else {
204 None
205 }
206 },
207 display_name: nested_item.get_string_owned(&[2]),
208 })
209 } else {
210 None
211 }
212 } else {
213 None
214 }
215 },
216 }))
217 } else {
221 Ok(None)
222 }
224}
225
226pub fn decode_available_audio_tracks(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Track>> {
228 let mut res = Vec::new();
229 if let tlv::TlvItemValue::List(v) = inp {
230 for item in v {
231 res.push(Track {
232 id: item.get_string_owned(&[0]),
233 track_attributes: {
234 if let Some(nested_tlv) = item.get(&[1]) {
235 if let tlv::TlvItemValue::List(_) = nested_tlv {
236 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
237 Some(TrackAttributes {
238 language_code: nested_item.get_string_owned(&[0]),
239 characteristics: {
240 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
241 let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
242 Some(items)
243 } else {
244 None
245 }
246 },
247 display_name: nested_item.get_string_owned(&[2]),
248 })
249 } else {
250 None
251 }
252 } else {
253 None
254 }
255 },
256 });
257 }
258 }
259 Ok(res)
260}
261
262pub fn decode_active_text_track(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Track>> {
264 if let tlv::TlvItemValue::List(_fields) = inp {
265 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
267 Ok(Some(Track {
268 id: item.get_string_owned(&[0]),
269 track_attributes: {
270 if let Some(nested_tlv) = item.get(&[1]) {
271 if let tlv::TlvItemValue::List(_) = nested_tlv {
272 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
273 Some(TrackAttributes {
274 language_code: nested_item.get_string_owned(&[0]),
275 characteristics: {
276 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
277 let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
278 Some(items)
279 } else {
280 None
281 }
282 },
283 display_name: nested_item.get_string_owned(&[2]),
284 })
285 } else {
286 None
287 }
288 } else {
289 None
290 }
291 },
292 }))
293 } else {
297 Ok(None)
298 }
300}
301
302pub fn decode_available_text_tracks(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Track>> {
304 let mut res = Vec::new();
305 if let tlv::TlvItemValue::List(v) = inp {
306 for item in v {
307 res.push(Track {
308 id: item.get_string_owned(&[0]),
309 track_attributes: {
310 if let Some(nested_tlv) = item.get(&[1]) {
311 if let tlv::TlvItemValue::List(_) = nested_tlv {
312 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
313 Some(TrackAttributes {
314 language_code: nested_item.get_string_owned(&[0]),
315 characteristics: {
316 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
317 let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
318 Some(items)
319 } else {
320 None
321 }
322 },
323 display_name: nested_item.get_string_owned(&[2]),
324 })
325 } else {
326 None
327 }
328 } else {
329 None
330 }
331 },
332 });
333 }
334 }
335 Ok(res)
336}
337
338
339pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
351 if cluster_id != 0x0506 {
353 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0506, got {}\"}}", cluster_id);
354 }
355
356 match attribute_id {
357 0x0000 => {
358 match decode_current_state(tlv_value) {
359 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
360 Err(e) => format!("{{\"error\": \"{}\"}}", e),
361 }
362 }
363 0x0001 => {
364 match decode_start_time(tlv_value) {
365 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
366 Err(e) => format!("{{\"error\": \"{}\"}}", e),
367 }
368 }
369 0x0002 => {
370 match decode_duration(tlv_value) {
371 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
372 Err(e) => format!("{{\"error\": \"{}\"}}", e),
373 }
374 }
375 0x0003 => {
376 match decode_sampled_position(tlv_value) {
377 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
378 Err(e) => format!("{{\"error\": \"{}\"}}", e),
379 }
380 }
381 0x0004 => {
382 match decode_playback_speed(tlv_value) {
383 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
384 Err(e) => format!("{{\"error\": \"{}\"}}", e),
385 }
386 }
387 0x0005 => {
388 match decode_seek_range_end(tlv_value) {
389 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
390 Err(e) => format!("{{\"error\": \"{}\"}}", e),
391 }
392 }
393 0x0006 => {
394 match decode_seek_range_start(tlv_value) {
395 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
396 Err(e) => format!("{{\"error\": \"{}\"}}", e),
397 }
398 }
399 0x0007 => {
400 match decode_active_audio_track(tlv_value) {
401 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
402 Err(e) => format!("{{\"error\": \"{}\"}}", e),
403 }
404 }
405 0x0008 => {
406 match decode_available_audio_tracks(tlv_value) {
407 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
408 Err(e) => format!("{{\"error\": \"{}\"}}", e),
409 }
410 }
411 0x0009 => {
412 match decode_active_text_track(tlv_value) {
413 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
414 Err(e) => format!("{{\"error\": \"{}\"}}", e),
415 }
416 }
417 0x000A => {
418 match decode_available_text_tracks(tlv_value) {
419 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
420 Err(e) => format!("{{\"error\": \"{}\"}}", e),
421 }
422 }
423 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
424 }
425}
426
427pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
432 vec![
433 (0x0000, "CurrentState"),
434 (0x0001, "StartTime"),
435 (0x0002, "Duration"),
436 (0x0003, "SampledPosition"),
437 (0x0004, "PlaybackSpeed"),
438 (0x0005, "SeekRangeEnd"),
439 (0x0006, "SeekRangeStart"),
440 (0x0007, "ActiveAudioTrack"),
441 (0x0008, "AvailableAudioTracks"),
442 (0x0009, "ActiveTextTrack"),
443 (0x000A, "AvailableTextTracks"),
444 ]
445}
446