Files
MeBox/internal/handler/media_extra.go
T
truewhile b0fe40142a Rebrand MMTL to MeBox (name, logo, Docker image) (#17)
* Rebrand MMTL to MeBox across codebase and assets

Rename the project display name, Go module path, environment variable
prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl
to MeBox/mebox. Replace logo assets with the new MeBox icon and keep
legacy SQLite migration support for existing mmtl.db deployments.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

* Fix logo icons: use cube-only crop without truncated text

Previous icon generation cropped too much of the source image, including
partial MeBox wordmark text that was cut off in square icon containers.
Regenerate logo-64/192/512, favicon, and SVG from cube-only region.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
2026-09-02 16:26:28 +08:00

71 lines
2.4 KiB
Go

// Package handler — auxiliary media endpoints used by the home page
// rails (recent additions) and the admin dashboard summary card.
package handler
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/truewhile/MeBox/internal/model"
"github.com/truewhile/MeBox/internal/service"
)
func recentMediaHandler(svc *service.Container) gin.HandlerFunc {
return func(c *gin.Context) {
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "12"))
if limit <= 0 || limit > 100 {
limit = 12
}
items, err := svc.Media.ListRecentSeriesCards(c.Request.Context(), limit, mediaVisibilityForRequest(c, svc))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, items)
}
}
func mediaStatsHandler(svc *service.Container) gin.HandlerFunc {
return func(c *gin.Context) {
var totals struct {
Movies int64 `json:"movies"`
TV int64 `json:"tv"`
Anime int64 `json:"anime"`
Music int64 `json:"music"`
Unscaped int64 `json:"unscraped"`
}
// Per-library type rolls up to per-media-type via the JOIN.
_ = svc.Repo.DB.Model(&model.Media{}).
Joins("JOIN libraries ON libraries.id = media.library_id").
Where("libraries.type = ?", "movie").Count(&totals.Movies).Error
_ = svc.Repo.DB.Model(&model.Media{}).
Joins("JOIN libraries ON libraries.id = media.library_id").
Where("libraries.type = ?", "tv").Count(&totals.TV).Error
_ = svc.Repo.DB.Model(&model.Media{}).
Joins("JOIN libraries ON libraries.id = media.library_id").
Where("libraries.type = ?", "anime").Count(&totals.Anime).Error
_ = svc.Repo.DB.Model(&model.Media{}).
Joins("JOIN libraries ON libraries.id = media.library_id").
Where("libraries.type = ?", "music").Count(&totals.Music).Error
_ = svc.Repo.DB.Model(&model.Media{}).
Where("scrape_status IS NULL OR scrape_status = '' OR scrape_status = 'pending'").
Count(&totals.Unscaped).Error
var totalCount, totalSize, totalSeconds int64
_ = svc.Repo.DB.Model(&model.Media{}).Count(&totalCount).Error
_ = svc.Repo.DB.Model(&model.Media{}).
Select("COALESCE(SUM(size_bytes),0)").Row().Scan(&totalSize)
_ = svc.Repo.DB.Model(&model.Media{}).
Select("COALESCE(SUM(duration_sec),0)").Row().Scan(&totalSeconds)
c.JSON(http.StatusOK, gin.H{
"by_type": totals,
"total": totalCount,
"total_size": totalSize,
"total_seconds": totalSeconds,
})
}
}