various fixes

This commit is contained in:
nixo 2019-05-19 18:28:43 +02:00
parent 3d5510cedb
commit 0395955afc
3 changed files with 118 additions and 91 deletions

View File

@ -61,12 +61,17 @@ function getMusicFolders()
end end
end end
# Implement:
# getIndexes
# getMusicDirectory
"Returns all genres." "Returns all genres."
function getGenres() function getGenres()
@subsonic begin (xdoc, xroot) = subsonic()
songs = allsongs(); songs = Beets.songs();
res = Dict{String,Int}() res = Dict{String,Int}()
for genre in filter!(!isempty, getfield.(songs, :genre)) genrelist = strip.(sort(filter!(!isempty, Beets.genre.(Beets.albums))))
for genre in genrelist
t = get(res, genre, 0) t = get(res, genre, 0)
res[genre] = t+1 res[genre] = t+1
end end
@ -76,39 +81,28 @@ function getGenres()
set_attributes(genre, [ set_attributes(genre, [
("songCount", string(res[k])), ("songCount", string(res[k])),
# FIXME # FIXME
("albumCount", "0"), ("albumCount", string(count(genrelist .== k))),
]) ])
add_text(genre, k) add_text(genre, k)
end end
end return subsonic_return(xdoc)
end end
"Similar to getIndexes, but organizes music according to ID3 tags." "Similar to getIndexes, but organizes music according to ID3 tags."
function getArtists() function getArtists()
# TODO (xdoc, xroot) = subsonic()
(xdoc, xroot) = subsonic(version = "1.12.0")
indexes = new_child(xroot, "artists") indexes = new_child(xroot, "artists")
set_attribute(indexes, "ignoredArticles", "") set_attribute(indexes, "ignoredArticles", "")
beetsdb = Beets.getartists() artists = Beets.artists()
artists = unique(beetsdb) firstletters = unique(first.(filter(!isempty, Beets.name.(artists))) .|> uppercase)
# albums = group_albums_as_artists() for index in string.(firstletters)
# .|> does not work in a macro. What to do?
for index in unique(first.(filter(!isempty,
getfield.(artists, Ref(:name)))) .|> uppercase)
indexXML = new_child(indexes, "index") indexXML = new_child(indexes, "index")
set_attribute(indexXML, "name", string(index)) set_attribute(indexXML, "name", index)
for artist in filter(x -> startswith(x.name, string(index)), artists) for artist in unique(filter(x -> startswith(x.name, index), artists))
artistXML = new_child(indexXML, "artist") artistXML = push!(indexXML, artist)
set_attributes(artistXML,
[("name", artist.name),
("id", artist.uuid),
("coverArt", ""),
("albumCount", "")])
end end
end end
doc_str = string(xdoc) return subsonic_return(xdoc)
free(xdoc)
return doc_str
end end
"""Returns details for an artist, including a list of albums. """Returns details for an artist, including a list of albums.
@ -116,22 +110,14 @@ end
This method organizes music according to ID3 tags.""" This method organizes music according to ID3 tags."""
function getArtist(id::String) function getArtist(id::String)
artists = Beets.getartists() artists = Beets.getartists()
matching = artists[getfield.(artists, :uuid) .== id] matching = findfirst(a -> a.uuid == id, artists)
name = length(matching) > 0 ? first(matching).name : "" matching === nothing && return not_found("id")
isempty(name) && return not_found() artist = artists[matching]
# Create the response # Create the response
(xdoc, xroot) = subsonic() (xdoc, xroot) = subsonic()
artistXML = new_child(xroot, "artist") artistXML = push!(xroot, artist)
artist = first(matching) for album in Beets.albums(artist)
artist_albums = [i for i in Beets.getalbums() if i.artist == artist] push!(artistXML, album)
set_attributes(artistXML, [
("id", artist.uuid),
("albumCount", string(length(artist_albums))),
("name", artist.name),
("coverArt", "false")
])
for album in artist_albums
push!(xroot, album)
end end
return subsonic_return(xdoc) return subsonic_return(xdoc)
end end
@ -150,9 +136,17 @@ function getAlbum(req::Dict)
return getAlbum(albumid) return getAlbum(albumid)
end end
function getAlbum(albumid)
album = Beets.album(string(albumid))
album === nothing && return not_found("album")
(xdoc, xroot) = subsonic()
# push!(albumXML, [(s, album) for s in album.songs])
append!(xroot, album)
return subsonic_return(xdoc)
end
function getAlbumList(req::Dict) function getAlbumList(req::Dict)
query = HTTP.URIs.queryparams(req[:query]) query = HTTP.URIs.queryparams(req[:query])
@show query
albumtype = get(query, "type", "") albumtype = get(query, "type", "")
isempty(albumtype) && return missing_parameter("type") isempty(albumtype) && return missing_parameter("type")
@subsonic begin @subsonic begin
@ -161,18 +155,6 @@ function getAlbumList(req::Dict)
end end
end end
function getAlbum(albumid)
matching = [album for album in Beets.getalbums() if album.uuid == albumid]
if length(matching) < 1
return not_found("album")
end
album = first(matching)
(xdoc, xroot) = subsonic()
albumXML = push!(xroot, album)
push!(albumXML, [(s, album) for s in album.songs])
return subsonic_return(xdoc)
end
function getSong(req) function getSong(req)
query = HTTP.URIs.queryparams(req[:query]) query = HTTP.URIs.queryparams(req[:query])
id = get(query, "id", "") id = get(query, "id", "")
@ -192,7 +174,7 @@ function getRandomSongs(; size = 10,
fromYear::Union{Missing,Int} = missing, fromYear::Union{Missing,Int} = missing,
toYear::Union{Missing,Int} = missing, toYear::Union{Missing,Int} = missing,
musicFolderId::Union{Missing,String} = missing) musicFolderId::Union{Missing,String} = missing)
songs = allsongs(); songs = Beets.songs();
# Randomize # Randomize
songs = songs[Random.randperm(length(songs))]; songs = songs[Random.randperm(length(songs))];
# Filter # Filter
@ -299,7 +281,7 @@ function createPlaylist(req)
songId = get(query, "songId", "") songId = get(query, "songId", "")
# Check required params # Check required params
isempty(songId) && return missing_parameter("songId") isempty(songId) && return missing_parameter("songId")
songs = allsongs(); songs = Beets.songs();
songn = findfirst(s -> s.uuid == songId, songs) songn = findfirst(s -> s.uuid == songId, songs)
songn === nothing && return not_found("songId") songn === nothing && return not_found("songId")
song = songs[songn] song = songs[songn]
@ -355,7 +337,6 @@ end
function updatePlaylist(req) function updatePlaylist(req)
global user_playlists global user_playlists
query = HTTP.URIs.queryparams(req[:query]) query = HTTP.URIs.queryparams(req[:query])
@show query
playlistId = get(query, "playlistId", "") playlistId = get(query, "playlistId", "")
isempty(playlistId) && return missing_parameter("playlistId") isempty(playlistId) && return missing_parameter("playlistId")
# FIXME: check ownership # FIXME: check ownership
@ -374,7 +355,7 @@ function updatePlaylist(req)
# TODO: Support multiple (repeated) parameter # TODO: Support multiple (repeated) parameter
if !isempty(songIdAdd) if !isempty(songIdAdd)
songs = allsongs(); songs = Beets.songs();
songn = findfirst(s -> s.uuid == songIdAdd, songs) songn = findfirst(s -> s.uuid == songIdAdd, songs)
songn === nothing && return not_found("songIdToAdd") songn === nothing && return not_found("songIdToAdd")
song = songs[songn] song = songs[songn]
@ -405,17 +386,15 @@ function getUser(req)
end end
# Media retriveal # Media retriveal
"Returns a cover art image." "Returns a cover art image."
function getCoverArt(req::Dict) function getCoverArt(req::Dict)
query = HTTP.URIs.queryparams(req[:query]) query = HTTP.URIs.queryparams(req[:query])
id = get(query, "id", "") id = get(query, "id", "")
isempty(id) && return missing_parameter("id") isempty(id) && return missing_parameter("id")
albums = Beets.getalbums() n = findfirst(a -> a.uuid == id, Beets.albums)
n = findfirst(a -> album.uuid == id, albums)
n === nothing && return not_found("id") n === nothing && return not_found("id")
# @show matching.cover headers = Dict{String,String}()
return Dict(:body => read(albums)) return sendfile(Beets.albums[n].cover)
end end
"Streams a given media file." "Streams a given media file."
@ -423,8 +402,19 @@ function stream(req::Dict)
query = HTTP.URIs.queryparams(req[:query]) query = HTTP.URIs.queryparams(req[:query])
id = get(query, "id", "") id = get(query, "id", "")
isempty(id) && return missing_parameter("id") isempty(id) && return missing_parameter("id")
songs = allsongs() songs = Beets.songs()
m = findfirst(x -> (x.uuid == id), songs) m = findfirst(x -> (x.uuid == id), songs)
m === nothing && return not_found("id") m === nothing && return not_found("id")
return Dict(:body => read(songs[m].path))
return sendfile(songs[m].path)
end
function sendfile(path; suffix = nothing)
isfile(path) || return Dict{String,String}(:body => "Not Found")
suffix = suffix == nothing ? lowercase(split(path, '.')[end]) : suffix
headers = Dict{String,String}()
headers["Content-Type"] = Mux.mimetypes[suffix]
headers["Content-Length"] = string(filesize(path))
return Dict(:body => read(path),
:headers => headers)
end end

