mirror of
https://github.com/truewhile/MeBox.git
synced 2026-11-15 20:34:23 +08:00
优化
This commit is contained in:
@@ -30,10 +30,12 @@ import (
|
||||
//
|
||||
// include_media=0 只返回历史行本身。客户端判断「继续播放」指向哪一集时只
|
||||
// 需要 media_id/position_ms;带上整条 media 会让 300 条的响应从 ~40KB 涨到
|
||||
// ~420KB,手机链路上光传输就要十几秒,详情页的续播解析会被它拖住。
|
||||
// ~420KB。更重要的是服务端不再 hydrate:不查整条 media、也不为挂载条目
|
||||
// 逐条请求上游 Emby(见 PlaybackService.RecentHistoryWithoutMedia)。
|
||||
func historyListHandler(svc *service.Container) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid, _ := c.Get(middleware.CtxUserID)
|
||||
userID := toString(uid)
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
||||
if limit <= 0 || limit > 500 {
|
||||
limit = 50
|
||||
@@ -44,19 +46,27 @@ func historyListHandler(svc *service.Container) gin.HandlerFunc {
|
||||
includeMedia = parsed
|
||||
}
|
||||
}
|
||||
items, err := svc.Playback.RecentHistory(c.Request.Context(), toString(uid), limit)
|
||||
ctx := c.Request.Context()
|
||||
visibility := mediaVisibilityForRequest(c, svc)
|
||||
if !includeMedia {
|
||||
// 可见性由服务层用窄列查询判定,handler 不再需要 media。
|
||||
items, err := svc.Playback.RecentHistoryWithoutMedia(ctx, userID, limit, visibility)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, items)
|
||||
return
|
||||
}
|
||||
items, err := svc.Playback.RecentHistory(ctx, userID, limit)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
visibility := mediaVisibilityForRequest(c, svc)
|
||||
filtered := make([]service.HistoryItem, 0, len(items))
|
||||
for _, item := range items {
|
||||
// 可见性仍然按 media 判断,只是决定要不要把它写进响应。
|
||||
// 可见性按 media 判断:媒体已删除/远端取不到时仍然保留这条历史。
|
||||
if item.Media == nil || visibility.Allows(item.Media) {
|
||||
if !includeMedia {
|
||||
item.Media = nil
|
||||
}
|
||||
filtered = append(filtered, item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,3 +175,77 @@ func TestHistoryListCanOmitEmbeddedMedia(t *testing.T) {
|
||||
t.Fatalf("include_media=0 must not embed media: %s", lean.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// TestHistoryListIncludeMediaFalseSkipsHydration 保证 include_media=0 时服务端
|
||||
// 真的不 hydrate media:响应里只有历史行(客户端按 media_id 匹配续播目标)。
|
||||
func TestHistoryListIncludeMediaFalseSkipsHydration(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)
|
||||
}
|
||||
if err := db.Create(&model.Media{
|
||||
Base: model.Base{ID: "media-1"},
|
||||
Title: "影片",
|
||||
Path: "/media/1.mkv",
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
now := time.Now()
|
||||
for i, mediaID := range []string{"media-1", "media-deleted"} {
|
||||
if err := db.Create(&model.PlaybackHistory{
|
||||
Base: model.Base{ID: "history-" + mediaID},
|
||||
UserID: "user-1",
|
||||
MediaID: mediaID,
|
||||
PositionMs: 30_000,
|
||||
DurationMs: 120_000,
|
||||
WatchedAt: now.Add(-time.Duration(i) * time.Minute),
|
||||
}).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", historyListHandler(svc))
|
||||
|
||||
for _, path := range []string{
|
||||
"/watch-history?limit=10&include_media=0",
|
||||
"/watch-history?limit=10&include_media=false",
|
||||
} {
|
||||
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("%s status = %d, body = %s", path, rec.Code, rec.Body.String())
|
||||
}
|
||||
var rows []map[string]any
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &rows); err != nil {
|
||||
t.Fatalf("%s decode: %v", path, err)
|
||||
}
|
||||
// 媒体已删除的历史也保留:客户端只按 media_id 判断有没有记录。
|
||||
if len(rows) != 2 {
|
||||
t.Fatalf("%s rows = %d, want 2 (%s)", path, len(rows), rec.Body.String())
|
||||
}
|
||||
for _, row := range rows {
|
||||
if _, ok := row["media"]; ok {
|
||||
t.Fatalf("%s should not embed media: %v", path, row)
|
||||
}
|
||||
if row["media_id"] == "" || row["media_id"] == nil {
|
||||
t.Fatalf("%s missing media_id: %v", path, row)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user