cleanup torrent cache
This commit is contained in:
@@ -552,6 +552,10 @@ func (c *Cache) GetTorrents() map[string]CachedTorrent {
|
|||||||
return c.torrents.getAll()
|
return c.torrents.getAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cache) TotalTorrents() int {
|
||||||
|
return c.torrents.getAllCount()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) GetTorrentByName(name string) *CachedTorrent {
|
func (c *Cache) GetTorrentByName(name string) *CachedTorrent {
|
||||||
if torrent, ok := c.torrents.getByName(name); ok {
|
if torrent, ok := c.torrents.getByName(name); ok {
|
||||||
return &torrent
|
return &torrent
|
||||||
|
|||||||
@@ -137,10 +137,10 @@ func (c *Cache) refreshRclone() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 10 * time.Second,
|
Timeout: 60 * time.Second,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
MaxIdleConns: 10,
|
MaxIdleConns: 10,
|
||||||
IdleConnTimeout: 30 * time.Second,
|
IdleConnTimeout: 60 * time.Second,
|
||||||
DisableCompression: false,
|
DisableCompression: false,
|
||||||
MaxIdleConnsPerHost: 5,
|
MaxIdleConnsPerHost: 5,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,13 +40,22 @@ type directoryFilter struct {
|
|||||||
ageThreshold time.Duration // only for last_added
|
ageThreshold time.Duration // only for last_added
|
||||||
}
|
}
|
||||||
|
|
||||||
type torrentCache struct {
|
type torrents struct {
|
||||||
mu sync.Mutex
|
sync.RWMutex
|
||||||
byID map[string]CachedTorrent
|
byID map[string]CachedTorrent
|
||||||
byName map[string]CachedTorrent
|
byName map[string]CachedTorrent
|
||||||
|
}
|
||||||
|
|
||||||
|
type folders struct {
|
||||||
|
sync.RWMutex
|
||||||
|
listing map[string][]os.FileInfo // folder name to file listing
|
||||||
|
}
|
||||||
|
|
||||||
|
type torrentCache struct {
|
||||||
|
torrents torrents
|
||||||
|
|
||||||
listing atomic.Value
|
listing atomic.Value
|
||||||
folderListing map[string][]os.FileInfo
|
folders folders
|
||||||
folderListingMu sync.RWMutex
|
|
||||||
directoriesFilters map[string][]directoryFilter
|
directoriesFilters map[string][]directoryFilter
|
||||||
sortNeeded atomic.Bool
|
sortNeeded atomic.Bool
|
||||||
}
|
}
|
||||||
@@ -62,9 +71,13 @@ type sortableFile struct {
|
|||||||
func newTorrentCache(dirFilters map[string][]directoryFilter) *torrentCache {
|
func newTorrentCache(dirFilters map[string][]directoryFilter) *torrentCache {
|
||||||
|
|
||||||
tc := &torrentCache{
|
tc := &torrentCache{
|
||||||
|
torrents: torrents{
|
||||||
byID: make(map[string]CachedTorrent),
|
byID: make(map[string]CachedTorrent),
|
||||||
byName: make(map[string]CachedTorrent),
|
byName: make(map[string]CachedTorrent),
|
||||||
folderListing: make(map[string][]os.FileInfo),
|
},
|
||||||
|
folders: folders{
|
||||||
|
listing: make(map[string][]os.FileInfo),
|
||||||
|
},
|
||||||
directoriesFilters: dirFilters,
|
directoriesFilters: dirFilters,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,41 +87,42 @@ func newTorrentCache(dirFilters map[string][]directoryFilter) *torrentCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) reset() {
|
func (tc *torrentCache) reset() {
|
||||||
tc.mu.Lock()
|
tc.torrents.Lock()
|
||||||
tc.byID = make(map[string]CachedTorrent)
|
tc.torrents.byID = make(map[string]CachedTorrent)
|
||||||
tc.byName = make(map[string]CachedTorrent)
|
tc.torrents.byName = make(map[string]CachedTorrent)
|
||||||
tc.mu.Unlock()
|
tc.torrents.Unlock()
|
||||||
|
|
||||||
// reset the sorted listing
|
// reset the sorted listing
|
||||||
tc.sortNeeded.Store(false)
|
tc.sortNeeded.Store(false)
|
||||||
tc.listing.Store(make([]os.FileInfo, 0))
|
tc.listing.Store(make([]os.FileInfo, 0))
|
||||||
|
|
||||||
// reset any per-folder views
|
// reset any per-folder views
|
||||||
tc.folderListingMu.Lock()
|
tc.folders.Lock()
|
||||||
tc.folderListing = make(map[string][]os.FileInfo)
|
tc.folders.listing = make(map[string][]os.FileInfo)
|
||||||
tc.folderListingMu.Unlock()
|
tc.folders.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) getByID(id string) (CachedTorrent, bool) {
|
func (tc *torrentCache) getByID(id string) (CachedTorrent, bool) {
|
||||||
tc.mu.Lock()
|
tc.torrents.RLock()
|
||||||
defer tc.mu.Unlock()
|
defer tc.torrents.RUnlock()
|
||||||
torrent, exists := tc.byID[id]
|
torrent, exists := tc.torrents.byID[id]
|
||||||
return torrent, exists
|
return torrent, exists
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) getByName(name string) (CachedTorrent, bool) {
|
func (tc *torrentCache) getByName(name string) (CachedTorrent, bool) {
|
||||||
tc.mu.Lock()
|
tc.torrents.RLock()
|
||||||
defer tc.mu.Unlock()
|
defer tc.torrents.RUnlock()
|
||||||
torrent, exists := tc.byName[name]
|
torrent, exists := tc.torrents.byName[name]
|
||||||
return torrent, exists
|
return torrent, exists
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) set(name string, torrent, newTorrent CachedTorrent) {
|
func (tc *torrentCache) set(name string, torrent, newTorrent CachedTorrent) {
|
||||||
tc.mu.Lock()
|
tc.torrents.Lock()
|
||||||
// Set the id first
|
// Set the id first
|
||||||
tc.byID[newTorrent.Id] = torrent // This is the unadulterated torrent
|
|
||||||
tc.byName[name] = newTorrent // This is likely the modified torrent
|
tc.torrents.byName[name] = torrent
|
||||||
tc.mu.Unlock()
|
tc.torrents.byID[torrent.Id] = torrent // This is the unadulterated torrent
|
||||||
|
tc.torrents.Unlock()
|
||||||
tc.sortNeeded.Store(true)
|
tc.sortNeeded.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,12 +138,12 @@ func (tc *torrentCache) getListing() []os.FileInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) getFolderListing(folderName string) []os.FileInfo {
|
func (tc *torrentCache) getFolderListing(folderName string) []os.FileInfo {
|
||||||
tc.folderListingMu.RLock()
|
tc.folders.RLock()
|
||||||
defer tc.folderListingMu.RUnlock()
|
defer tc.folders.RUnlock()
|
||||||
if folderName == "" {
|
if folderName == "" {
|
||||||
return tc.getListing()
|
return tc.getListing()
|
||||||
}
|
}
|
||||||
if folder, ok := tc.folderListing[folderName]; ok {
|
if folder, ok := tc.folders.listing[folderName]; ok {
|
||||||
return folder
|
return folder
|
||||||
}
|
}
|
||||||
// If folder not found, return empty slice
|
// If folder not found, return empty slice
|
||||||
@@ -138,13 +152,13 @@ func (tc *torrentCache) getFolderListing(folderName string) []os.FileInfo {
|
|||||||
|
|
||||||
func (tc *torrentCache) refreshListing() {
|
func (tc *torrentCache) refreshListing() {
|
||||||
|
|
||||||
tc.mu.Lock()
|
tc.torrents.RLock()
|
||||||
all := make([]sortableFile, 0, len(tc.byName))
|
all := make([]sortableFile, 0, len(tc.torrents.byName))
|
||||||
for name, t := range tc.byName {
|
for name, t := range tc.torrents.byName {
|
||||||
all = append(all, sortableFile{t.Id, name, t.AddedOn, t.Bytes, t.Bad})
|
all = append(all, sortableFile{t.Id, name, t.AddedOn, t.Bytes, t.Bad})
|
||||||
}
|
}
|
||||||
tc.sortNeeded.Store(false)
|
tc.sortNeeded.Store(false)
|
||||||
tc.mu.Unlock()
|
tc.torrents.RUnlock()
|
||||||
|
|
||||||
sort.Slice(all, func(i, j int) bool {
|
sort.Slice(all, func(i, j int) bool {
|
||||||
if all[i].name != all[j].name {
|
if all[i].name != all[j].name {
|
||||||
@@ -181,13 +195,13 @@ func (tc *torrentCache) refreshListing() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tc.folderListingMu.Lock()
|
tc.folders.Lock()
|
||||||
if len(listing) > 0 {
|
if len(listing) > 0 {
|
||||||
tc.folderListing["__bad__"] = listing
|
tc.folders.listing["__bad__"] = listing
|
||||||
} else {
|
} else {
|
||||||
delete(tc.folderListing, "__bad__")
|
delete(tc.folders.listing, "__bad__")
|
||||||
}
|
}
|
||||||
tc.folderListingMu.Unlock()
|
tc.folders.Unlock()
|
||||||
}()
|
}()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
|
|
||||||
@@ -207,13 +221,13 @@ func (tc *torrentCache) refreshListing() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tc.folderListingMu.Lock()
|
tc.folders.Lock()
|
||||||
if len(matched) > 0 {
|
if len(matched) > 0 {
|
||||||
tc.folderListing[dir] = matched
|
tc.folders.listing[dir] = matched
|
||||||
} else {
|
} else {
|
||||||
delete(tc.folderListing, dir)
|
delete(tc.folders.listing, dir)
|
||||||
}
|
}
|
||||||
tc.folderListingMu.Unlock()
|
tc.folders.Unlock()
|
||||||
}(dir, filters)
|
}(dir, filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,35 +278,41 @@ func (tc *torrentCache) torrentMatchDirectory(filters []directoryFilter, file so
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) getAll() map[string]CachedTorrent {
|
func (tc *torrentCache) getAll() map[string]CachedTorrent {
|
||||||
tc.mu.Lock()
|
tc.torrents.RLock()
|
||||||
defer tc.mu.Unlock()
|
defer tc.torrents.RUnlock()
|
||||||
result := make(map[string]CachedTorrent)
|
result := make(map[string]CachedTorrent, len(tc.torrents.byID))
|
||||||
for name, torrent := range tc.byID {
|
for name, torrent := range tc.torrents.byID {
|
||||||
result[name] = torrent
|
result[name] = torrent
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc *torrentCache) getAllCount() int {
|
||||||
|
tc.torrents.RLock()
|
||||||
|
defer tc.torrents.RUnlock()
|
||||||
|
return len(tc.torrents.byID)
|
||||||
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) getIdMaps() map[string]struct{} {
|
func (tc *torrentCache) getIdMaps() map[string]struct{} {
|
||||||
tc.mu.Lock()
|
tc.torrents.RLock()
|
||||||
defer tc.mu.Unlock()
|
defer tc.torrents.RUnlock()
|
||||||
res := make(map[string]struct{}, len(tc.byID))
|
res := make(map[string]struct{}, len(tc.torrents.byID))
|
||||||
for id := range tc.byID {
|
for id := range tc.torrents.byID {
|
||||||
res[id] = struct{}{}
|
res[id] = struct{}{}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) removeId(id string) {
|
func (tc *torrentCache) removeId(id string) {
|
||||||
tc.mu.Lock()
|
tc.torrents.Lock()
|
||||||
defer tc.mu.Unlock()
|
defer tc.torrents.Unlock()
|
||||||
delete(tc.byID, id)
|
delete(tc.torrents.byID, id)
|
||||||
tc.sortNeeded.Store(true)
|
tc.sortNeeded.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *torrentCache) remove(name string) {
|
func (tc *torrentCache) remove(name string) {
|
||||||
tc.mu.Lock()
|
tc.torrents.Lock()
|
||||||
defer tc.mu.Unlock()
|
defer tc.torrents.Unlock()
|
||||||
delete(tc.byName, name)
|
delete(tc.torrents.byName, name)
|
||||||
tc.sortNeeded.Store(true)
|
tc.sortNeeded.Store(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -293,6 +293,8 @@ func (r *Repair) StopJob(id string) error {
|
|||||||
go func() {
|
go func() {
|
||||||
if job.Status == JobStarted || job.Status == JobProcessing {
|
if job.Status == JobStarted || job.Status == JobProcessing {
|
||||||
job.Status = JobCancelled
|
job.Status = JobCancelled
|
||||||
|
job.BrokenItems = nil
|
||||||
|
job.ctx = nil // Clear context to prevent further processing
|
||||||
job.CompletedAt = time.Now()
|
job.CompletedAt = time.Now()
|
||||||
job.Error = "Job was cancelled by user"
|
job.Error = "Job was cancelled by user"
|
||||||
r.saveToFile()
|
r.saveToFile()
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
|||||||
cache, ok := caches[debridName]
|
cache, ok := caches[debridName]
|
||||||
if ok {
|
if ok {
|
||||||
// Get torrent data
|
// Get torrent data
|
||||||
profile.LibrarySize = len(cache.GetTorrents())
|
profile.LibrarySize = cache.TotalTorrents()
|
||||||
profile.BadTorrents = len(cache.GetListing("__bad__"))
|
profile.BadTorrents = len(cache.GetListing("__bad__"))
|
||||||
profile.ActiveLinks = cache.GetTotalActiveDownloadLinks()
|
profile.ActiveLinks = cache.GetTotalActiveDownloadLinks()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user