node build fixed
This commit is contained in:
323
seanime-2.9.10/internal/manga/downloads.go
Normal file
323
seanime-2.9.10/internal/manga/downloads.go
Normal file
@@ -0,0 +1,323 @@
|
||||
package manga
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/extension"
|
||||
hibikemanga "seanime/internal/extension/hibike/manga"
|
||||
"seanime/internal/hook"
|
||||
chapter_downloader "seanime/internal/manga/downloader"
|
||||
manga_providers "seanime/internal/manga/providers"
|
||||
"slices"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
// GetDownloadedMangaChapterContainers retrieves downloaded chapter containers for a specific manga ID.
|
||||
// It filters the complete set of downloaded chapters to return only those matching the provided manga ID.
|
||||
func (r *Repository) GetDownloadedMangaChapterContainers(mId int, mangaCollection *anilist.MangaCollection) (ret []*ChapterContainer, err error) {
|
||||
|
||||
containers, err := r.GetDownloadedChapterContainers(mangaCollection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
if container.MediaId == mId {
|
||||
ret = append(ret, container)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetDownloadedChapterContainers retrieves all downloaded manga chapter containers.
|
||||
// It scans the download directory for chapter folders, matches them with manga collection entries,
|
||||
// and collects chapter details from file cache or provider API when necessary.
|
||||
//
|
||||
// Ideally, the provider API should never be called assuming the chapter details are cached.
|
||||
func (r *Repository) GetDownloadedChapterContainers(mangaCollection *anilist.MangaCollection) (ret []*ChapterContainer, err error) {
|
||||
ret = make([]*ChapterContainer, 0)
|
||||
|
||||
// Trigger hook event
|
||||
reqEvent := &MangaDownloadedChapterContainersRequestedEvent{
|
||||
MangaCollection: mangaCollection,
|
||||
ChapterContainers: ret,
|
||||
}
|
||||
err = hook.GlobalHookManager.OnMangaDownloadedChapterContainersRequested().Trigger(reqEvent)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Msg("manga: Exception occurred while triggering hook event")
|
||||
return nil, fmt.Errorf("manga: Error in hook, %w", err)
|
||||
}
|
||||
mangaCollection = reqEvent.MangaCollection
|
||||
|
||||
// Default prevented, return the chapter containers
|
||||
if reqEvent.DefaultPrevented {
|
||||
ret = reqEvent.ChapterContainers
|
||||
if ret == nil {
|
||||
return nil, fmt.Errorf("manga: No chapter containers returned by hook event")
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Read download directory
|
||||
files, err := os.ReadDir(r.downloadDir)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Msg("manga: Failed to read download directory")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get all chapter directories
|
||||
// e.g. manga_comick_123_10010_13
|
||||
chapterDirs := make([]string, 0)
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
_, ok := chapter_downloader.ParseChapterDirName(file.Name())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
chapterDirs = append(chapterDirs, file.Name())
|
||||
}
|
||||
}
|
||||
|
||||
if len(chapterDirs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Now that we have all the chapter directories, we can get the chapter containers
|
||||
|
||||
keys := make([]*chapter_downloader.DownloadID, 0)
|
||||
for _, dir := range chapterDirs {
|
||||
downloadId, ok := chapter_downloader.ParseChapterDirName(dir)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, &downloadId)
|
||||
}
|
||||
|
||||
providerAndMediaIdPairs := make(map[struct {
|
||||
provider string
|
||||
mediaId int
|
||||
}]bool)
|
||||
|
||||
for _, key := range keys {
|
||||
providerAndMediaIdPairs[struct {
|
||||
provider string
|
||||
mediaId int
|
||||
}{
|
||||
provider: key.Provider,
|
||||
mediaId: key.MediaId,
|
||||
}] = true
|
||||
}
|
||||
|
||||
// Get the chapter containers
|
||||
for pair := range providerAndMediaIdPairs {
|
||||
provider := pair.provider
|
||||
mediaId := pair.mediaId
|
||||
|
||||
// Get the manga from the collection
|
||||
mangaEntry, ok := mangaCollection.GetListEntryFromMangaId(mediaId)
|
||||
if !ok {
|
||||
r.logger.Warn().Int("mediaId", mediaId).Msg("manga: [GetDownloadedChapterContainers] Manga not found in collection")
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the list of chapters for the manga
|
||||
// Check the permanent file cache
|
||||
container, found := r.getChapterContainerFromPermanentFilecache(provider, mediaId)
|
||||
if !found {
|
||||
// Check the temporary file cache
|
||||
container, found = r.getChapterContainerFromFilecache(provider, mediaId)
|
||||
if !found {
|
||||
// Get the chapters from the provider
|
||||
// This stays here for backwards compatibility, but ideally the method should not require an internet connection
|
||||
// so this will fail if the chapters were not cached & with no internet
|
||||
opts := GetMangaChapterContainerOptions{
|
||||
Provider: provider,
|
||||
MediaId: mediaId,
|
||||
Titles: mangaEntry.GetMedia().GetAllTitles(),
|
||||
Year: mangaEntry.GetMedia().GetStartYearSafe(),
|
||||
}
|
||||
container, err = r.GetMangaChapterContainer(&opts)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Int("mediaId", mediaId).Msg("manga: [GetDownloadedChapterContainers] Failed to retrieve cached list of manga chapters")
|
||||
continue
|
||||
}
|
||||
// Cache the chapter container in the permanent bucket
|
||||
go func() {
|
||||
chapterContainerKey := getMangaChapterContainerCacheKey(provider, mediaId)
|
||||
chapterContainer, found := r.getChapterContainerFromFilecache(provider, mediaId)
|
||||
if found {
|
||||
// Store the chapter container in the permanent bucket
|
||||
permBucket := getPermanentChapterContainerCacheBucket(provider, mediaId)
|
||||
_ = r.fileCacher.SetPerm(permBucket, chapterContainerKey, chapterContainer)
|
||||
}
|
||||
}()
|
||||
}
|
||||
} else {
|
||||
r.logger.Trace().Int("mediaId", mediaId).Msg("manga: Found chapter container in permanent bucket")
|
||||
}
|
||||
|
||||
downloadedContainer := &ChapterContainer{
|
||||
MediaId: container.MediaId,
|
||||
Provider: container.Provider,
|
||||
Chapters: make([]*hibikemanga.ChapterDetails, 0),
|
||||
}
|
||||
|
||||
// Now that we have the container, we'll filter out the chapters that are not downloaded
|
||||
// Go through each chapter and check if it's downloaded
|
||||
for _, chapter := range container.Chapters {
|
||||
// For each chapter, check if the chapter directory exists
|
||||
for _, dir := range chapterDirs {
|
||||
if dir == chapter_downloader.FormatChapterDirName(provider, mediaId, chapter.ID, chapter.Chapter) {
|
||||
downloadedContainer.Chapters = append(downloadedContainer.Chapters, chapter)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(downloadedContainer.Chapters) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, downloadedContainer)
|
||||
}
|
||||
|
||||
// Add chapter containers from local provider
|
||||
localProviderB, ok := extension.GetExtension[extension.MangaProviderExtension](r.providerExtensionBank, manga_providers.LocalProvider)
|
||||
if ok {
|
||||
_, ok := localProviderB.GetProvider().(*manga_providers.Local)
|
||||
if ok {
|
||||
for _, list := range mangaCollection.MediaListCollection.GetLists() {
|
||||
for _, entry := range list.GetEntries() {
|
||||
media := entry.GetMedia()
|
||||
opts := GetMangaChapterContainerOptions{
|
||||
Provider: manga_providers.LocalProvider,
|
||||
MediaId: media.GetID(),
|
||||
Titles: media.GetAllTitles(),
|
||||
Year: media.GetStartYearSafe(),
|
||||
}
|
||||
container, err := r.GetMangaChapterContainer(&opts)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, container)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event
|
||||
ev := &MangaDownloadedChapterContainersEvent{
|
||||
ChapterContainers: ret,
|
||||
}
|
||||
err = hook.GlobalHookManager.OnMangaDownloadedChapterContainers().Trigger(ev)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Msg("manga: Exception occurred while triggering hook event")
|
||||
return nil, fmt.Errorf("manga: Error in hook, %w", err)
|
||||
}
|
||||
ret = ev.ChapterContainers
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// getDownloadedMangaPageContainer retrieves page information for a downloaded manga chapter.
|
||||
// It reads the chapter directory and parses the registry file to build a PageContainer
|
||||
// with details about each downloaded page including dimensions and file paths.
|
||||
func (r *Repository) getDownloadedMangaPageContainer(
|
||||
provider string,
|
||||
mediaId int,
|
||||
chapterId string,
|
||||
) (*PageContainer, error) {
|
||||
|
||||
// Check if the chapter is downloaded
|
||||
found := false
|
||||
|
||||
// Read download directory
|
||||
files, err := os.ReadDir(r.downloadDir)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Msg("manga: Failed to read download directory")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chapterDir := "" // e.g. manga_comick_123_10010_13
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
|
||||
downloadId, ok := chapter_downloader.ParseChapterDirName(file.Name())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if downloadId.Provider == provider &&
|
||||
downloadId.MediaId == mediaId &&
|
||||
downloadId.ChapterId == chapterId {
|
||||
found = true
|
||||
chapterDir = file.Name()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, ErrChapterNotDownloaded
|
||||
}
|
||||
|
||||
r.logger.Debug().Msg("manga: Found downloaded chapter directory")
|
||||
|
||||
// Open registry file
|
||||
registryFile, err := os.Open(filepath.Join(r.downloadDir, chapterDir, "registry.json"))
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Msg("manga: Failed to open registry file")
|
||||
return nil, err
|
||||
}
|
||||
defer registryFile.Close()
|
||||
|
||||
r.logger.Debug().Str("chapterId", chapterId).Msg("manga: Reading registry file")
|
||||
|
||||
// Read registry file
|
||||
var pageRegistry *chapter_downloader.Registry
|
||||
err = json.NewDecoder(registryFile).Decode(&pageRegistry)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Msg("manga: Failed to decode registry file")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pageList := make([]*hibikemanga.ChapterPage, 0)
|
||||
pageDimensions := make(map[int]*PageDimension)
|
||||
|
||||
// Get the downloaded pages
|
||||
for pageIndex, pageInfo := range *pageRegistry {
|
||||
pageList = append(pageList, &hibikemanga.ChapterPage{
|
||||
Index: pageIndex,
|
||||
URL: filepath.Join(chapterDir, pageInfo.Filename),
|
||||
Provider: provider,
|
||||
})
|
||||
pageDimensions[pageIndex] = &PageDimension{
|
||||
Width: pageInfo.Width,
|
||||
Height: pageInfo.Height,
|
||||
}
|
||||
}
|
||||
|
||||
slices.SortStableFunc(pageList, func(i, j *hibikemanga.ChapterPage) int {
|
||||
return cmp.Compare(i.Index, j.Index)
|
||||
})
|
||||
|
||||
container := &PageContainer{
|
||||
MediaId: mediaId,
|
||||
Provider: provider,
|
||||
ChapterId: chapterId,
|
||||
Pages: pageList,
|
||||
PageDimensions: pageDimensions,
|
||||
IsDownloaded: true,
|
||||
}
|
||||
|
||||
r.logger.Debug().Str("chapterId", chapterId).Msg("manga: Found downloaded chapter")
|
||||
|
||||
return container, nil
|
||||
}
|
||||
Reference in New Issue
Block a user