use super::*;
pub fn plan_album_artifacts(
desired: &[AlbumDesired],
albums: &BTreeMap<String, AlbumArt>,
can_delete: bool,
local: &HashMap<String, LocalFile>,
) -> Vec<Action> {
let mut actions: Vec<Action> = Vec::new();
let by_root: BTreeMap<&str, &AlbumDesired> =
desired.iter().map(|d| (d.root_id.as_str(), d)).collect();
for d in desired {
let stored = albums.get(&d.root_id);
for artifact in [
d.folder_jpg.as_ref(),
d.folder_webp.as_ref(),
d.folder_mp4.as_ref(),
]
.into_iter()
.flatten()
{
let needs_write = needs_write_drift(
stored
.and_then(|a| a.artifact(artifact.kind))
.map(|state| (state.hash.as_str(), state.path.as_str())),
artifact.hash.as_str(),
artifact.path.as_str(),
local,
);
if needs_write {
actions.push(Action::WriteArtifact {
kind: artifact.kind,
path: artifact.path.clone(),
source_url: artifact.source_url.clone(),
hash: artifact.hash.clone(),
owner_id: d.root_id.clone(),
content: None,
});
}
}
}
for (root_id, art) in albums {
for (kind, state) in album_artifacts(art) {
let desired_here = by_root
.get(root_id.as_str())
.is_some_and(|d| album_desires_kind(d, kind));
if !desired_here
&& let Some(action) =
delete_album_artifact_action(root_id, kind, &state.path, can_delete)
{
actions.push(action);
}
}
}
actions.sort_by(|a, b| album_action_key(a).cmp(&album_action_key(b)));
actions
}
fn album_artifacts(art: &AlbumArt) -> Vec<(ArtifactKind, &ArtifactState)> {
let mut out = Vec::new();
if let Some(state) = &art.folder_jpg {
out.push((ArtifactKind::FolderJpg, state));
}
if let Some(state) = &art.folder_webp {
out.push((ArtifactKind::FolderWebp, state));
}
if let Some(state) = &art.folder_mp4 {
out.push((ArtifactKind::FolderMp4, state));
}
out
}
fn album_desires_kind(d: &AlbumDesired, kind: ArtifactKind) -> bool {
match kind {
ArtifactKind::FolderJpg => d.folder_jpg.is_some(),
ArtifactKind::FolderWebp => d.folder_webp.is_some(),
ArtifactKind::FolderMp4 => d.folder_mp4.is_some(),
ArtifactKind::CoverJpg
| ArtifactKind::CoverWebp
| ArtifactKind::DetailsTxt
| ArtifactKind::LyricsTxt
| ArtifactKind::Lrc
| ArtifactKind::VideoMp4
| ArtifactKind::Playlist => false,
}
}
fn album_action_key(action: &Action) -> (&str, ArtifactKind) {
match action {
Action::WriteArtifact { owner_id, kind, .. }
| Action::DeleteArtifact { owner_id, kind, .. } => (owner_id.as_str(), *kind),
_ => ("", ArtifactKind::CoverJpg),
}
}
pub(crate) fn delete_album_artifact_action(
owner_id: &str,
kind: ArtifactKind,
path: &str,
can_delete: bool,
) -> Option<Action> {
if !can_delete || path.is_empty() {
return None;
}
Some(Action::DeleteArtifact {
kind,
path: path.to_string(),
owner_id: owner_id.to_string(),
})
}