import Dates ms2string(m::Dates.DateTime) = Dates.format(m, Dates.dateformat"YYYY-mm-ddTHH:MM:SS") import Base.push! # Try to fix missing mime types Mux.mimetypes["alac"] = "audio/x-m4a" Mux.mimetypes["m4a"] = "audio/x-m4a" function push!(root::XMLElement, p::Playlist) playlistXML = new_child(root, "playlist") set_attributes(playlistXML, [ ("id", p.uuid), ("name", p.name), ("comment", p.comment), ("owner", p.owner), ("public", string(p.public)), ("songCount", string(length(p.songs))), ("duration", reduce(+, p.songs, init = 0.0) |> floor |> Int |> string), ("created", ms2string(p.created)), ("coverArt", p.cover), ]) playlistXML end function append!(root::XMLElement, p::Playlist) playlistXML = push!(root, p) # Allowed users for al in p.allowed set_content(new_child(playlistXML, "allowedUser"), al) end for song in p.songs entry = new_child(playlistXML, "entry") set_attributes(entry, props(song)) try artist = Beets.artist(song) n = artist == nothing ? artist.name : "" set_attribute(entry, "artist", "") catch e @warn e end end playlistXML end push!(p::Playlist, s::Song) = push!(p.songs, s) function push!(root::XMLElement, album::Beets.Album) albumXML = new_child(root, "album") set_attributes(albumXML, [ ("id", album.uuid), ("name", album.title), ("coverArt", album.uuid), ("songCount", string(length(album.songs))), ("created", ms2string(album.added)), ("duration", string(sum([t.length for t in album.songs]) |> floor |> Int)), ("artist", album.artist.name), ("artistId", album.artist.uuid) ]) albumXML end import Base.sort sort(ss::Vector{Beets.Song}) = sort(ss, by = x -> x.track) sort(a::Vector{Beets.Album}) = sort(a, by = a -> a.year) function append!(root::XMLElement, a::Beets.Album) albumXML = push!(root, a) for song in sort(a.songs) songXML = push!(albumXML, song) set_attributes(songXML, [ ("album", a.title), ("parent", a.artist.uuid), # Not clear ("artist", a.artist.name), ("coverArt", a.uuid), ("albumId", a.uuid), ("artistId", a.artist.uuid), ]) end albumXML end function push!(root::XMLElement, artist::Beets.Artist) artistXML = new_child(root, "artist") set_attributes(artistXML, [ ("id", artist.uuid), ("name", artist.name), ("coverArt", artist.uuid), ("albumCount", Beets.album(artist) |> length |> string) ]) artistXML end function push!(root::XMLElement, song::Beets.Song) songXML = new_child(root, "song") suffix = lowercase(song.format) mime = suffix in keys(Mux.mimetypes) ? Mux.mimetypes[suffix] : suffix set_attributes(songXML, [ ("id", song.uuid), ("title", song.title), ("isDir", "false"), ("created", ms2string(song.added)), ("duration", string(floor(song.length) |> Int)), ("bitrate", string(song.bitrate)), ("size", string(filesize(song.path))), ("suffix", suffix), ("contentType", mime), ("isVideo", "false"), ("path", relpath(song.path, Beets.musicdir())), ("type", "music") ]) songXML end function push!(root::XMLElement, song_album::Tuple{Beets.Song,Beets.Album}) songXML = new_child(root, "song") song, album = song_album suffix = lowercase(song.format) mime = suffix in keys(Mux.mimetypes) ? Mux.mimetypes[suffix] : suffix set_attributes(songXML, [ ("id", song.uuid), ("parent", album.artist.uuid), # Not clear ("title", song.title), ("album", album.title), ("artist", album.artist.name), ("isDir", "false"), ("coverArt", album.uuid), ("created", ms2string(album.added)), ("duration", string(floor(song.length) |> Int)), ("bitrate", string(song.bitrate)), ("size", string(filesize(song.path))), ("suffix", suffix), ("contentType", mime), ("isVideo", "false"), ("path", relpath(song.path, Beets.musicdir())), ("albumId", album.uuid), ("artistId", album.artist.uuid), ("type", "music") ]) root end function props(song::Song) suffix = lowercase(song.format) mime = suffix in keys(Mux.mimetypes) ? Mux.mimetypes[suffix] : suffix [ ("id", song.uuid), # ("parent", album.artist.uuid), # Not clear ("title", song.title), ("album", song.title), # ("artist", song.album.artist.name), ("isDir", "false"), ("coverArt", song.uuid), ("created", ms2string(song.added)), ("duration", string(floor(song.length) |> Int)), ("bitrate", string(song.bitrate)), ("size", string(filesize(song.path))), ("suffix", suffix), ("contentType", mime), # mpeg ("isVideo", "false"), ("path", relpath(song.path, Beets.musicdir())), #("albumId", song.album.uuid), #("artistId", song.album.artist.uuid), ("type", "music") ] end function push!(root::XMLElement, songs::Vector{Tuple{Beets.Song,Beets.Album}}) for (song, album) in songs songXML = new_child(root, "song") set_attributes(songXML, props(song)) set_attribute(songXML, "artistId", album.artist.uuid) set_attribute(songXML, "albumId", album.uuid) end root end function push!(root::XMLElement, user::User) userXML = new_child(root, "user") set_attributes(userXML, [ ("username", user.name) ("email", user.email) ("adminRole", string(user.admin)) ("scrobblingEnabled", "false") ("settingsRole", string(user.settings)) ("downloadRole", string(user.download)) ("uploadRole", string(user.upload)) ("playlistRole", string(user.playlist)) ("coverArtRole", string(user.cover)) ("commentRole", string(user.comment)) ("podcastRole", "false") ("streamRole", string(user.stream)) ("jukeboxRole", "false") ("shareRole", string(user.share)) ]) end