diff --git a/.gitignore b/.gitignore index f26623e..bbed4d9 100644 --- a/.gitignore +++ b/.gitignore @@ -36,8 +36,10 @@ mnt/ error_codes.json memlog.sh stressTestAddRemove.py +fileMissingZurg.py *.zip pkg/anidb/ logs/ +*.log diff --git a/internal/config/load.go b/internal/config/load.go index f3bce8d..68a8ea9 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -22,7 +22,7 @@ func LoadZurgConfig(filename string, log *logutil.Logger) (ConfigInterface, erro switch initialConfig.Version { case "v1": - log.Debug("Detected config version: v1") + log.Debug("Config version: v1") return loadV1Config(content, log) default: diff --git a/internal/dav/delete.go b/internal/dav/delete.go index 73313aa..43fb908 100644 --- a/internal/dav/delete.go +++ b/internal/dav/delete.go @@ -32,7 +32,7 @@ func HandleDeleteFile(directory, torrentName, fileName string, torMgr *torrent.T return fmt.Errorf("cannot find file %s", fileName) } file.Link = "unselect" - if torMgr.CheckDeletedState(torrent) { + if torMgr.CheckDeletedStatus(torrent) { torMgr.Delete(torrentName, true) } return nil diff --git a/internal/torrent/delete.go b/internal/torrent/delete.go index 169c9d6..be7136d 100644 --- a/internal/torrent/delete.go +++ b/internal/torrent/delete.go @@ -2,7 +2,7 @@ package torrent import cmap "github.com/orcaman/concurrent-map/v2" -func (t *TorrentManager) CheckDeletedState(torrent *Torrent) bool { +func (t *TorrentManager) CheckDeletedStatus(torrent *Torrent) bool { var unselectedIDs []int torrent.SelectedFiles.IterCb(func(_ string, file *File) { if file.Link == "unselect" { diff --git a/internal/torrent/latestState.go b/internal/torrent/latestState.go index 42ecc9c..66d1103 100644 --- a/internal/torrent/latestState.go +++ b/internal/torrent/latestState.go @@ -7,23 +7,107 @@ import ( ) type LibraryState struct { - TotalCount int - FirstTorrent *realdebrid.Torrent - DownloadingCount int + TotalCount int + ActiveCount int + FirstTorrent realdebrid.Torrent + FirstActiveTorrent realdebrid.Torrent } func (ls LibraryState) equal(a LibraryState) bool { - return a.TotalCount == ls.TotalCount && a.FirstTorrent.ID == ls.FirstTorrent.ID && a.DownloadingCount == ls.DownloadingCount + return a.TotalCount == ls.TotalCount && + a.ActiveCount == ls.ActiveCount && + a.FirstTorrent.ID == ls.FirstTorrent.ID && + a.FirstTorrent.Status == ls.FirstTorrent.Status && + a.FirstActiveTorrent.ID == ls.FirstActiveTorrent.ID && + a.FirstActiveTorrent.Status == ls.FirstActiveTorrent.Status } func EmptyState() LibraryState { - oldestTime := time.Time{} + empty := realdebrid.Torrent{ + ID: "", + Added: (time.Time{}).Format(time.RFC3339), + } return LibraryState{ - TotalCount: 0, - FirstTorrent: &realdebrid.Torrent{ - ID: "", - Added: oldestTime.Format(time.RFC3339), - }, - DownloadingCount: 0, + TotalCount: 0, + ActiveCount: 0, + FirstTorrent: empty, + FirstActiveTorrent: empty, + } +} + +func (t *TorrentManager) SetNewLatestState(checksum LibraryState) { + t.latestState.ActiveCount = checksum.ActiveCount + t.latestState.TotalCount = checksum.TotalCount + t.latestState.FirstTorrent = checksum.FirstTorrent + t.latestState.FirstActiveTorrent = checksum.FirstActiveTorrent +} + +type torrentsResp struct { + torrents []realdebrid.Torrent + totalCount int +} + +// generates a checksum based on the number of torrents, the first torrent id and the number of active torrents +func (t *TorrentManager) getCurrentState() LibraryState { + torrentChan := make(chan torrentsResp, 1) + activeChan := make(chan torrentsResp, 1) + countChan := make(chan int, 1) + errChan := make(chan error, 3) // accommodate errors from both goroutines + defer close(torrentChan) + defer close(activeChan) + defer close(countChan) + defer close(errChan) + + _ = t.workerPool.Submit(func() { + torrents, totalCount, err := t.Api.GetTorrents(1, false) + if err != nil { + errChan <- err + return + } + torrentChan <- torrentsResp{torrents: torrents, totalCount: totalCount} + }) + + _ = t.workerPool.Submit(func() { + torrents, totalCount, err := t.Api.GetTorrents(1, true) + if err != nil { + errChan <- err + return + } + activeChan <- torrentsResp{torrents: torrents, totalCount: totalCount} + }) + + _ = t.workerPool.Submit(func() { + count, err := t.Api.GetActiveTorrentCount() + if err != nil { + errChan <- err + return + } + countChan <- count.DownloadingCount + }) + + // Existing goroutines for GetTorrents and GetActiveTorrentCount + var first realdebrid.Torrent + var active realdebrid.Torrent + var totalCount, count int + + for i := 0; i < 3; i++ { + select { + case firstResp := <-torrentChan: + first = firstResp.torrents[0] + totalCount = firstResp.totalCount + case activeResp := <-activeChan: + active = activeResp.torrents[0] + case count = <-countChan: + case err := <-errChan: + t.log.Warnf("Checksum API Error: %v\n", err) + return EmptyState() + } + } + + return LibraryState{ + TotalCount: totalCount, + ActiveCount: count, + FirstTorrent: first, + FirstActiveTorrent: active, } } diff --git a/internal/torrent/manager.go b/internal/torrent/manager.go index f2abe70..ed59577 100644 --- a/internal/torrent/manager.go +++ b/internal/torrent/manager.go @@ -85,6 +85,7 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, p } t.RefreshTorrents() + t.SetNewLatestState(t.getCurrentState()) if t.Config.EnableRepair() { repairWorker, err := ants.NewPool(1) diff --git a/internal/torrent/refresh.go b/internal/torrent/refresh.go index dffedc0..3abd84c 100644 --- a/internal/torrent/refresh.go +++ b/internal/torrent/refresh.go @@ -14,13 +14,14 @@ import ( ) func (t *TorrentManager) RefreshTorrents() []string { - instances, _, err := t.Api.GetTorrents(0) + instances, _, err := t.Api.GetTorrents(0, false) if err != nil { t.log.Warnf("Cannot get torrents: %v\n", err) return nil } infoChan := make(chan *Torrent, len(instances)) var wg sync.WaitGroup + for i := range instances { wg.Add(1) idx := i // capture the loop variable @@ -29,6 +30,7 @@ func (t *TorrentManager) RefreshTorrents() []string { infoChan <- t.getMoreInfo(instances[idx]) }) } + wg.Wait() close(infoChan) t.log.Debugf("Fetched info for %d torrents", len(instances)) @@ -41,7 +43,9 @@ func (t *TorrentManager) RefreshTorrents() []string { noInfoCount++ continue } - freshKeys.Add(info.AccessKey) + if !info.AnyInProgress() { + freshKeys.Add(info.AccessKey) + } if torrent, exists := allTorrents.Get(info.AccessKey); !exists { allTorrents.Set(info.AccessKey, info) } else if !strset.Difference(info.DownloadedIDs, torrent.DownloadedIDs).IsEmpty() { @@ -80,12 +84,6 @@ func (t *TorrentManager) RefreshTorrents() []string { return updatedPaths } -func (t *TorrentManager) SetNewLatestState(checksum LibraryState) { - t.latestState.DownloadingCount = checksum.DownloadingCount - t.latestState.FirstTorrent = checksum.FirstTorrent - t.latestState.TotalCount = checksum.TotalCount -} - // startRefreshJob periodically refreshes the torrents func (t *TorrentManager) startRefreshJob() { t.log.Info("Starting periodic refresh") @@ -115,11 +113,11 @@ func (t *TorrentManager) startRefreshJob() { // getMoreInfo gets original name, size and files for a torrent func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent { infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE) - if tor, exists := infoCache.Get(rdTorrent.ID); exists && tor.SelectedFiles.Count() == len(rdTorrent.Links) { - return tor + if torrentFromCache, exists := infoCache.Get(rdTorrent.ID); exists && !torrentFromCache.AnyInProgress() && torrentFromCache.SelectedFiles.Count() == len(rdTorrent.Links) { + return torrentFromCache } torrentFromFile := t.readTorrentFromFile(rdTorrent.ID) - if torrentFromFile != nil && torrentFromFile.SelectedFiles.Count() == len(rdTorrent.Links) { + if torrentFromFile != nil && !torrentFromFile.AnyInProgress() && torrentFromFile.SelectedFiles.Count() == len(rdTorrent.Links) { infoCache.Set(rdTorrent.ID, torrentFromFile) return torrentFromFile } @@ -244,63 +242,3 @@ func (t *TorrentManager) mergeToMain(existing, toMerge *Torrent) Torrent { return mainTorrent } - -type torrentsResp struct { - torrents []realdebrid.Torrent - totalCount int -} - -// generates a checksum based on the number of torrents, the first torrent id and the number of active torrents -func (t *TorrentManager) getCurrentState() LibraryState { - torrentsChan := make(chan torrentsResp, 1) - countChan := make(chan int, 1) - errChan := make(chan error, 2) // accommodate errors from both goroutines - defer close(torrentsChan) - defer close(countChan) - defer close(errChan) - - _ = t.workerPool.Submit(func() { - torrents, totalCount, err := t.Api.GetTorrents(1) - if err != nil { - errChan <- err - return - } - torrentsChan <- torrentsResp{torrents: torrents, totalCount: totalCount} - }) - - _ = t.workerPool.Submit(func() { - count, err := t.Api.GetActiveTorrentCount() - if err != nil { - errChan <- err - return - } - countChan <- count.DownloadingCount - }) - - // Existing goroutines for GetTorrents and GetActiveTorrentCount - var torrents []realdebrid.Torrent - var totalCount, count int - - for i := 0; i < 2; i++ { - select { - case resp := <-torrentsChan: - torrents = resp.torrents - totalCount = resp.totalCount - case count = <-countChan: - case err := <-errChan: - t.log.Warnf("Checksum API Error: %v\n", err) - return EmptyState() - } - } - - if len(torrents) == 0 { - t.log.Error("Huh, no torrents returned") - return EmptyState() - } - - return LibraryState{ - TotalCount: totalCount, - FirstTorrent: &torrents[0], - DownloadingCount: count, - } -} diff --git a/pkg/realdebrid/api.go b/pkg/realdebrid/api.go index e60e5a2..b84a36c 100644 --- a/pkg/realdebrid/api.go +++ b/pkg/realdebrid/api.go @@ -62,7 +62,7 @@ func (rd *RealDebrid) UnrestrictCheck(link string) (*Download, error) { // GetTorrents returns all torrents, paginated // if customLimit is 0, the default limit of 1000 is used -func (rd *RealDebrid) GetTorrents(customLimit int) ([]Torrent, int, error) { +func (rd *RealDebrid) GetTorrents(customLimit int, active bool) ([]Torrent, int, error) { baseURL := "https://api.real-debrid.com/rest/1.0/torrents" var allTorrents []Torrent page := 1 @@ -76,7 +76,9 @@ func (rd *RealDebrid) GetTorrents(customLimit int) ([]Torrent, int, error) { params := url.Values{} params.Set("page", fmt.Sprintf("%d", page)) params.Set("limit", fmt.Sprintf("%d", limit)) - // params.Set("filter", "active") + if active { + params.Set("filter", "active") + } reqURL := baseURL + "?" + params.Encode() diff --git a/pkg/realdebrid/types.go b/pkg/realdebrid/types.go index 825907d..42681a5 100644 --- a/pkg/realdebrid/types.go +++ b/pkg/realdebrid/types.go @@ -27,6 +27,7 @@ type Torrent struct { ID string `json:"id"` Name string `json:"filename"` Progress int `json:"-"` + Status string `json:"status"` Links []string `json:"links"` Added string `json:"-"` }