refactor and fixes
This commit is contained in:
parent
61449fd235
commit
34956871d7
|
@ -11,15 +11,18 @@ using JSON2
|
|||
# The idea is to sum all the album arts in some way. But it's easier to get one random
|
||||
# using FileIO, Images
|
||||
|
||||
const domain = "nixo.xyz"
|
||||
|
||||
include("types.jl")
|
||||
export Playlist, Album, Artist
|
||||
|
||||
const domain = "nixo.xyz"
|
||||
const users = User[]
|
||||
const user_playlists = Vector{Playlist}()
|
||||
|
||||
include("api.jl")
|
||||
export ping, getLicense,
|
||||
# Browsing
|
||||
getMusicFolders, # getIndexes, getMusicDirectory,
|
||||
getMusicFolders, # getIndexes,
|
||||
getMusicDirectory,
|
||||
getGenres, getArtists, getArtist, getAlbum, getSong,
|
||||
# Album/song list
|
||||
getAlbumList, getAlbumList2, getRandomSongs,
|
||||
|
@ -42,5 +45,6 @@ export auth_failed
|
|||
|
||||
include("beethelpers.jl")
|
||||
include("beet2xml.jl")
|
||||
include("login.jl")
|
||||
|
||||
end # module JlSonic
|
||||
|
|
168
JlSonic/api.jl
168
JlSonic/api.jl
|
@ -63,7 +63,88 @@ end
|
|||
|
||||
# Implement:
|
||||
# getIndexes
|
||||
# getMusicDirectory
|
||||
function getMusicDirectory(req)
|
||||
query = HTTP.URIs.queryparams(req[:query])
|
||||
id = get(query, "id", "")
|
||||
isempty(id) && return missing_parameter()
|
||||
(xdoc, xroot) = subsonic(version = "1.0.0")
|
||||
directory = new_child(xroot, "directory")
|
||||
# We simulate directory listing. Root directory has id ==
|
||||
# 1. Other directories are identified by uuids
|
||||
artists = Beets.getartists()
|
||||
## Structure is: Music(id=1)/Artist/Album/Song
|
||||
if id == "1"
|
||||
# List artists
|
||||
for artist in artists
|
||||
content = new_child(directory, "child")
|
||||
set_attributes(content,
|
||||
[("id", artist.uuid),
|
||||
# Always under /Music, id = 1
|
||||
("parent", "1"),
|
||||
("name", artist.name),
|
||||
# ("starred", "FIXME")
|
||||
])
|
||||
end
|
||||
else
|
||||
# List content
|
||||
# 1. Search if uuid matches artist
|
||||
# 2. Else, check if matches albums
|
||||
artistmatch = findfirst(a -> a.uuid == id, artists)
|
||||
albums = Beets.getalbums();
|
||||
if artistmatch != nothing
|
||||
@show id
|
||||
artist = artists[artistmatch]
|
||||
set_attributes(directory,
|
||||
[("id", artist.uuid),
|
||||
# Always under /Music, id = 1
|
||||
("parent", "1"),
|
||||
("name", artist.name),
|
||||
("starred", "2013-11-02T12:30:00")
|
||||
])
|
||||
# List albums
|
||||
content = new_child(directory, "child")
|
||||
for albumn in findall(alb -> alb.artist == artist, albums)
|
||||
album = albums[albumn]
|
||||
set_attributes(content, [
|
||||
("id", album.uuid),
|
||||
("parent", album.artist.uuid),
|
||||
# ("artistId", album.artist.uuid),
|
||||
("title", album.title),
|
||||
("artist", album.artist.name),
|
||||
("isDir", "true"),
|
||||
("coverArt", album.uuid),
|
||||
])
|
||||
end
|
||||
elseif false
|
||||
content = new_child(directory, "child")
|
||||
# List album content (songs)
|
||||
set_attributes(content, [
|
||||
("id", "FIXME"),
|
||||
("parent", "PARENT:ID"),
|
||||
("title", "FIXME"),
|
||||
("isDir", "false"),
|
||||
("album", "FIXME"),
|
||||
("artist", "FIXME"),
|
||||
("track", "FIXME"),
|
||||
("year", "FIXME"),
|
||||
("genre", "FIXME"),
|
||||
("coverArt", "FIXME"),
|
||||
("size", "FIXME"),
|
||||
# FIXME
|
||||
("contentType", "audio/mpeg"),
|
||||
("suffix", "FIXME"),
|
||||
("duration", "FIXME"),
|
||||
("bitrate", "FIXME"),
|
||||
("path", "FIXME"),
|
||||
])
|
||||
else
|
||||
return not_found()
|
||||
end
|
||||
end
|
||||
doc_str = string(xdoc)
|
||||
free(xdoc)
|
||||
return doc_str
|
||||
end
|
||||
|
||||
"Returns all genres."
|
||||
function getGenres()
|
||||
|
@ -98,7 +179,7 @@ function getArtists()
|
|||
for index in string.(firstletters)
|
||||
indexXML = new_child(indexes, "index")
|
||||
set_attribute(indexXML, "name", index)
|
||||
for artist in unique(filter(x -> startswith(x.name, index), artists))
|
||||
for artist in filter(x -> startswith(x.name, index), artists)
|
||||
artistXML = push!(indexXML, artist)
|
||||
end
|
||||
end
|
||||
|
@ -109,7 +190,7 @@ end
|
|||
|
||||
This method organizes music according to ID3 tags."""
|
||||
function getArtist(id::String)
|
||||
artists = Beets.getartists()
|
||||
artists = Beets.artists()
|
||||
matching = findfirst(a -> a.uuid == id, artists)
|
||||
matching === nothing && return not_found("id")
|
||||
artist = artists[matching]
|
||||
|
@ -264,8 +345,6 @@ function search3(req)
|
|||
return subsonic_return(xdoc)
|
||||
end
|
||||
|
||||
const user_playlists = Vector{Playlist}()
|
||||
|
||||
"Create (or update) a playlist" # WTF create can update?
|
||||
function createPlaylist(req)
|
||||
global user_playlists
|
||||
|
@ -293,10 +372,10 @@ function createPlaylist(req)
|
|||
end
|
||||
elseif !isempty(name)
|
||||
playlist = Playlist(req[:login][:user].name,
|
||||
name = name,
|
||||
# cover = ???
|
||||
name = name # cover = ???
|
||||
)
|
||||
push!(playlist, song)
|
||||
@show "THERE"
|
||||
else
|
||||
return missing_parameter("either name or playlistId")
|
||||
end
|
||||
|
@ -323,11 +402,11 @@ function getPlaylists(req)
|
|||
end
|
||||
|
||||
import Base.get
|
||||
function get(::Type{Playlist}, u::User, id::AbstractString)
|
||||
function get(::Type{Playlist}, u::User, id::AbstractString)::Union{Nothing,Playlist}
|
||||
global user_playlists
|
||||
findfirst(p -> p.uuid == id,
|
||||
filter(p -> canread(u, p),
|
||||
user_playlists))
|
||||
playlists = filter(p -> canread(u, p), user_playlists)
|
||||
m = findfirst(p -> p.uuid == id, playlists)
|
||||
m == nothing ? nothing : playlists[m]
|
||||
end
|
||||
|
||||
"Returns a listing of files in a saved playlist."
|
||||
|
@ -336,10 +415,10 @@ function getPlaylist(req)
|
|||
query = HTTP.URIs.queryparams(req[:query])
|
||||
id = get(query, "id", "")
|
||||
isempty(id) && return missing_parameter("id")
|
||||
m = get(Playlist, req[:login][:user], id)
|
||||
m == nothing && return not_found("id")
|
||||
playlist = get(Playlist, req[:login][:user], id)
|
||||
playlist == nothing && return not_found("id")
|
||||
(xdoc, xroot) = subsonic()
|
||||
append!(xroot, user_playlists[m])
|
||||
append!(xroot, playlist)
|
||||
return subsonic_return(xroot)
|
||||
end
|
||||
|
||||
|
@ -349,16 +428,26 @@ function updatePlaylist(req)
|
|||
query = HTTP.URIs.queryparams(req[:query])
|
||||
playlistId = get(query, "playlistId", "")
|
||||
isempty(playlistId) && return missing_parameter("playlistId")
|
||||
m = get(Playlist, req[:login][:user], playlistId)
|
||||
m == nothing && return not_found("playlistId")
|
||||
playlist = user_playlists[m]
|
||||
playlist = get(Playlist, req[:login][:user], playlistId)
|
||||
playlist == nothing && return not_found("playlistId")
|
||||
|
||||
# Check ownership (if not allowed, should not even reach this (canread is false))
|
||||
canedit(req[:login][:user], playlist) || return not_allowed()
|
||||
canedit(req[:login][:user], playlist) || return unuthorized()
|
||||
# Want to make public. Is allowed?
|
||||
wantpublic = try
|
||||
parse(Bool,get(query, "public", string(playlist.public)))
|
||||
catch e
|
||||
isa(e, ArgumentError) ? false : @error e
|
||||
end
|
||||
if wantpublic
|
||||
canmakepublic(req[:login][:user]) || return unuthorized()
|
||||
playlist.public = true
|
||||
else
|
||||
playlist.public = false
|
||||
end
|
||||
|
||||
playlist.name = get(query, "name", playlist.name)
|
||||
playlist.comment = get(query, "comment", playlist.comment)
|
||||
# FIXME: use try/catch
|
||||
playlist.public = parse(Bool,get(query, "public", string(playlist.public)))
|
||||
songIdAdd = get(query, "songIdToAdd", "")
|
||||
# WTF by the index!?
|
||||
IndexToRemove = get(query, "songIndexToRemove", "")
|
||||
|
@ -416,9 +505,11 @@ function getCoverArt(req::Dict)
|
|||
return sendfile(Beets.albums[n].cover)
|
||||
end
|
||||
|
||||
canstream(u::User) = u.stream
|
||||
"Streams a given media file."
|
||||
function stream(req::Dict)
|
||||
query = HTTP.URIs.queryparams(req[:query])
|
||||
canstream(req[:login][:user]) || return unuthorized()
|
||||
id = get(query, "id", "")
|
||||
isempty(id) && return missing_parameter("id")
|
||||
songs = Beets.songs()
|
||||
|
@ -432,35 +523,20 @@ 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]
|
||||
mime = suffix in keys(Mux.mimetypes) ? Mux.mimetypes[suffix] : suffix
|
||||
headers["Content-Type"] = mime
|
||||
headers["Content-Length"] = string(filesize(path))
|
||||
return Dict(:body => read(path),
|
||||
:headers => headers)
|
||||
end
|
||||
|
||||
function saveplaylists(; file = expanduser("~/.config/beets/playlists.jsonl"))
|
||||
global user_playlists
|
||||
open(file, "w") do f
|
||||
write(f,
|
||||
join(JSON2.write.(user_playlists), "\n"))
|
||||
end
|
||||
canread(u::User, p::Playlist) = p.public ||
|
||||
(u.admin || p.owner == u.name) ||
|
||||
u in p.allowed
|
||||
function canedit(u::User, p::Playlist)
|
||||
@show p.owner
|
||||
@show u.name
|
||||
@show u.admin
|
||||
(p.owner == u.name) || u.admin
|
||||
end
|
||||
|
||||
function loadplaylists(; file = expanduser("~/.config/beets/playlists.jsonl"))
|
||||
global user_playlists
|
||||
isfile(file) || touch(file)
|
||||
ps = JSON2.readlines(file)
|
||||
empty!(user_playlists)
|
||||
for p in ps
|
||||
# try
|
||||
pl = JSON2.read(p, Playlist)
|
||||
push!(user_playlists, pl)
|
||||
# catch e
|
||||
# @warn "Failed to read with error $e"
|
||||
# isa(e, ArgumentError) && continue
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
canread(u::User, p::Playlist) = p.public || p.owner == u.name || u in p.allowed
|
||||
canedit(u::User, p::Playlist) = p.owner == u.name
|
||||
canmakepublic(u::User) = u.playlist
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
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, [
|
||||
|
@ -10,7 +17,7 @@ function push!(root::XMLElement, p::Playlist)
|
|||
("public", string(p.public)),
|
||||
("songCount", string(length(p.songs))),
|
||||
("duration", reduce(+, p.songs, init = 0.0) |> floor |> Int |> string),
|
||||
("created", "2012-04-17T19:53:44"),
|
||||
("created", ms2string(p.created)),
|
||||
("coverArt", p.cover),
|
||||
])
|
||||
playlistXML
|
||||
|
@ -20,8 +27,7 @@ function append!(root::XMLElement, p::Playlist)
|
|||
playlistXML = push!(root, p)
|
||||
# Allowed users
|
||||
for al in p.allowed
|
||||
set_content(new_child(playlistXML, "allowedUser"),
|
||||
al)
|
||||
set_content(new_child(playlistXML, "allowedUser"), al)
|
||||
end
|
||||
for song in p.songs
|
||||
entry = new_child(playlistXML, "entry")
|
||||
|
@ -40,7 +46,7 @@ function push!(root::XMLElement, album::Beets.Album)
|
|||
("name", album.title),
|
||||
("coverArt", album.uuid),
|
||||
("songCount", string(length(album.songs))),
|
||||
("created", "0"), # FIXME
|
||||
("created", ms2string(album.added)),
|
||||
("duration", string(sum([t.length for t in album.songs]) |> floor |> Int)),
|
||||
("artist", album.artist.name),
|
||||
("artistId", album.artist.uuid)
|
||||
|
@ -78,16 +84,17 @@ 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", "FIXME"),
|
||||
("created", ms2string(song.added)),
|
||||
("duration", string(floor(song.length) |> Int)),
|
||||
("bitrate", string(song.bitrate)),
|
||||
("size", string(filesize(song.path))),
|
||||
("suffix", suffix),
|
||||
("contentType", Mux.mimetypes[suffix]),
|
||||
("contentType", mime),
|
||||
("isVideo", "false"),
|
||||
("path", relpath(song.path, Beets.musicdir())),
|
||||
("type", "music")
|
||||
|
@ -99,6 +106,7 @@ 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
|
||||
|
@ -107,12 +115,12 @@ function push!(root::XMLElement, song_album::Tuple{Beets.Song,Beets.Album})
|
|||
("artist", album.artist.name),
|
||||
("isDir", "false"),
|
||||
("coverArt", album.uuid),
|
||||
("created", "FIXME"),
|
||||
("created", ms2string(album.added)),
|
||||
("duration", string(floor(song.length) |> Int)),
|
||||
("bitrate", string(song.bitrate)),
|
||||
("size", string(filesize(song.path))),
|
||||
("suffix", suffix),
|
||||
("contentType", Mux.mimetypes[suffix]), # mpeg
|
||||
("contentType", mime),
|
||||
("isVideo", "false"),
|
||||
("path", relpath(song.path, Beets.musicdir())),
|
||||
("albumId", album.uuid),
|
||||
|
@ -124,6 +132,7 @@ 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
|
||||
|
@ -132,12 +141,12 @@ function props(song::Song)
|
|||
# ("artist", song.album.artist.name),
|
||||
("isDir", "false"),
|
||||
("coverArt", song.uuid),
|
||||
("created", "FIXME"),
|
||||
("created", ms2string(song.added)),
|
||||
("duration", string(floor(song.length) |> Int)),
|
||||
("bitrate", string(song.bitrate)),
|
||||
("size", string(filesize(song.path))),
|
||||
("suffix", suffix),
|
||||
("contentType", Mux.mimetypes[suffix]), # mpeg
|
||||
("contentType", mime), # mpeg
|
||||
("isVideo", "false"),
|
||||
("path", relpath(song.path, Beets.musicdir())),
|
||||
# ("albumId", song.album.uuid),
|
||||
|
|
|
@ -1,4 +1,44 @@
|
|||
function allsongs()
|
||||
albums = [album.songs for album in Beets.getalbums()];
|
||||
songs = Iterators.flatten(albums) |> collect;
|
||||
playlistfile(path) = joinpath(path, "playlists.jsonl")
|
||||
|
||||
function saveplaylists(; file = playlistfile(Beets.confdir()))
|
||||
global user_playlists
|
||||
open(file, "w") do f
|
||||
write(f,
|
||||
join(JSON2.write.(user_playlists), "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
function loadplaylists(; file = playlistfile(Beets.confdir()))
|
||||
global user_playlists
|
||||
isfile(file) || touch(file)
|
||||
ps = JSON2.readlines(file)
|
||||
empty!(user_playlists)
|
||||
for p in ps
|
||||
try
|
||||
pl = JSON2.read(p, Playlist)
|
||||
push!(user_playlists, pl)
|
||||
catch e
|
||||
@warn "Failed to read with error $e"
|
||||
isa(e, ArgumentError) && continue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function saveusers(file = expanduser("~/.config/beets/users.jsonl"))
|
||||
global users
|
||||
open(file, "w") do f
|
||||
write(f, join(JSON2.write.(users), "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
function loadusers(; file = expanduser("~/.config/beets/users.jsonl"))
|
||||
global users
|
||||
isfile(file) || touch(file)
|
||||
ps = JSON2.readlines(file)
|
||||
p = JSON2.read.(ps, JlSonic.User)
|
||||
empty!(users)
|
||||
for pl in p
|
||||
push!(users, pl)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Random
|
||||
|
||||
import Dates
|
||||
mutable struct User
|
||||
name::String
|
||||
password::String
|
||||
|
@ -28,6 +28,7 @@ mutable struct Playlist
|
|||
songs::Vector{Song}
|
||||
cover::String
|
||||
allowed::Vector{String}
|
||||
created::Dates.DateTime
|
||||
end
|
||||
|
||||
function Playlist(owner::String
|
||||
|
@ -37,8 +38,12 @@ function Playlist(owner::String
|
|||
public = false,
|
||||
songs = Song[],
|
||||
cover = "",
|
||||
allowed = String[])
|
||||
Playlist(uuid, name, comment, owner, public, songs, cover, allowed)
|
||||
allowed = String[],
|
||||
creation = Dates.now())
|
||||
Playlist(uuid,
|
||||
name, comment, owner, public,
|
||||
songs, cover,
|
||||
allowed, creation)
|
||||
end
|
||||
|
||||
function User(name::String)
|
||||
|
|
|
@ -15,8 +15,8 @@ GET :url/getLicense
|
|||
# Returns all configured top-level music folders. Takes no extra parameters.
|
||||
GET :url/getMusicFolders:auth
|
||||
|
||||
# getIndexes = Returns an indexed structure of all artists.
|
||||
# getMusicDirectory = Returns a listing of all files in a music directory. Typically used to get list of albums for an artist, or list of songs for an album.
|
||||
#
|
||||
GET :url/getMusicDirectory:auth&id=fab34286-b8e1-4879-bce3-194e1358fbd2
|
||||
|
||||
# Returns all genres.
|
||||
GET :url/getGenres:auth
|
||||
|
@ -24,26 +24,27 @@ GET :url/getGenres:auth
|
|||
# Similar to getIndexes, but organizes music according to ID3 tags.
|
||||
GET :url/getArtists:auth
|
||||
|
||||
# Returns details for an artist, including a list of albums. This method organizes music according to ID3 tags.
|
||||
GET :url/getArtist:auth&id=14d44067-99c2-4f77-b58b-138f0b6911fa
|
||||
# Returns details for an artist, including a list of albums. This method organizes music according to ID3 tags.14d44067-99c2-4f77-b58b-138f0b6911fa
|
||||
GET :url/getArtist:auth&id=ba853904-ae25-4ebb-89d6-c44cfbd71bd2
|
||||
|
||||
|
||||
# Returns details for an album, including a list of songs. This method organizes music according to ID3 tags.
|
||||
GET :url/getAlbum:auth&id=d9522a40-887f-4a15-a59f-0d3bccfa908f
|
||||
GET :url/getAlbum:auth&id=f281e63f-589d-4691-8f13-9906ccc09aa0
|
||||
|
||||
# Returns details for a song.
|
||||
GET :url/getSong:auth&id=df5937fd-d79b-40b5-bf14-8c29c54e1bdb
|
||||
GET :url/getSong:auth&id=e1ebe027-2e21-45c9-bff8-94ba538f895f
|
||||
|
||||
# Returns a cover art image.
|
||||
GET :url/getCoverArt:auth&id=7167f941-efef-49dd-a54f-8e2d41e3f4a7
|
||||
|
||||
# Stream
|
||||
GET :url/stream:auth&id=df5937fd-d79b-40b5-bf14-8c29c54e1bdb
|
||||
GET :url/stream:auth&id=e1ebe027-2e21-45c9-bff8-94ba538f895f
|
||||
|
||||
# Get playlists
|
||||
GET :url/getPlaylists:auth
|
||||
|
||||
# Get single playlist
|
||||
GET :url/getPlaylist:auth&id=a2df9320-4775-40a5-9830-8960f3eb9203
|
||||
GET :url/getPlaylist:auth&id=1455e415-8718-4453-a5f5-490a00b62d34
|
||||
|
||||
# Get not owned playlist
|
||||
GET :url/getPlaylist:auth&id=799f5074-5db2-4daa-b449-9677d0c7744c
|
||||
|
@ -52,7 +53,7 @@ GET :url/getPlaylist:auth&id=799f5074-5db2-4daa-b449-9677d0c7744c
|
|||
GET :url/deletePlaylist:auth&id=799f5074-5db2-4daa-b449-9677d0c7744c
|
||||
|
||||
# Update not owned playlist
|
||||
GET :url/updatePlaylist:auth&playlistId=799f5074-5db2-4daa-b449-9677d0c7744c
|
||||
GET :url/updatePlaylist:auth&playlistId=e39d8798-473e-45a9-8a1f-d5d0485ed274
|
||||
|
||||
# Update owned playlist
|
||||
GET :url/updatePlaylist:auth&playlistId=a2df9320-4775-40a5-9830-8960f3eb9203&name=nuovo
|
||||
|
@ -60,5 +61,8 @@ GET :url/updatePlaylist:auth&playlistId=a2df9320-4775-40a5-9830-8960f3eb9203&nam
|
|||
# Delete owned playlist
|
||||
GET :url/deletePlaylist:auth&id=a2df9320-4775-40a5-9830-8960f3eb9203
|
||||
|
||||
# Get random songs
|
||||
GET :url/getRandomSongs:auth
|
||||
|
||||
|
||||
|
||||
|
|
57
login.jl
57
login.jl
|
@ -2,59 +2,28 @@ using MD5
|
|||
using HTTP
|
||||
using JSON2
|
||||
|
||||
const users = JlSonic.User[]
|
||||
|
||||
function getlogin(app, req)
|
||||
query = HTTP.URIs.queryparams(req[:query])
|
||||
username = string(get(query, "u", ""))
|
||||
token = get(query, "t", "")
|
||||
salt = get(query, "s", "")
|
||||
password = get(query, "p", "")
|
||||
req[:login] = Dict(:name => username,
|
||||
:token => token,
|
||||
:salt => salt,
|
||||
:password => password,
|
||||
req[:login] = Dict(:name => string(get(query, "u", "")),
|
||||
:token => get(query, "t", ""),
|
||||
:salt => get(query, "s", ""),
|
||||
:password => get(query, "p", ""),
|
||||
:login => false)
|
||||
return app(req)
|
||||
end
|
||||
|
||||
function checkpassword(app, req)
|
||||
global users
|
||||
usern = findfirst(u -> u.name == req[:login][:name], users)
|
||||
usern === nothing && return app(req)
|
||||
user = users[usern]
|
||||
req[:login][:user] = user
|
||||
if !isempty(req[:login][:salt])
|
||||
if bytes2hex(MD5.md5(string(user.password, req[:login][:salt]))) ==
|
||||
req[:login][:token]
|
||||
req[:login][:login] = true
|
||||
end
|
||||
elseif !isempty(req[:login][:password])
|
||||
if startswith(req[:login][:password], "enc:")
|
||||
req[:login][:login] =
|
||||
String(hex2bytes(split(req[:login][:password], ":")[2])) ==
|
||||
user.password
|
||||
else
|
||||
req[:login][:login] = user.password == req[:login][:password]
|
||||
end
|
||||
user = JlSonic.checkpass(req[:login][:name],
|
||||
req[:login][:salt],
|
||||
req[:login][:token],
|
||||
req[:login][:password])
|
||||
if user == nothing
|
||||
req[:login][:login] = false
|
||||
else
|
||||
req[:login][:login] = true
|
||||
req[:login][:user] = user
|
||||
end
|
||||
return app(req)
|
||||
end
|
||||
|
||||
function saveusers(file = expanduser("~/.config/beets/users.jsonl"))
|
||||
global users
|
||||
open(file, "w") do f
|
||||
write(f, join(JSON2.write.(users), "\n"))
|
||||
end
|
||||
end
|
||||
|
||||
function loadusers(; file = expanduser("~/.config/beets/users.jsonl"))
|
||||
global users
|
||||
isfile(file) || touch(file)
|
||||
ps = JSON2.readlines(file)
|
||||
p = JSON2.read.(ps, JlSonic.User)
|
||||
empty!(users)
|
||||
append!(users, p)
|
||||
end
|
||||
|
||||
sonic_login = stack(getlogin, checkpassword)
|
||||
|
|
|
@ -9,7 +9,7 @@ restp(p, app...) = branch(req -> restpath!(p, req), app...)
|
|||
dispatch = stack(
|
||||
# Browsing
|
||||
restp("getMusicFolders", _ -> getMusicFolders()),
|
||||
restp("getMusicDirectory", req -> getmusicdirectory(req)),
|
||||
restp("getMusicDirectory", req -> getMusicDirectory(req)),
|
||||
restp("getAlbumList", req -> getAlbumList(req)),
|
||||
restp("getGenres", _ -> getGenres()),
|
||||
restp("getArtists", _ -> getArtists()),
|
||||
|
|
Loading…
Reference in New Issue