initial code for tag stripping and m3u generation
This commit is contained in:
@@ -9,15 +9,18 @@ use sanitise_file_name::sanitise;
|
||||
|
||||
use crate::rss;
|
||||
|
||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
||||
pub (crate) struct Specification<'a> {
|
||||
files : HashMap<Cow<'a, str>, Cow<'a, path::Path>>,
|
||||
feed : BTreeMap<chrono::NaiveDateTime, Vec<Episode<'a>>>,
|
||||
image_url : Option<Cow<'a, str>>,
|
||||
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub(crate) struct Specification<'a> {
|
||||
files: HashMap<Cow<'a, str>, Cow<'a, path::Path>>,
|
||||
/// This is a collection of episodes, where each entry contains a `Vec` of
|
||||
/// episodes to allow for the possibility that multiple episodes have the
|
||||
/// same timestamp.
|
||||
feed: BTreeMap<chrono::NaiveDateTime, Vec<Episode<'a>>>,
|
||||
image_url: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
impl<'a> Specification<'a> {
|
||||
pub (crate) fn read_from_with_default(path : &path::Path) -> Result<Self, anyhow::Error> {
|
||||
pub(crate) fn read_from_with_default(path: &path::Path) -> Result<Self, anyhow::Error> {
|
||||
Ok(if path.is_file() {
|
||||
toml::from_str(&fs::read_to_string(&path)?[..])?
|
||||
} else {
|
||||
@@ -25,7 +28,7 @@ impl<'a> Specification<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub (crate) fn read_from(path : &path::Path) -> Result<Self, anyhow::Error> {
|
||||
pub(crate) fn read_from(path: &path::Path) -> Result<Self, anyhow::Error> {
|
||||
Ok(if path.is_file() {
|
||||
toml::from_str(&fs::read_to_string(&path)?[..])?
|
||||
} else {
|
||||
@@ -33,41 +36,49 @@ impl<'a> Specification<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub (crate) fn write_to(&self, path : &path::Path) -> Result<(), anyhow::Error> {
|
||||
pub(crate) fn write_to(&self, path: &path::Path) -> Result<(), anyhow::Error> {
|
||||
Ok(fs::write(path, toml::to_string(self)?.as_bytes())?)
|
||||
}
|
||||
|
||||
pub (crate) fn feed_iter(&self) -> impl Iterator<Item = (&chrono::NaiveDateTime, &Vec<Episode<'a>>)> {
|
||||
pub(crate) fn feed_iter(&self) -> impl Iterator<Item = (&chrono::NaiveDateTime, &Vec<Episode<'a>>)> {
|
||||
self.feed.iter()
|
||||
}
|
||||
|
||||
pub (crate) fn feed_iter_mut(&mut self) -> impl Iterator<Item = (&chrono::NaiveDateTime, &mut Vec<Episode<'a>>)> {
|
||||
pub(crate) fn feed_iter_mut(&mut self) -> impl Iterator<Item = (&chrono::NaiveDateTime, &mut Vec<Episode<'a>>)> {
|
||||
self.feed.iter_mut()
|
||||
}
|
||||
|
||||
pub(crate) fn path_from_id(&self, id: &str) -> Option<&path::Path> {
|
||||
self.files.get(id).map(|v| &**v)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub (crate) struct Episode<'a> {
|
||||
/// Episode title.
|
||||
title : Cow<'a, str>,
|
||||
title: Cow<'a, str>,
|
||||
/// Show notes pulled from description or summary tag.
|
||||
show_notes : Option<Cow<'a, str>>,
|
||||
show_notes: Option<Cow<'a, str>>,
|
||||
/// This is the GUID or the URL if the GUID is not present.
|
||||
id : Cow<'a, str>,
|
||||
id: Cow<'a, str>,
|
||||
/// If the episode exists in the latest version of the feed.
|
||||
current : bool,
|
||||
current: bool,
|
||||
/// Flag to keep track of which episodes have been listened to.
|
||||
#[serde(default)]
|
||||
pub (crate) listened : bool,
|
||||
pub(crate) listened: bool,
|
||||
}
|
||||
|
||||
impl<'a> Episode<'a> {
|
||||
pub (crate) fn title(&self) -> &str {
|
||||
self.title.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
fn download_to_file(url : &str, path : &path::Path) -> anyhow::Result<()> {
|
||||
fn download_to_file(url: &str, path: &path::Path) -> anyhow::Result<()> {
|
||||
let response = minreq::get(url)
|
||||
.send()?;
|
||||
|
||||
@@ -80,10 +91,10 @@ fn download_to_file(url : &str, path : &path::Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub (crate) fn update_podcast(
|
||||
alias : &str,
|
||||
root : &path::Path,
|
||||
feed_location : &str,
|
||||
pub(crate) fn update_podcast(
|
||||
alias: &str,
|
||||
root: &path::Path,
|
||||
feed_location: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
|
||||
// Create output directory
|
||||
@@ -126,7 +137,7 @@ pub (crate) fn update_podcast(
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_extension_from_url(url : &str) -> Result<Option<String>, url::ParseError> {
|
||||
fn extract_extension_from_url(url: &str) -> Result<Option<String>, url::ParseError> {
|
||||
let mut url_edited = url::Url::parse(url)?;
|
||||
url_edited.set_query(None);
|
||||
|
||||
@@ -137,10 +148,10 @@ fn extract_extension_from_url(url : &str) -> Result<Option<String>, url::ParseEr
|
||||
}
|
||||
|
||||
fn update_artwork<'a, 'b>(
|
||||
channel : &rss::Channel<'a>,
|
||||
spec : &mut Specification<'b>,
|
||||
output : &path::Path,
|
||||
) -> anyhow::Result<()> where 'a : 'b {
|
||||
channel: &rss::Channel<'a>,
|
||||
spec: &mut Specification<'b>,
|
||||
output: &path::Path,
|
||||
) -> anyhow::Result<()> where 'a: 'b {
|
||||
|
||||
let image_url = match (&channel.image, &channel.itunes_image) {
|
||||
(Some(image), _) => Some(&image.url),
|
||||
@@ -156,7 +167,7 @@ fn update_artwork<'a, 'b>(
|
||||
|
||||
match extract_extension_from_url(new.as_ref()) {
|
||||
Ok(Some(extension)) => {
|
||||
let cover_path = output.join(format!("cover.{}", extension));
|
||||
let cover_path = output.join(format!("cover-original.{}", extension));
|
||||
|
||||
// Remove cover with conflicting file path if it exists
|
||||
if cover_path.exists() {
|
||||
@@ -184,9 +195,9 @@ fn update_artwork<'a, 'b>(
|
||||
}
|
||||
|
||||
|
||||
pub (crate) fn update_podcast_from_feed(
|
||||
output : &path::Path,
|
||||
feed : &str,
|
||||
pub(crate) fn update_podcast_from_feed(
|
||||
output: &path::Path,
|
||||
feed: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
|
||||
let feed = match xml_serde::from_str::<rss::Feed>(&feed) {
|
||||
@@ -300,11 +311,11 @@ pub (crate) fn update_podcast_from_feed(
|
||||
);
|
||||
|
||||
let episode = Episode {
|
||||
show_notes : description,
|
||||
id : Cow::from(id.to_owned()),
|
||||
current : true,
|
||||
show_notes: description,
|
||||
id: Cow::from(id.to_owned()),
|
||||
current: true,
|
||||
title,
|
||||
listened : false,
|
||||
listened: false,
|
||||
};
|
||||
|
||||
match spec.feed.get_mut(&item.published) {
|
||||
@@ -352,12 +363,12 @@ pub (crate) fn update_podcast_from_feed(
|
||||
/// Given a file path `something.xyz`, returns the first path of the form
|
||||
/// `something(a).xyz` where `a` is a non-negative integer which does not
|
||||
/// currently exist, or `something.xyz` if it itself does not exist.
|
||||
fn increment_file_name(path : &path::Path) -> Cow<'_, path::Path> {
|
||||
fn increment_file_name(path: &path::Path) -> Cow<'_, path::Path> {
|
||||
|
||||
if path.exists() {
|
||||
let mut new_path = path.to_owned();
|
||||
|
||||
let mut i : u32 = 0;
|
||||
let mut i: u32 = 0;
|
||||
while new_path.exists() {
|
||||
let mut stem = path.file_stem().unwrap().to_owned();
|
||||
|
||||
|
Reference in New Issue
Block a user