mirror of
https://github.com/5rahim/seanime
synced 2026-04-18 22:24:55 +02:00
fix autodownloader download now for queued items
This commit is contained in:
@@ -2,14 +2,19 @@ package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"seanime/internal/api/anilist"
|
||||
"seanime/internal/database/db_bridge"
|
||||
"seanime/internal/database/models"
|
||||
hibiketorrent "seanime/internal/extension/hibike/torrent"
|
||||
"seanime/internal/library/autodownloader"
|
||||
"seanime/internal/torrent_clients/torrent_client"
|
||||
torrentrepo "seanime/internal/torrents/torrent"
|
||||
"seanime/internal/util"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
@@ -314,10 +319,30 @@ func (h *Handler) HandleTorrentClientAddMagnetFromRule(c echo.Context) error {
|
||||
return h.RespondWithError(c, err)
|
||||
}
|
||||
|
||||
if b.MagnetUrl == "" || b.RuleId == 0 {
|
||||
if b.RuleId == 0 || (b.MagnetUrl == "" && b.QueuedItemId == 0) {
|
||||
return h.RespondWithError(c, errors.New("missing parameters"))
|
||||
}
|
||||
|
||||
magnetURL := b.MagnetUrl
|
||||
if magnetURL == "" {
|
||||
item, err := h.App.Database.GetAutoDownloaderItem(b.QueuedItemId)
|
||||
if err != nil {
|
||||
return h.RespondWithError(c, err)
|
||||
}
|
||||
|
||||
magnetURL, err = resolveAutoDownloaderItemMagnet(item, h.App.TorrentRepository)
|
||||
if err != nil {
|
||||
return h.RespondWithError(c, err)
|
||||
}
|
||||
|
||||
if item.Magnet != magnetURL {
|
||||
item.Magnet = magnetURL
|
||||
if err := h.App.Database.UpdateAutoDownloaderItem(item.ID, item); err != nil {
|
||||
h.App.Logger.Warn().Err(err).Uint("queuedItemId", item.ID).Msg("torrent client: Failed to cache resolved queued magnet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get rule from database
|
||||
rule, err := db_bridge.GetAutoDownloaderRule(h.App.Database, b.RuleId)
|
||||
if err != nil {
|
||||
@@ -331,7 +356,7 @@ func (h *Handler) HandleTorrentClientAddMagnetFromRule(c echo.Context) error {
|
||||
}
|
||||
|
||||
// try to add torrents to client, on error return error
|
||||
err = h.App.TorrentClientRepository.AddMagnets([]string{b.MagnetUrl}, rule.Destination)
|
||||
err = h.App.TorrentClientRepository.AddMagnets([]string{magnetURL}, rule.Destination)
|
||||
if err != nil {
|
||||
return h.RespondWithError(c, err)
|
||||
}
|
||||
@@ -344,3 +369,49 @@ func (h *Handler) HandleTorrentClientAddMagnetFromRule(c echo.Context) error {
|
||||
return h.RespondWithData(c, true)
|
||||
|
||||
}
|
||||
|
||||
func resolveAutoDownloaderItemMagnet(item *models.AutoDownloaderItem, torrentRepository *torrentrepo.Repository) (string, error) {
|
||||
if item == nil {
|
||||
return "", errors.New("queued item not found")
|
||||
}
|
||||
|
||||
if item.Magnet != "" {
|
||||
return item.Magnet, nil
|
||||
}
|
||||
|
||||
fallbackHash := item.Hash
|
||||
var resolveErr error
|
||||
|
||||
if len(item.TorrentData) > 0 {
|
||||
var storedTorrent autodownloader.NormalizedTorrent
|
||||
if err := json.Unmarshal(item.TorrentData, &storedTorrent); err != nil {
|
||||
resolveErr = err
|
||||
} else if storedTorrent.AnimeTorrent != nil {
|
||||
if fallbackHash == "" {
|
||||
fallbackHash = storedTorrent.AnimeTorrent.InfoHash
|
||||
}
|
||||
|
||||
if storedTorrent.AnimeTorrent.Provider == "" && storedTorrent.ExtensionID != "" {
|
||||
storedTorrent.AnimeTorrent.Provider = storedTorrent.ExtensionID
|
||||
}
|
||||
|
||||
if torrentRepository != nil {
|
||||
magnet, err := torrentRepository.ResolveMagnetLink(storedTorrent.AnimeTorrent)
|
||||
if err == nil && magnet != "" {
|
||||
return magnet, nil
|
||||
}
|
||||
resolveErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fallbackHash != "" {
|
||||
return fmt.Sprintf("magnet:?xt=urn:btih:%s", fallbackHash), nil
|
||||
}
|
||||
|
||||
if resolveErr != nil {
|
||||
return "", resolveErr
|
||||
}
|
||||
|
||||
return "", errors.New("magnet link not found")
|
||||
}
|
||||
|
||||
129
internal/handlers/torrent_client_test.go
Normal file
129
internal/handlers/torrent_client_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"seanime/internal/api/metadata_provider"
|
||||
"seanime/internal/database/models"
|
||||
"seanime/internal/extension"
|
||||
hibiketorrent "seanime/internal/extension/hibike/torrent"
|
||||
"seanime/internal/library/autodownloader"
|
||||
torrentrepo "seanime/internal/torrents/torrent"
|
||||
"seanime/internal/util"
|
||||
)
|
||||
|
||||
type ruleMagnetTestProvider struct {
|
||||
magnet string
|
||||
err error
|
||||
calls int
|
||||
}
|
||||
|
||||
func (p *ruleMagnetTestProvider) Search(hibiketorrent.AnimeSearchOptions) ([]*hibiketorrent.AnimeTorrent, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *ruleMagnetTestProvider) SmartSearch(hibiketorrent.AnimeSmartSearchOptions) ([]*hibiketorrent.AnimeTorrent, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *ruleMagnetTestProvider) GetTorrentInfoHash(torrent *hibiketorrent.AnimeTorrent) (string, error) {
|
||||
if torrent == nil {
|
||||
return "", nil
|
||||
}
|
||||
return torrent.InfoHash, nil
|
||||
}
|
||||
|
||||
func (p *ruleMagnetTestProvider) GetTorrentMagnetLink(*hibiketorrent.AnimeTorrent) (string, error) {
|
||||
p.calls++
|
||||
if p.err != nil {
|
||||
return "", p.err
|
||||
}
|
||||
return p.magnet, nil
|
||||
}
|
||||
|
||||
func (p *ruleMagnetTestProvider) GetLatest() ([]*hibiketorrent.AnimeTorrent, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *ruleMagnetTestProvider) GetSettings() hibiketorrent.AnimeProviderSettings {
|
||||
return hibiketorrent.AnimeProviderSettings{Type: hibiketorrent.AnimeProviderTypeMain}
|
||||
}
|
||||
|
||||
func TestResolveAutoDownloaderItemMagnetUsesStoredTorrentExtension(t *testing.T) {
|
||||
provider := &ruleMagnetTestProvider{magnet: "magnet:?xt=urn:btih:resolved-from-provider"}
|
||||
repo := newTorrentRepositoryForRuleMagnetTests(map[string]*ruleMagnetTestProvider{"fake": provider})
|
||||
|
||||
torrentData, err := json.Marshal(&autodownloader.NormalizedTorrent{
|
||||
AnimeTorrent: &hibiketorrent.AnimeTorrent{
|
||||
Name: "Example torrent",
|
||||
InfoHash: "hash-from-torrent",
|
||||
},
|
||||
ExtensionID: "fake",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
item := &models.AutoDownloaderItem{
|
||||
Hash: "hash-from-item",
|
||||
TorrentData: torrentData,
|
||||
}
|
||||
|
||||
magnet, err := resolveAutoDownloaderItemMagnet(item, repo)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "magnet:?xt=urn:btih:resolved-from-provider", magnet)
|
||||
assert.Equal(t, 1, provider.calls)
|
||||
}
|
||||
|
||||
func TestResolveAutoDownloaderItemMagnetFallsBackToHash(t *testing.T) {
|
||||
provider := &ruleMagnetTestProvider{err: errors.New("provider failed")}
|
||||
repo := newTorrentRepositoryForRuleMagnetTests(map[string]*ruleMagnetTestProvider{"fake": provider})
|
||||
|
||||
torrentData, err := json.Marshal(&autodownloader.NormalizedTorrent{
|
||||
AnimeTorrent: &hibiketorrent.AnimeTorrent{
|
||||
Name: "Example torrent",
|
||||
InfoHash: "hash-from-torrent",
|
||||
},
|
||||
ExtensionID: "fake",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
item := &models.AutoDownloaderItem{
|
||||
Hash: "hash-from-item",
|
||||
TorrentData: torrentData,
|
||||
}
|
||||
|
||||
magnet, err := resolveAutoDownloaderItemMagnet(item, repo)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "magnet:?xt=urn:btih:hash-from-item", magnet)
|
||||
assert.Equal(t, 1, provider.calls)
|
||||
}
|
||||
|
||||
func newTorrentRepositoryForRuleMagnetTests(providers map[string]*ruleMagnetTestProvider) *torrentrepo.Repository {
|
||||
logger := zerolog.Nop()
|
||||
bank := extension.NewUnifiedBank()
|
||||
for id, provider := range providers {
|
||||
bank.Set(id, extension.NewAnimeTorrentProviderExtension(&extension.Extension{
|
||||
ID: id,
|
||||
Name: id,
|
||||
Version: "1.0.0",
|
||||
ManifestURI: "builtin",
|
||||
Language: extension.LanguageGo,
|
||||
Type: extension.TypeAnimeTorrentProvider,
|
||||
}, provider))
|
||||
}
|
||||
|
||||
var metadata metadata_provider.Provider
|
||||
repo := torrentrepo.NewRepository(&torrentrepo.NewRepositoryOptions{
|
||||
Logger: &logger,
|
||||
MetadataProviderRef: util.NewRef[metadata_provider.Provider](metadata),
|
||||
ExtensionBankRef: util.NewRef(bank),
|
||||
})
|
||||
repo.SetSettings(&torrentrepo.RepositorySettings{DefaultAnimeProvider: "fake"})
|
||||
|
||||
return repo
|
||||
}
|
||||
@@ -665,6 +665,7 @@ func (ad *AutoDownloader) handleDelayedItem(
|
||||
|
||||
storedItem.Link = bestCandidate.Torrent.Link
|
||||
storedItem.Hash = bestCandidate.Torrent.InfoHash
|
||||
storedItem.Magnet = bestCandidate.Torrent.MagnetLink
|
||||
storedItem.TorrentName = bestCandidate.Torrent.Name
|
||||
storedItem.Score = bestCandidate.Score
|
||||
// Do NOT reset DelayUntil, keep the original timer
|
||||
@@ -899,6 +900,7 @@ func (ad *AutoDownloader) queueTorrentForDelay(isSimulation bool, rule *anime.Au
|
||||
Episode: episode,
|
||||
Link: candidate.Torrent.Link,
|
||||
Hash: candidate.Torrent.InfoHash,
|
||||
Magnet: candidate.Torrent.MagnetLink,
|
||||
TorrentName: candidate.Torrent.Name,
|
||||
Downloaded: false,
|
||||
IsDelayed: true,
|
||||
|
||||
@@ -1250,7 +1250,7 @@ func TestDelayIntegration(t *testing.T) {
|
||||
{
|
||||
name: "Queue item for delay",
|
||||
torrents: []*hibiketorrent.AnimeTorrent{
|
||||
{Name: "[SubsPlease] Sousou no Frieren - 01 (1080p).mkv", InfoHash: "hash1", Seeders: 100},
|
||||
{Name: "[SubsPlease] Sousou no Frieren - 01 (1080p).mkv", InfoHash: "hash1", MagnetLink: "magnet:?xt=urn:btih:hash1", Seeders: 100},
|
||||
},
|
||||
profile: &anime.AutoDownloaderProfile{
|
||||
Conditions: []anime.AutoDownloaderCondition{{Term: "1080p", Action: anime.AutoDownloaderProfileRuleFormatActionScore, Score: 10}},
|
||||
@@ -1263,6 +1263,7 @@ func TestDelayIntegration(t *testing.T) {
|
||||
assert.True(t, items[0].IsDelayed) // MUST be true
|
||||
assert.False(t, items[0].Downloaded) // will always be false
|
||||
assert.Equal(t, "hash1", items[0].Hash)
|
||||
assert.Equal(t, "magnet:?xt=urn:btih:hash1", items[0].Magnet)
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1312,7 +1313,7 @@ func TestDelayIntegration(t *testing.T) {
|
||||
{
|
||||
name: "Upgrade delayed item",
|
||||
torrents: []*hibiketorrent.AnimeTorrent{
|
||||
{Name: "[BetterGroup] Sousou no Frieren - 01 (1080p).mkv", InfoHash: "hash_better", Seeders: 100}, // Score 20
|
||||
{Name: "[BetterGroup] Sousou no Frieren - 01 (1080p).mkv", InfoHash: "hash_better", MagnetLink: "magnet:?xt=urn:btih:hash_better", Seeders: 100}, // Score 20
|
||||
},
|
||||
existingItems: []*models.AutoDownloaderItem{
|
||||
{
|
||||
@@ -1320,6 +1321,7 @@ func TestDelayIntegration(t *testing.T) {
|
||||
MediaID: mediaId,
|
||||
Episode: 1,
|
||||
Hash: "hash_bad",
|
||||
Magnet: "magnet:?xt=urn:btih:hash_bad",
|
||||
Score: 10,
|
||||
IsDelayed: true,
|
||||
DelayUntil: time.Now().Add(5 * time.Minute), // Not expired
|
||||
@@ -1339,6 +1341,7 @@ func TestDelayIntegration(t *testing.T) {
|
||||
require.Len(t, items, 1)
|
||||
assert.True(t, items[0].IsDelayed)
|
||||
assert.Equal(t, "hash_better", items[0].Hash) // Updated hash
|
||||
assert.Equal(t, "magnet:?xt=urn:btih:hash_better", items[0].Magnet)
|
||||
assert.Equal(t, 20, items[0].Score)
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user