mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-01 12:06:38 +08:00
67b85840dc
* Add favourite buttons to library list and series detail pages Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix favourites list for remote Emby mounted media Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Route favourites to library series detail when appropriate Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix favourites link to library series for remote anime paths Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix remote Emby playback history and continue watching - Preserve duration_ms when web player reports duration=0 - Add GET /playback/:id/resume for reliable per-item resume - Switch home continue watching to /watch-history/continue API - Set library_id on remote media for play-profile visibility - Keep continue-watching rows when remote metadata hydration fails Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix mobile sort dropdown overflowing off-screen Align dropdown to button left edge on small screens and cap width to viewport so sort options stay fully visible on mobile. Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Remove stale Windows binary backup files Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix STRM account edit form not echoing saved config Return config_preview from account API for server/url/username and secret flags. Merge partial config updates so empty password/token fields do not wipe stored credentials. Co-authored-by: truewhile <truewhile@users.noreply.github.com> * perf(strm): speed up OpenList metadata downloads with provider-aware concurrency - Split download semaphore: 115 stays capped at 3 for WAF safety; OpenList/CloudDrive2 WebDAV metadata uses strm.download_threads (default raised to 6, max 16) - Scope 115 WAF cooldown to 115 tasks only so OpenList downloads are not paused - Requeue claimed tasks back to pending when slot acquisition fails or 115 is cooling - Add tests for separate semaphores and requeue behavior 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>
94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ShukeBta/MMTL/internal/model"
|
|
"github.com/ShukeBta/MMTL/internal/repository"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestRecordProgressPreservesDurationWhenMissing(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.PlaybackHistory{}, &model.Media{})
|
|
repos := repository.New(db)
|
|
userID := "user-1"
|
|
mediaID := "local-movie-1"
|
|
if err := db.Create(&model.PlaybackHistory{
|
|
Base: model.Base{ID: "hist-1"},
|
|
UserID: userID,
|
|
MediaID: mediaID,
|
|
PositionMs: 30_000,
|
|
DurationMs: 120_000,
|
|
WatchedAt: time.Now(),
|
|
}).Error; err != nil {
|
|
t.Fatalf("seed history: %v", err)
|
|
}
|
|
|
|
svc := NewPlaybackService(zap.NewNop(), repos)
|
|
if err := svc.RecordProgress(context.Background(), userID, mediaID, 60_000, 0); err != nil {
|
|
t.Fatalf("RecordProgress: %v", err)
|
|
}
|
|
|
|
var hist model.PlaybackHistory
|
|
if err := db.Where("user_id = ? AND media_id = ?", userID, mediaID).First(&hist).Error; err != nil {
|
|
t.Fatalf("find history: %v", err)
|
|
}
|
|
if hist.DurationMs != 120_000 {
|
|
t.Fatalf("expected duration preserved, got %d", hist.DurationMs)
|
|
}
|
|
if hist.PositionMs != 60_000 {
|
|
t.Fatalf("expected position updated, got %d", hist.PositionMs)
|
|
}
|
|
}
|
|
|
|
func TestGetProgressReturnsNilWhenMissing(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.PlaybackHistory{})
|
|
repos := repository.New(db)
|
|
svc := NewPlaybackService(zap.NewNop(), repos)
|
|
|
|
row, err := svc.GetProgress(context.Background(), "user-1", "missing-media")
|
|
if err != nil {
|
|
t.Fatalf("GetProgress: %v", err)
|
|
}
|
|
if row != nil {
|
|
t.Fatalf("expected nil progress, got %#v", row)
|
|
}
|
|
}
|
|
|
|
func TestListFavouritesIncludesLocalAndRemoteIDs(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.Favorite{}, &model.Media{})
|
|
repos := repository.New(db)
|
|
userID := "user-1"
|
|
local := model.Media{
|
|
Base: model.Base{ID: "local-movie-1"},
|
|
Title: "Local Movie",
|
|
}
|
|
if err := db.Create(&local).Error; err != nil {
|
|
t.Fatalf("create local media: %v", err)
|
|
}
|
|
remoteID := EncodeEmbyRemoteID("mount-1", "remote-series-1")
|
|
favs := []model.Favorite{
|
|
{Base: model.Base{CreatedAt: time.Now().Add(-time.Minute)}, UserID: userID, MediaID: remoteID},
|
|
{Base: model.Base{CreatedAt: time.Now()}, UserID: userID, MediaID: local.ID},
|
|
}
|
|
for i := range favs {
|
|
if err := db.Create(&favs[i]).Error; err != nil {
|
|
t.Fatalf("create favorite %d: %v", i, err)
|
|
}
|
|
}
|
|
|
|
svc := NewPlaybackService(zap.NewNop(), repos)
|
|
items, err := svc.ListFavourites(context.Background(), userID)
|
|
if err != nil {
|
|
t.Fatalf("ListFavourites: %v", err)
|
|
}
|
|
if len(items) != 1 {
|
|
t.Fatalf("expected one hydrated local favourite without remote service, got %d: %#v", len(items), items)
|
|
}
|
|
if items[0].ID != local.ID {
|
|
t.Fatalf("expected local favourite first by created_at desc, got %#v", items[0])
|
|
}
|
|
}
|