use url::Url;
use crate::{
play_url,
};
use std::{fs, path::Path, process::exit};
struct M3uEntry {
path: String,
title: Option<String>,
#[allow(dead_code)]
duration: Option<i32>,
}
fn parse(m3u: &str) -> Vec<M3uEntry> {
let mut entries = Vec::new();
let mut current_title = None;
let mut current_duration = None;
if !m3u.lines().next().unwrap_or("").starts_with("#EXTM3U") {
return m3u
.lines()
.map(|line| line.trim())
.filter(|line| !line.is_empty() && !line.starts_with('#'))
.map(|line| M3uEntry {
path: line.to_string(),
title: None,
duration: None,
})
.collect();
}
for line in m3u.lines().map(|l| l.trim()).filter(|l| !l.is_empty()) {
if line.starts_with("#EXTINF:") {
let info = line.strip_prefix("#EXTINF:").unwrap_or("").trim();
let mut parts = info.splitn(2, ',');
current_duration = parts.next().and_then(|s| s.parse::<i32>().ok());
current_title = parts.next().map(String::from);
} else if !line.starts_with('#') {
entries.push(M3uEntry {
path: line.to_string(),
title: current_title.take(),
duration: current_duration.take(),
});
}
}
entries
}
pub struct Playlist {
entries: Vec<PlaylistEntry>,
}
pub struct PlaylistEntry {
pub path: String,
pub title: Option<String>,
pub duration: Option<i32>,
}
impl Playlist {
pub fn from_file<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
let content = std::fs::read_to_string(path)?;
Ok(Self::from_string(&content))
}
pub fn from_string(content: &str) -> Self {
let entries = crate::m3u::parse(content)
.into_iter()
.map(|e| PlaylistEntry {
path: e.path,
title: e.title,
duration: e.duration,
})
.collect();
Self { entries }
}
pub fn entries(&self) -> &[PlaylistEntry] {
&self.entries
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}