1extern crate quick_xml;
6
7mod pls;
8mod m3u;
9mod asx;
10mod xspf;
11mod decode_error;
12
13use std::collections::HashSet;
14
15pub use decode_error::PlaylistDecodeError;
16
17pub fn decode(content: &str) -> Result<Vec<String>, PlaylistDecodeError> {
43 let mut set = HashSet::new();
44 let content_small = content.to_lowercase();
45 match content_small.find("<playlist"){
46 Some(_)=>{
47 let xspf_items = xspf::decode(content)?;
48 for item in xspf_items {
49 if item.url != "" {
50 set.insert(item.url);
51 }
52 if item.identifier != "" {
53 set.insert(item.identifier);
54 }
55 }
56 }
57 None =>{
58 match content_small.find("<asx"){
59 Some(_)=>{
60 let pls_items = asx::decode(content)?;
61 for item in pls_items {
62 set.insert(item.url);
63 }
64 }
65 None =>{
66 match content_small.find("[playlist]"){
67 Some(_) => {
68 let pls_items = pls::decode(content);
69 for item in pls_items {
70 set.insert(item.url);
71 }
72 }
73 None => {
74 let m3u_items = m3u::decode(content);
75 for item in m3u_items {
76 set.insert(item.url);
77 }
78 }
79 }
80 }
81 }
82 }
83 }
84 let v: Vec<String> = set.into_iter().collect();
85 Ok(v)
86}
87
88pub fn is_content_hls(content: &str) -> bool {
89 if content.contains("EXT-X-STREAM-INF"){
90 return true;
91 }
92 if content.contains("EXT-X-TARGETDURATION"){
93 return true;
94 }
95 return false;
96}
97
98#[cfg(test)]
99mod tests {
100 #[test]
101 fn xspf() {
102 let s = r#"<?xml version="1.0" encoding="UTF-8"?>
103<playlist version="1" xmlns="http://xspf.org/ns/0/">
104 <trackList>
105 <track>
106 <title>Title</title>
107 <identifier>Identifier</identifier>
108 <location>http://this.is.an.example</location>
109 </track>
110 <track>
111 <title>Title2</title>
112 <identifier>Identifier2</identifier>
113 <location>http://this.is.an.example2</location>
114 </track>
115 </trackList>
116</playlist>"#;
117 let items = crate::xspf::decode(s);
118 assert!(items.is_ok());
119 let items = items.unwrap();
120 assert!(items.len() == 2);
121 assert!(items[0].url == "http://this.is.an.example");
122 assert!(items[0].title == "Title");
123 assert!(items[0].identifier == "Identifier");
124 assert!(items[1].url == "http://this.is.an.example2");
125 assert!(items[1].title == "Title2");
126 assert!(items[1].identifier == "Identifier2");
127 }
128
129 #[test]
130 fn asx() {
131 let s = r#"<asx version="3.0">
132 <title>Test-Liste</title>
133 <entry>
134 <title>title1</title>
135 <ref href="ref1"/>
136 </entry>
137 <entry>
138 <title>title2</title>
139 <ref href="ref2"/>
140 </entry>
141</asx>"#;
142 let items = crate::asx::decode(s);
143 assert!(items.is_ok());
144 let items = items.unwrap();
145 assert!(items.len() == 2);
146 assert!(items[0].url == "ref1");
147 assert!(items[0].title == "title1");
148 assert!(items[1].url == "ref2");
149 assert!(items[1].title == "title2");
150 }
151
152 #[test]
153 fn m3u() {
154 let items = crate::m3u::decode("http://this.is.an.example");
155 assert!(items.len() == 1);
156 assert!(items[0].url == "http://this.is.an.example");
157 }
158
159 #[test]
160 fn pls() {
161 let items = crate::pls::decode("[playlist]
162File1=http://this.is.an.example
163Title1=mytitle
164 ");
165 assert!(items.len() == 1);
166 assert!(items[0].url == "http://this.is.an.example");
167 assert!(items[0].title == "mytitle");
168 }
169
170 #[test]
171 fn pls2() {
172 let items = crate::pls::decode("[playlist]
173File1=http://this.is.an.example
174Title=mytitle
175 ");
176 assert!(items.len() == 1);
177 assert!(items[0].url == "http://this.is.an.example");
178 assert!(items[0].title == "mytitle");
179 }
180
181 #[test]
182 fn pls3() {
183 let items = crate::pls::decode("[Playlist]
184File1=http://this.is.an.example
185Title=mytitle
186 ");
187 assert!(items.len() == 1);
188 assert!(items[0].url == "http://this.is.an.example");
189 assert!(items[0].title == "mytitle");
190 }
191}