mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-07 13:38:15 -08:00
feat: v0.0.5
This commit is contained in:
parent
4c4ebc593d
commit
242a82ad8c
36 changed files with 694 additions and 174 deletions
|
|
@ -42,8 +42,19 @@ type LbzTrackMeta struct {
|
|||
ArtistName string `json:"artist_name"` // required
|
||||
TrackName string `json:"track_name"` // required
|
||||
ReleaseName string `json:"release_name,omitempty"`
|
||||
MBIDMapping LbzMBIDMapping `json:"mbid_mapping"`
|
||||
AdditionalInfo LbzAdditionalInfo `json:"additional_info,omitempty"`
|
||||
}
|
||||
type LbzArtist struct {
|
||||
ArtistMBID string `json:"artist_mbid"`
|
||||
ArtistName string `json:"artist_credit_name"`
|
||||
}
|
||||
type LbzMBIDMapping struct {
|
||||
ReleaseMBID string `json:"release_mbid"`
|
||||
RecordingMBID string `json:"recording_mbid"`
|
||||
ArtistMBIDs []string `json:"artist_mbids"`
|
||||
Artists []LbzArtist `json:"artists"`
|
||||
}
|
||||
|
||||
type LbzAdditionalInfo struct {
|
||||
MediaPlayer string `json:"media_player,omitempty"`
|
||||
|
|
@ -128,17 +139,30 @@ func LbzSubmitListenHandler(store db.DB, mbzc mbz.MusicBrainzCaller) func(w http
|
|||
if err != nil {
|
||||
l.Debug().Err(err).Msg("LbzSubmitListenHandler: Failed to parse one or more UUIDs")
|
||||
}
|
||||
if len(artistMbzIDs) < 1 {
|
||||
l.Debug().Err(err).Msg("LbzSubmitListenHandler: Attempting to parse artist UUIDs from mbid_mapping")
|
||||
utils.ParseUUIDSlice(payload.TrackMeta.MBIDMapping.ArtistMBIDs)
|
||||
if err != nil {
|
||||
l.Debug().Err(err).Msg("LbzSubmitListenHandler: Failed to parse one or more UUIDs")
|
||||
}
|
||||
}
|
||||
rgMbzID, err := uuid.Parse(payload.TrackMeta.AdditionalInfo.ReleaseGroupMBID)
|
||||
if err != nil {
|
||||
rgMbzID = uuid.Nil
|
||||
}
|
||||
releaseMbzID, err := uuid.Parse(payload.TrackMeta.AdditionalInfo.ReleaseMBID)
|
||||
if err != nil {
|
||||
releaseMbzID = uuid.Nil
|
||||
releaseMbzID, err = uuid.Parse(payload.TrackMeta.MBIDMapping.ReleaseMBID)
|
||||
if err != nil {
|
||||
releaseMbzID = uuid.Nil
|
||||
}
|
||||
}
|
||||
recordingMbzID, err := uuid.Parse(payload.TrackMeta.AdditionalInfo.RecordingMBID)
|
||||
if err != nil {
|
||||
recordingMbzID = uuid.Nil
|
||||
recordingMbzID, err = uuid.Parse(payload.TrackMeta.MBIDMapping.RecordingMBID)
|
||||
if err != nil {
|
||||
recordingMbzID = uuid.Nil
|
||||
}
|
||||
}
|
||||
|
||||
var client string
|
||||
|
|
@ -160,20 +184,33 @@ func LbzSubmitListenHandler(store db.DB, mbzc mbz.MusicBrainzCaller) func(w http
|
|||
listenedAt = time.Unix(payload.ListenedAt, 0)
|
||||
}
|
||||
|
||||
var artistMbidMap []catalog.ArtistMbidMap
|
||||
for _, a := range payload.TrackMeta.MBIDMapping.Artists {
|
||||
if a.ArtistMBID == "" || a.ArtistName == "" {
|
||||
continue
|
||||
}
|
||||
mbid, err := uuid.Parse(a.ArtistMBID)
|
||||
if err != nil {
|
||||
l.Err(err).Msgf("LbzSubmitListenHandler: Failed to parse UUID for artist '%s'", a.ArtistName)
|
||||
}
|
||||
artistMbidMap = append(artistMbidMap, catalog.ArtistMbidMap{Artist: a.ArtistName, Mbid: mbid})
|
||||
}
|
||||
|
||||
opts := catalog.SubmitListenOpts{
|
||||
MbzCaller: mbzc,
|
||||
ArtistNames: payload.TrackMeta.AdditionalInfo.ArtistNames,
|
||||
Artist: payload.TrackMeta.ArtistName,
|
||||
ArtistMbzIDs: artistMbzIDs,
|
||||
TrackTitle: payload.TrackMeta.TrackName,
|
||||
RecordingMbzID: recordingMbzID,
|
||||
ReleaseTitle: payload.TrackMeta.ReleaseName,
|
||||
ReleaseMbzID: releaseMbzID,
|
||||
ReleaseGroupMbzID: rgMbzID,
|
||||
Duration: duration,
|
||||
Time: listenedAt,
|
||||
UserID: u.ID,
|
||||
Client: client,
|
||||
MbzCaller: mbzc,
|
||||
ArtistNames: payload.TrackMeta.AdditionalInfo.ArtistNames,
|
||||
Artist: payload.TrackMeta.ArtistName,
|
||||
ArtistMbzIDs: artistMbzIDs,
|
||||
TrackTitle: payload.TrackMeta.TrackName,
|
||||
RecordingMbzID: recordingMbzID,
|
||||
ReleaseTitle: payload.TrackMeta.ReleaseName,
|
||||
ReleaseMbzID: releaseMbzID,
|
||||
ReleaseGroupMbzID: rgMbzID,
|
||||
ArtistMbidMappings: artistMbidMap,
|
||||
Duration: duration,
|
||||
Time: listenedAt,
|
||||
UserID: u.ID,
|
||||
Client: client,
|
||||
}
|
||||
|
||||
if req.ListenType == ListenTypePlayingNow {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package handlers
|
|||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gabehf/koito/internal/db"
|
||||
"github.com/gabehf/koito/internal/logger"
|
||||
|
|
@ -67,9 +68,16 @@ func MergeReleaseGroupsHandler(store db.DB) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
var replaceImage bool
|
||||
replaceImgStr := r.URL.Query().Get("replace_image")
|
||||
if strings.ToLower(replaceImgStr) == "true" {
|
||||
l.Debug().Msg("MergeReleaseGroupsHandler: Merge will replace image")
|
||||
replaceImage = true
|
||||
}
|
||||
|
||||
l.Debug().Msgf("MergeReleaseGroupsHandler: Merging release groups from ID %d to ID %d", fromId, toId)
|
||||
|
||||
err = store.MergeAlbums(r.Context(), int32(fromId), int32(toId))
|
||||
err = store.MergeAlbums(r.Context(), int32(fromId), int32(toId), replaceImage)
|
||||
if err != nil {
|
||||
l.Err(err).Msg("MergeReleaseGroupsHandler: Failed to merge release groups")
|
||||
utils.WriteError(w, "Failed to merge release groups: "+err.Error(), http.StatusInternalServerError)
|
||||
|
|
@ -103,9 +111,16 @@ func MergeArtistsHandler(store db.DB) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
var replaceImage bool
|
||||
replaceImgStr := r.URL.Query().Get("replace_image")
|
||||
if strings.ToLower(replaceImgStr) == "true" {
|
||||
l.Debug().Msg("MergeReleaseGroupsHandler: Merge will replace image")
|
||||
replaceImage = true
|
||||
}
|
||||
|
||||
l.Debug().Msgf("MergeArtistsHandler: Merging artists from ID %d to ID %d", fromId, toId)
|
||||
|
||||
err = store.MergeArtists(r.Context(), int32(fromId), int32(toId))
|
||||
err = store.MergeArtists(r.Context(), int32(fromId), int32(toId), replaceImage)
|
||||
if err != nil {
|
||||
l.Err(err).Msg("MergeArtistsHandler: Failed to merge artists")
|
||||
utils.WriteError(w, "Failed to merge artists: "+err.Error(), http.StatusInternalServerError)
|
||||
|
|
|
|||
|
|
@ -119,6 +119,40 @@ func TestImportLastFM(t *testing.T) {
|
|||
truncateTestData(t)
|
||||
}
|
||||
|
||||
func TestImportLastFM_MbzDisabled(t *testing.T) {
|
||||
|
||||
src := path.Join("..", "test_assets", "recenttracks-shoko2-1749776100.json")
|
||||
destDir := filepath.Join(cfg.ConfigDir(), "import")
|
||||
dest := filepath.Join(destDir, "recenttracks-shoko2-1749776100.json")
|
||||
|
||||
// not going to make the dest dir because engine should make it already
|
||||
|
||||
input, err := os.ReadFile(src)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, os.WriteFile(dest, input, os.ModePerm))
|
||||
|
||||
engine.RunImporter(logger.Get(), store, &mbz.MbzErrorCaller{})
|
||||
|
||||
album, err := store.GetAlbum(context.Background(), db.GetAlbumOpts{MusicBrainzID: uuid.MustParse("e9e78802-0bf8-4ca3-9655-1d943d2d2fa0")})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "ZOO!!", album.Title)
|
||||
artist, err := store.GetArtist(context.Background(), db.GetArtistOpts{MusicBrainzID: uuid.MustParse("4b00640f-3be6-43f8-9b34-ff81bd89320a")})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "OurR", artist.Name)
|
||||
artist, err = store.GetArtist(context.Background(), db.GetArtistOpts{Name: "CHUU"})
|
||||
require.NoError(t, err)
|
||||
track, err := store.GetTrack(context.Background(), db.GetTrackOpts{Title: "because I'm stupid?", ArtistIDs: []int32{artist.ID}})
|
||||
require.NoError(t, err)
|
||||
t.Log(track)
|
||||
listens, err := store.GetListensPaginated(context.Background(), db.GetItemsOpts{TrackID: int(track.ID), Period: db.PeriodAllTime})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, listens.Items, 1)
|
||||
assert.WithinDuration(t, time.Unix(1749776100, 0), listens.Items[0].Time, 1*time.Second)
|
||||
|
||||
truncateTestData(t)
|
||||
}
|
||||
|
||||
func TestImportListenBrainz(t *testing.T) {
|
||||
|
||||
src := path.Join("..", "test_assets", "listenbrainz_shoko1_1749780844.zip")
|
||||
|
|
@ -188,3 +222,41 @@ func TestImportListenBrainz(t *testing.T) {
|
|||
|
||||
truncateTestData(t)
|
||||
}
|
||||
|
||||
func TestImportListenBrainz_MbzDisabled(t *testing.T) {
|
||||
|
||||
src := path.Join("..", "test_assets", "listenbrainz_shoko1_1749780844.zip")
|
||||
destDir := filepath.Join(cfg.ConfigDir(), "import")
|
||||
dest := filepath.Join(destDir, "listenbrainz_shoko1_1749780844.zip")
|
||||
|
||||
// not going to make the dest dir because engine should make it already
|
||||
|
||||
input, err := os.ReadFile(src)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, os.WriteFile(dest, input, os.ModePerm))
|
||||
|
||||
engine.RunImporter(logger.Get(), store, &mbz.MbzErrorCaller{})
|
||||
|
||||
album, err := store.GetAlbum(context.Background(), db.GetAlbumOpts{MusicBrainzID: uuid.MustParse("ce330d67-9c46-4a3b-9d62-08406370f234")})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "酸欠少女", album.Title)
|
||||
artist, err := store.GetArtist(context.Background(), db.GetArtistOpts{MusicBrainzID: uuid.MustParse("4b00640f-3be6-43f8-9b34-ff81bd89320a")})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "OurR", artist.Name)
|
||||
artist, err = store.GetArtist(context.Background(), db.GetArtistOpts{MusicBrainzID: uuid.MustParse("09887aa7-226e-4ecc-9a0c-02d2ae5777e1")})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Carly Rae Jepsen", artist.Name)
|
||||
artist, err = store.GetArtist(context.Background(), db.GetArtistOpts{MusicBrainzID: uuid.MustParse("78e46ae5-9bfd-433b-be3f-19e993d67ecc")})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Rufus Wainwright", artist.Name)
|
||||
track, err := store.GetTrack(context.Background(), db.GetTrackOpts{MusicBrainzID: uuid.MustParse("08e8f55b-f1a4-46b8-b2d1-fab4c592165c")})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Desert", track.Title)
|
||||
listens, err := store.GetListensPaginated(context.Background(), db.GetItemsOpts{TrackID: int(track.ID), Period: db.PeriodAllTime})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, listens.Items, 1)
|
||||
assert.WithinDuration(t, time.Unix(1749780612, 0), listens.Items[0].Time, 1*time.Second)
|
||||
|
||||
truncateTestData(t)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,16 +87,16 @@ func ValidateApiKey(store db.DB) func(next http.Handler) http.Handler {
|
|||
}
|
||||
|
||||
authh := r.Header.Get("Authorization")
|
||||
s := strings.Split(authh, "Token ")
|
||||
if len(s) < 2 {
|
||||
l.Debug().Msg("ValidateApiKey: Authorization header must be formatted 'Token {token}'")
|
||||
var token string
|
||||
if strings.HasPrefix(strings.ToLower(authh), "token ") {
|
||||
token = strings.TrimSpace(authh[6:]) // strip "Token "
|
||||
} else {
|
||||
l.Error().Msg("ValidateApiKey: Authorization header must be formatted 'Token {token}'")
|
||||
utils.WriteError(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
key := s[1]
|
||||
|
||||
u, err := store.GetUserByApiKey(ctx, key)
|
||||
u, err := store.GetUserByApiKey(ctx, token)
|
||||
if err != nil {
|
||||
l.Err(err).Msg("Failed to get user from database using api key")
|
||||
utils.WriteError(w, "internal server error", http.StatusInternalServerError)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue