mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 03:26:37 +08:00
155 lines
4.6 KiB
Go
155 lines
4.6 KiB
Go
// Package handler — stats / dashboard endpoints.
|
|
package handler
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
|
"github.com/ShukeBta/MediaStationGo/internal/service"
|
|
)
|
|
|
|
func statsHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
snap, err := svc.Stats.Compute(c.Request.Context(), svc.Cfg.App.DataDir)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if err := applyStatsVisibility(c, svc, snap); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, snap)
|
|
}
|
|
}
|
|
|
|
func applyStatsVisibility(c *gin.Context, svc *service.Container, snap *service.Snapshot) error {
|
|
visibility := mediaVisibilityForRequest(c, svc)
|
|
libs, err := svc.Repo.Library.List(c.Request.Context())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
libs = service.FilterDisplayCloudLibraries(c.Request.Context(), svc.Repo, libs)
|
|
var visibleLibraries int64
|
|
activeLibraryIDs := make([]string, 0, len(libs))
|
|
for _, lib := range libs {
|
|
if !lib.Enabled {
|
|
continue
|
|
}
|
|
activeLibraryIDs = append(activeLibraryIDs, lib.ID)
|
|
if service.LibraryVisibleForUser(c.Request.Context(), svc.Repo, lib, visibility) {
|
|
visibleLibraries++
|
|
}
|
|
}
|
|
snap.Libraries = visibleLibraries
|
|
cacheKey := visibleStatsCacheKey(visibility, activeLibraryIDs)
|
|
if svc.Cache != nil {
|
|
var cached visibleStatsCacheValue
|
|
if svc.Cache.GetJSON(c.Request.Context(), cacheKey, &cached) {
|
|
snap.Libraries = cached.Libraries
|
|
snap.MediaCount = cached.MediaCount
|
|
snap.TotalSizeBytes = cached.TotalSizeBytes
|
|
snap.TotalSeconds = cached.TotalSeconds
|
|
snap.RecentlyAdded = cached.RecentlyAdded
|
|
return nil
|
|
}
|
|
}
|
|
|
|
q := applyMediaVisibilityQuery(svc.Repo.DB.WithContext(c.Request.Context()).Model(&model.Media{}), visibility)
|
|
q = applyActiveLibraryQuery(q, activeLibraryIDs)
|
|
if err := q.Count(&snap.MediaCount).Error; err != nil {
|
|
return err
|
|
}
|
|
type sumRow struct {
|
|
Size int64
|
|
Seconds int64
|
|
}
|
|
var sum sumRow
|
|
if err := applyActiveLibraryQuery(applyMediaVisibilityQuery(svc.Repo.DB.WithContext(c.Request.Context()).Model(&model.Media{}), visibility), activeLibraryIDs).
|
|
Select("COALESCE(SUM(size_bytes),0) as size, COALESCE(SUM(duration_sec),0) as seconds").
|
|
Scan(&sum).Error; err != nil {
|
|
return err
|
|
}
|
|
snap.TotalSizeBytes = sum.Size
|
|
snap.TotalSeconds = sum.Seconds
|
|
|
|
var recent []model.Media
|
|
if err := applyActiveLibraryQuery(applyMediaVisibilityQuery(svc.Repo.DB.WithContext(c.Request.Context()).Model(&model.Media{}), visibility), activeLibraryIDs).
|
|
Order("created_at desc").
|
|
Limit(12).
|
|
Find(&recent).Error; err != nil {
|
|
return err
|
|
}
|
|
snap.RecentlyAdded = recent
|
|
if svc.Cache != nil {
|
|
svc.Cache.SetJSON(c.Request.Context(), cacheKey, visibleStatsCacheValue{
|
|
Libraries: snap.Libraries,
|
|
MediaCount: snap.MediaCount,
|
|
TotalSizeBytes: snap.TotalSizeBytes,
|
|
TotalSeconds: snap.TotalSeconds,
|
|
RecentlyAdded: snap.RecentlyAdded,
|
|
}, 10*time.Second)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type visibleStatsCacheValue struct {
|
|
Libraries int64 `json:"libraries"`
|
|
MediaCount int64 `json:"media_count"`
|
|
TotalSizeBytes int64 `json:"total_size_bytes"`
|
|
TotalSeconds int64 `json:"total_seconds"`
|
|
RecentlyAdded []model.Media `json:"recently_added"`
|
|
}
|
|
|
|
func visibleStatsCacheKey(visibility service.MediaVisibility, activeLibraryIDs []string) string {
|
|
allowed := append([]string(nil), visibility.AllowedLibraryIDs...)
|
|
hidden := append([]string(nil), visibility.HiddenLibraryIDs...)
|
|
active := append([]string(nil), activeLibraryIDs...)
|
|
sort.Strings(allowed)
|
|
sort.Strings(hidden)
|
|
sort.Strings(active)
|
|
sum := sha1.Sum([]byte(strings.Join([]string{
|
|
"visible",
|
|
strings.Join(active, ","),
|
|
strings.Join(allowed, ","),
|
|
strings.Join(hidden, ","),
|
|
boolString(visibility.IncludeNSFW),
|
|
}, "|")))
|
|
return "stats:visible:" + hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func boolString(value bool) string {
|
|
if value {
|
|
return "1"
|
|
}
|
|
return "0"
|
|
}
|
|
|
|
func applyMediaVisibilityQuery(q *gorm.DB, visibility service.MediaVisibility) *gorm.DB {
|
|
if !visibility.IncludeNSFW {
|
|
q = q.Where("nsfw = ?", false)
|
|
}
|
|
if len(visibility.HiddenLibraryIDs) > 0 {
|
|
q = q.Where("library_id NOT IN ?", visibility.HiddenLibraryIDs)
|
|
}
|
|
if len(visibility.AllowedLibraryIDs) > 0 {
|
|
q = q.Where("library_id IN ?", visibility.AllowedLibraryIDs)
|
|
}
|
|
return q
|
|
}
|
|
|
|
func applyActiveLibraryQuery(q *gorm.DB, libraryIDs []string) *gorm.DB {
|
|
if len(libraryIDs) == 0 {
|
|
return q.Where("1 = 0")
|
|
}
|
|
return q.Where("library_id IN ?", libraryIDs)
|
|
}
|