node build fixed
This commit is contained in:
223
seanime-2.9.10/internal/manga/downloader/queue.go
Normal file
223
seanime-2.9.10/internal/manga/downloader/queue.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package chapter_downloader
|
||||
|
||||
import (
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/rs/zerolog"
|
||||
"seanime/internal/database/db"
|
||||
"seanime/internal/database/models"
|
||||
"seanime/internal/events"
|
||||
hibikemanga "seanime/internal/extension/hibike/manga"
|
||||
"seanime/internal/util"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
QueueStatusNotStarted QueueStatus = "not_started"
|
||||
QueueStatusDownloading QueueStatus = "downloading"
|
||||
QueueStatusErrored QueueStatus = "errored"
|
||||
)
|
||||
|
||||
type (
|
||||
// Queue is used to manage the download queue.
|
||||
// If feeds the downloader with the next item in the queue.
|
||||
Queue struct {
|
||||
logger *zerolog.Logger
|
||||
mu sync.Mutex
|
||||
db *db.Database
|
||||
current *QueueInfo
|
||||
runCh chan *QueueInfo // Channel to tell downloader to run the next item
|
||||
active bool
|
||||
wsEventManager events.WSEventManagerInterface
|
||||
}
|
||||
|
||||
QueueStatus string
|
||||
|
||||
// QueueInfo stores details about the download progress of a chapter.
|
||||
QueueInfo struct {
|
||||
DownloadID
|
||||
Pages []*hibikemanga.ChapterPage
|
||||
DownloadedUrls []string
|
||||
Status QueueStatus
|
||||
}
|
||||
)
|
||||
|
||||
func NewQueue(db *db.Database, logger *zerolog.Logger, wsEventManager events.WSEventManagerInterface, runCh chan *QueueInfo) *Queue {
|
||||
return &Queue{
|
||||
logger: logger,
|
||||
db: db,
|
||||
runCh: runCh,
|
||||
wsEventManager: wsEventManager,
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a chapter to the download queue.
|
||||
// It tells the queue to download the next item if possible.
|
||||
func (q *Queue) Add(id DownloadID, pages []*hibikemanga.ChapterPage, runNext bool) error {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
marshalled, err := json.Marshal(pages)
|
||||
if err != nil {
|
||||
q.logger.Error().Err(err).Msgf("Failed to marshal pages for id %v", id)
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.db.InsertChapterDownloadQueueItem(&models.ChapterDownloadQueueItem{
|
||||
BaseModel: models.BaseModel{},
|
||||
Provider: id.Provider,
|
||||
MediaID: id.MediaId,
|
||||
ChapterNumber: id.ChapterNumber,
|
||||
ChapterID: id.ChapterId,
|
||||
PageData: marshalled,
|
||||
Status: string(QueueStatusNotStarted),
|
||||
})
|
||||
if err != nil {
|
||||
q.logger.Error().Err(err).Msgf("Failed to insert chapter download queue item for id %v", id)
|
||||
return err
|
||||
}
|
||||
|
||||
q.logger.Info().Msgf("chapter downloader: Added chapter to download queue: %s", id.ChapterId)
|
||||
|
||||
q.wsEventManager.SendEvent(events.ChapterDownloadQueueUpdated, nil)
|
||||
|
||||
if runNext && q.active {
|
||||
// Tells queue to run next if possible
|
||||
go q.runNext()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Queue) HasCompleted(queueInfo *QueueInfo) {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
if queueInfo.Status == QueueStatusErrored {
|
||||
q.logger.Warn().Msgf("chapter downloader: Errored %s", queueInfo.DownloadID.ChapterId)
|
||||
// Update the status of the current item in the database.
|
||||
_ = q.db.UpdateChapterDownloadQueueItemStatus(q.current.DownloadID.Provider, q.current.DownloadID.MediaId, q.current.DownloadID.ChapterId, string(QueueStatusErrored))
|
||||
} else {
|
||||
q.logger.Debug().Msgf("chapter downloader: Dequeueing %s", queueInfo.DownloadID.ChapterId)
|
||||
// Dequeue the item from the database.
|
||||
_, err := q.db.DequeueChapterDownloadQueueItem()
|
||||
if err != nil {
|
||||
q.logger.Error().Err(err).Msgf("Failed to dequeue chapter download queue item for id %v", queueInfo.DownloadID)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
q.wsEventManager.SendEvent(events.ChapterDownloadQueueUpdated, nil)
|
||||
q.wsEventManager.SendEvent(events.RefreshedMangaDownloadData, nil)
|
||||
|
||||
// Reset current item
|
||||
q.current = nil
|
||||
|
||||
if q.active {
|
||||
// Tells queue to run next if possible
|
||||
q.runNext()
|
||||
}
|
||||
}
|
||||
|
||||
// Run activates the queue and invokes runNext
|
||||
func (q *Queue) Run() {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
if !q.active {
|
||||
q.logger.Debug().Msg("chapter downloader: Starting queue")
|
||||
}
|
||||
|
||||
q.active = true
|
||||
|
||||
// Tells queue to run next if possible
|
||||
q.runNext()
|
||||
}
|
||||
|
||||
// Stop deactivates the queue
|
||||
func (q *Queue) Stop() {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
if q.active {
|
||||
q.logger.Debug().Msg("chapter downloader: Stopping queue")
|
||||
}
|
||||
|
||||
q.active = false
|
||||
}
|
||||
|
||||
// runNext runs the next item in the queue.
|
||||
// - Checks if there is a current item, if so, it returns.
|
||||
// - If nothing is running, it gets the next item (QueueInfo) from the database, sets it as current and sends it to the downloader.
|
||||
func (q *Queue) runNext() {
|
||||
|
||||
q.logger.Debug().Msg("chapter downloader: Processing next item in queue")
|
||||
|
||||
// Catch panic in runNext, so it doesn't bubble up and stop goroutines.
|
||||
defer util.HandlePanicInModuleThen("internal/manga/downloader/runNext", func() {
|
||||
q.logger.Error().Msg("chapter downloader: Panic in 'runNext'")
|
||||
})
|
||||
|
||||
if q.current != nil {
|
||||
q.logger.Debug().Msg("chapter downloader: Current item is not nil")
|
||||
return
|
||||
}
|
||||
|
||||
q.logger.Debug().Msg("chapter downloader: Checking next item in queue")
|
||||
|
||||
// Get next item from the database.
|
||||
next, _ := q.db.GetNextChapterDownloadQueueItem()
|
||||
if next == nil {
|
||||
q.logger.Debug().Msg("chapter downloader: No next item in queue")
|
||||
return
|
||||
}
|
||||
|
||||
id := DownloadID{
|
||||
Provider: next.Provider,
|
||||
MediaId: next.MediaID,
|
||||
ChapterId: next.ChapterID,
|
||||
ChapterNumber: next.ChapterNumber,
|
||||
}
|
||||
|
||||
q.logger.Debug().Msgf("chapter downloader: Preparing next item in queue: %s", id.ChapterId)
|
||||
|
||||
q.wsEventManager.SendEvent(events.ChapterDownloadQueueUpdated, nil)
|
||||
// Update status
|
||||
_ = q.db.UpdateChapterDownloadQueueItemStatus(id.Provider, id.MediaId, id.ChapterId, string(QueueStatusDownloading))
|
||||
|
||||
// Set the current item.
|
||||
q.current = &QueueInfo{
|
||||
DownloadID: id,
|
||||
DownloadedUrls: make([]string, 0),
|
||||
Status: QueueStatusDownloading,
|
||||
}
|
||||
|
||||
// Unmarshal the page data.
|
||||
err := json.Unmarshal(next.PageData, &q.current.Pages)
|
||||
if err != nil {
|
||||
q.logger.Error().Err(err).Msgf("Failed to unmarshal pages for id %v", id)
|
||||
_ = q.db.UpdateChapterDownloadQueueItemStatus(id.Provider, id.MediaId, id.ChapterId, string(QueueStatusNotStarted))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: This is a temporary fix to prevent the downloader from running too fast.
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
q.logger.Info().Msgf("chapter downloader: Running next item in queue: %s", id.ChapterId)
|
||||
|
||||
// Tell Downloader to run
|
||||
q.runCh <- q.current
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (q *Queue) GetCurrent() (qi *QueueInfo, ok bool) {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
|
||||
if q.current == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return q.current, true
|
||||
}
|
||||
Reference in New Issue
Block a user