mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 03:06:38 +08:00
b0fe40142a
* 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>
248 lines
6.9 KiB
Go
248 lines
6.9 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/truewhile/MeBox/internal/service"
|
|
)
|
|
|
|
func parseEmbyItemsParams(c *gin.Context) service.ItemsParams {
|
|
limit, _ := strconv.Atoi(embyFirstNonEmptyString(firstQueryValue(c, "Limit", "limit"), "50"))
|
|
offset, _ := strconv.Atoi(embyFirstNonEmptyString(firstQueryValue(c, "StartIndex", "startIndex", "startindex"), "0"))
|
|
uid := c.Param("userId")
|
|
if uid == "" {
|
|
uid = firstQueryValue(c, "UserId", "userId", "userid")
|
|
}
|
|
if uid == "" {
|
|
uid = embyUserID(c)
|
|
}
|
|
splitOpt := func(s string) []string {
|
|
if s == "" {
|
|
return nil
|
|
}
|
|
parts := strings.Split(s, ",")
|
|
out := make([]string, 0, len(parts))
|
|
for _, p := range parts {
|
|
p = strings.TrimSpace(p)
|
|
if p != "" {
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
return service.ItemsParams{
|
|
UserID: uid,
|
|
ParentID: firstQueryValue(c, "ParentId", "parentId", "parentid"),
|
|
IDs: splitOpt(firstQueryValue(c, "Ids", "ids")),
|
|
SearchTerm: firstQueryValue(c, "SearchTerm", "searchTerm", "searchterm"),
|
|
IncludeItemTypes: splitOpt(firstQueryValue(c, "IncludeItemTypes", "includeItemTypes", "includeitemtypes")),
|
|
Filters: splitOpt(firstQueryValue(c, "Filters", "filters")),
|
|
Recursive: strings.EqualFold(firstQueryValue(c, "Recursive", "recursive"), "true"),
|
|
SortBy: firstQueryValue(c, "SortBy", "sortBy", "sortby"),
|
|
SortOrder: firstQueryValue(c, "SortOrder", "sortOrder", "sortorder"),
|
|
Limit: limit,
|
|
StartIndex: offset,
|
|
}
|
|
}
|
|
|
|
func embyFirstNonEmptyString(values ...string) string {
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) != "" {
|
|
return strings.TrimSpace(value)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func embyItemsHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
out, err := svc.Emby.Items(c.Request.Context(), parseEmbyItemsParams(c))
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
embyAttachRequestTokenToMediaSources(c, out)
|
|
c.JSON(http.StatusOK, out)
|
|
}
|
|
}
|
|
|
|
func embyItemByIDHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := c.Param("id")
|
|
uid := c.Param("userId")
|
|
if uid == "" {
|
|
uid = embyUserID(c)
|
|
}
|
|
out, err := svc.Emby.Item(c.Request.Context(), id, uid)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if out == nil {
|
|
embyError(c, http.StatusNotFound, "item not found")
|
|
return
|
|
}
|
|
embyAttachRequestTokenToMediaSources(c, out)
|
|
c.JSON(http.StatusOK, out)
|
|
}
|
|
}
|
|
|
|
func embyUserItemByIDHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
switch strings.ToLower(c.Param("id")) {
|
|
case "latest":
|
|
embyLatestItemsHandler(svc)(c)
|
|
case "resume":
|
|
embyResumeItemsHandler(svc)(c)
|
|
default:
|
|
embyItemByIDHandler(svc)(c)
|
|
}
|
|
}
|
|
}
|
|
|
|
func embyLatestItemsHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
uid := c.Param("userId")
|
|
if uid == "" {
|
|
uid = firstQueryValue(c, "UserId", "userId", "userid")
|
|
}
|
|
if uid == "" {
|
|
uid = embyUserID(c)
|
|
}
|
|
limit, _ := strconv.Atoi(embyFirstNonEmptyString(firstQueryValue(c, "Limit", "limit"), "20"))
|
|
out, err := svc.Emby.LatestItems(c.Request.Context(), uid, firstQueryValue(c, "ParentId", "parentId", "parentid"), limit)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
embyAttachRequestTokenToMediaSources(c, out)
|
|
c.JSON(http.StatusOK, out)
|
|
}
|
|
}
|
|
|
|
func embyResumeItemsHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
uid := c.Param("userId")
|
|
if uid == "" {
|
|
uid = firstQueryValue(c, "UserId", "userId", "userid")
|
|
}
|
|
if uid == "" {
|
|
uid = embyUserID(c)
|
|
}
|
|
limit, _ := strconv.Atoi(embyFirstNonEmptyString(firstQueryValue(c, "Limit", "limit"), "20"))
|
|
out, err := svc.Emby.ResumeItems(c.Request.Context(), uid, limit)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
embyAttachRequestTokenToMediaSources(c, out)
|
|
c.JSON(http.StatusOK, out)
|
|
}
|
|
}
|
|
|
|
func embyItemsCountsHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if svc != nil && svc.Emby != nil {
|
|
uid := firstQueryValue(c, "UserId", "userId")
|
|
if uid == "" {
|
|
uid = c.Param("userId")
|
|
}
|
|
if uid == "" {
|
|
uid = embyUserID(c)
|
|
}
|
|
out, err := svc.Emby.ItemCounts(c.Request.Context(), uid)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, out)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"MovieCount": 0,
|
|
"SeriesCount": 0,
|
|
"EpisodeCount": 0,
|
|
"ItemCount": 0,
|
|
})
|
|
}
|
|
}
|
|
|
|
func embyDisplayPreferencesHandler(_ *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"Id": c.Param("id"),
|
|
"ViewType": "Poster",
|
|
"SortBy": "SortName",
|
|
"SortOrder": "Ascending",
|
|
"IndexBy": "SortName",
|
|
"RememberIndexing": false,
|
|
"PrimaryImageHeight": 250,
|
|
"PrimaryImageWidth": 250,
|
|
"ScrollDirection": "Vertical",
|
|
"ShowSidebar": true,
|
|
"CustomPrefs": gin.H{
|
|
"homeexploresection": "1",
|
|
"homesection0": "smalllibrarytiles",
|
|
"homesection1": "resume",
|
|
"homesection2": "none",
|
|
"homesection3": "nextup",
|
|
"homesection4": "none",
|
|
"homesection5": "none",
|
|
"homesection6": "none",
|
|
"latestItems": "false",
|
|
"landing-livetv": "false",
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
func embySaveDisplayPreferencesHandler(_ *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
}
|
|
|
|
func embyShowSeasonsHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
params := service.ItemsParams{
|
|
UserID: firstQueryValue(c, "UserId", "userId"),
|
|
ParentID: c.Param("id"),
|
|
Limit: 500,
|
|
}
|
|
out, err := svc.Emby.Items(c.Request.Context(), params)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
embyAttachRequestTokenToMediaSources(c, out)
|
|
c.JSON(http.StatusOK, out)
|
|
}
|
|
}
|
|
|
|
func embyShowEpisodesHandler(svc *service.Container) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
parentID := firstQueryValue(c, "SeasonId", "seasonId")
|
|
if parentID == "" {
|
|
parentID = c.Param("id")
|
|
}
|
|
params := service.ItemsParams{
|
|
UserID: firstQueryValue(c, "UserId", "userId"),
|
|
ParentID: parentID,
|
|
IncludeItemTypes: []string{"Episode"},
|
|
Recursive: true,
|
|
Limit: 500,
|
|
}
|
|
out, err := svc.Emby.Items(c.Request.Context(), params)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
embyAttachRequestTokenToMediaSources(c, out)
|
|
c.JSON(http.StatusOK, out)
|
|
}
|
|
}
|