Files
seanime-docker/seanime-2.9.10/internal/library/anime/localfile_wrapper.go
2025-09-20 14:08:38 +01:00

208 lines
5.1 KiB
Go

package anime
import (
"cmp"
"slices"
)
type (
// LocalFileWrapper takes a slice of LocalFiles and provides helper methods.
LocalFileWrapper struct {
LocalFiles []*LocalFile `json:"localFiles"`
LocalEntries []*LocalFileWrapperEntry `json:"localEntries"`
UnmatchedLocalFiles []*LocalFile `json:"unmatchedLocalFiles"`
}
LocalFileWrapperEntry struct {
MediaId int `json:"mediaId"`
LocalFiles []*LocalFile `json:"localFiles"`
}
)
// NewLocalFileWrapper creates and returns a reference to a new LocalFileWrapper
func NewLocalFileWrapper(lfs []*LocalFile) *LocalFileWrapper {
lfw := &LocalFileWrapper{
LocalFiles: lfs,
LocalEntries: make([]*LocalFileWrapperEntry, 0),
UnmatchedLocalFiles: make([]*LocalFile, 0),
}
// Group local files by media id
groupedLfs := GroupLocalFilesByMediaID(lfs)
for mId, gLfs := range groupedLfs {
if mId == 0 {
lfw.UnmatchedLocalFiles = gLfs
continue
}
lfw.LocalEntries = append(lfw.LocalEntries, &LocalFileWrapperEntry{
MediaId: mId,
LocalFiles: gLfs,
})
}
return lfw
}
func (lfw *LocalFileWrapper) GetLocalEntryById(mId int) (*LocalFileWrapperEntry, bool) {
for _, me := range lfw.LocalEntries {
if me.MediaId == mId {
return me, true
}
}
return nil, false
}
// GetMainLocalFiles returns the *main* local files.
func (e *LocalFileWrapperEntry) GetMainLocalFiles() ([]*LocalFile, bool) {
lfs := make([]*LocalFile, 0)
for _, lf := range e.LocalFiles {
if lf.IsMain() {
lfs = append(lfs, lf)
}
}
if len(lfs) == 0 {
return nil, false
}
return lfs, true
}
// GetUnwatchedLocalFiles returns the *main* local files that have not been watched.
// It returns an empty slice if all local files have been watched.
//
// /!\ IF Episode 0 is present, progress will be decremented by 1. This is because we assume AniList includes the episode 0 in the total count.
func (e *LocalFileWrapperEntry) GetUnwatchedLocalFiles(progress int) []*LocalFile {
ret := make([]*LocalFile, 0)
lfs, ok := e.GetMainLocalFiles()
if !ok {
return ret
}
for _, lf := range lfs {
if lf.GetEpisodeNumber() == 0 {
progress = progress - 1
break
}
}
for _, lf := range lfs {
if lf.GetEpisodeNumber() > progress {
ret = append(ret, lf)
}
}
return ret
}
// GetFirstUnwatchedLocalFiles is like GetUnwatchedLocalFiles but returns local file with the lowest episode number.
func (e *LocalFileWrapperEntry) GetFirstUnwatchedLocalFiles(progress int) (*LocalFile, bool) {
lfs := e.GetUnwatchedLocalFiles(progress)
if len(lfs) == 0 {
return nil, false
}
// Sort local files by episode number
slices.SortStableFunc(lfs, func(a, b *LocalFile) int {
return cmp.Compare(a.GetEpisodeNumber(), b.GetEpisodeNumber())
})
return lfs[0], true
}
// HasMainLocalFiles returns true if there are any *main* local files.
func (e *LocalFileWrapperEntry) HasMainLocalFiles() bool {
for _, lf := range e.LocalFiles {
if lf.IsMain() {
return true
}
}
return false
}
// FindLocalFileWithEpisodeNumber returns the *main* local file with the given episode number.
func (e *LocalFileWrapperEntry) FindLocalFileWithEpisodeNumber(ep int) (*LocalFile, bool) {
for _, lf := range e.LocalFiles {
if !lf.IsMain() {
continue
}
if lf.GetEpisodeNumber() == ep {
return lf, true
}
}
return nil, false
}
// FindLatestLocalFile returns the *main* local file with the highest episode number.
func (e *LocalFileWrapperEntry) FindLatestLocalFile() (*LocalFile, bool) {
lfs, ok := e.GetMainLocalFiles()
if !ok {
return nil, false
}
// Get the local file with the highest episode number
latest := lfs[0]
for _, lf := range lfs {
if lf.GetEpisodeNumber() > latest.GetEpisodeNumber() {
latest = lf
}
}
return latest, true
}
// FindNextEpisode returns the *main* local file whose episode number is after the given local file.
func (e *LocalFileWrapperEntry) FindNextEpisode(lf *LocalFile) (*LocalFile, bool) {
lfs, ok := e.GetMainLocalFiles()
if !ok {
return nil, false
}
// Get the local file whose episode number is after the given local file
var next *LocalFile
for _, l := range lfs {
if l.GetEpisodeNumber() == lf.GetEpisodeNumber()+1 {
next = l
break
}
}
if next == nil {
return nil, false
}
return next, true
}
// GetProgressNumber returns the progress number of a **main** local file.
func (e *LocalFileWrapperEntry) GetProgressNumber(lf *LocalFile) int {
lfs, ok := e.GetMainLocalFiles()
if !ok {
return 0
}
var hasEpZero bool
for _, l := range lfs {
if l.GetEpisodeNumber() == 0 {
hasEpZero = true
break
}
}
if hasEpZero {
return lf.GetEpisodeNumber() + 1
}
return lf.GetEpisodeNumber()
}
func (lfw *LocalFileWrapper) GetUnmatchedLocalFiles() []*LocalFile {
return lfw.UnmatchedLocalFiles
}
func (lfw *LocalFileWrapper) GetLocalEntries() []*LocalFileWrapperEntry {
return lfw.LocalEntries
}
func (lfw *LocalFileWrapper) GetLocalFiles() []*LocalFile {
return lfw.LocalFiles
}
func (e *LocalFileWrapperEntry) GetLocalFiles() []*LocalFile {
return e.LocalFiles
}
func (e *LocalFileWrapperEntry) GetMediaId() int {
return e.MediaId
}