Pre-release version v0.0.13 (#52)

* feat: search/merge items by id

* feat: update track duration using musicbrainz

* chore: changelog

* fix: make username updates case insensitive

* feat: add minutes listened to ui and fix image drop

* chore: changelog

* fix: embed db migrations (#37)

* feat: Add support for ARM in publish workflow (#51)

* chore: changelog

* docs: search by id and custom theme support

---------

Co-authored-by: potatoattack <lvl70nub@gmail.com>
Co-authored-by: Benjamin Jonard <benjaminjonard@users.noreply.github.com>
This commit is contained in:
Gabe Farrell 2025-07-26 15:57:46 -04:00 committed by GitHub
parent 5537b6fb89
commit 5419178012
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 1252 additions and 100 deletions

View file

@ -2,6 +2,8 @@ package handlers
import (
"net/http"
"strconv"
"strings"
"github.com/gabehf/koito/internal/db"
"github.com/gabehf/koito/internal/logger"
@ -20,27 +22,62 @@ func SearchHandler(store db.DB) http.HandlerFunc {
ctx := r.Context()
l := logger.FromContext(ctx)
q := r.URL.Query().Get("q")
artists, err := store.SearchArtists(ctx, q)
l.Debug().Msgf("SearchHandler: Received search with query: %s", r.URL.Query().Encode())
if err != nil {
l.Err(err).Msg("Failed to search for artists")
utils.WriteError(w, "failed to search in database", http.StatusInternalServerError)
return
}
albums, err := store.SearchAlbums(ctx, q)
if err != nil {
l.Err(err).Msg("Failed to search for albums")
utils.WriteError(w, "failed to search in database", http.StatusInternalServerError)
return
}
tracks, err := store.SearchTracks(ctx, q)
if err != nil {
l.Err(err).Msg("Failed to search for tracks")
utils.WriteError(w, "failed to search in database", http.StatusInternalServerError)
return
var artists []*models.Artist
var albums []*models.Album
var tracks []*models.Track
if strings.HasPrefix(q, "id:") {
idStr := strings.TrimPrefix(q, "id:")
id, _ := strconv.Atoi(idStr)
artist, err := store.GetArtist(ctx, db.GetArtistOpts{ID: int32(id)})
if err != nil {
l.Debug().Msg("No artists found with id")
}
if artist != nil {
artists = append(artists, artist)
}
album, err := store.GetAlbum(ctx, db.GetAlbumOpts{ID: int32(id)})
if err != nil {
l.Debug().Msg("No albums found with id")
}
if album != nil {
albums = append(albums, album)
}
track, err := store.GetTrack(ctx, db.GetTrackOpts{ID: int32(id)})
if err != nil {
l.Debug().Msg("No tracks found with id")
}
if track != nil {
tracks = append(tracks, track)
}
} else {
var err error
artists, err = store.SearchArtists(ctx, q)
if err != nil {
l.Err(err).Msg("Failed to search for artists")
utils.WriteError(w, "failed to search in database", http.StatusInternalServerError)
return
}
albums, err = store.SearchAlbums(ctx, q)
if err != nil {
l.Err(err).Msg("Failed to search for albums")
utils.WriteError(w, "failed to search in database", http.StatusInternalServerError)
return
}
tracks, err = store.SearchTracks(ctx, q)
if err != nil {
l.Err(err).Msg("Failed to search for tracks")
utils.WriteError(w, "failed to search in database", http.StatusInternalServerError)
return
}
}
utils.WriteJSON(w, http.StatusOK, SearchResults{
Artists: artists,
Albums: albums,

View file

@ -10,11 +10,11 @@ import (
)
type StatsResponse struct {
ListenCount int64 `json:"listen_count"`
TrackCount int64 `json:"track_count"`
AlbumCount int64 `json:"album_count"`
ArtistCount int64 `json:"artist_count"`
HoursListened int64 `json:"hours_listened"`
ListenCount int64 `json:"listen_count"`
TrackCount int64 `json:"track_count"`
AlbumCount int64 `json:"album_count"`
ArtistCount int64 `json:"artist_count"`
MinutesListened int64 `json:"minutes_listened"`
}
func StatsHandler(store db.DB) http.HandlerFunc {
@ -79,11 +79,11 @@ func StatsHandler(store db.DB) http.HandlerFunc {
l.Debug().Msg("StatsHandler: Successfully fetched statistics")
utils.WriteJSON(w, http.StatusOK, StatsResponse{
ListenCount: listens,
TrackCount: tracks,
AlbumCount: albums,
ArtistCount: artists,
HoursListened: timeListenedS / 60 / 60,
ListenCount: listens,
TrackCount: tracks,
AlbumCount: albums,
ArtistCount: artists,
MinutesListened: timeListenedS / 60,
})
}
}

View file

@ -447,7 +447,7 @@ func TestStats(t *testing.T) {
assert.EqualValues(t, 3, actual.TrackCount)
assert.EqualValues(t, 3, actual.AlbumCount)
assert.EqualValues(t, 3, actual.ArtistCount)
assert.EqualValues(t, 0, actual.HoursListened)
assert.EqualValues(t, 11, actual.MinutesListened)
}
func TestListenActivity(t *testing.T) {