Files
MusicServer/internal/backend/statistics.go
T
2026-06-29 18:39:09 +02:00

278 lines
8.3 KiB
Go

package backend
import (
"encoding/json"
"time"
"music-server/internal/logging"
"go.uber.org/zap"
)
// SoundtrackWithSongs represents a soundtrack with its songs for statistics
type SoundtrackWithSongs struct {
SoundtrackID int32 `json:"soundtrack_id"`
SoundtrackName string `json:"soundtrack_name"`
SoundtrackPlayed int32 `json:"soundtrack_played"`
SoundtrackLastPlayed *time.Time `json:"soundtrack_last_played,omitempty"`
Songs []SongInfoForStats `json:"songs"`
}
// SongInfoForStats represents a song with soundtrack info for statistics
type SongInfoForStats struct {
SoundtrackID int32 `json:"soundtrack_id"`
SoundtrackName string `json:"soundtrack_name"`
SongName string `json:"song_name"`
Path string `json:"path"`
TimesPlayed int32 `json:"times_played"`
FileName *string `json:"file_name,omitempty"`
}
// StatisticsSummary holds overall statistics
type StatisticsSummary struct {
TotalSoundtracks int64 `json:"total_soundtracks"`
PlayedSoundtracks int64 `json:"played_soundtracks"`
NeverPlayedSoundtracks int64 `json:"never_played_soundtracks"`
TotalSoundtrackPlays int64 `json:"total_soundtrack_plays"`
AvgSoundtrackPlays float64 `json:"avg_soundtrack_plays"`
MaxSoundtrackPlays int64 `json:"max_soundtrack_plays"`
MinSoundtrackPlays int64 `json:"min_soundtrack_plays"`
}
// StatisticsHandler manages statistics operations
type StatisticsHandler struct {
// Uses the global backend repo initialized via InitBackend
}
// NewStatisticsHandler creates a new StatisticsHandler
func NewStatisticsHandler() *StatisticsHandler {
return &StatisticsHandler{}
}
// GetMostPlayedSoundtracksWithSongs returns the top N most played soundtracks with their songs
func (h *StatisticsHandler) GetMostPlayedSoundtracksWithSongs(limit int32) ([]SoundtrackWithSongs, error) {
queries := BackendRepo()
ctx := BackendCtx()
// Get raw results
rows, err := queries.GetMostPlayedSoundtracksWithSongs(ctx, limit)
if err != nil {
return nil, err
}
// Convert to SoundtrackWithSongs
var result []SoundtrackWithSongs
for _, row := range rows {
var songs []SongInfoForStats
if row.Songs != nil {
// Parse JSON songs array
if err := json.Unmarshal(row.Songs, &songs); err != nil {
// Fallback: if JSON parsing fails, create empty song entries
songs = make([]SongInfoForStats, 0)
}
}
result = append(result, SoundtrackWithSongs{
SoundtrackID: row.SoundtrackID,
SoundtrackName: row.SoundtrackName,
SoundtrackPlayed: row.SoundtrackPlayed,
SoundtrackLastPlayed: row.SoundtrackLastPlayed,
Songs: songs,
})
}
return result, nil
}
// GetLeastPlayedSoundtracksWithSongs returns the top N least played soundtracks with their songs
func (h *StatisticsHandler) GetLeastPlayedSoundtracksWithSongs(limit int32) ([]SoundtrackWithSongs, error) {
queries := BackendRepo()
ctx := BackendCtx()
rows, err := queries.GetLeastPlayedSoundtracksWithSongs(ctx, limit)
if err != nil {
return nil, err
}
var result []SoundtrackWithSongs
for _, row := range rows {
var songs []SongInfoForStats
if row.Songs != nil {
if err := json.Unmarshal(row.Songs, &songs); err != nil {
songs = make([]SongInfoForStats, 0)
}
}
result = append(result, SoundtrackWithSongs{
SoundtrackID: row.SoundtrackID,
SoundtrackName: row.SoundtrackName,
SoundtrackPlayed: row.SoundtrackPlayed,
SoundtrackLastPlayed: row.SoundtrackLastPlayed,
Songs: songs,
})
}
return result, nil
}
// GetMostPlayedSongsWithSoundtrack returns the top N most played songs with their soundtrack info
func (h *StatisticsHandler) GetMostPlayedSongsWithSoundtrack(limit int32) ([]SongInfoForStats, error) {
queries := BackendRepo()
ctx := BackendCtx()
rows, err := queries.GetMostPlayedSongsWithSoundtrack(ctx, limit)
if err != nil {
return nil, err
}
var result []SongInfoForStats
for _, row := range rows {
result = append(result, SongInfoForStats{
SoundtrackID: row.SoundtrackID,
SoundtrackName: row.SoundtrackName,
SongName: row.SongName,
Path: row.Path,
TimesPlayed: row.TimesPlayed,
FileName: row.FileName,
})
}
return result, nil
}
// GetLeastPlayedSongsWithSoundtrack returns the top N least played songs with their soundtrack info
func (h *StatisticsHandler) GetLeastPlayedSongsWithSoundtrack(limit int32) ([]SongInfoForStats, error) {
queries := BackendRepo()
ctx := BackendCtx()
rows, err := queries.GetLeastPlayedSongsWithSoundtrack(ctx, limit)
if err != nil {
return nil, err
}
var result []SongInfoForStats
for _, row := range rows {
result = append(result, SongInfoForStats{
SoundtrackID: row.SoundtrackID,
SoundtrackName: row.SoundtrackName,
SongName: row.SongName,
Path: row.Path,
TimesPlayed: row.TimesPlayed,
FileName: row.FileName,
})
}
return result, nil
}
// GetNeverPlayedSoundtracks returns soundtracks that have never been played
func (h *StatisticsHandler) GetNeverPlayedSoundtracks() ([]SoundtrackWithSongs, error) {
queries := BackendRepo()
ctx := BackendCtx()
rows, err := queries.GetNeverPlayedSoundtracks(ctx)
if err != nil {
return nil, err
}
var result []SoundtrackWithSongs
for _, row := range rows {
var songs []SongInfoForStats
if row.Songs != nil {
if err := json.Unmarshal(row.Songs, &songs); err != nil {
songs = make([]SongInfoForStats, 0)
}
}
result = append(result, SoundtrackWithSongs{
SoundtrackID: row.SoundtrackID,
SoundtrackName: row.SoundtrackName,
SoundtrackPlayed: row.SoundtrackPlayed,
SoundtrackLastPlayed: nil,
Songs: songs,
})
}
return result, nil
}
// GetLastPlayedSoundtracks returns the most recently played soundtracks
func (h *StatisticsHandler) GetLastPlayedSoundtracks(limit int32) ([]SoundtrackWithSongs, error) {
queries := BackendRepo()
ctx := BackendCtx()
rows, err := queries.GetLastPlayedSoundtracks(ctx, limit)
if err != nil {
return nil, err
}
var result []SoundtrackWithSongs
for _, row := range rows {
var songs []SongInfoForStats
if row.Songs != nil {
if err := json.Unmarshal(row.Songs, &songs); err != nil {
songs = make([]SongInfoForStats, 0)
}
}
result = append(result, SoundtrackWithSongs{
SoundtrackID: row.SoundtrackID,
SoundtrackName: row.SoundtrackName,
SoundtrackPlayed: row.SoundtrackPlayed,
SoundtrackLastPlayed: row.SoundtrackLastPlayed,
Songs: songs,
})
}
return result, nil
}
// GetOldestPlayedSoundtracks returns the least recently played soundtracks
func (h *StatisticsHandler) GetOldestPlayedSoundtracks(limit int32) ([]SoundtrackWithSongs, error) {
queries := BackendRepo()
ctx := BackendCtx()
rows, err := queries.GetOldestPlayedSoundtracks(ctx, limit)
if err != nil {
return nil, err
}
var result []SoundtrackWithSongs
for _, row := range rows {
var songs []SongInfoForStats
if row.Songs != nil {
if err := json.Unmarshal(row.Songs, &songs); err != nil {
songs = make([]SongInfoForStats, 0)
}
}
result = append(result, SoundtrackWithSongs{
SoundtrackID: row.SoundtrackID,
SoundtrackName: row.SoundtrackName,
SoundtrackPlayed: row.SoundtrackPlayed,
SoundtrackLastPlayed: row.SoundtrackLastPlayed,
Songs: songs,
})
}
return result, nil
}
// GetStatisticsSummary returns overall statistics
func (h *StatisticsHandler) GetStatisticsSummary() (*StatisticsSummary, error) {
queries := BackendRepo()
ctx := BackendCtx()
row, err := queries.GetStatisticsSummary(ctx)
if err != nil {
return nil, err
}
return &StatisticsSummary{
TotalSoundtracks: int64(row.TotalSoundtracks),
PlayedSoundtracks: int64(row.PlayedSoundtracks),
NeverPlayedSoundtracks: int64(row.NeverPlayedSoundtracks),
TotalSoundtrackPlays: int64(row.TotalSoundtrackPlays),
AvgSoundtrackPlays: float64(row.AvgSoundtrackPlays),
MaxSoundtrackPlays: int64(row.MaxSoundtrackPlays),
MinSoundtrackPlays: int64(row.MinSoundtrackPlays),
}, nil
}
// Log helper for statistics operations
func logStatisticsError(err error, operation string) {
if err != nil {
logging.GetLogger().Error("Statistics error",
zap.String("operation", operation),
zap.String("error", err.Error()))
}
}