Funkwhale: Implemented LovesExport

This commit is contained in:
Philipp Wolfer 2023-11-12 15:57:18 +01:00
parent 48c8843f91
commit 9316838d59
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
6 changed files with 446 additions and 13 deletions

View file

@ -49,20 +49,39 @@ func NewClient(serverUrl string, token string) Client {
return client
}
func (c Client) GetHistoryListenings(user string, page int, perPage int) (ListeningsResult, error) {
func (c Client) GetHistoryListenings(user string, page int, perPage int) (result ListeningsResult, err 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),
"ordering": "-creation_date",
}).
SetResult(result).
SetResult(&result).
Get(path)
if response.StatusCode() != 200 {
return *result, errors.New(response.String())
err = errors.New(response.String())
return
}
return *result, err
return
}
func (c Client) GetFavoriteTracks(page int, perPage int) (result FavoriteTracksResult, err error) {
const path = "/api/v1/favorites/tracks"
response, err := c.HttpClient.R().
SetQueryParams(map[string]string{
"page": strconv.Itoa(page),
"page_size": strconv.Itoa(perPage),
"ordering": "-creation_date",
}).
SetResult(&result).
Get(path)
if response.StatusCode() != 200 {
err = errors.New(response.String())
return
}
return
}

View file

@ -54,6 +54,7 @@ func TestGetHistoryListenings(t *testing.T) {
assert := assert.New(t)
assert.Equal(2204, result.Count)
require.Len(t, result.Results, 2)
listen1 := result.Results[0]
assert.Equal("2023-11-09T23:59:29.022005Z", listen1.CreationDate)
assert.Equal("Way to Eden", listen1.Track.Title)
@ -62,6 +63,30 @@ func TestGetHistoryListenings(t *testing.T) {
assert.Equal("phw", listen1.User.UserName)
}
func TestGetFavoriteTracks(t *testing.T) {
defer httpmock.DeactivateAndReset()
token := "thetoken"
serverUrl := "https://funkwhale.example.com"
client := funkwhale.NewClient(serverUrl, token)
setupHttpMock(t, client.HttpClient.GetClient(),
"https://funkwhale.example.com/api/v1/favorites/tracks",
"testdata/favorite-tracks.json")
result, err := client.GetFavoriteTracks(0, 2)
require.NoError(t, err)
assert := assert.New(t)
assert.Equal(76, result.Count)
require.Len(t, result.Results, 2)
fav1 := result.Results[0]
assert.Equal("2023-11-05T20:32:32.339738Z", fav1.CreationDate)
assert.Equal("Reign", fav1.Track.Title)
assert.Equal("Home Economics", fav1.Track.Album.Title)
assert.Equal("Prinzhorn Dance School", fav1.Track.Artist.Name)
assert.Equal("phw", fav1.User.UserName)
}
func setupHttpMock(t *testing.T, client *http.Client, url string, testDataPath string) {
httpmock.ActivateNonDefault(client)

View file

@ -49,7 +49,7 @@ func (b FunkwhaleApiBackend) ExportListens(oldestTimestamp time.Time) ([]models.
page := 1
perPage := MaxItemsPerGet
listens := make([]models.Listen, 0)
listens := make([]models.Listen, 0, 2*MaxItemsPerGet)
out:
for {
@ -64,7 +64,7 @@ out:
}
for _, fwListen := range result.Results {
listen := ListenFromFunkwhale(fwListen)
listen := fwListen.ToListen()
if listen.ListenedAt.Unix() > oldestTimestamp.Unix() {
listens = append(listens, listen)
} else {
@ -84,10 +84,49 @@ out:
return listens, nil
}
func ListenFromFunkwhale(fwListen Listening) models.Listen {
track := fwListen.Track
func (b FunkwhaleApiBackend) ExportLoves(oldestTimestamp time.Time) ([]models.Love, error) {
page := 1
perPage := MaxItemsPerGet
loves := make([]models.Love, 0, 2*MaxItemsPerGet)
out:
for {
result, err := b.client.GetFavoriteTracks(page, perPage)
if err != nil {
return nil, err
}
count := len(result.Results)
if count == 0 {
break out
}
for _, favorite := range result.Results {
love := favorite.ToLove()
if love.Created.Unix() > oldestTimestamp.Unix() {
loves = append(loves, love)
} else {
break out
}
}
if result.Next == "" {
// No further results
break out
}
page += 1
}
slices.Reverse(loves)
return loves, nil
}
func (l Listening) ToListen() models.Listen {
track := l.Track
listen := models.Listen{
UserName: fwListen.User.UserName,
UserName: l.User.UserName,
Track: models.Track{
TrackName: track.Title,
ReleaseName: track.Album.Title,
@ -103,7 +142,7 @@ func ListenFromFunkwhale(fwListen Listening) models.Listen {
},
}
listenedAt, err := time.Parse(time.RFC3339, fwListen.CreationDate)
listenedAt, err := time.Parse(time.RFC3339, l.CreationDate)
if err == nil {
listen.ListenedAt = listenedAt
}
@ -114,3 +153,36 @@ func ListenFromFunkwhale(fwListen Listening) models.Listen {
return listen
}
func (f FavoriteTrack) ToLove() models.Love {
track := f.Track
recordingMbid := models.MBID(track.RecordingMbid)
love := models.Love{
UserName: f.User.UserName,
RecordingMbid: recordingMbid,
Track: models.Track{
TrackName: track.Title,
ReleaseName: track.Album.Title,
ArtistNames: []string{track.Artist.Name},
TrackNumber: track.Position,
RecordingMbid: 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,
},
},
}
created, err := time.Parse(time.RFC3339, f.CreationDate)
if err == nil {
love.Created = created
}
if len(track.Uploads) > 0 {
love.Track.Duration = time.Duration(track.Uploads[0].Duration * int(time.Second))
}
return love
}

View file

@ -30,7 +30,7 @@ import (
"go.uploadedlobster.com/scotty/models"
)
func TestListenFromFunkwhale(t *testing.T) {
func TestFunkwhaleListeningToListen(t *testing.T) {
fwListen := funkwhale.Listening{
CreationDate: "2023-11-09T23:59:29.022005Z",
User: funkwhale.User{
@ -57,7 +57,7 @@ func TestListenFromFunkwhale(t *testing.T) {
},
},
}
listen := funkwhale.ListenFromFunkwhale(fwListen)
listen := fwListen.ToListen()
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)
@ -72,3 +72,45 @@ func TestListenFromFunkwhale(t *testing.T) {
assert.Equal(t, models.MBID(fwListen.Track.Artist.ArtistMbid), listen.ArtistMbids[0])
assert.Equal(t, funkwhale.FunkwhaleClientName, listen.AdditionalInfo["media_player"])
}
func TestFunkwhaleFavoriteTrackToLove(t *testing.T) {
favorite := funkwhale.FavoriteTrack{
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,
},
},
},
}
love := favorite.ToLove()
assert.Equal(t, time.Unix(1699574369, 0).Unix(), love.Created.Unix())
assert.Equal(t, favorite.User.UserName, love.UserName)
assert.Equal(t, time.Duration(414*time.Second), love.Duration)
assert.Equal(t, favorite.Track.Title, love.TrackName)
assert.Equal(t, favorite.Track.Album.Title, love.ReleaseName)
assert.Equal(t, []string{favorite.Track.Artist.Name}, love.ArtistNames)
assert.Equal(t, favorite.Track.Position, love.Track.TrackNumber)
assert.Equal(t, favorite.Track.Tags, love.Track.Tags)
assert.Equal(t, models.MBID(favorite.Track.RecordingMbid), love.RecordingMbid)
assert.Equal(t, models.MBID(favorite.Track.Album.ReleaseMbid), love.ReleaseMbid)
assert.Equal(t, models.MBID(favorite.Track.Artist.ArtistMbid), love.ArtistMbids[0])
assert.Equal(t, funkwhale.FunkwhaleClientName, love.AdditionalInfo["media_player"])
}

View file

@ -35,6 +35,20 @@ type Listening struct {
CreationDate string `json:"creation_date"`
}
type FavoriteTracksResult struct {
Count int `json:"count"`
Previous string `json:"previous"`
Next string `json:"next"`
Results []FavoriteTrack `json:"results"`
}
type FavoriteTrack 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"`

View file

@ -0,0 +1,261 @@
{
"count": 76,
"next": "https://music.uploadedlobster.com/api/v1/favorites/tracks/?page=2&page_size=2",
"previous": null,
"results": [
{
"id": 80,
"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": 211,
"fid": "https://music.uploadedlobster.com/federation/music/artists/92ab65ce-95d4-405f-a162-959e9a69ec3e",
"mbid": "a1f2450a-c076-4a0d-ac9c-764bfc4225f7",
"name": "Prinzhorn Dance School",
"creation_date": "2020-11-14T08:27:27.964479Z",
"modification_date": "2020-11-14T08:27:27.964602Z",
"is_local": true,
"content_category": "music",
"description": null,
"attachment_cover": null,
"channel": null
},
"album": {
"id": 237,
"fid": "https://music.uploadedlobster.com/federation/music/albums/d301df18-c0b0-4608-8c68-bba86a65eabc",
"mbid": "7cb1093c-bfa5-4ffc-b3ac-943fb5c7f39f",
"title": "Home Economics",
"artist": {
"id": 211,
"fid": "https://music.uploadedlobster.com/federation/music/artists/92ab65ce-95d4-405f-a162-959e9a69ec3e",
"mbid": "a1f2450a-c076-4a0d-ac9c-764bfc4225f7",
"name": "Prinzhorn Dance School",
"creation_date": "2020-11-14T08:27:27.964479Z",
"modification_date": "2020-11-14T08:27:27.964602Z",
"is_local": true,
"content_category": "music",
"description": null,
"attachment_cover": null,
"channel": null
},
"release_date": "2015-06-08",
"cover": {
"uuid": "e2022441-b120-431d-912c-aa930c668cd5",
"size": 279103,
"mimetype": "image/jpeg",
"creation_date": "2023-03-30T07:51:33.366860Z",
"urls": {
"source": null,
"original": "https://music.uploadedlobster.com/media/attachments/98/0a/af/attachment_cover-d301df18-c0b0-4608-8c68-bba86a65eabc.jpg",
"medium_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/98/0a/af/attachment_cover-d301df18-c0b0-4608-8c68-bba86a65eabc-crop-c0-5__0-5-200x200-95.jpg",
"large_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/98/0a/af/attachment_cover-d301df18-c0b0-4608-8c68-bba86a65eabc-crop-c0-5__0-5-600x600-95.jpg"
}
},
"creation_date": "2020-11-14T08:27:28.805848Z",
"is_local": true,
"tracks_count": 6
},
"uploads": [
{
"uuid": "6647a0d0-5119-4dd0-bf1f-dfc64a1d956c",
"listen_url": "/api/v1/listen/b4cb522f-ce1b-49fb-81b8-0cc0d1cb5495/?upload=6647a0d0-5119-4dd0-bf1f-dfc64a1d956c",
"size": 11147671,
"duration": 271,
"bitrate": 320000,
"mimetype": "audio/mpeg",
"extension": "mp3",
"is_local": true
}
],
"listen_url": "/api/v1/listen/b4cb522f-ce1b-49fb-81b8-0cc0d1cb5495/",
"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": 2833,
"fid": "https://music.uploadedlobster.com/federation/music/tracks/b4cb522f-ce1b-49fb-81b8-0cc0d1cb5495",
"mbid": "b59cf4e7-caee-4019-a844-79d2c58d4dff",
"title": "Reign",
"creation_date": "2020-11-14T08:27:28.954529Z",
"is_local": true,
"position": 1,
"disc_number": 1,
"downloads_count": 3,
"copyright": null,
"license": null,
"cover": null,
"is_playable": true
},
"creation_date": "2023-11-05T20:32:32.339738Z",
"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": 79,
"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": 3800,
"fid": "https://music.uploadedlobster.com/federation/music/artists/2ef92f34-f4bc-4bf5-85de-a6847ea51a62",
"mbid": "055b6082-b9cc-4688-85c4-8153c0ef2d70",
"name": "Crippled Black Phoenix",
"creation_date": "2023-07-19T06:39:22.078159Z",
"modification_date": "2023-07-19T06:39:22.078238Z",
"is_local": true,
"content_category": "music",
"description": null,
"attachment_cover": null,
"channel": null
},
"album": {
"id": 2684,
"fid": "https://music.uploadedlobster.com/federation/music/albums/138bd960-8e94-4794-adaa-51de5fba33c3",
"mbid": "97509ec0-93cc-47ca-9033-1ac27678d799",
"title": "Ellengæst",
"artist": {
"id": 3800,
"fid": "https://music.uploadedlobster.com/federation/music/artists/2ef92f34-f4bc-4bf5-85de-a6847ea51a62",
"mbid": "055b6082-b9cc-4688-85c4-8153c0ef2d70",
"name": "Crippled Black Phoenix",
"creation_date": "2023-07-19T06:39:22.078159Z",
"modification_date": "2023-07-19T06:39:22.078238Z",
"is_local": true,
"content_category": "music",
"description": null,
"attachment_cover": null,
"channel": null
},
"release_date": "2020-11-04",
"cover": {
"uuid": "2c191778-0c1c-467e-9bf1-9949e3d98507",
"size": 129633,
"mimetype": "image/jpeg",
"creation_date": "2023-07-19T06:39:22.102600Z",
"urls": {
"source": null,
"original": "https://music.uploadedlobster.com/media/attachments/e5/d6/bf/attachment_cover-138bd960-8e94-4794-adaa-51de5fba33c3.jpg",
"medium_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/e5/d6/bf/attachment_cover-138bd960-8e94-4794-adaa-51de5fba33c3-crop-c0-5__0-5-200x200-95.jpg",
"large_square_crop": "https://music.uploadedlobster.com/media/__sized__/attachments/e5/d6/bf/attachment_cover-138bd960-8e94-4794-adaa-51de5fba33c3-crop-c0-5__0-5-600x600-95.jpg"
}
},
"creation_date": "2023-07-19T06:39:22.094428Z",
"is_local": true,
"tracks_count": 8
},
"uploads": [
{
"uuid": "f28de29b-6928-4879-a9da-32dcf9dd5ee4",
"listen_url": "/api/v1/listen/9263d5c0-dee3-4273-beef-7585e1d0041a/?upload=f28de29b-6928-4879-a9da-32dcf9dd5ee4",
"size": 12065231,
"duration": 491,
"bitrate": 0,
"mimetype": "audio/opus",
"extension": "opus",
"is_local": true
}
],
"listen_url": "/api/v1/listen/9263d5c0-dee3-4273-beef-7585e1d0041a/",
"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": 28095,
"fid": "https://music.uploadedlobster.com/federation/music/tracks/9263d5c0-dee3-4273-beef-7585e1d0041a",
"mbid": "14d612f0-4022-4adc-8cef-87a569e2d65c",
"title": "Lost",
"creation_date": "2023-07-19T06:39:22.459072Z",
"is_local": true,
"position": 2,
"disc_number": 1,
"downloads_count": 2,
"copyright": null,
"license": null,
"cover": null,
"is_playable": true
},
"creation_date": "2023-10-25T16:14:36.112517Z",
"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
}
}
]
}