node build fixed
This commit is contained in:
418
seanime-2.9.10/internal/continuity/history.go
Normal file
418
seanime-2.9.10/internal/continuity/history.go
Normal file
@@ -0,0 +1,418 @@
|
||||
package continuity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"seanime/internal/database/db_bridge"
|
||||
"seanime/internal/hook"
|
||||
"seanime/internal/library/anime"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxWatchHistoryItems = 100
|
||||
IgnoreRatioThreshold = 0.9
|
||||
WatchHistoryBucketName = "watch_history"
|
||||
)
|
||||
|
||||
type (
|
||||
// WatchHistory is a map of WatchHistoryItem.
|
||||
// The key is the WatchHistoryItem.MediaId.
|
||||
WatchHistory map[int]*WatchHistoryItem
|
||||
|
||||
// WatchHistoryItem are stored in the file cache.
|
||||
// The history is used to resume playback from the last known position.
|
||||
// Item.MediaId and Item.EpisodeNumber are used to identify the media and episode.
|
||||
// Only one Item per MediaId should exist in the history.
|
||||
WatchHistoryItem struct {
|
||||
Kind Kind `json:"kind"`
|
||||
// Used for MediastreamKind and ExternalPlayerKind.
|
||||
Filepath string `json:"filepath"`
|
||||
MediaId int `json:"mediaId"`
|
||||
EpisodeNumber int `json:"episodeNumber"`
|
||||
// The current playback time in seconds.
|
||||
// Used to determine when to remove the item from the history.
|
||||
CurrentTime float64 `json:"currentTime"`
|
||||
// The duration of the media in seconds.
|
||||
Duration float64 `json:"duration"`
|
||||
// Timestamp of when the item was added to the history.
|
||||
TimeAdded time.Time `json:"timeAdded"`
|
||||
// TimeAdded is used in conjunction with TimeUpdated
|
||||
// Timestamp of when the item was last updated.
|
||||
// Used to determine when to remove the item from the history (First in, first out).
|
||||
TimeUpdated time.Time `json:"timeUpdated"`
|
||||
}
|
||||
|
||||
WatchHistoryItemResponse struct {
|
||||
Item *WatchHistoryItem `json:"item"`
|
||||
Found bool `json:"found"`
|
||||
}
|
||||
|
||||
UpdateWatchHistoryItemOptions struct {
|
||||
CurrentTime float64 `json:"currentTime"`
|
||||
Duration float64 `json:"duration"`
|
||||
MediaId int `json:"mediaId"`
|
||||
EpisodeNumber int `json:"episodeNumber"`
|
||||
Filepath string `json:"filepath,omitempty"`
|
||||
Kind Kind `json:"kind"`
|
||||
}
|
||||
)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (m *Manager) GetWatchHistory() WatchHistory {
|
||||
defer util.HandlePanicInModuleThen("continuity/GetWatchHistory", func() {})
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
items, err := filecache.GetAll[*WatchHistoryItem](m.fileCacher, *m.watchHistoryFileCacheBucket)
|
||||
if err != nil {
|
||||
m.logger.Error().Err(err).Msg("continuity: Failed to get watch history")
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make(WatchHistory)
|
||||
for _, item := range items {
|
||||
ret[item.MediaId] = item
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *Manager) GetWatchHistoryItem(mediaId int) *WatchHistoryItemResponse {
|
||||
defer util.HandlePanicInModuleThen("continuity/GetWatchHistoryItem", func() {})
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
i, found := m.getWatchHistory(mediaId)
|
||||
return &WatchHistoryItemResponse{
|
||||
Item: i,
|
||||
Found: found,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateWatchHistoryItem updates the WatchHistoryItem in the file cache.
|
||||
func (m *Manager) UpdateWatchHistoryItem(opts *UpdateWatchHistoryItemOptions) (err error) {
|
||||
defer util.HandlePanicInModuleWithError("continuity/UpdateWatchHistoryItem", &err)
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
added := false
|
||||
|
||||
// Get the current history
|
||||
i, found := m.getWatchHistory(opts.MediaId)
|
||||
if !found {
|
||||
added = true
|
||||
i = &WatchHistoryItem{
|
||||
Kind: opts.Kind,
|
||||
Filepath: opts.Filepath,
|
||||
MediaId: opts.MediaId,
|
||||
EpisodeNumber: opts.EpisodeNumber,
|
||||
CurrentTime: opts.CurrentTime,
|
||||
Duration: opts.Duration,
|
||||
TimeAdded: time.Now(),
|
||||
TimeUpdated: time.Now(),
|
||||
}
|
||||
} else {
|
||||
i.Kind = opts.Kind
|
||||
i.EpisodeNumber = opts.EpisodeNumber
|
||||
i.CurrentTime = opts.CurrentTime
|
||||
i.Duration = opts.Duration
|
||||
i.TimeUpdated = time.Now()
|
||||
}
|
||||
|
||||
// Save the i
|
||||
err = m.fileCacher.Set(*m.watchHistoryFileCacheBucket, strconv.Itoa(opts.MediaId), i)
|
||||
if err != nil {
|
||||
return fmt.Errorf("continuity: Failed to save watch history item: %w", err)
|
||||
}
|
||||
|
||||
_ = hook.GlobalHookManager.OnWatchHistoryItemUpdated().Trigger(&WatchHistoryItemUpdatedEvent{
|
||||
WatchHistoryItem: i,
|
||||
})
|
||||
|
||||
// If the item was added, check if we need to remove the oldest item
|
||||
if added {
|
||||
_ = m.trimWatchHistoryItems()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) DeleteWatchHistoryItem(mediaId int) (err error) {
|
||||
defer util.HandlePanicInModuleWithError("continuity/DeleteWatchHistoryItem", &err)
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
err = m.fileCacher.Delete(*m.watchHistoryFileCacheBucket, strconv.Itoa(mediaId))
|
||||
if err != nil {
|
||||
return fmt.Errorf("continuity: Failed to delete watch history item: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GetExternalPlayerEpisodeWatchHistoryItem is called before launching the external player to get the last known position.
|
||||
// Unlike GetWatchHistoryItem, this checks if the episode numbers match.
|
||||
func (m *Manager) GetExternalPlayerEpisodeWatchHistoryItem(path string, isStream bool, episode, mediaId int) (ret *WatchHistoryItemResponse) {
|
||||
defer util.HandlePanicInModuleThen("continuity/GetExternalPlayerEpisodeWatchHistoryItem", func() {})
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if !m.settings.WatchContinuityEnabled {
|
||||
return &WatchHistoryItemResponse{
|
||||
Item: nil,
|
||||
Found: false,
|
||||
}
|
||||
}
|
||||
|
||||
ret = &WatchHistoryItemResponse{
|
||||
Item: nil,
|
||||
Found: false,
|
||||
}
|
||||
|
||||
m.logger.Debug().
|
||||
Str("path", path).
|
||||
Bool("isStream", isStream).
|
||||
Int("episode", episode).
|
||||
Int("mediaId", mediaId).
|
||||
Msg("continuity: Retrieving watch history item")
|
||||
|
||||
// Normalize path
|
||||
path = util.NormalizePath(path)
|
||||
|
||||
if isStream {
|
||||
|
||||
event := &WatchHistoryStreamEpisodeItemRequestedEvent{
|
||||
WatchHistoryItem: &WatchHistoryItem{},
|
||||
}
|
||||
|
||||
hook.GlobalHookManager.OnWatchHistoryStreamEpisodeItemRequested().Trigger(event)
|
||||
if event.DefaultPrevented {
|
||||
return &WatchHistoryItemResponse{
|
||||
Item: event.WatchHistoryItem,
|
||||
Found: event.WatchHistoryItem != nil,
|
||||
}
|
||||
}
|
||||
|
||||
if episode == 0 || mediaId == 0 {
|
||||
m.logger.Debug().
|
||||
Int("episode", episode).
|
||||
Int("mediaId", mediaId).
|
||||
Msg("continuity: No episode or media provided")
|
||||
return
|
||||
}
|
||||
|
||||
i, found := m.getWatchHistory(mediaId)
|
||||
if !found || i.EpisodeNumber != episode {
|
||||
m.logger.Trace().
|
||||
Interface("item", i).
|
||||
Msg("continuity: No watch history item found or episode number does not match")
|
||||
return
|
||||
}
|
||||
|
||||
m.logger.Debug().
|
||||
Interface("item", i).
|
||||
Msg("continuity: Watch history item found")
|
||||
|
||||
return &WatchHistoryItemResponse{
|
||||
Item: i,
|
||||
Found: found,
|
||||
}
|
||||
|
||||
} else {
|
||||
// Find the local file from the path
|
||||
lfs, _, err := db_bridge.GetLocalFiles(m.db)
|
||||
if err != nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
event := &WatchHistoryLocalFileEpisodeItemRequestedEvent{
|
||||
Path: path,
|
||||
LocalFiles: lfs,
|
||||
WatchHistoryItem: &WatchHistoryItem{},
|
||||
}
|
||||
hook.GlobalHookManager.OnWatchHistoryLocalFileEpisodeItemRequested().Trigger(event)
|
||||
if event.DefaultPrevented {
|
||||
return &WatchHistoryItemResponse{
|
||||
Item: event.WatchHistoryItem,
|
||||
Found: event.WatchHistoryItem != nil,
|
||||
}
|
||||
}
|
||||
|
||||
var lf *anime.LocalFile
|
||||
// Find the local file from the path
|
||||
for _, l := range lfs {
|
||||
if l.GetNormalizedPath() == path {
|
||||
lf = l
|
||||
m.logger.Trace().Msg("continuity: Local file found from path")
|
||||
break
|
||||
}
|
||||
}
|
||||
// If the local file is not found, the path might be a filename (in the case of VLC)
|
||||
if lf == nil {
|
||||
for _, l := range lfs {
|
||||
if strings.ToLower(l.Name) == path {
|
||||
lf = l
|
||||
m.logger.Trace().Msg("continuity: Local file found from filename")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if lf == nil || lf.MediaId == 0 || !lf.IsMain() {
|
||||
m.logger.Trace().Msg("continuity: Local file not found or not main")
|
||||
return
|
||||
}
|
||||
|
||||
i, found := m.getWatchHistory(lf.MediaId)
|
||||
if !found || i.EpisodeNumber != lf.GetEpisodeNumber() {
|
||||
m.logger.Trace().
|
||||
Interface("item", i).
|
||||
Msg("continuity: No watch history item found or episode number does not match")
|
||||
return
|
||||
}
|
||||
|
||||
m.logger.Debug().
|
||||
Interface("item", i).
|
||||
Msg("continuity: Watch history item found")
|
||||
|
||||
return &WatchHistoryItemResponse{
|
||||
Item: i,
|
||||
Found: found,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) UpdateExternalPlayerEpisodeWatchHistoryItem(currentTime, duration float64) {
|
||||
defer util.HandlePanicInModuleThen("continuity/UpdateWatchHistoryItem", func() {})
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if !m.settings.WatchContinuityEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
if m.externalPlayerEpisodeDetails.IsAbsent() {
|
||||
return
|
||||
}
|
||||
|
||||
added := false
|
||||
|
||||
opts, ok := m.externalPlayerEpisodeDetails.Get()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the current history
|
||||
i, found := m.getWatchHistory(opts.MediaId)
|
||||
if !found {
|
||||
added = true
|
||||
i = &WatchHistoryItem{
|
||||
Kind: ExternalPlayerKind,
|
||||
Filepath: opts.Filepath,
|
||||
MediaId: opts.MediaId,
|
||||
EpisodeNumber: opts.EpisodeNumber,
|
||||
CurrentTime: currentTime,
|
||||
Duration: duration,
|
||||
TimeAdded: time.Now(),
|
||||
TimeUpdated: time.Now(),
|
||||
}
|
||||
} else {
|
||||
i.Kind = ExternalPlayerKind
|
||||
i.EpisodeNumber = opts.EpisodeNumber
|
||||
i.CurrentTime = currentTime
|
||||
i.Duration = duration
|
||||
i.TimeUpdated = time.Now()
|
||||
}
|
||||
|
||||
// Save the i
|
||||
_ = m.fileCacher.Set(*m.watchHistoryFileCacheBucket, strconv.Itoa(opts.MediaId), i)
|
||||
|
||||
// If the item was added, check if we need to remove the oldest item
|
||||
if added {
|
||||
_ = m.trimWatchHistoryItems()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (m *Manager) getWatchHistory(mediaId int) (ret *WatchHistoryItem, exists bool) {
|
||||
defer util.HandlePanicInModuleThen("continuity/getWatchHistory", func() {
|
||||
ret = nil
|
||||
exists = false
|
||||
})
|
||||
|
||||
reqEvent := &WatchHistoryItemRequestedEvent{
|
||||
MediaId: mediaId,
|
||||
WatchHistoryItem: ret,
|
||||
}
|
||||
hook.GlobalHookManager.OnWatchHistoryItemRequested().Trigger(reqEvent)
|
||||
ret = reqEvent.WatchHistoryItem
|
||||
|
||||
if reqEvent.DefaultPrevented {
|
||||
return reqEvent.WatchHistoryItem, reqEvent.WatchHistoryItem != nil
|
||||
}
|
||||
|
||||
exists, _ = m.fileCacher.Get(*m.watchHistoryFileCacheBucket, strconv.Itoa(mediaId), &ret)
|
||||
|
||||
if exists && ret != nil && ret.Duration > 0 {
|
||||
// If the item completion ratio is equal or above IgnoreRatioThreshold, don't return anything
|
||||
ratio := ret.CurrentTime / ret.Duration
|
||||
if ratio >= IgnoreRatioThreshold {
|
||||
// Delete the item
|
||||
go func() {
|
||||
defer util.HandlePanicInModuleThen("continuity/getWatchHistory", func() {})
|
||||
_ = m.fileCacher.Delete(*m.watchHistoryFileCacheBucket, strconv.Itoa(mediaId))
|
||||
}()
|
||||
return nil, false
|
||||
}
|
||||
if ratio < 0.05 {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// removes the oldest WatchHistoryItem from the file cache.
|
||||
func (m *Manager) trimWatchHistoryItems() error {
|
||||
defer util.HandlePanicInModuleThen("continuity/TrimWatchHistoryItems", func() {})
|
||||
|
||||
// Get all the items
|
||||
items, err := filecache.GetAll[*WatchHistoryItem](m.fileCacher, *m.watchHistoryFileCacheBucket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("continuity: Failed to get watch history items: %w", err)
|
||||
}
|
||||
|
||||
// If there are too many items, remove the oldest one
|
||||
if len(items) > MaxWatchHistoryItems {
|
||||
var oldestKey string
|
||||
for key := range items {
|
||||
if oldestKey == "" || items[key].TimeUpdated.Before(items[oldestKey].TimeUpdated) {
|
||||
oldestKey = key
|
||||
}
|
||||
}
|
||||
err = m.fileCacher.Delete(*m.watchHistoryFileCacheBucket, oldestKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("continuity: Failed to remove oldest watch history item: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
79
seanime-2.9.10/internal/continuity/history_test.go
Normal file
79
seanime-2.9.10/internal/continuity/history_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package continuity
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"path/filepath"
|
||||
"seanime/internal/database/db"
|
||||
"seanime/internal/test_utils"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHistoryItems(t *testing.T) {
|
||||
test_utils.SetTwoLevelDeep()
|
||||
test_utils.InitTestProvider(t)
|
||||
|
||||
logger := util.NewLogger()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
t.Log(tempDir)
|
||||
|
||||
database, err := db.NewDatabase(test_utils.ConfigData.Path.DataDir, test_utils.ConfigData.Database.Name, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
cacher, err := filecache.NewCacher(filepath.Join(tempDir, "cache"))
|
||||
require.NoError(t, err)
|
||||
|
||||
manager := NewManager(&NewManagerOptions{
|
||||
FileCacher: cacher,
|
||||
Logger: logger,
|
||||
Database: database,
|
||||
})
|
||||
require.NotNil(t, manager)
|
||||
|
||||
var mediaIds = make([]int, MaxWatchHistoryItems+1)
|
||||
for i := 0; i < MaxWatchHistoryItems+1; i++ {
|
||||
mediaIds[i] = i + 1
|
||||
}
|
||||
|
||||
// Add items to the history
|
||||
for _, mediaId := range mediaIds {
|
||||
err = manager.UpdateWatchHistoryItem(&UpdateWatchHistoryItemOptions{
|
||||
MediaId: mediaId,
|
||||
EpisodeNumber: 1,
|
||||
CurrentTime: 10,
|
||||
Duration: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Check if the oldest item was removed
|
||||
items, err := filecache.GetAll[WatchHistoryItem](cacher, *manager.watchHistoryFileCacheBucket)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, items, MaxWatchHistoryItems)
|
||||
|
||||
// Update an item
|
||||
err = manager.UpdateWatchHistoryItem(&UpdateWatchHistoryItemOptions{
|
||||
MediaId: mediaIds[0], // 1
|
||||
EpisodeNumber: 2,
|
||||
CurrentTime: 30,
|
||||
Duration: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check if the item was updated
|
||||
items, err = filecache.GetAll[WatchHistoryItem](cacher, *manager.watchHistoryFileCacheBucket)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, items, MaxWatchHistoryItems)
|
||||
|
||||
item, found := items["1"]
|
||||
require.True(t, found)
|
||||
|
||||
require.Equal(t, 2, item.EpisodeNumber)
|
||||
require.Equal(t, 30., item.CurrentTime)
|
||||
require.Equal(t, 100., item.Duration)
|
||||
|
||||
}
|
||||
38
seanime-2.9.10/internal/continuity/hook_events.go
Normal file
38
seanime-2.9.10/internal/continuity/hook_events.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package continuity
|
||||
|
||||
import (
|
||||
"seanime/internal/hook_resolver"
|
||||
"seanime/internal/library/anime"
|
||||
)
|
||||
|
||||
// WatchHistoryItemRequestedEvent is triggered when a watch history item is requested.
|
||||
// Prevent default to skip getting the watch history item from the file cache, in this case the event should have a valid WatchHistoryItem object or set it to nil to indicate that the watch history item was not found.
|
||||
type WatchHistoryItemRequestedEvent struct {
|
||||
hook_resolver.Event
|
||||
MediaId int `json:"mediaId"`
|
||||
// Empty WatchHistoryItem object, will be used if the hook prevents the default behavior
|
||||
WatchHistoryItem *WatchHistoryItem `json:"watchHistoryItem"`
|
||||
}
|
||||
|
||||
// WatchHistoryItemUpdatedEvent is triggered when a watch history item is updated.
|
||||
type WatchHistoryItemUpdatedEvent struct {
|
||||
hook_resolver.Event
|
||||
WatchHistoryItem *WatchHistoryItem `json:"watchHistoryItem"`
|
||||
}
|
||||
|
||||
type WatchHistoryLocalFileEpisodeItemRequestedEvent struct {
|
||||
hook_resolver.Event
|
||||
Path string
|
||||
// All scanned local files
|
||||
LocalFiles []*anime.LocalFile
|
||||
// Empty WatchHistoryItem object, will be used if the hook prevents the default behavior
|
||||
WatchHistoryItem *WatchHistoryItem `json:"watchHistoryItem"`
|
||||
}
|
||||
|
||||
type WatchHistoryStreamEpisodeItemRequestedEvent struct {
|
||||
hook_resolver.Event
|
||||
Episode int
|
||||
MediaId int
|
||||
// Empty WatchHistoryItem object, will be used if the hook prevents the default behavior
|
||||
WatchHistoryItem *WatchHistoryItem `json:"watchHistoryItem"`
|
||||
}
|
||||
106
seanime-2.9.10/internal/continuity/manager.go
Normal file
106
seanime-2.9.10/internal/continuity/manager.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package continuity
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/samber/mo"
|
||||
"seanime/internal/database/db"
|
||||
"seanime/internal/util/filecache"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
OnlinestreamKind Kind = "onlinestream"
|
||||
MediastreamKind Kind = "mediastream"
|
||||
ExternalPlayerKind Kind = "external_player"
|
||||
)
|
||||
|
||||
type (
|
||||
// Manager is used to manage the user's viewing history across different media types.
|
||||
Manager struct {
|
||||
fileCacher *filecache.Cacher
|
||||
db *db.Database
|
||||
watchHistoryFileCacheBucket *filecache.Bucket
|
||||
|
||||
externalPlayerEpisodeDetails mo.Option[*ExternalPlayerEpisodeDetails]
|
||||
|
||||
logger *zerolog.Logger
|
||||
settings *Settings
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// ExternalPlayerEpisodeDetails is used to store the episode details when using an external player.
|
||||
// Since the media player module only cares about the filepath, the PlaybackManager will store the episode number and media id here when playback starts.
|
||||
ExternalPlayerEpisodeDetails struct {
|
||||
EpisodeNumber int `json:"episodeNumber"`
|
||||
MediaId int `json:"mediaId"`
|
||||
Filepath string `json:"filepath"`
|
||||
}
|
||||
|
||||
Settings struct {
|
||||
WatchContinuityEnabled bool
|
||||
}
|
||||
|
||||
Kind string
|
||||
)
|
||||
|
||||
type (
|
||||
NewManagerOptions struct {
|
||||
FileCacher *filecache.Cacher
|
||||
Logger *zerolog.Logger
|
||||
Database *db.Database
|
||||
}
|
||||
)
|
||||
|
||||
// NewManager creates a new Manager, it should be initialized once.
|
||||
func NewManager(opts *NewManagerOptions) *Manager {
|
||||
watchHistoryFileCacheBucket := filecache.NewBucket(WatchHistoryBucketName, time.Hour*24*99999)
|
||||
|
||||
ret := &Manager{
|
||||
fileCacher: opts.FileCacher,
|
||||
logger: opts.Logger,
|
||||
db: opts.Database,
|
||||
watchHistoryFileCacheBucket: &watchHistoryFileCacheBucket,
|
||||
settings: &Settings{
|
||||
WatchContinuityEnabled: false,
|
||||
},
|
||||
externalPlayerEpisodeDetails: mo.None[*ExternalPlayerEpisodeDetails](),
|
||||
}
|
||||
|
||||
ret.logger.Info().Msg("continuity: Initialized manager")
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// SetSettings should be called after initializing the Manager.
|
||||
func (m *Manager) SetSettings(settings *Settings) {
|
||||
if m == nil || settings == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.settings = settings
|
||||
}
|
||||
|
||||
// GetSettings returns the current settings.
|
||||
func (m *Manager) GetSettings() *Settings {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.settings
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (m *Manager) SetExternalPlayerEpisodeDetails(details *ExternalPlayerEpisodeDetails) {
|
||||
if m == nil || details == nil {
|
||||
return
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.externalPlayerEpisodeDetails = mo.Some(details)
|
||||
}
|
||||
24
seanime-2.9.10/internal/continuity/mock.go
Normal file
24
seanime-2.9.10/internal/continuity/mock.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package continuity
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"path/filepath"
|
||||
"seanime/internal/database/db"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func GetMockManager(t *testing.T, db *db.Database) *Manager {
|
||||
logger := util.NewLogger()
|
||||
cacher, err := filecache.NewCacher(filepath.Join(t.TempDir(), "cache"))
|
||||
require.NoError(t, err)
|
||||
|
||||
manager := NewManager(&NewManagerOptions{
|
||||
FileCacher: cacher,
|
||||
Logger: logger,
|
||||
Database: db,
|
||||
})
|
||||
|
||||
return manager
|
||||
}
|
||||
Reference in New Issue
Block a user