Skip to content

Commit

Permalink
Add spiniton API functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
toots committed Oct 4, 2024
1 parent 1dc6fac commit a6cc682
Show file tree
Hide file tree
Showing 2 changed files with 256 additions and 0 deletions.
255 changes: 255 additions & 0 deletions src/libs/extra/spinitron.liq
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
let spinitron = {submit=()}

# Submit a track to the spinitron track system
# and return the raw response.
# @category Interaction
# @param ~api_key API key
def spinitron.submit.raw(
~host="https://spinitron.com/api",
~api_key,
~live=true,
~start=null(),
~duration=null(),
~artist,
~release=null(),
~label=null(),
~genre=null(),
~song,
~composer=null(),
~isrc=null()
) =
params = [("song", song), ("artist", artist)]

def fold_optional_string_params(params, param) =
let (label, param) = param
if
null.defined(param)
then
[(label, null.get(param)), ...params]
else
params
end
end

params =
list.fold(
fold_optional_string_params,
params,
[
("live", null.map(string, (live : bool?))),
("start", start),
("duration", null.map(string, (duration : int?))),
("release", release),
("label", label),
("genre", genre),
("composer", composer),
("isrc", isrc)
]
)

def encode_param(param) =
let (label, param) = param
"#{label}=#{url.encode(param)}"
end

params = string.concat(separator="&", list.map(encode_param, params))

http.post(
data=params,
headers=
[
("Accept", "application/json"),
("Content-Type", "application/x-www-form-urlencoded"),
(
"Authorization",
"Bearer #{(api_key : string)}"
)
],
"#{host}/spins"
)
end

# Submit a track to the spinitron track system
# and return the parsed response
# @category Interaction
# @param ~api_key API key
def replaces spinitron.submit(%argsof(spinitron.submit.raw)) =
resp = spinitron.submit.raw(%argsof(spinitron.submit.raw))

if
resp.status_code == 201
then
let json.parse (resp :
{
id: int,
playlist_id: int,
"start" as spin_start: string,
"end" as spin_end: string?,
duration: int?,
timezone: string?,
image: string?,
classical: bool,
artist: string,
"artist-custom" as artist_custom: string?,
composer: string?,
release: string?,
"release-custom" as release_custom: string?,
va: bool,
label: string?,
"label-custom" as label_custom: string?,
released: int?,
medium: string?,
genre: string?,
song: string,
note: string?,
request: bool,
local: bool,
new: bool,
work: string?,
conductor: string?,
performers: string?,
ensemble: string?,
"catalog-number" as catalog_number: string?,
isrc: string?,
upc: string?,
iswc: string?,
"_links" as links: {self: {href: string}?, playlist: {href: string}?}?
}
) = resp

resp
elsif
resp.status_code == 422
then
let json.parse (errors : [{field: string, message: string}]) = resp

errors =
list.map(
fun (p) ->
begin
let {field, message} = p
"#{field}: #{message}"
end,
errors
)

errors =
string.concat(
separator=
", ",
errors
)

error.raise(
error.raise(
error.http,
"Invalid fields: #{errors}"
)
)
else
let json.parse ({name, message, code, status, type} :
{name: string, message: string, code: int, status: int, type: string?}
) = resp

type = type ?? "undefined"

error.raise(
error.raise(
error.http,
"#{name}: #{message} (code: #{code}, status: #{status}, type: #{type})"
)
)
end
end

# Submit a spin using the given metadata to the spinitron track system
# and return the parsed response. `artist` and `song` (or `title`) must
# be present either as metadata or as optional argument.
# @category Interaction
# @param m Metadata to submit. Overrides optional arguments when present.
# @param ~api_key API key
def spinitron.submit.metadata(
%argsof(spinitron.submit[!artist,!song]),
~artist=null(),
~song=null(),
m
) =
def conv_opt_arg(convert, label, default) =
list.assoc.mem(label, m) ? convert(m[label]) : default
end

opt_arg =
fun (label, default) -> conv_opt_arg(fun (x) -> null(x), label, default)

live = conv_opt_arg(bool_of_string, "live", live)
start = opt_arg("start", start)
duration = conv_opt_arg(int_of_string, "duration", duration)
artist = opt_arg("artist", artist)
release = opt_arg("release", release)
label = opt_arg("label", label)
genre = opt_arg("genre", genre)
song = opt_arg("title", song)
song = opt_arg("song", song)
composer = opt_arg("composer", composer)
isrc = opt_arg("isrc", isrc)

if
artist == null() or song == null()
then
error.raise(
error.invalid,
"Both \"artist\" and \"song\" (or \"title\" metadata) must be provided!"
)
end

artist = null.get(artist)
song = null.get(song)

spinitron.submit(%argsof(spinitron.submit))
end

# Specialized version of `source.on_metadata` that submits spins using
# the source's metadata to the spinitron track system. `artist` and `song`
# (or `title`) must be present either as metadata or as optional argument.
# @category Interaction
# @param m Metadata to submit. Overrides optional arguments when present.
# @param ~api_key API key
def spinitron.submit.on_metadata(
~id=null(),
%argsof(spinitron.submit.metadata),
s
) =
def on_metadata(m) =
if
m["title"] == "" and m["song"] == ""
then
log.important(
label=source.id(s),
"Field \"song\" or \"title\" missing, skipping metadata spinitron \
submission."
)
elsif
m["artist"] == ""
then
log.important(
label=source.id(s),
"Field \"artist\" missing, skipping metadata spinitron submission."
)
else
try
ignore(spinitron.submit.metadata(%argsof(spinitron.submit.metadata), m))
log.info(
label=source.id(s),
"Successfully submitted spin from metadata"
)
catch err do
log.important(
label=source.id(s),
"Error while submitting spin from metadata: #{err}"
)
end
end
end

source.on_metadata(id=id, s, on_metadata)
end
1 change: 1 addition & 0 deletions src/libs/stdlib.liq
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
%include_extra "extra/interactive.liq"
%include_extra "extra/visualization.liq"
%include_extra "extra/openai.liq"
%include_extra "extra/spinitron.liq"
%include_extra "extra/metadata.liq"
%include_extra "extra/fades.liq"
%include_extra "extra/video.liq"
Expand Down

0 comments on commit a6cc682

Please sign in to comment.