View File

@ -9,7 +9,7 @@ function push!(root::XMLElement, p::Playlist)
("owner", p.owner), ("owner", p.owner),
("public", string(p.public)), ("public", string(p.public)),
("songCount", string(length(p.songs))), ("songCount", string(length(p.songs))),
("duration", reduce(+, p.songs, init = 0.0) |> floor |> string), ("duration", reduce(+, p.songs, init = 0.0) |> floor |> Int |> string),
("created", "FIXME"), ("created", "FIXME"),
("coverArt", p.cover), ("coverArt", p.cover),
]) ])
@ -35,38 +35,72 @@ push!(p::Playlist, s::Song) = push!(p.songs, s)
function push!(root::XMLElement, album::Beets.Album) function push!(root::XMLElement, album::Beets.Album)
albumXML = new_child(root, "album") albumXML = new_child(root, "album")
set_attributes(albumXML, [ set_attributes(albumXML, [
("name", album.title),
("id", album.uuid), ("id", album.uuid),
("name", album.artist.name), ("name", album.title),
("artistId", album.artist.uuid),
("artist", album.artist.name),
# FIXME
("created", "0"),
("coverArt", album.uuid), ("coverArt", album.uuid),
("songs", "FIXME"),
("songCount", string(length(album.songs))), ("songCount", string(length(album.songs))),
("duration", string(sum([t.length for t in album.songs]))) ("created", "0"), # FIXME
("duration", string(sum([t.length for t in album.songs]) |> floor |> Int)),
("artist", album.artist.name),
("artistId", album.artist.uuid)
]) ])
albumXML albumXML
end end
function append!(root::XMLElement, a::Beets.Album)
albumXML = push!(root, a)
for song in 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) function push!(root::XMLElement, artist::Beets.Artist)
artistXML = new_child(root, "artist") artistXML = new_child(root, "artist")
set_attributes(artistXML, [ set_attributes(artistXML, [
("id", artist.uuid), ("id", artist.uuid),
("name", artist.name), ("name", artist.name),
("coverArt", artist.uuid), ("coverArt", artist.uuid),
("albumCount", "0") ("albumCount", Beets.albums(artist) |> length |> string)
]) ])
artistXML artistXML
end end
function push!(root::XMLElement, song::Beets.Song)
songXML = new_child(root, "song")
suffix = lowercase(song.format)
set_attributes(songXML, [
("id", song.uuid),
("title", song.title),
("isDir", "false"),
("created", "FIXME"),
("duration", string(floor(song.length) |> Int)),
("bitrate", string(song.bitrate)),
("size", string(filesize(song.path))),
("suffix", suffix),
("contentType", Mux.mimetypes[suffix]),
("isVideo", "false"),
("path", relpath(song.path, Beets.musicdir())),
("type", "music")
])
songXML
end
function push!(root::XMLElement, song_album::Tuple{Beets.Song,Beets.Album}) function push!(root::XMLElement, song_album::Tuple{Beets.Song,Beets.Album})
songXML = new_child(root, "song") songXML = new_child(root, "song")
song, album = song_album song, album = song_album
suffix = lowercase(song.format)
set_attributes(songXML, [ set_attributes(songXML, [
("id", song.uuid), ("id", song.uuid),
# ("parent", album.artist.uuid), # Not clear ("parent", album.artist.uuid), # Not clear
("title", song.title), ("title", song.title),
("album", album.title), ("album", album.title),
("artist", album.artist.name), ("artist", album.artist.name),
@ -76,9 +110,8 @@ function push!(root::XMLElement, song_album::Tuple{Beets.Song,Beets.Album})
("duration", string(floor(song.length) |> Int)), ("duration", string(floor(song.length) |> Int)),
("bitrate", string(song.bitrate)), ("bitrate", string(song.bitrate)),
("size", string(filesize(song.path))), ("size", string(filesize(song.path))),
("suffix", lowercase(song.format)), ("suffix", suffix),
## FIXME ("contentType", Mux.mimetypes[suffix]), # mpeg
("contentType", "audio/flac"), # mpeg
("isVideo", "false"), ("isVideo", "false"),
("path", relpath(song.path, Beets.musicdir())), ("path", relpath(song.path, Beets.musicdir())),
("albumId", album.uuid), ("albumId", album.uuid),
@ -89,6 +122,7 @@ function push!(root::XMLElement, song_album::Tuple{Beets.Song,Beets.Album})
end end
function props(song::Song) function props(song::Song)
suffix = lowercase(song.format)
[ [
("id", song.uuid), ("id", song.uuid),
# ("parent", album.artist.uuid), # Not clear # ("parent", album.artist.uuid), # Not clear
@ -101,9 +135,8 @@ function props(song::Song)
("duration", string(floor(song.length) |> Int)), ("duration", string(floor(song.length) |> Int)),
("bitrate", string(song.bitrate)), ("bitrate", string(song.bitrate)),
("size", string(filesize(song.path))), ("size", string(filesize(song.path))),
("suffix", lowercase(song.format)), ("suffix", suffix),
## FIXME ("contentType", Mux.mimetypes[suffix]), # mpeg
("contentType", "audio/flac"), # mpeg
("isVideo", "false"), ("isVideo", "false"),
("path", relpath(song.path, Beets.musicdir())), ("path", relpath(song.path, Beets.musicdir())),
# ("albumId", song.album.uuid), # ("albumId", song.album.uuid),

View File

@ -2,10 +2,13 @@ using Mux
using HTTP using HTTP
using Revise using Revise
push!(LOAD_PATH, "/home/nixo/memories/projects/2018-2019/musicjl")
import Beets import Beets
Beets.update_albums();
push!(LOAD_PATH, realpath("JlSonic")) push!(LOAD_PATH, realpath("JlSonic"))
using JlSonic using JlSonic
include("router.jl") include("router.jl")
include("login.jl") include("login.jl")
@app sonic = ( @app sonic = (
@ -21,3 +24,4 @@ if !isdefined(Main, :started)
serve(sonic) serve(sonic)
started = true started = true
end end