deleting and not generating empty unlistened playlists

This commit is contained in:
Aaron Manning
2026-04-07 21:20:34 +01:00
parent 9ba198c2c1
commit f0c77aed29
4 changed files with 65 additions and 3 deletions

View File

@@ -11,6 +11,9 @@ pub(crate) const IPOD_PODCASTS_DIR: &str = "Podcasts";
pub(crate) const LISTENED_PLAYLIST_PATH: &str = "[PC Meta] [Listened].m3u";
pub(crate) const MASTER_PLAYLIST_PATH: &str = "[PC Meta] [Master Feed].m3u";
pub(crate) const PLAYLIST_PREFIX: &str = "[PC]";
pub(crate) const UNLISTENED_SUFFIX: &str = "(unlistened)";
pub(crate) fn podcast_folder(
root: &path::Path,
alias: &str,

View File

@@ -80,6 +80,9 @@ fn main() -> anyhow::Result<()> {
spec.write_to(&spec_file)?;
}
Command::Playlist { podcast } => {
// Empty playlist folder.
// playlist::empty_playlists(root)?;
if let Some(alias) = podcast {
playlist::generate_podcast_m3u(alias.as_str(), root, false)?;
playlist::generate_podcast_m3u(alias.as_str(), root, true)?;
@@ -106,6 +109,9 @@ fn main() -> anyhow::Result<()> {
}
}
// Empty playlist folder.
// playlist::empty_playlists(root)?;
// Generate updated playlist files
for (alias, _) in &config.podcasts {
playlist::generate_podcast_m3u(alias.as_str(), root, true)?;

View File

@@ -29,6 +29,10 @@ impl<'a> Playlist<'a> {
}
}
fn is_empty(&self) -> bool {
self.files.is_empty()
}
/// Writes the playlist file based on the specified filename.
///
/// Output boolean indicates if the playlist was written (if it was
@@ -41,11 +45,21 @@ impl<'a> Playlist<'a> {
let playlists_folder = self.root.join(folders::LOCAL_PLAYLISTS_DIR);
if !playlists_folder.exists() {
fs::create_dir(&playlists_folder)
.context(format!("failed to create output directory for playlists"))?;
.context("failed to create output directory for playlists")?;
}
let mut path = playlists_folder.join(sanitise(name));
path.set_extension("m3u");
// If the playlist is empty, then don't write it,
// and delete the existing one if it exists.
if self.is_empty() {
if path.exists() {
fs::remove_file(&path)
.context("failed to remove existing playlist to empty")?;
}
return Ok(false)
}
let mut output = io::BufWriter::new(Vec::new());
let mut writer = m3u::Writer::new(&mut output);
let entries =
@@ -136,6 +150,20 @@ pub(crate) fn generate_master_m3u(
Ok(())
}
/// Empties the playlist output folder.
pub(crate) fn empty_playlists(
root: &path::Path,
) -> anyhow::Result<()> {
let playlists_folder = root.join(folders::LOCAL_PLAYLISTS_DIR);
if playlists_folder.exists() {
fs::remove_dir_all(&playlists_folder)
.context("failed to remove output directory for playlists")?;
}
fs::create_dir(&playlists_folder)
.context("failed to create output directory for playlists")?;
Ok(())
}
pub(crate) fn generate_podcast_m3u(
alias: &str,
@@ -170,9 +198,9 @@ pub(crate) fn generate_podcast_m3u(
}
let written = if unlistened_only {
playlist.write_as(&format!("[PC] {} (unlistened)", alias), false)
playlist.write_as(&format!("{} {} {}", folders::PLAYLIST_PREFIX, alias, folders::UNLISTENED_SUFFIX), false)
} else {
playlist.write_as(&format!("[PC] {}", alias), false)
playlist.write_as(&format!("{} {}", folders::PLAYLIST_PREFIX, alias), false)
}?;
if written {

View File

@@ -175,15 +175,40 @@ pub(crate) fn sync(
anyhow::bail!("specified target directory does not contain a folder {:?}", folders::LOCAL_PLAYLISTS_DIR);
}
// This is a bit of a hack, but we use the list of acknowledged
// files in the target to recognise which unlistened playlists
// should be deleted (because they don't exist in the local directory).
// This won't delete any other extra podcasts, and is just a temporary
// fix to make sure empty unlistened playlists don't exist.
let mut acknowledged = BTreeSet::new();
for source in fs::read_dir(root.join(folders::LOCAL_PLAYLISTS_DIR))? {
let source = source?.path();
let target = target_dir
.join(folders::LOCAL_PLAYLISTS_DIR)
.join(source.file_name().unwrap());
acknowledged.insert(target.clone());
if !target.exists() || fs::metadata(&target)?.modified()? < fs::metadata(&source)?.modified()? {
println!("[info] copying playlist {:?}.", source.file_name().unwrap());
fs::copy(source, target)?;
}
}
// Here we delete the excess unlistened playlists (if the don't exist
// in local directory, and thus correspond to empty playlists).
//
// This could very easily be edited to remove other excess playlists.
for target in fs::read_dir(target_dir.join(folders::LOCAL_PLAYLISTS_DIR))? {
let target = target?.path();
let file_name = target.file_stem().unwrap();
let file_name = file_name.to_str().unwrap();
if file_name.starts_with(folders::PLAYLIST_PREFIX)
&& file_name.ends_with(folders::UNLISTENED_SUFFIX)
&& !acknowledged.contains(&target) {
fs::remove_file(&target)?;
}
}