mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 03:06:38 +08:00
优化
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
'Get-Content' is not recognized as an internal or external command,
|
||||||
|
operable program or batch file.
|
||||||
@@ -92,13 +92,14 @@ func historyStatsHandler(svc *service.Container) gin.HandlerFunc {
|
|||||||
func historyContinueHandler(svc *service.Container) gin.HandlerFunc {
|
func historyContinueHandler(svc *service.Container) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
uid, _ := c.Get(middleware.CtxUserID)
|
uid, _ := c.Get(middleware.CtxUserID)
|
||||||
|
userID := toString(uid)
|
||||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
||||||
if limit <= 0 || limit > 50 {
|
if limit <= 0 || limit > 50 {
|
||||||
limit = 10
|
limit = 10
|
||||||
}
|
}
|
||||||
var rows []model.PlaybackHistory
|
var rows []model.PlaybackHistory
|
||||||
if err := svc.Repo.DB.
|
if err := svc.Repo.DB.
|
||||||
Where("user_id = ? AND completed = ?", toString(uid), false).
|
Where("user_id = ? AND completed = ?", userID, false).
|
||||||
Order("watched_at desc").
|
Order("watched_at desc").
|
||||||
Limit(limit).
|
Limit(limit).
|
||||||
Find(&rows).Error; err != nil {
|
Find(&rows).Error; err != nil {
|
||||||
@@ -116,58 +117,49 @@ func historyContinueHandler(svc *service.Container) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
mIdx := make(map[string]model.Media, len(media))
|
mIdx := make(map[string]model.Media, len(media))
|
||||||
for _, m := range media {
|
for _, m := range media {
|
||||||
if !mediaVisibleForRequest(c, svc, &m) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mIdx[m.ID] = m
|
mIdx[m.ID] = m
|
||||||
}
|
}
|
||||||
out := make([]gin.H, 0, len(rows))
|
out := make([]gin.H, 0, len(rows))
|
||||||
|
staleIDs := make([]string, 0)
|
||||||
for _, r := range rows {
|
for _, r := range rows {
|
||||||
m, ok := mIdx[r.MediaID]
|
m, ok := mIdx[r.MediaID]
|
||||||
if !ok {
|
if ok {
|
||||||
if svc.EmbyRemote != nil && service.IsEmbyRemoteID(r.MediaID) {
|
if mediaVisibleForRequest(c, svc, &m) {
|
||||||
mountID, remoteID, _ := service.DecodeEmbyRemoteID(r.MediaID)
|
|
||||||
if mount, acct, _ := svc.EmbyRemote.ResolveMount(c.Request.Context(), mountID); mount != nil && acct != nil {
|
|
||||||
if rm, err := svc.EmbyRemote.RemoteMediaDetail(c.Request.Context(), mount, acct, remoteID); err == nil && rm != nil {
|
|
||||||
if mediaVisibleForRequest(c, svc, rm) {
|
|
||||||
out = append(out, gin.H{
|
|
||||||
"history": r,
|
|
||||||
"media": *rm,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fallback := fallbackHistoryMedia(r.MediaID)
|
|
||||||
if fallback != nil {
|
|
||||||
out = append(out, gin.H{
|
|
||||||
"history": r,
|
|
||||||
"media": *fallback,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out = append(out, gin.H{
|
out = append(out, gin.H{
|
||||||
"history": r,
|
"history": r,
|
||||||
"media": m,
|
"media": m,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, out)
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if svc.EmbyRemote != nil && service.IsEmbyRemoteID(r.MediaID) {
|
||||||
|
mountID, remoteID, _ := service.DecodeEmbyRemoteID(r.MediaID)
|
||||||
|
mount, acct, resolveErr := svc.EmbyRemote.ResolveMount(c.Request.Context(), mountID)
|
||||||
|
if resolveErr == nil && mount != nil && acct != nil {
|
||||||
|
remoteMedia, detailErr := svc.EmbyRemote.RemoteMediaDetail(c.Request.Context(), mount, acct, remoteID)
|
||||||
|
if detailErr == nil && remoteMedia != nil {
|
||||||
|
if mediaVisibleForRequest(c, svc, remoteMedia) {
|
||||||
|
out = append(out, gin.H{
|
||||||
|
"history": r,
|
||||||
|
"media": *remoteMedia,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fallbackHistoryMedia(mediaID string) *model.Media {
|
// 媒体记录已不存在。继续返回占位卡只会让用户点击后遇到 404,
|
||||||
if mediaID == "" {
|
// 因此清理这条失效播放记录,不再占用继续观看列表。
|
||||||
return nil
|
staleIDs = append(staleIDs, r.MediaID)
|
||||||
}
|
}
|
||||||
title := "媒体"
|
if len(staleIDs) > 0 {
|
||||||
if service.IsEmbyRemoteID(mediaID) {
|
_ = svc.Repo.DB.WithContext(c.Request.Context()).Unscoped().
|
||||||
title = "远程媒体"
|
Where("user_id = ? AND media_id IN ?", userID, staleIDs).
|
||||||
|
Delete(&model.PlaybackHistory{}).Error
|
||||||
}
|
}
|
||||||
return &model.Media{
|
c.JSON(http.StatusOK, out)
|
||||||
Base: model.Base{ID: mediaID},
|
|
||||||
Title: title,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/glebarez/sqlite"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"github.com/truewhile/MeBox/internal/middleware"
|
||||||
|
"github.com/truewhile/MeBox/internal/model"
|
||||||
|
"github.com/truewhile/MeBox/internal/repository"
|
||||||
|
"github.com/truewhile/MeBox/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHistoryContinueRemovesMissingMedia(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := db.AutoMigrate(&model.PlaybackHistory{}, &model.Media{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
valid := model.Media{
|
||||||
|
Base: model.Base{ID: "media-valid"},
|
||||||
|
Title: "有效影片",
|
||||||
|
Path: "/media/valid.mkv",
|
||||||
|
}
|
||||||
|
if err := db.Create(&valid).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
if err := db.Create(&model.PlaybackHistory{
|
||||||
|
Base: model.Base{ID: "history-valid"},
|
||||||
|
UserID: "user-1",
|
||||||
|
MediaID: valid.ID,
|
||||||
|
PositionMs: 30_000,
|
||||||
|
DurationMs: 120_000,
|
||||||
|
WatchedAt: now.Add(-time.Minute),
|
||||||
|
}).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := db.Create(&model.PlaybackHistory{
|
||||||
|
Base: model.Base{ID: "history-stale"},
|
||||||
|
UserID: "user-1",
|
||||||
|
MediaID: "media-deleted",
|
||||||
|
PositionMs: 60_000,
|
||||||
|
DurationMs: 120_000,
|
||||||
|
WatchedAt: now,
|
||||||
|
}).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
repos := repository.New(db)
|
||||||
|
svc := &service.Container{
|
||||||
|
Log: zap.NewNop(),
|
||||||
|
Repo: repos,
|
||||||
|
Playback: service.NewPlaybackService(zap.NewNop(), repos),
|
||||||
|
}
|
||||||
|
router := gin.New()
|
||||||
|
router.Use(func(c *gin.Context) {
|
||||||
|
c.Set(middleware.CtxUserID, "user-1")
|
||||||
|
c.Next()
|
||||||
|
})
|
||||||
|
router.GET("/watch-history/continue", historyContinueHandler(svc))
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/watch-history/continue?limit=10", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
router.ServeHTTP(rec, req)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("status = %d, body = %s", rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var body []struct {
|
||||||
|
History model.PlaybackHistory `json:"history"`
|
||||||
|
Media model.Media `json:"media"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(body) != 1 {
|
||||||
|
t.Fatalf("continue watching rows = %d, want 1: %s", len(body), rec.Body.String())
|
||||||
|
}
|
||||||
|
if body[0].Media.ID != valid.ID || body[0].History.ID != "history-valid" {
|
||||||
|
t.Fatalf("unexpected row: %#v", body[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
var staleCount int64
|
||||||
|
if err := db.Unscoped().Model(&model.PlaybackHistory{}).
|
||||||
|
Where("user_id = ? AND media_id = ?", "user-1", "media-deleted").
|
||||||
|
Count(&staleCount).Error; err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if staleCount != 0 {
|
||||||
|
t.Fatalf("stale history rows remaining = %d, want 0", staleCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user