Files
unified-media-manager/internal/service/dashboard.go
2026-04-24 10:45:19 -07:00

98 lines
3.4 KiB
Go

package service
import (
"context"
"fmt"
"log/slog"
"github.com/TopherMayor/unified-media-manager/internal/db"
)
type DashboardStats struct {
TotalMedia int64 `json:"total_media"`
Monitored int64 `json:"monitored"`
Unavailable int64 `json:"unavailable"`
Available int64 `json:"available"`
QualityUpgrades int64 `json:"quality_upgrades"`
QueuePending int64 `json:"queue_pending"`
QueueDownloading int64 `json:"queue_downloading"`
QueueFailed int64 `json:"queue_failed"`
BlocklistCount int64 `json:"blocklist_count"`
BlocklistExpired int64 `json:"blocklist_expired"`
IndexersEnabled int64 `json:"indexers_enabled"`
MediaByType map[string]int64 `json:"media_by_type"`
StorageByType map[string]int64 `json:"storage_by_type"`
RecentDownloads int64 `json:"recent_downloads"`
}
type DashboardService struct {
db *db.DB
}
func NewDashboardService(database *db.DB) *DashboardService {
return &DashboardService{db: database}
}
func (s *DashboardService) Stats(ctx context.Context) (*DashboardStats, error) {
stats := &DashboardStats{
MediaByType: make(map[string]int64),
StorageByType: make(map[string]int64),
}
combinedQuery := `
SELECT
(SELECT COUNT(*) FROM media WHERE deleted_at IS NULL),
(SELECT COUNT(*) FROM media WHERE monitored = true AND deleted_at IS NULL),
(SELECT COUNT(*) FROM media WHERE status = 'unavailable' AND deleted_at IS NULL),
(SELECT COUNT(*) FROM media WHERE status = 'available' AND deleted_at IS NULL),
(SELECT COUNT(*) FROM media WHERE desired_quality IS NOT NULL AND current_quality IS NULL AND deleted_at IS NULL),
(SELECT COUNT(*) FROM download_queue WHERE status = 'pending'),
(SELECT COUNT(*) FROM download_queue WHERE status = 'downloading'),
(SELECT COUNT(*) FROM download_queue WHERE status = 'failed'),
(SELECT COUNT(*) FROM blocklist),
(SELECT COUNT(*) FROM blocklist WHERE auto_expires_at IS NOT NULL AND auto_expires_at < NOW()),
(SELECT COUNT(*) FROM indexers WHERE enabled = true),
(SELECT COUNT(*) FROM download_history WHERE created_at > NOW() - INTERVAL '24 hours')`
err := s.db.Pool.QueryRow(ctx, combinedQuery).Scan(
&stats.TotalMedia, &stats.Monitored, &stats.Unavailable, &stats.Available,
&stats.QualityUpgrades, &stats.QueuePending, &stats.QueueDownloading, &stats.QueueFailed,
&stats.BlocklistCount, &stats.BlocklistExpired, &stats.IndexersEnabled, &stats.RecentDownloads)
if err != nil {
slog.Error("dashboard combined query failed", "error", err)
return nil, fmt.Errorf("dashboard stats: %w", err)
}
rows, err := s.db.Pool.Query(ctx,
"SELECT media_type, COUNT(*) FROM media WHERE deleted_at IS NULL GROUP BY media_type")
if err == nil {
defer rows.Close()
for rows.Next() {
var mediaType string
var count int64
if err := rows.Scan(&mediaType, &count); err == nil {
stats.MediaByType[mediaType] = count
}
}
}
sRows, err := s.db.Pool.Query(ctx,
`SELECT m.media_type, COALESCE(SUM(mf.file_size), 0)
FROM media m
JOIN media_files mf ON m.id = mf.media_id AND mf.deleted_at IS NULL
WHERE m.deleted_at IS NULL
GROUP BY m.media_type`)
if err == nil {
defer sRows.Close()
for sRows.Next() {
var mediaType string
var totalSize int64
if err := sRows.Scan(&mediaType, &totalSize); err == nil {
stats.StorageByType[mediaType] = totalSize
}
}
}
return stats, nil
}