Models for marshalling JSPF playlists

This commit is contained in:
Philipp Wolfer 2023-11-13 23:47:52 +01:00
parent 27685d617a
commit dcb5e9c2c6
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
7 changed files with 397 additions and 0 deletions

23
backends/jspf/jspf.go Normal file
View file

@ -0,0 +1,23 @@
/*
Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package jspf

View file

@ -0,0 +1,23 @@
/*
Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package jspf_test

67
backends/jspf/models.go Normal file
View file

@ -0,0 +1,67 @@
/*
Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package jspf
import "time"
type Jspf struct {
Playlist Playlist `json:"playlist"`
}
type Playlist struct {
Title string `json:"title,omitempty"`
Creator string `json:"creator,omitempty"`
Annotation string `json:"annotation,omitempty"`
Info string `json:"info,omitempty"`
Location string `json:"location,omitempty"`
Identifier string `json:"identifier,omitempty"`
Image string `json:"image,omitempty"`
Date time.Time `json:"date,omitempty"`
License string `json:"license,omitempty"`
Attribution []Attribution `json:"attribution,omitempty"`
Links []Link `json:"link,omitempty"`
Meta []Meta `json:"meta,omitempty"`
Extension map[string]any `json:"extension,omitempty"`
Tracks []Track `json:"track"`
}
type Track struct {
Location []string `json:"location,omitempty"`
Identifier []string `json:"identifier,omitempty"`
Title string `json:"title,omitempty"`
Creator string `json:"creator,omitempty"`
Annotation string `json:"annotation,omitempty"`
Info string `json:"info,omitempty"`
Album string `json:"album,omitempty"`
TrackNum int `json:"trackNum,omitempty"`
Duration int `json:"duration,omitempty"`
Links []Link `json:"link,omitempty"`
Meta []Meta `json:"meta,omitempty"`
Extension map[string]any `json:"extension,omitempty"`
}
type Attribution map[string]string
type Link map[string]string
type Meta map[string]string

View file

@ -0,0 +1,111 @@
/*
Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package jspf_test
import (
"encoding/json"
"fmt"
"io"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uploadedlobster.com/scotty/backends/jspf"
)
func TestUnmarshalSimple(t *testing.T) {
data, err := readSampleJson("testdata/simple.jspf")
require.NoError(t, err)
assert := assert.New(t)
playlist := data.Playlist
assert.Equal("Two Songs From Thriller", playlist.Title)
assert.Equal("MJ Fan", playlist.Creator)
require.Len(t, playlist.Tracks, 2)
track1 := playlist.Tracks[0]
require.Len(t, track1.Location, 1)
assert.Equal("http://example.com/billiejean.mp3", track1.Location[0])
assert.Equal("Billie Jean", track1.Title)
assert.Equal("Michael Jackson", track1.Creator)
assert.Equal("Thriller", track1.Album)
}
func TestUnmarshalComprehensive(t *testing.T) {
data, err := readSampleJson("testdata/comprehensive.jspf")
require.NoError(t, err)
assert := assert.New(t)
playlist := data.Playlist
assert.Equal("http://example.com/", playlist.License)
require.Len(t, playlist.Attribution, 2)
assert.Equal("http://example.com/", playlist.Attribution[0]["identifier"])
assert.Equal("http://example.com/", playlist.Attribution[1]["location"])
require.Len(t, playlist.Meta, 2)
assert.Equal("345", playlist.Meta[1]["http://example.com/rel/2/"])
require.Len(t, playlist.Links, 2)
assert.Equal("http://example.com/body/1/", playlist.Links[0]["http://example.com/rel/1/"])
}
func TestUnmarshalListenBrainzPlaylist(t *testing.T) {
data, err := readSampleJson("testdata/lb-playlist.jspf")
require.NoError(t, err)
assert := assert.New(t)
playlist := data.Playlist
assert.Equal(
"https://listenbrainz.org/playlist/96485e27-967a-492a-9d04-c5a819baa2f3",
playlist.Identifier)
expectedPlaylistDate, err := time.Parse(time.RFC3339, "2023-07-04T21:03:52.317148+00:00")
require.NoError(t, err)
assert.Equal(expectedPlaylistDate, playlist.Date)
assert.NotNil(playlist.Extension["https://musicbrainz.org/doc/jspf#playlist"])
require.Len(t, playlist.Tracks, 2)
track1 := playlist.Tracks[0]
assert.Equal(
"https://musicbrainz.org/recording/3f2bdbbd-063e-478c-a394-6da0cb303302",
track1.Identifier[0])
fmt.Printf("Ext: %v\n", track1.Extension["https://musicbrainz.org/doc/jspf#track"])
extension := track1.Extension["https://musicbrainz.org/doc/jspf#track"].(map[string]any)
assert.NotNil(extension)
assert.Equal("outsidecontext", extension["added_by"])
}
func readSampleJson(path string) (jspf.Jspf, error) {
var result jspf.Jspf
jsonFile, err := os.Open(path)
if err != nil {
return result, err
}
defer jsonFile.Close()
byteValue, err := io.ReadAll(jsonFile)
if err != nil {
return result, err
}
err = json.Unmarshal(byteValue, &result)
return result, err
}

View file

@ -0,0 +1,91 @@
{
"playlist": {
"title": "JSPF example",
"creator": "Name of playlist author",
"annotation": "Super playlist",
"info": "http://example.com/",
"location": "http://example.com/",
"identifier": "http://example.com/",
"image": "http://example.com/",
"date": "2005-01-08T17:10:47-05:00",
"license": "http://example.com/",
"attribution": [
{
"identifier": "http://example.com/"
},
{
"location": "http://example.com/"
}
],
"link": [
{
"http://example.com/rel/1/": "http://example.com/body/1/"
},
{
"http://example.com/rel/2/": "http://example.com/body/2/"
}
],
"meta": [
{
"http://example.com/rel/1/": "my meta 14"
},
{
"http://example.com/rel/2/": "345"
}
],
"extension": {
"http://example.com/app/1/": [
"ARBITRARY_EXTENSION_BODY",
{}
],
"http://example.com/app/2/": [
"ARBITRARY_EXTENSION_BODY"
]
},
"track": [
{
"location": [
"http://example.com/1.ogg",
"http://example.com/2.mp3"
],
"identifier": [
"http://example.com/1/",
"http://example.com/2/"
],
"title": "Track title",
"creator": "Artist name",
"annotation": "Some text",
"info": "http://example.com/",
"image": "http://example.com/",
"album": "Album name",
"trackNum": 1,
"duration": 0,
"link": [
{
"http://example.com/rel/1/": "http://example.com/body/1/"
},
{
"http://example.com/rel/2/": "http://example.com/body/2/"
}
],
"meta": [
{
"http://example.com/rel/1/": "my meta 14"
},
{
"http://example.com/rel/2/": "345"
}
],
"extension": {
"http://example.com/app/1/": [
"ARBITRARY_EXTENSION_BODY",
{}
],
"http://example.com/app/2/": [
"ARBITRARY_EXTENSION_BODY"
]
}
}
]
}
}

58
backends/jspf/testdata/lb-playlist.jspf vendored Normal file
View file

@ -0,0 +1,58 @@
{
"playlist": {
"creator": "outsidecontext",
"date": "2023-07-04T21:03:52.317148+00:00",
"extension": {
"https://musicbrainz.org/doc/jspf#playlist": {
"creator": "outsidecontext",
"last_modified_at": "2023-07-10T10:03:48.833282+00:00",
"public": false
}
},
"identifier": "https://listenbrainz.org/playlist/96485e27-967a-492a-9d04-c5a819baa2f3",
"title": "Fundst\u00fccke",
"track": [
{
"creator": "Airghoul feat. Priest",
"extension": {
"https://musicbrainz.org/doc/jspf#track": {
"added_at": "2023-07-04T21:05:59.492439+00:00",
"added_by": "outsidecontext",
"additional_metadata": {
"caa_id": 32981136309,
"caa_release_mbid": "fb7d69d6-0b4b-4f99-a77a-c3a0d786b52c"
},
"artist_identifiers": [
"https://musicbrainz.org/artist/554a5819-6c3f-4734-ae4c-11eabb7ca2e0",
"https://musicbrainz.org/artist/56ff293f-ec9a-4741-9d14-0537c4fb8f97"
]
}
},
"identifier": [
"https://musicbrainz.org/recording/3f2bdbbd-063e-478c-a394-6da0cb303302"
],
"title": "Orange Forest"
},
{
"creator": "Crippled Black Phoenix",
"extension": {
"https://musicbrainz.org/doc/jspf#track": {
"added_at": "2023-07-10T10:03:48.833330+00:00",
"added_by": "outsidecontext",
"additional_metadata": {
"caa_id": 28699084533,
"caa_release_mbid": "92e7cf6c-e626-4409-81b0-0fcb8a0c3699"
},
"artist_identifiers": [
"https://musicbrainz.org/artist/055b6082-b9cc-4688-85c4-8153c0ef2d70"
]
}
},
"identifier": [
"https://musicbrainz.org/recording/14d612f0-4022-4adc-8cef-87a569e2d65c"
],
"title": "Lost"
}
]
}
}

24
backends/jspf/testdata/simple.jspf vendored Normal file
View file

@ -0,0 +1,24 @@
{
"playlist": {
"title": "Two Songs From Thriller",
"creator": "MJ Fan",
"track": [
{
"location": [
"http://example.com/billiejean.mp3"
],
"title": "Billie Jean",
"creator": "Michael Jackson",
"album": "Thriller"
},
{
"location": [
"http://example.com/thegirlismine.mp3"
],
"title": "The Girl Is Mine",
"creator": "Michael Jackson",
"album": "Thriller"
}
]
}
}