Hotfixes:
- Fix % error in url encode - FIx alldebrid downloading bug - Fix dupicate checks for newly added torrents
This commit is contained in:
@@ -10,3 +10,12 @@ func EscapePath(path string) string {
|
|||||||
|
|
||||||
return escapedPath
|
return escapedPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UnescapePath(path string) string {
|
||||||
|
// unescape %
|
||||||
|
unescapedPath := strings.ReplaceAll(path, "%25", "%")
|
||||||
|
|
||||||
|
// add others
|
||||||
|
|
||||||
|
return unescapedPath
|
||||||
|
}
|
||||||
|
|||||||
@@ -247,6 +247,12 @@ func (a *Arr) DeleteFiles(files []ContentFile) error {
|
|||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
ids = append(ids, f.FileId)
|
ids = append(ids, f.FileId)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
// Delete files, or at least try
|
||||||
|
for _, f := range files {
|
||||||
|
f.Delete()
|
||||||
|
}
|
||||||
|
}()
|
||||||
var payload interface{}
|
var payload interface{}
|
||||||
switch a.Type {
|
switch a.Type {
|
||||||
case Sonarr:
|
case Sonarr:
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package arr
|
package arr
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
type Movie struct {
|
type Movie struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
OriginalTitle string `json:"originalTitle"`
|
OriginalTitle string `json:"originalTitle"`
|
||||||
@@ -25,6 +27,12 @@ type ContentFile struct {
|
|||||||
SeasonNumber int `json:"seasonNumber"`
|
SeasonNumber int `json:"seasonNumber"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (file *ContentFile) Delete() {
|
||||||
|
// This is useful for when sonarr bulk delete fails(this usually happens)
|
||||||
|
// and we need to delete the file manually
|
||||||
|
_ = os.Remove(file.Path) // nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
type Content struct {
|
type Content struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
|
|||||||
@@ -198,15 +198,16 @@ func (ad *AllDebrid) UpdateTorrent(t *types.Torrent) error {
|
|||||||
t.Folder = name
|
t.Folder = name
|
||||||
t.MountPath = ad.MountPath
|
t.MountPath = ad.MountPath
|
||||||
t.Debrid = ad.Name
|
t.Debrid = ad.Name
|
||||||
|
t.Bytes = data.Size
|
||||||
|
t.Seeders = data.Seeders
|
||||||
if status == "downloaded" {
|
if status == "downloaded" {
|
||||||
t.Bytes = data.Size
|
t.Progress = 100
|
||||||
|
|
||||||
t.Progress = float64((data.Downloaded / data.Size) * 100)
|
|
||||||
t.Speed = data.DownloadSpeed
|
|
||||||
t.Seeders = data.Seeders
|
|
||||||
index := -1
|
index := -1
|
||||||
files := flattenFiles(data.Files, "", &index)
|
files := flattenFiles(data.Files, "", &index)
|
||||||
t.Files = files
|
t.Files = files
|
||||||
|
} else {
|
||||||
|
t.Progress = float64(data.Downloaded) / float64(data.Size) * 100
|
||||||
|
t.Speed = data.DownloadSpeed
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -269,7 +270,7 @@ func (ad *AllDebrid) GenerateDownloadLinks(t *types.Torrent) error {
|
|||||||
}
|
}
|
||||||
file.DownloadLink = link
|
file.DownloadLink = link
|
||||||
if link != nil {
|
if link != nil {
|
||||||
errCh <- fmt.Errorf("error getting download links %w", err)
|
errCh <- fmt.Errorf("download link is empty")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
filesCh <- file
|
filesCh <- file
|
||||||
@@ -310,9 +311,13 @@ func (ad *AllDebrid) GetDownloadLink(t *types.Torrent, file *types.File) (*types
|
|||||||
if err = json.Unmarshal(resp, &data); err != nil {
|
if err = json.Unmarshal(resp, &data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data.Error != nil {
|
||||||
|
return nil, fmt.Errorf("error getting download link: %s", data.Error.Message)
|
||||||
|
}
|
||||||
link := data.Data.Link
|
link := data.Data.Link
|
||||||
if link == "" {
|
if link == "" {
|
||||||
return nil, fmt.Errorf("error getting download links %s", data.Error.Message)
|
return nil, fmt.Errorf("download link is empty")
|
||||||
}
|
}
|
||||||
return &types.DownloadLink{
|
return &types.DownloadLink{
|
||||||
Link: file.Link,
|
Link: file.Link,
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ func (c *Cache) load() (map[string]*CachedTorrent, error) {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
file, ok := <-workChan
|
file, ok := <-workChan
|
||||||
@@ -227,11 +226,10 @@ func (c *Cache) load() (map[string]*CachedTorrent, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isComplete {
|
if isComplete {
|
||||||
addedOn, err := time.Parse(time.RFC3339, ct.Added)
|
|
||||||
if err != nil {
|
if addedOn, err := time.Parse(time.RFC3339, ct.Added); err == nil {
|
||||||
addedOn = now
|
ct.AddedOn = addedOn
|
||||||
}
|
}
|
||||||
ct.AddedOn = addedOn
|
|
||||||
ct.IsComplete = true
|
ct.IsComplete = true
|
||||||
ct.Name = path.Clean(ct.Name)
|
ct.Name = path.Clean(ct.Name)
|
||||||
results.Store(ct.Id, &ct)
|
results.Store(ct.Id, &ct)
|
||||||
@@ -278,9 +276,9 @@ func (c *Cache) Sync() error {
|
|||||||
c.logger.Info().Msgf("Got %d torrents from %s", len(torrents), c.client.GetName())
|
c.logger.Info().Msgf("Got %d torrents from %s", len(torrents), c.client.GetName())
|
||||||
|
|
||||||
newTorrents := make([]*types.Torrent, 0)
|
newTorrents := make([]*types.Torrent, 0)
|
||||||
idStore := make(map[string]struct{}, len(torrents))
|
idStore := make(map[string]string, len(torrents))
|
||||||
for _, t := range torrents {
|
for _, t := range torrents {
|
||||||
idStore[t.Id] = struct{}{}
|
idStore[t.Id] = t.Added
|
||||||
if _, ok := cachedTorrents[t.Id]; !ok {
|
if _, ok := cachedTorrents[t.Id]; !ok {
|
||||||
newTorrents = append(newTorrents, t)
|
newTorrents = append(newTorrents, t)
|
||||||
}
|
}
|
||||||
@@ -289,6 +287,10 @@ func (c *Cache) Sync() error {
|
|||||||
// Check for deleted torrents
|
// Check for deleted torrents
|
||||||
deletedTorrents := make([]string, 0)
|
deletedTorrents := make([]string, 0)
|
||||||
for _, t := range cachedTorrents {
|
for _, t := range cachedTorrents {
|
||||||
|
t.Added = idStore[t.Id]
|
||||||
|
if addedOn, err := time.Parse(time.RFC3339, t.Added); err == nil {
|
||||||
|
t.AddedOn = addedOn
|
||||||
|
}
|
||||||
if _, ok := idStore[t.Id]; !ok {
|
if _, ok := idStore[t.Id]; !ok {
|
||||||
deletedTorrents = append(deletedTorrents, t.Id)
|
deletedTorrents = append(deletedTorrents, t.Id)
|
||||||
}
|
}
|
||||||
@@ -399,20 +401,35 @@ func (c *Cache) GetTorrentFolder(torrent *types.Torrent) string {
|
|||||||
|
|
||||||
func (c *Cache) setTorrent(t *CachedTorrent) {
|
func (c *Cache) setTorrent(t *CachedTorrent) {
|
||||||
c.torrents.Store(t.Id, t)
|
c.torrents.Store(t.Id, t)
|
||||||
|
torrentKey := c.GetTorrentFolder(t.Torrent)
|
||||||
c.torrentsNames.Store(c.GetTorrentFolder(t.Torrent), t)
|
if o, ok := c.torrentsNames.Load(torrentKey); ok && o.Id != t.Id {
|
||||||
|
// Save the most recent torrent
|
||||||
|
if t.AddedOn.After(o.AddedOn) {
|
||||||
|
c.torrentsNames.Delete(torrentKey)
|
||||||
|
} else {
|
||||||
|
t = o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.torrentsNames.Store(torrentKey, t)
|
||||||
c.SaveTorrent(t)
|
c.SaveTorrent(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) setTorrents(torrents map[string]*CachedTorrent) {
|
func (c *Cache) setTorrents(torrents map[string]*CachedTorrent) {
|
||||||
for _, t := range torrents {
|
for _, t := range torrents {
|
||||||
c.torrents.Store(t.Id, t)
|
c.torrents.Store(t.Id, t)
|
||||||
c.torrentsNames.Store(c.GetTorrentFolder(t.Torrent), t)
|
torrentKey := c.GetTorrentFolder(t.Torrent)
|
||||||
|
if o, ok := c.torrentsNames.Load(torrentKey); ok && o.Id != t.Id {
|
||||||
|
// Save the most recent torrent
|
||||||
|
if t.AddedOn.After(o.AddedOn) {
|
||||||
|
c.torrentsNames.Delete(torrentKey)
|
||||||
|
} else {
|
||||||
|
t = o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.torrentsNames.Store(torrentKey, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.RefreshListings(true)
|
c.RefreshListings(false)
|
||||||
|
|
||||||
c.SaveTorrents()
|
c.SaveTorrents()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,13 +569,6 @@ func (c *Cache) ProcessTorrent(t *types.Torrent) error {
|
|||||||
|
|
||||||
if !isComplete(t.Files) {
|
if !isComplete(t.Files) {
|
||||||
c.logger.Debug().Msgf("Torrent %s is still not complete. Triggering a reinsert(disabled)", t.Id)
|
c.logger.Debug().Msgf("Torrent %s is still not complete. Triggering a reinsert(disabled)", t.Id)
|
||||||
//err := c.reInsertTorrent(t)
|
|
||||||
//if err != nil {
|
|
||||||
// c.logger.Error().Err(err).Msgf("Failed to reinsert torrent %s", t.Id)
|
|
||||||
// return err
|
|
||||||
//}
|
|
||||||
//c.logger.Debug().Msgf("Reinserted torrent %s", ct.Id)
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
addedOn, err := time.Parse(time.RFC3339, t.Added)
|
addedOn, err := time.Parse(time.RFC3339, t.Added)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -601,10 +611,12 @@ func (c *Cache) GetDownloadLink(torrentId, filename, fileLink string) string {
|
|||||||
if file.Link == "" {
|
if file.Link == "" {
|
||||||
c.logger.Debug().Msgf("File link is empty for %s. Release is probably nerfed", filename)
|
c.logger.Debug().Msgf("File link is empty for %s. Release is probably nerfed", filename)
|
||||||
// Try to reinsert the torrent?
|
// Try to reinsert the torrent?
|
||||||
if err := c.reInsertTorrent(ct); err != nil {
|
newCt, err := c.reInsertTorrent(ct)
|
||||||
|
if err != nil {
|
||||||
c.logger.Error().Err(err).Msgf("Failed to reinsert torrent %s", ct.Name)
|
c.logger.Error().Err(err).Msgf("Failed to reinsert torrent %s", ct.Name)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
ct = newCt
|
||||||
file = ct.Files[filename]
|
file = ct.Files[filename]
|
||||||
c.logger.Debug().Msgf("Reinserted torrent %s", ct.Name)
|
c.logger.Debug().Msgf("Reinserted torrent %s", ct.Name)
|
||||||
}
|
}
|
||||||
@@ -614,11 +626,12 @@ func (c *Cache) GetDownloadLink(torrentId, filename, fileLink string) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, request.HosterUnavailableError) {
|
if errors.Is(err, request.HosterUnavailableError) {
|
||||||
c.logger.Error().Err(err).Msgf("Hoster is unavailable. Triggering repair for %s", ct.Name)
|
c.logger.Error().Err(err).Msgf("Hoster is unavailable. Triggering repair for %s", ct.Name)
|
||||||
err := c.reInsertTorrent(ct)
|
newCt, err := c.reInsertTorrent(ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error().Err(err).Msgf("Failed to reinsert torrent %s", ct.Name)
|
c.logger.Error().Err(err).Msgf("Failed to reinsert torrent %s", ct.Name)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
ct = newCt
|
||||||
c.logger.Debug().Msgf("Reinserted torrent %s", ct.Name)
|
c.logger.Debug().Msgf("Reinserted torrent %s", ct.Name)
|
||||||
file = ct.Files[filename]
|
file = ct.Files[filename]
|
||||||
// Retry getting the download link
|
// Retry getting the download link
|
||||||
|
|||||||
@@ -109,11 +109,11 @@ func (c *Cache) refreshTorrents() {
|
|||||||
if len(newTorrents) == 0 {
|
if len(newTorrents) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.logger.Debug().Msgf("Found %d new torrents", len(newTorrents))
|
|
||||||
|
|
||||||
workChan := make(chan *types.Torrent, min(100, len(newTorrents)))
|
workChan := make(chan *types.Torrent, min(100, len(newTorrents)))
|
||||||
errChan := make(chan error, len(newTorrents))
|
errChan := make(chan error, len(newTorrents))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
counter := 0
|
||||||
|
|
||||||
for i := 0; i < c.workers; i++ {
|
for i := 0; i < c.workers; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
@@ -121,13 +121,12 @@ func (c *Cache) refreshTorrents() {
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for t := range workChan {
|
for t := range workChan {
|
||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
|
||||||
return
|
|
||||||
default:
|
default:
|
||||||
}
|
if err := c.ProcessTorrent(t); err != nil {
|
||||||
if err := c.ProcessTorrent(t); err != nil {
|
c.logger.Error().Err(err).Msgf("Failed to process new torrent %s", t.Id)
|
||||||
c.logger.Error().Err(err).Msgf("Failed to process new torrent %s", t.Id)
|
errChan <- err
|
||||||
errChan <- err
|
}
|
||||||
|
counter++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -135,8 +134,6 @@ func (c *Cache) refreshTorrents() {
|
|||||||
|
|
||||||
for _, t := range newTorrents {
|
for _, t := range newTorrents {
|
||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
|
||||||
break
|
|
||||||
default:
|
default:
|
||||||
workChan <- t
|
workChan <- t
|
||||||
}
|
}
|
||||||
@@ -146,7 +143,7 @@ func (c *Cache) refreshTorrents() {
|
|||||||
|
|
||||||
c.RefreshListings(true)
|
c.RefreshListings(true)
|
||||||
|
|
||||||
c.logger.Debug().Msgf("Processed %d new torrents", len(newTorrents))
|
c.logger.Debug().Msgf("Processed %d new torrents", counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) RefreshRclone() error {
|
func (c *Cache) RefreshRclone() error {
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ func (c *Cache) IsTorrentBroken(t *CachedTorrent, filenames []string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t == nil || t.Torrent == nil {
|
||||||
|
c.logger.Error().Str("torrentId", t.Torrent.Id).Msg("Failed to refresh torrent")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
files = t.Files
|
files = t.Files
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
@@ -58,13 +63,11 @@ func (c *Cache) IsTorrentBroken(t *CachedTorrent, filenames []string) bool {
|
|||||||
// Try to reinsert the torrent if it's broken
|
// Try to reinsert the torrent if it's broken
|
||||||
if cfg.Repair.ReInsert && isBroken && t.Torrent != nil {
|
if cfg.Repair.ReInsert && isBroken && t.Torrent != nil {
|
||||||
// Check if the torrent is already in progress
|
// Check if the torrent is already in progress
|
||||||
if err := c.reInsertTorrent(t); err != nil {
|
if _, err := c.reInsertTorrent(t); err != nil {
|
||||||
c.logger.Error().Err(err).Str("torrentId", t.Torrent.Id).Msg("Failed to reinsert torrent")
|
c.logger.Error().Err(err).Str("torrentId", t.Torrent.Id).Msg("Failed to reinsert torrent")
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
c.logger.Debug().Str("torrentId", t.Torrent.Id).Msg("Reinserted torrent")
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return isBroken
|
return isBroken
|
||||||
@@ -86,7 +89,7 @@ func (c *Cache) repairWorker() {
|
|||||||
switch req.Type {
|
switch req.Type {
|
||||||
case RepairTypeReinsert:
|
case RepairTypeReinsert:
|
||||||
c.logger.Debug().Str("torrentId", torrentId).Msg("Reinserting torrent")
|
c.logger.Debug().Str("torrentId", torrentId).Msg("Reinserting torrent")
|
||||||
if err := c.reInsertTorrent(cachedTorrent); err != nil {
|
if _, err := c.reInsertTorrent(cachedTorrent); err != nil {
|
||||||
c.logger.Error().Err(err).Str("torrentId", cachedTorrent.Id).Msg("Failed to reinsert torrent")
|
c.logger.Error().Err(err).Str("torrentId", cachedTorrent.Id).Msg("Failed to reinsert torrent")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -100,12 +103,12 @@ func (c *Cache) repairWorker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) reInsertTorrent(ct *CachedTorrent) error {
|
func (c *Cache) reInsertTorrent(ct *CachedTorrent) (*CachedTorrent, error) {
|
||||||
// Check if Magnet is not empty, if empty, reconstruct the magnet
|
// Check if Magnet is not empty, if empty, reconstruct the magnet
|
||||||
torrent := ct.Torrent
|
torrent := ct.Torrent
|
||||||
oldID := torrent.Id // Store the old ID
|
oldID := torrent.Id // Store the old ID
|
||||||
if _, ok := c.repairsInProgress.Load(oldID); ok {
|
if _, ok := c.repairsInProgress.Load(oldID); ok {
|
||||||
return fmt.Errorf("repair already in progress for torrent %s", torrent.Id)
|
return ct, fmt.Errorf("repair already in progress for torrent %s", torrent.Id)
|
||||||
}
|
}
|
||||||
c.repairsInProgress.Store(oldID, struct{}{})
|
c.repairsInProgress.Store(oldID, struct{}{})
|
||||||
defer c.repairsInProgress.Delete(oldID)
|
defer c.repairsInProgress.Delete(oldID)
|
||||||
@@ -120,53 +123,53 @@ func (c *Cache) reInsertTorrent(ct *CachedTorrent) error {
|
|||||||
torrent, err = c.client.SubmitMagnet(torrent)
|
torrent, err = c.client.SubmitMagnet(torrent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Remove the old torrent from the cache and debrid service
|
// Remove the old torrent from the cache and debrid service
|
||||||
return fmt.Errorf("failed to submit magnet: %w", err)
|
return ct, fmt.Errorf("failed to submit magnet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the torrent was submitted
|
// Check if the torrent was submitted
|
||||||
if torrent == nil || torrent.Id == "" {
|
if torrent == nil || torrent.Id == "" {
|
||||||
return fmt.Errorf("failed to submit magnet: empty torrent")
|
return ct, fmt.Errorf("failed to submit magnet: empty torrent")
|
||||||
}
|
}
|
||||||
torrent.DownloadUncached = false // Set to false, avoid re-downloading
|
torrent.DownloadUncached = false // Set to false, avoid re-downloading
|
||||||
torrent, err = c.client.CheckStatus(torrent, true)
|
torrent, err = c.client.CheckStatus(torrent, true)
|
||||||
if err != nil && torrent != nil {
|
if err != nil && torrent != nil {
|
||||||
// Torrent is likely in progress
|
// Torrent is likely in progress
|
||||||
_ = c.DeleteTorrent(torrent.Id)
|
_ = c.DeleteTorrent(torrent.Id)
|
||||||
|
return ct, fmt.Errorf("failed to check status: %w", err)
|
||||||
return fmt.Errorf("failed to check status: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if torrent == nil {
|
if torrent == nil {
|
||||||
return fmt.Errorf("failed to check status: empty torrent")
|
return ct, fmt.Errorf("failed to check status: empty torrent")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the torrent in the cache
|
// Update the torrent in the cache
|
||||||
addedOn, err := time.Parse(time.RFC3339, torrent.Added)
|
addedOn, err := time.Parse(time.RFC3339, torrent.Added)
|
||||||
|
if err != nil {
|
||||||
|
addedOn = time.Now()
|
||||||
|
}
|
||||||
for _, f := range torrent.Files {
|
for _, f := range torrent.Files {
|
||||||
if f.Link == "" {
|
if f.Link == "" {
|
||||||
// Delete the new torrent
|
// Delete the new torrent
|
||||||
_ = c.DeleteTorrent(torrent.Id)
|
_ = c.DeleteTorrent(torrent.Id)
|
||||||
return fmt.Errorf("failed to reinsert torrent: empty link")
|
return ct, fmt.Errorf("failed to reinsert torrent: empty link")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
addedOn = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can safely delete the old torrent here
|
// We can safely delete the old torrent here
|
||||||
if oldID != "" {
|
if oldID != "" {
|
||||||
if err := c.DeleteTorrent(oldID); err != nil {
|
if err := c.DeleteTorrent(oldID); err != nil {
|
||||||
return fmt.Errorf("failed to delete old torrent: %w", err)
|
return ct, fmt.Errorf("failed to delete old torrent: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ct.Torrent = torrent
|
ct = &CachedTorrent{
|
||||||
ct.IsComplete = len(torrent.Files) > 0
|
Torrent: torrent,
|
||||||
ct.AddedOn = addedOn
|
AddedOn: addedOn,
|
||||||
|
IsComplete: len(torrent.Files) > 0,
|
||||||
|
}
|
||||||
c.setTorrent(ct)
|
c.setTorrent(ct)
|
||||||
c.RefreshListings(true)
|
c.RefreshListings(true)
|
||||||
c.logger.Debug().Str("torrentId", torrent.Id).Msg("Reinserted torrent")
|
c.logger.Debug().Str("torrentId", torrent.Id).Msg("Reinserted torrent")
|
||||||
|
|
||||||
return nil
|
return ct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) resetInvalidLinks() {
|
func (c *Cache) resetInvalidLinks() {
|
||||||
|
|||||||
@@ -181,8 +181,7 @@ func (q *QBit) UpdateTorrentMin(t *Torrent, debridTorrent *debrid.Torrent) *Torr
|
|||||||
addedOn = time.Now()
|
addedOn = time.Now()
|
||||||
}
|
}
|
||||||
totalSize := debridTorrent.Bytes
|
totalSize := debridTorrent.Bytes
|
||||||
progress := cmp.Or(debridTorrent.Progress, 0.0)
|
progress := (cmp.Or(debridTorrent.Progress, 0.0)) / 100.0
|
||||||
progress = progress / 100.0
|
|
||||||
sizeCompleted := int64(float64(totalSize) * progress)
|
sizeCompleted := int64(float64(totalSize) * progress)
|
||||||
|
|
||||||
var speed int64
|
var speed int64
|
||||||
@@ -218,9 +217,11 @@ func (q *QBit) UpdateTorrent(t *Torrent, debridTorrent *debrid.Torrent) *Torrent
|
|||||||
if debridTorrent == nil {
|
if debridTorrent == nil {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
_db := service.GetDebrid().GetClient(debridTorrent.Debrid)
|
|
||||||
if debridTorrent.Status != "downloaded" {
|
if debridClient := service.GetDebrid().GetClient(debridTorrent.Debrid); debridClient != nil {
|
||||||
_ = _db.UpdateTorrent(debridTorrent)
|
if debridTorrent.Status != "downloaded" {
|
||||||
|
_ = debridClient.UpdateTorrent(debridTorrent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t = q.UpdateTorrentMin(t, debridTorrent)
|
t = q.UpdateTorrentMin(t, debridTorrent)
|
||||||
t.ContentPath = t.TorrentPath + string(os.PathSeparator)
|
t.ContentPath = t.TorrentPath + string(os.PathSeparator)
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ type Torrent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) IsReady() bool {
|
func (t *Torrent) IsReady() bool {
|
||||||
return t.AmountLeft <= 0 && t.TorrentPath != ""
|
return (t.AmountLeft <= 0 || t.Progress == 1) && t.TorrentPath != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Torrent) discordContext() string {
|
func (t *Torrent) discordContext() string {
|
||||||
|
|||||||
@@ -81,14 +81,14 @@ func (f *File) stream() (*http.Response, error) {
|
|||||||
|
|
||||||
downloadLink = f.getDownloadLink() // Uses the first API key
|
downloadLink = f.getDownloadLink() // Uses the first API key
|
||||||
if downloadLink == "" {
|
if downloadLink == "" {
|
||||||
_log.Error().Msgf("Failed to get download link for %s. Empty download link", f.name)
|
_log.Trace().Msgf("Failed to get download link for %s. Empty download link", f.name)
|
||||||
return nil, fmt.Errorf("failed to get download link")
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", downloadLink, nil)
|
req, err := http.NewRequest("GET", downloadLink, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_log.Error().Msgf("Failed to create HTTP request: %s", err)
|
_log.Trace().Msgf("Failed to create HTTP request: %s", err)
|
||||||
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.offset > 0 {
|
if f.offset > 0 {
|
||||||
@@ -97,7 +97,7 @@ func (f *File) stream() (*http.Response, error) {
|
|||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("HTTP request error: %w", err)
|
return resp, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
|
||||||
@@ -113,15 +113,17 @@ func (f *File) stream() (*http.Response, error) {
|
|||||||
b, _ := io.ReadAll(resp.Body)
|
b, _ := io.ReadAll(resp.Body)
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
_log.Trace().Msgf("Failed to close response body: %s", err)
|
||||||
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
if strings.Contains(string(b), "You can not download this file because you have exceeded your traffic on this hoster") {
|
if strings.Contains(string(b), "You can not download this file because you have exceeded your traffic on this hoster") {
|
||||||
_log.Error().Msgf("Failed to get download link for %s. Download link expired", f.name)
|
_log.Trace().Msgf("Failed to get download link for %s. Download link expired", f.name)
|
||||||
f.cache.MarkDownloadLinkAsInvalid(f.link, downloadLink, "bandwidth_exceeded")
|
f.cache.MarkDownloadLinkAsInvalid(f.link, downloadLink, "bandwidth_exceeded")
|
||||||
// Retry with a different API key if it's available
|
// Retry with a different API key if it's available
|
||||||
return f.stream()
|
return f.stream()
|
||||||
} else {
|
} else {
|
||||||
return resp, fmt.Errorf("link not found")
|
_log.Trace().Msgf("Failed to get download link for %s. %s", f.name, string(b))
|
||||||
|
return resp, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if resp.StatusCode == http.StatusNotFound {
|
} else if resp.StatusCode == http.StatusNotFound {
|
||||||
@@ -132,12 +134,12 @@ func (f *File) stream() (*http.Response, error) {
|
|||||||
// Generate a new download link
|
// Generate a new download link
|
||||||
downloadLink = f.getDownloadLink()
|
downloadLink = f.getDownloadLink()
|
||||||
if downloadLink == "" {
|
if downloadLink == "" {
|
||||||
_log.Error().Msgf("Failed to get download link for %s", f.name)
|
_log.Trace().Msgf("Failed to get download link for %s", f.name)
|
||||||
return nil, fmt.Errorf("failed to get download link")
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
req, err = http.NewRequest("GET", downloadLink, nil)
|
req, err = http.NewRequest("GET", downloadLink, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
if f.offset > 0 {
|
if f.offset > 0 {
|
||||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", f.offset))
|
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", f.offset))
|
||||||
@@ -151,13 +153,13 @@ func (f *File) stream() (*http.Response, error) {
|
|||||||
closeResp()
|
closeResp()
|
||||||
// Read the body to consume the response
|
// Read the body to consume the response
|
||||||
f.cache.MarkDownloadLinkAsInvalid(f.link, downloadLink, "link_not_found")
|
f.cache.MarkDownloadLinkAsInvalid(f.link, downloadLink, "link_not_found")
|
||||||
return resp, fmt.Errorf("link not found")
|
return resp, io.EOF
|
||||||
}
|
}
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
closeResp()
|
closeResp()
|
||||||
return resp, fmt.Errorf("unexpected HTTP status: %d", resp.StatusCode)
|
return resp, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -194,7 +196,7 @@ func (f *File) Read(p []byte) (n int, err error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
return 0, fmt.Errorf("failed to get response")
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
f.reader = resp.Body
|
f.reader = resp.Body
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/sirrobot01/decypharr/internal/request"
|
"github.com/sirrobot01/decypharr/internal/request"
|
||||||
|
"github.com/sirrobot01/decypharr/internal/utils"
|
||||||
"github.com/sirrobot01/decypharr/pkg/debrid/debrid"
|
"github.com/sirrobot01/decypharr/pkg/debrid/debrid"
|
||||||
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
"github.com/sirrobot01/decypharr/pkg/debrid/types"
|
||||||
"github.com/sirrobot01/decypharr/pkg/version"
|
"github.com/sirrobot01/decypharr/pkg/version"
|
||||||
@@ -103,7 +104,7 @@ func (h *Handler) getParentFiles() []os.FileInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
|
func (h *Handler) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
|
||||||
name = path.Clean("/" + name)
|
name = utils.UnescapePath(path.Clean("/" + name))
|
||||||
rootDir := h.getRootPath()
|
rootDir := h.getRootPath()
|
||||||
|
|
||||||
metadataOnly := ctx.Value("metadataOnly") != nil
|
metadataOnly := ctx.Value("metadataOnly") != nil
|
||||||
@@ -261,16 +262,16 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// __all__ or torrents folder
|
// __all__ or torrents folder
|
||||||
// Manually build the xml
|
// Manually build the xml
|
||||||
ttl = 30 * time.Second
|
ttl = 30 * time.Second
|
||||||
if served := h.serveFromCacheIfValid(w, r, cacheKey, ttl); served {
|
//if served := h.serveFromCacheIfValid(w, r, cacheKey, ttl); served {
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
// Refresh the parent XML
|
//// Refresh the parent XML
|
||||||
h.cache.RefreshListings(false)
|
//h.cache.RefreshListings(false)
|
||||||
// Check again if the cache is valid
|
//// Check again if the cache is valid
|
||||||
// If not, we will use the default WebDAV handler
|
//// If not, we will use the default WebDAV handler
|
||||||
if served := h.serveFromCacheIfValid(w, r, cacheKey, ttl); served {
|
//if served := h.serveFromCacheIfValid(w, r, cacheKey, ttl); served {
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
if served := h.serveFromCacheIfValid(w, r, cacheKey, ttl); served {
|
if served := h.serveFromCacheIfValid(w, r, cacheKey, ttl); served {
|
||||||
|
|||||||
Reference in New Issue
Block a user