98 lines
3.4 KiB
Go
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
|
|
}
|