diff --git a/backends/funkwhale.go b/backends/funkwhale.go new file mode 100644 index 0000000..f074858 --- /dev/null +++ b/backends/funkwhale.go @@ -0,0 +1,117 @@ +/* +Copyright © 2023 Philipp Wolfer + +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 backends + +import ( + "slices" + "time" + + "github.com/spf13/viper" + "go.uploadedlobster.com/scotty/backends/funkwhale" + "go.uploadedlobster.com/scotty/models" +) + +const FunkwhaleClientName = "Funkwhale" + +type FunkwhaleApiBackend struct { + client funkwhale.Client + username string +} + +func (b FunkwhaleApiBackend) FromConfig(config *viper.Viper) Backend { + b.client = funkwhale.New( + config.GetString("server-url"), + config.GetString("token"), + ) + b.username = config.GetString("username") + return b +} + +func (b FunkwhaleApiBackend) ExportListens(oldestTimestamp time.Time) ([]models.Listen, error) { + page := 1 + perPage := funkwhale.MaxItemsPerGet + + listens := make([]models.Listen, 0) + +out: + for { + result, err := b.client.GetHistoryListenings(b.username, page, perPage) + if err != nil { + return nil, err + } + + count := len(result.Results) + if count == 0 { + break out + } + + for _, fwListen := range result.Results { + listen := ListenFromFunkwhale(fwListen) + if listen.ListenedAt.Unix() > oldestTimestamp.Unix() { + listens = append(listens, listen) + } else { + break out + } + } + + if result.Next == "" { + // No further results + break out + } + + page += 1 + } + + slices.Reverse(listens) + return listens, nil +} + +func ListenFromFunkwhale(fwListen funkwhale.Listening) models.Listen { + track := fwListen.Track + listen := models.Listen{ + UserName: fwListen.User.UserName, + Track: models.Track{ + TrackName: track.Title, + ReleaseName: track.Album.Title, + ArtistNames: []string{track.Artist.Name}, + TrackNumber: track.Position, + RecordingMbid: models.MBID(track.RecordingMbid), + ReleaseMbid: models.MBID(track.Album.ReleaseMbid), + ArtistMbids: []models.MBID{models.MBID(track.Artist.ArtistMbid)}, + Tags: track.Tags, + AdditionalInfo: map[string]any{ + "media_player": FunkwhaleClientName, + }, + }, + } + + listenedAt, err := time.Parse(time.RFC3339, fwListen.CreationDate) + if err == nil { + listen.ListenedAt = listenedAt + } + + if len(track.Uploads) > 0 { + listen.Track.Duration = time.Duration(track.Uploads[0].Duration * int(time.Second)) + } + + return listen +} diff --git a/backends/funkwhale/client.go b/backends/funkwhale/client.go new file mode 100644 index 0000000..0aa2d46 --- /dev/null +++ b/backends/funkwhale/client.go @@ -0,0 +1,68 @@ +/* +Copyright © 2023 Philipp Wolfer + +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 funkwhale + +import ( + "errors" + "strconv" + + "github.com/go-resty/resty/v2" +) + +const MaxItemsPerGet = 50 + +type Client struct { + HttpClient *resty.Client + token string +} + +func New(serverUrl string, token string) Client { + resty := resty.New() + resty.SetBaseURL(serverUrl) + resty.SetAuthScheme("Bearer") + resty.SetAuthToken(token) + resty.SetHeader("Accept", "application/json") + client := Client{ + HttpClient: resty, + token: token, + } + + return client +} + +func (c Client) GetHistoryListenings(user string, page int, perPage int) (ListeningsResult, error) { + const path = "/api/v1/history/listenings" + result := &ListeningsResult{} + response, err := c.HttpClient.R(). + SetQueryParams(map[string]string{ + "username": user, + "page": strconv.Itoa(page), + "page_size": strconv.Itoa(perPage), + }). + SetResult(result). + Get(path) + + if response.StatusCode() != 200 { + return *result, errors.New(response.String()) + } + return *result, err +} diff --git a/backends/funkwhale/client_test.go b/backends/funkwhale/client_test.go new file mode 100644 index 0000000..d2bcca1 --- /dev/null +++ b/backends/funkwhale/client_test.go @@ -0,0 +1,73 @@ +/* +Copyright © 2023 Philipp Wolfer + +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 funkwhale_test + +import ( + "net/http" + "testing" + + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uploadedlobster.com/scotty/backends/funkwhale" +) + +func TestNewClient(t *testing.T) { + serverUrl := "https://funkwhale.example.com" + token := "foobar123" + client := funkwhale.New(serverUrl, token) + assert.Equal(t, serverUrl, client.HttpClient.BaseURL) + assert.Equal(t, token, client.HttpClient.Token) +} + +func TestGetHistoryListenings(t *testing.T) { + defer httpmock.DeactivateAndReset() + + token := "thetoken" + serverUrl := "https://funkwhale.example.com" + client := funkwhale.New(serverUrl, token) + setupHttpMock(t, client.HttpClient.GetClient(), + "https://funkwhale.example.com/api/v1/history/listenings", + "testdata/listenings.json") + + result, err := client.GetHistoryListenings("outsidecontext", 0, 2) + require.NoError(t, err) + + assert := assert.New(t) + assert.Equal(2204, result.Count) + listen1 := result.Results[0] + assert.Equal("2023-11-09T23:59:29.022005Z", listen1.CreationDate) + assert.Equal("Way to Eden", listen1.Track.Title) + assert.Equal("Hazeshuttle", listen1.Track.Album.Title) + assert.Equal("Hazeshuttle", listen1.Track.Artist.Name) + assert.Equal("phw", listen1.User.UserName) +} + +func setupHttpMock(t *testing.T, client *http.Client, url string, testDataPath string) { + httpmock.ActivateNonDefault(client) + + responder, err := httpmock.NewJsonResponder(200, httpmock.File(testDataPath)) + if err != nil { + t.Fatal(err) + } + httpmock.RegisterResponder("GET", url, responder) +} diff --git a/backends/funkwhale/models.go b/backends/funkwhale/models.go new file mode 100644 index 0000000..aa0b071 --- /dev/null +++ b/backends/funkwhale/models.go @@ -0,0 +1,73 @@ +/* +Copyright © 2023 Philipp Wolfer + +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 funkwhale + +type ListeningsResult struct { + Count int `json:"count"` + Previous string `json:"previous"` + Next string `json:"next"` + Results []Listening `json:"results"` +} + +type Listening struct { + Id int `json:"int"` + User User `json:"user"` + Track Track `json:"track"` + CreationDate string `json:"creation_date"` +} + +type Track struct { + Id int `json:"int"` + Artist Artist `json:"artist"` + Album Album `json:"album"` + Title string `json:"title"` + Position int `json:"position"` + DiscNumber int `json:"disc_number"` + RecordingMbid string `json:"mbid"` + Tags []string `json:"tags"` + Uploads []Upload `json:"uploads"` +} + +type Artist struct { + Id int `json:"int"` + Name string `json:"name"` + ArtistMbid string `json:"mbid"` +} + +type Album struct { + Id int `json:"int"` + Title string `json:"title"` + AlbumArtist Artist `json:"artist"` + ReleaseDate string `json:"release_date"` + TrackCount int `json:"track_count"` + ReleaseMbid string `json:"mbid"` +} + +type User struct { + Id int `json:"int"` + UserName string `json:"username"` +} + +type Upload struct { + UUID string `json:"uuid"` + Duration int `json:"duration"` +} diff --git a/backends/funkwhale/testdata/listenings.json b/backends/funkwhale/testdata/listenings.json new file mode 100644 index 0000000..0373b8c --- /dev/null +++ b/backends/funkwhale/testdata/listenings.json @@ -0,0 +1,261 @@ +{ + "count": 2204, + "next": "https://music.uploadedlobster.com/api/v1/history/listenings/?page=2&page_size=2&username=phw", + "previous": null, + "results": [ + { + "id": 2204, + "user": { + "id": 1, + "username": "phw", + "name": "", + "date_joined": "2020-11-13T16:22:52.464109Z", + "avatar": { + "uuid": "8f87ca98-fe9e-4f7e-aa54-aa8276c25fd4", + "size": 262868, + "mimetype": "image/png", + "creation_date": "2021-08-30T12:02:20.962405Z", + "urls": { + "source": null, + "original": "https://music.uploadedlobster.com/media/attachments/4b/e2/c3/canned-ape.png", + "medium_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/4b/e2/c3/canned-ape-crop-c0-5__0-5-200x200.png", + "large_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/4b/e2/c3/canned-ape-crop-c0-5__0-5-600x600.png" + } + } + }, + "track": { + "artist": { + "id": 3824, + "fid": "https://music.uploadedlobster.com/federation/music/artists/0c0fc2f3-becd-4b24-a2c4-04523cbd5934", + "mbid": "54292079-790c-4e99-bf8d-12efa29fa3e9", + "name": "Hazeshuttle", + "creation_date": "2023-10-27T16:35:36.458347Z", + "modification_date": "2023-10-27T16:35:36.458426Z", + "is_local": true, + "content_category": "music", + "description": null, + "attachment_cover": null, + "channel": null + }, + "album": { + "id": 2700, + "fid": "https://music.uploadedlobster.com/federation/music/albums/ff6bb1d6-4334-4280-95a1-0d49c64c8c7d", + "mbid": "6d0ee27f-dc9f-4dab-8d7d-f4dcd14dc54a", + "title": "Hazeshuttle", + "artist": { + "id": 3824, + "fid": "https://music.uploadedlobster.com/federation/music/artists/0c0fc2f3-becd-4b24-a2c4-04523cbd5934", + "mbid": "54292079-790c-4e99-bf8d-12efa29fa3e9", + "name": "Hazeshuttle", + "creation_date": "2023-10-27T16:35:36.458347Z", + "modification_date": "2023-10-27T16:35:36.458426Z", + "is_local": true, + "content_category": "music", + "description": null, + "attachment_cover": null, + "channel": null + }, + "release_date": "2023-04-01", + "cover": { + "uuid": "d91a26cd-6132-4762-9f33-dba9b78ece69", + "size": 143159, + "mimetype": "image/jpeg", + "creation_date": "2023-10-27T16:35:36.473913Z", + "urls": { + "source": null, + "original": "https://music.uploadedlobster.com/media/attachments/5e/4d/7f/attachment_cover-ff6bb1d6-4334-4280-95a1-0d49c64c8c7d.jpg", + "medium_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/5e/4d/7f/attachment_cover-ff6bb1d6-4334-4280-95a1-0d49c64c8c7d-crop-c0-5__0-5-200x200-95.jpg", + "large_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/5e/4d/7f/attachment_cover-ff6bb1d6-4334-4280-95a1-0d49c64c8c7d-crop-c0-5__0-5-600x600-95.jpg" + } + }, + "creation_date": "2023-10-27T16:35:36.468407Z", + "is_local": true, + "tracks_count": 5 + }, + "uploads": [ + { + "uuid": "4d3ef919-8683-42f0-bb1a-edbec258006a", + "listen_url": "/api/v1/listen/a7fc119f-67c2-4a64-9609-d5dc3d42e3ee/?upload=4d3ef919-8683-42f0-bb1a-edbec258006a", + "size": 13837685, + "duration": 567, + "bitrate": 0, + "mimetype": "audio/opus", + "extension": "opus", + "is_local": true + } + ], + "listen_url": "/api/v1/listen/a7fc119f-67c2-4a64-9609-d5dc3d42e3ee/", + "tags": [], + "attributed_to": { + "fid": "https://music.uploadedlobster.com/federation/actors/phw", + "url": null, + "creation_date": "2020-11-13T16:54:45.182645Z", + "summary": null, + "preferred_username": "phw", + "name": "phw", + "last_fetch_date": "2020-11-13T16:54:45.182661Z", + "domain": "music.uploadedlobster.com", + "type": "Person", + "manually_approves_followers": false, + "full_username": "phw@music.uploadedlobster.com", + "is_local": true + }, + "id": 28224, + "fid": "https://music.uploadedlobster.com/federation/music/tracks/a7fc119f-67c2-4a64-9609-d5dc3d42e3ee", + "mbid": "db8488cb-d665-4853-8e7e-970e7c2d9225", + "title": "Way to Eden", + "creation_date": "2023-10-27T16:35:36.661199Z", + "is_local": true, + "position": 2, + "disc_number": 1, + "downloads_count": 2, + "copyright": null, + "license": null, + "cover": null, + "is_playable": true + }, + "creation_date": "2023-11-09T23:59:29.022005Z", + "actor": { + "fid": "https://music.uploadedlobster.com/federation/actors/phw", + "url": null, + "creation_date": "2020-11-13T16:54:45.182645Z", + "summary": null, + "preferred_username": "phw", + "name": "phw", + "last_fetch_date": "2020-11-13T16:54:45.182661Z", + "domain": "music.uploadedlobster.com", + "type": "Person", + "manually_approves_followers": false, + "full_username": "phw@music.uploadedlobster.com", + "is_local": true + } + }, + { + "id": 2203, + "user": { + "id": 1, + "username": "phw", + "name": "", + "date_joined": "2020-11-13T16:22:52.464109Z", + "avatar": { + "uuid": "8f87ca98-fe9e-4f7e-aa54-aa8276c25fd4", + "size": 262868, + "mimetype": "image/png", + "creation_date": "2021-08-30T12:02:20.962405Z", + "urls": { + "source": null, + "original": "https://music.uploadedlobster.com/media/attachments/4b/e2/c3/canned-ape.png", + "medium_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/4b/e2/c3/canned-ape-crop-c0-5__0-5-200x200.png", + "large_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/4b/e2/c3/canned-ape-crop-c0-5__0-5-600x600.png" + } + } + }, + "track": { + "artist": { + "id": 3824, + "fid": "https://music.uploadedlobster.com/federation/music/artists/0c0fc2f3-becd-4b24-a2c4-04523cbd5934", + "mbid": "54292079-790c-4e99-bf8d-12efa29fa3e9", + "name": "Hazeshuttle", + "creation_date": "2023-10-27T16:35:36.458347Z", + "modification_date": "2023-10-27T16:35:36.458426Z", + "is_local": true, + "content_category": "music", + "description": null, + "attachment_cover": null, + "channel": null + }, + "album": { + "id": 2700, + "fid": "https://music.uploadedlobster.com/federation/music/albums/ff6bb1d6-4334-4280-95a1-0d49c64c8c7d", + "mbid": "6d0ee27f-dc9f-4dab-8d7d-f4dcd14dc54a", + "title": "Hazeshuttle", + "artist": { + "id": 3824, + "fid": "https://music.uploadedlobster.com/federation/music/artists/0c0fc2f3-becd-4b24-a2c4-04523cbd5934", + "mbid": "54292079-790c-4e99-bf8d-12efa29fa3e9", + "name": "Hazeshuttle", + "creation_date": "2023-10-27T16:35:36.458347Z", + "modification_date": "2023-10-27T16:35:36.458426Z", + "is_local": true, + "content_category": "music", + "description": null, + "attachment_cover": null, + "channel": null + }, + "release_date": "2023-04-01", + "cover": { + "uuid": "d91a26cd-6132-4762-9f33-dba9b78ece69", + "size": 143159, + "mimetype": "image/jpeg", + "creation_date": "2023-10-27T16:35:36.473913Z", + "urls": { + "source": null, + "original": "https://music.uploadedlobster.com/media/attachments/5e/4d/7f/attachment_cover-ff6bb1d6-4334-4280-95a1-0d49c64c8c7d.jpg", + "medium_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/5e/4d/7f/attachment_cover-ff6bb1d6-4334-4280-95a1-0d49c64c8c7d-crop-c0-5__0-5-200x200-95.jpg", + "large_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/5e/4d/7f/attachment_cover-ff6bb1d6-4334-4280-95a1-0d49c64c8c7d-crop-c0-5__0-5-600x600-95.jpg" + } + }, + "creation_date": "2023-10-27T16:35:36.468407Z", + "is_local": true, + "tracks_count": 5 + }, + "uploads": [ + { + "uuid": "32a6e065-cfb3-4bd5-a1a8-08416e2211f2", + "listen_url": "/api/v1/listen/24c207d8-abeb-4541-829f-cf6c6a04a44a/?upload=32a6e065-cfb3-4bd5-a1a8-08416e2211f2", + "size": 24121224, + "duration": 1007, + "bitrate": 0, + "mimetype": "audio/opus", + "extension": "opus", + "is_local": true + } + ], + "listen_url": "/api/v1/listen/24c207d8-abeb-4541-829f-cf6c6a04a44a/", + "tags": [], + "attributed_to": { + "fid": "https://music.uploadedlobster.com/federation/actors/phw", + "url": null, + "creation_date": "2020-11-13T16:54:45.182645Z", + "summary": null, + "preferred_username": "phw", + "name": "phw", + "last_fetch_date": "2020-11-13T16:54:45.182661Z", + "domain": "music.uploadedlobster.com", + "type": "Person", + "manually_approves_followers": false, + "full_username": "phw@music.uploadedlobster.com", + "is_local": true + }, + "id": 28223, + "fid": "https://music.uploadedlobster.com/federation/music/tracks/24c207d8-abeb-4541-829f-cf6c6a04a44a", + "mbid": "ba49cada-9873-4bdb-9506-533cb63372c8", + "title": "Homosativa", + "creation_date": "2023-10-27T16:35:36.559260Z", + "is_local": true, + "position": 1, + "disc_number": 1, + "downloads_count": 1, + "copyright": null, + "license": null, + "cover": null, + "is_playable": true + }, + "creation_date": "2023-11-09T23:42:42.345349Z", + "actor": { + "fid": "https://music.uploadedlobster.com/federation/actors/phw", + "url": null, + "creation_date": "2020-11-13T16:54:45.182645Z", + "summary": null, + "preferred_username": "phw", + "name": "phw", + "last_fetch_date": "2020-11-13T16:54:45.182661Z", + "domain": "music.uploadedlobster.com", + "type": "Person", + "manually_approves_followers": false, + "full_username": "phw@music.uploadedlobster.com", + "is_local": true + } + } + ] +} \ No newline at end of file diff --git a/backends/funkwhale_test.go b/backends/funkwhale_test.go new file mode 100644 index 0000000..541a104 --- /dev/null +++ b/backends/funkwhale_test.go @@ -0,0 +1,75 @@ +/* +Copyright © 2023 Philipp Wolfer + +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 backends_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.uploadedlobster.com/scotty/backends" + "go.uploadedlobster.com/scotty/backends/funkwhale" + "go.uploadedlobster.com/scotty/models" +) + +func TestListenFromFunkwhale(t *testing.T) { + fwListen := funkwhale.Listening{ + CreationDate: "2023-11-09T23:59:29.022005Z", + User: funkwhale.User{ + UserName: "outsidecontext", + }, + Track: funkwhale.Track{ + Title: "Oweynagat", + RecordingMbid: "c0a1fc94-5f04-4a5f-bc09-e5de0c49cd12", + Position: 5, + DiscNumber: 1, + Tags: []string{"foo", "bar"}, + Artist: funkwhale.Artist{ + Name: "Dool", + ArtistMbid: "24412926-c7bd-48e8-afad-8a285b42e131", + }, + Album: funkwhale.Album{ + Title: "Here Now, There Then", + ReleaseMbid: "d7f22677-9803-4d21-ba42-081b633a6f68", + }, + Uploads: []funkwhale.Upload{ + { + Duration: 414, + }, + }, + }, + } + listen := backends.ListenFromFunkwhale(fwListen) + assert.Equal(t, time.Unix(1699574369, 0).Unix(), listen.ListenedAt.Unix()) + assert.Equal(t, fwListen.User.UserName, listen.UserName) + assert.Equal(t, time.Duration(414*time.Second), listen.Duration) + assert.Equal(t, fwListen.Track.Title, listen.TrackName) + assert.Equal(t, fwListen.Track.Album.Title, listen.ReleaseName) + assert.Equal(t, []string{fwListen.Track.Artist.Name}, listen.ArtistNames) + assert.Equal(t, fwListen.Track.Position, listen.Track.TrackNumber) + assert.Equal(t, fwListen.Track.Tags, listen.Track.Tags) + // assert.Equal(t, backends.FunkwhaleClientName, listen.AdditionalInfo["disc_number"]) + assert.Equal(t, models.MBID(fwListen.Track.RecordingMbid), listen.RecordingMbid) + assert.Equal(t, models.MBID(fwListen.Track.Album.ReleaseMbid), listen.ReleaseMbid) + assert.Equal(t, models.MBID(fwListen.Track.Artist.ArtistMbid), listen.ArtistMbids[0]) + assert.Equal(t, backends.FunkwhaleClientName, listen.AdditionalInfo["media_player"]) +} diff --git a/backends/interfaces.go b/backends/interfaces.go index d7f9c66..ff3ad25 100644 --- a/backends/interfaces.go +++ b/backends/interfaces.go @@ -98,6 +98,7 @@ func GetBackends() []BackendInfo { var knownBackends = map[string]func() Backend{ "dump": func() Backend { return &DumpBackend{} }, + "funkwhale-api": func() Backend { return &FunkwhaleApiBackend{} }, "listenbrainz-api": func() Backend { return &ListenBrainzApiBackend{} }, "maloja-api": func() Backend { return &MalojaApiBackend{} }, "scrobbler-log": func() Backend { return &ScrobblerLogBackend{} },