1use std::io::{Read, Seek};
2use std::path::Path;
3use symphonia::core::formats::probe::Hint;
4use symphonia::core::io::MediaSource;
5
6struct ReadSeekSource {
7 inner: Box<dyn ReadSeekSendSync>,
8 len: Option<u64>,
9}
10
11trait ReadSeekSendSync: Read + Seek + Send + Sync {}
12impl<T: Read + Seek + Send + Sync> ReadSeekSendSync for T {}
13
14impl ReadSeekSource {
15 fn new(inner: Box<dyn ReadSeekSendSync>, len: Option<u64>) -> Self {
16 Self { inner, len }
17 }
18}
19
20impl Read for ReadSeekSource {
21 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
22 self.inner.read(buf)
23 }
24}
25
26impl Seek for ReadSeekSource {
27 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
28 self.inner.seek(pos)
29 }
30}
31
32impl MediaSource for ReadSeekSource {
33 fn is_seekable(&self) -> bool {
34 true
35 }
36
37 fn byte_len(&self) -> Option<u64> {
38 self.len
39 }
40}
41
42pub fn open_file(path: &Path) -> Result<(Box<dyn MediaSource>, Hint), Box<dyn std::error::Error>> {
43 let file = std::fs::File::open(path)?;
44 let len = file.metadata().ok().map(|m| m.len());
45
46 let mut hint = Hint::new();
47 if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
48 hint.with_extension(ext);
49 }
50
51 let source: Box<dyn MediaSource> = Box::new(ReadSeekSource::new(Box::new(file), len));
52 Ok((source, hint))
53}
54
55pub fn from_stream(
56 mut stream: impl Read + Seek + Send + Sync + 'static,
57) -> (Box<dyn MediaSource>, Hint) {
58 let len = stream.seek(std::io::SeekFrom::End(0)).ok();
59 let _ = stream.seek(std::io::SeekFrom::Start(0));
60
61 let source: Box<dyn MediaSource> = Box::new(ReadSeekSource::new(Box::new(stream), len));
62 let hint = Hint::new();
63 (source, hint)
64}
65
66pub fn from_stream_with_len(
67 stream: impl Read + Seek + Send + Sync + 'static,
68 len: Option<u64>,
69) -> (Box<dyn MediaSource>, Hint) {
70 let source: Box<dyn MediaSource> = Box::new(ReadSeekSource::new(Box::new(stream), len));
71 let hint = Hint::new();
72 (source, hint)
73}
74
75struct ReadOnlySource {
77 inner: Box<dyn Read + Send + Sync>,
78}
79
80impl Read for ReadOnlySource {
81 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
82 self.inner.read(buf)
83 }
84}
85
86impl Seek for ReadOnlySource {
87 fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
88 Err(std::io::Error::new(
89 std::io::ErrorKind::Unsupported,
90 "seek not supported on radio stream",
91 ))
92 }
93}
94
95impl MediaSource for ReadOnlySource {
96 fn is_seekable(&self) -> bool {
97 false
98 }
99
100 fn byte_len(&self) -> Option<u64> {
101 None
102 }
103}
104
105pub fn from_stream_with_hint(
108 stream: impl Read + Send + Sync + 'static,
109 extension: &str,
110) -> (Box<dyn MediaSource>, Hint) {
111 let source: Box<dyn MediaSource> = Box::new(ReadOnlySource {
112 inner: Box::new(stream),
113 });
114 let mut hint = Hint::new();
115 hint.with_extension(extension);
116 (source, hint)
117}