278 lines
8.3 KiB
Go
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()))
|
|
}
|
|
}
|