node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1 @@
package torrent_client

View File

@@ -0,0 +1,420 @@
package torrent_client
import (
"context"
"errors"
"github.com/hekmon/transmissionrpc/v3"
"github.com/rs/zerolog"
"seanime/internal/api/metadata"
"seanime/internal/events"
"seanime/internal/torrent_clients/qbittorrent"
"seanime/internal/torrent_clients/qbittorrent/model"
"seanime/internal/torrent_clients/transmission"
"seanime/internal/torrents/torrent"
"strconv"
"time"
)
const (
QbittorrentClient = "qbittorrent"
TransmissionClient = "transmission"
NoneClient = "none"
)
type (
Repository struct {
logger *zerolog.Logger
qBittorrentClient *qbittorrent.Client
transmission *transmission.Transmission
torrentRepository *torrent.Repository
provider string
metadataProvider metadata.Provider
activeTorrentCountCtxCancel context.CancelFunc
activeTorrentCount *ActiveCount
}
NewRepositoryOptions struct {
Logger *zerolog.Logger
QbittorrentClient *qbittorrent.Client
Transmission *transmission.Transmission
TorrentRepository *torrent.Repository
Provider string
MetadataProvider metadata.Provider
}
ActiveCount struct {
Downloading int `json:"downloading"`
Seeding int `json:"seeding"`
Paused int `json:"paused"`
}
)
func NewRepository(opts *NewRepositoryOptions) *Repository {
if opts.Provider == "" {
opts.Provider = QbittorrentClient
}
return &Repository{
logger: opts.Logger,
qBittorrentClient: opts.QbittorrentClient,
transmission: opts.Transmission,
torrentRepository: opts.TorrentRepository,
provider: opts.Provider,
metadataProvider: opts.MetadataProvider,
activeTorrentCount: &ActiveCount{},
}
}
func (r *Repository) Shutdown() {
if r.activeTorrentCountCtxCancel != nil {
r.activeTorrentCountCtxCancel()
r.activeTorrentCountCtxCancel = nil
}
}
func (r *Repository) InitActiveTorrentCount(enabled bool, wsEventManager events.WSEventManagerInterface) {
if r.activeTorrentCountCtxCancel != nil {
r.activeTorrentCountCtxCancel()
}
if !enabled {
return
}
var ctx context.Context
ctx, r.activeTorrentCountCtxCancel = context.WithCancel(context.Background())
go func(ctx context.Context) {
ticker := time.NewTicker(time.Second * 5)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
r.GetActiveCount(r.activeTorrentCount)
wsEventManager.SendEvent(events.ActiveTorrentCountUpdated, r.activeTorrentCount)
}
}
}(ctx)
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (r *Repository) GetProvider() string {
return r.provider
}
func (r *Repository) Start() bool {
switch r.provider {
case QbittorrentClient:
return r.qBittorrentClient.CheckStart()
case TransmissionClient:
return r.transmission.CheckStart()
case NoneClient:
return true
default:
return false
}
}
func (r *Repository) TorrentExists(hash string) bool {
switch r.provider {
case QbittorrentClient:
p, err := r.qBittorrentClient.Torrent.GetProperties(hash)
return err == nil && p != nil
case TransmissionClient:
torrents, err := r.transmission.Client.TorrentGetAllForHashes(context.Background(), []string{hash})
return err == nil && len(torrents) > 0
default:
return false
}
}
// GetList will return all torrents from the torrent client.
func (r *Repository) GetList() ([]*Torrent, error) {
switch r.provider {
case QbittorrentClient:
torrents, err := r.qBittorrentClient.Torrent.GetList(&qbittorrent_model.GetTorrentListOptions{Filter: "all"})
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while getting torrent list (qBittorrent)")
return nil, err
}
return r.FromQbitTorrents(torrents), nil
case TransmissionClient:
torrents, err := r.transmission.Client.TorrentGetAll(context.Background())
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while getting torrent list (Transmission)")
return nil, err
}
return r.FromTransmissionTorrents(torrents), nil
default:
return nil, errors.New("torrent client: No torrent client provider found")
}
}
// GetActiveCount will return the count of active torrents (downloading, seeding, paused).
func (r *Repository) GetActiveCount(ret *ActiveCount) {
ret.Seeding = 0
ret.Downloading = 0
ret.Paused = 0
switch r.provider {
case QbittorrentClient:
torrents, err := r.qBittorrentClient.Torrent.GetList(&qbittorrent_model.GetTorrentListOptions{Filter: "downloading"})
if err != nil {
return
}
torrents2, err := r.qBittorrentClient.Torrent.GetList(&qbittorrent_model.GetTorrentListOptions{Filter: "seeding"})
if err != nil {
return
}
torrents = append(torrents, torrents2...)
for _, t := range torrents {
switch fromQbitTorrentStatus(t.State) {
case TorrentStatusDownloading:
ret.Downloading++
case TorrentStatusSeeding:
ret.Seeding++
case TorrentStatusPaused:
ret.Paused++
}
}
case TransmissionClient:
torrents, err := r.transmission.Client.TorrentGet(context.Background(), []string{"id", "status", "isFinished"}, nil)
if err != nil {
return
}
for _, t := range torrents {
if t.Status == nil || t.IsFinished == nil {
continue
}
switch fromTransmissionTorrentStatus(*t.Status, *t.IsFinished) {
case TorrentStatusDownloading:
ret.Downloading++
case TorrentStatusSeeding:
ret.Seeding++
case TorrentStatusPaused:
ret.Paused++
}
}
return
default:
return
}
}
// GetActiveTorrents will return all torrents that are currently downloading, paused or seeding.
func (r *Repository) GetActiveTorrents() ([]*Torrent, error) {
torrents, err := r.GetList()
if err != nil {
return nil, err
}
var active []*Torrent
for _, t := range torrents {
if t.Status == TorrentStatusDownloading || t.Status == TorrentStatusSeeding || t.Status == TorrentStatusPaused {
active = append(active, t)
}
}
return active, nil
}
func (r *Repository) AddMagnets(magnets []string, dest string) error {
r.logger.Trace().Any("magnets", magnets).Msg("torrent client: Adding magnets")
if len(magnets) == 0 {
r.logger.Debug().Msg("torrent client: No magnets to add")
return nil
}
var err error
switch r.provider {
case QbittorrentClient:
err = r.qBittorrentClient.Torrent.AddURLs(magnets, &qbittorrent_model.AddTorrentsOptions{
Savepath: dest,
Tags: r.qBittorrentClient.Tags,
})
case TransmissionClient:
for _, magnet := range magnets {
_, err = r.transmission.Client.TorrentAdd(context.Background(), transmissionrpc.TorrentAddPayload{
Filename: &magnet,
DownloadDir: &dest,
})
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while adding magnets (Transmission)")
break
}
}
case NoneClient:
return errors.New("torrent client: No torrent client selected")
}
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while adding magnets")
return err
}
r.logger.Debug().Msg("torrent client: Added torrents")
return nil
}
func (r *Repository) RemoveTorrents(hashes []string) error {
r.logger.Trace().Msg("torrent client: Removing torrents")
var err error
switch r.provider {
case QbittorrentClient:
err = r.qBittorrentClient.Torrent.DeleteTorrents(hashes, true)
case TransmissionClient:
torrents, err := r.transmission.Client.TorrentGetAllForHashes(context.Background(), hashes)
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while fetching torrents (Transmission)")
return err
}
ids := make([]int64, len(torrents))
for i, t := range torrents {
ids[i] = *t.ID
}
err = r.transmission.Client.TorrentRemove(context.Background(), transmissionrpc.TorrentRemovePayload{
IDs: ids,
DeleteLocalData: true,
})
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while removing torrents (Transmission)")
return err
}
}
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while removing torrents")
return err
}
r.logger.Debug().Any("hashes", hashes).Msg("torrent client: Removed torrents")
return nil
}
func (r *Repository) PauseTorrents(hashes []string) error {
r.logger.Trace().Msg("torrent client: Pausing torrents")
var err error
switch r.provider {
case QbittorrentClient:
err = r.qBittorrentClient.Torrent.StopTorrents(hashes)
case TransmissionClient:
err = r.transmission.Client.TorrentStopHashes(context.Background(), hashes)
}
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while pausing torrents")
return err
}
r.logger.Debug().Any("hashes", hashes).Msg("torrent client: Paused torrents")
return nil
}
func (r *Repository) ResumeTorrents(hashes []string) error {
r.logger.Trace().Msg("torrent client: Resuming torrents")
var err error
switch r.provider {
case QbittorrentClient:
err = r.qBittorrentClient.Torrent.ResumeTorrents(hashes)
case TransmissionClient:
err = r.transmission.Client.TorrentStartHashes(context.Background(), hashes)
}
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while resuming torrents")
return err
}
r.logger.Debug().Any("hashes", hashes).Msg("torrent client: Resumed torrents")
return nil
}
func (r *Repository) DeselectFiles(hash string, indices []int) error {
var err error
switch r.provider {
case QbittorrentClient:
strIndices := make([]string, len(indices), len(indices))
for i, v := range indices {
strIndices[i] = strconv.Itoa(v)
}
err = r.qBittorrentClient.Torrent.SetFilePriorities(hash, strIndices, 0)
case TransmissionClient:
torrents, err := r.transmission.Client.TorrentGetAllForHashes(context.Background(), []string{hash})
if err != nil || torrents[0].ID == nil {
r.logger.Err(err).Msg("torrent client: Error while deselecting files (Transmission)")
return err
}
id := *torrents[0].ID
ind := make([]int64, len(indices), len(indices))
for i, v := range indices {
ind[i] = int64(v)
}
err = r.transmission.Client.TorrentSet(context.Background(), transmissionrpc.TorrentSetPayload{
FilesUnwanted: ind,
IDs: []int64{id},
})
}
if err != nil {
r.logger.Err(err).Msg("torrent client: Error while deselecting files")
return err
}
r.logger.Debug().Str("hash", hash).Any("indices", indices).Msg("torrent client: Deselected torrent files")
return nil
}
// GetFiles blocks until the files are retrieved, or until timeout.
func (r *Repository) GetFiles(hash string) (filenames []string, err error) {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
filenames = make([]string, 0)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
done := make(chan struct{})
go func() {
r.logger.Debug().Str("hash", hash).Msg("torrent client: Getting torrent files")
defer close(done)
for {
select {
case <-ctx.Done():
err = errors.New("torrent client: Unable to retrieve torrent files (timeout)")
return
case <-ticker.C:
switch r.provider {
case QbittorrentClient:
qbitFiles, err := r.qBittorrentClient.Torrent.GetContents(hash)
if err == nil && qbitFiles != nil && len(qbitFiles) > 0 {
r.logger.Debug().Str("hash", hash).Int("count", len(qbitFiles)).Msg("torrent client: Retrieved torrent files")
for _, f := range qbitFiles {
filenames = append(filenames, f.Name)
}
return
}
case TransmissionClient:
torrents, err := r.transmission.Client.TorrentGetAllForHashes(context.Background(), []string{hash})
if err == nil && len(torrents) > 0 && torrents[0].Files != nil && len(torrents[0].Files) > 0 {
transmissionFiles := torrents[0].Files
r.logger.Debug().Str("hash", hash).Int("count", len(transmissionFiles)).Msg("torrent client: Retrieved torrent files")
for _, f := range transmissionFiles {
filenames = append(filenames, f.Name)
}
return
}
}
}
}
}()
<-done // wait for the files to be retrieved
return
}

View File

@@ -0,0 +1,154 @@
package torrent_client
import (
"errors"
"fmt"
"seanime/internal/api/anilist"
"seanime/internal/platforms/platform"
"seanime/internal/torrents/analyzer"
"time"
hibiketorrent "seanime/internal/extension/hibike/torrent"
)
type (
SmartSelectParams struct {
Torrent *hibiketorrent.AnimeTorrent
EpisodeNumbers []int
Media *anilist.CompleteAnime
Destination string
ShouldAddTorrent bool
Platform platform.Platform
}
)
// SmartSelect will automatically the provided episode files from the torrent.
// If the torrent has not been added yet, set SmartSelect.ShouldAddTorrent to true.
// The torrent will NOT be removed if the selection fails.
func (r *Repository) SmartSelect(p *SmartSelectParams) error {
if p.Media == nil || p.Platform == nil || r.torrentRepository == nil {
r.logger.Error().Msg("torrent client: media or platform is nil (smart select)")
return errors.New("media or anilist client wrapper is nil")
}
providerExtension, ok := r.torrentRepository.GetAnimeProviderExtension(p.Torrent.Provider)
if !ok {
r.logger.Error().Str("provider", p.Torrent.Provider).Msg("torrent client: provider extension not found (smart select)")
return errors.New("provider extension not found")
}
if p.Media.IsMovieOrSingleEpisode() {
return errors.New("smart select is not supported for movies or single-episode series")
}
if len(p.EpisodeNumbers) == 0 {
r.logger.Error().Msg("torrent client: no episode numbers provided (smart select)")
return errors.New("no episode numbers provided")
}
if p.ShouldAddTorrent {
r.logger.Info().Msg("torrent client: adding torrent (smart select)")
// Get magnet
magnet, err := providerExtension.GetProvider().GetTorrentMagnetLink(p.Torrent)
if err != nil {
return err
}
// Add the torrent
err = r.AddMagnets([]string{magnet}, p.Destination)
if err != nil {
return err
}
}
filepaths, err := r.GetFiles(p.Torrent.InfoHash)
if err != nil {
r.logger.Err(err).Msg("torrent client: error getting files (smart select)")
_ = r.RemoveTorrents([]string{p.Torrent.InfoHash})
return fmt.Errorf("error getting files, torrent still added: %w", err)
}
// Pause the torrent
err = r.PauseTorrents([]string{p.Torrent.InfoHash})
if err != nil {
r.logger.Err(err).Msg("torrent client: error while pausing torrent (smart select)")
_ = r.RemoveTorrents([]string{p.Torrent.InfoHash})
return fmt.Errorf("error while selecting files: %w", err)
}
// AnalyzeTorrentFiles the torrent files
analyzer := torrent_analyzer.NewAnalyzer(&torrent_analyzer.NewAnalyzerOptions{
Logger: r.logger,
Filepaths: filepaths,
Media: p.Media,
Platform: p.Platform,
MetadataProvider: r.metadataProvider,
})
r.logger.Debug().Msg("torrent client: analyzing torrent files (smart select)")
analysis, err := analyzer.AnalyzeTorrentFiles()
if err != nil {
r.logger.Err(err).Msg("torrent client: error while analyzing torrent files (smart select)")
_ = r.RemoveTorrents([]string{p.Torrent.InfoHash})
return fmt.Errorf("error while analyzing torrent files: %w", err)
}
r.logger.Debug().Msg("torrent client: finished analyzing torrent files (smart select)")
mainFiles := analysis.GetCorrespondingMainFiles()
// find episode number duplicates
dup := make(map[int]int) // map[episodeNumber]count
for _, f := range mainFiles {
if _, ok := dup[f.GetLocalFile().GetEpisodeNumber()]; ok {
dup[f.GetLocalFile().GetEpisodeNumber()]++
} else {
dup[f.GetLocalFile().GetEpisodeNumber()] = 1
}
}
dupCount := 0
for _, count := range dup {
if count > 1 {
dupCount++
}
}
if dupCount > 2 {
_ = r.RemoveTorrents([]string{p.Torrent.InfoHash})
return errors.New("failed to select files, can't tell seasons apart")
}
selectedFiles := make(map[int]*torrent_analyzer.File)
selectedCount := 0
for idx, f := range mainFiles {
for _, ep := range p.EpisodeNumbers {
if f.GetLocalFile().GetEpisodeNumber() == ep {
selectedCount++
selectedFiles[idx] = f
}
}
}
if selectedCount == 0 || selectedCount < len(p.EpisodeNumbers) {
_ = r.RemoveTorrents([]string{p.Torrent.InfoHash})
return errors.New("failed to select files, could not find the right season files")
}
indicesToRemove := analysis.GetUnselectedIndices(selectedFiles)
if len(indicesToRemove) > 0 {
// Deselect files
err = r.DeselectFiles(p.Torrent.InfoHash, indicesToRemove)
if err != nil {
r.logger.Err(err).Msg("torrent client: error while deselecting files (smart select)")
_ = r.RemoveTorrents([]string{p.Torrent.InfoHash})
return fmt.Errorf("error while deselecting files: %w", err)
}
}
time.Sleep(1 * time.Second)
// Resume the torrent
_ = r.ResumeTorrents([]string{p.Torrent.InfoHash})
return nil
}

View File

@@ -0,0 +1,80 @@
package torrent_client
//func TestSmartSelect(t *testing.T) {
// t.Skip("Refactor test")
// test_utils.InitTestProvider(t, test_utils.TorrentClient())
//
// _ = t.TempDir()
//
// anilistClient := anilist.TestGetMockAnilistClient()
// _ = anilist_platform.NewAnilistPlatform(anilistClient, util.NewLogger())
//
// // get repo
//
// tests := []struct {
// name string
// mediaId int
// url string
// selectedEpisodes []int
// client string
// }{
// {
// name: "Kakegurui xx (Season 2)",
// mediaId: 100876,
// url: "https://nyaa.si/view/1553978", // kakegurui season 1 + season 2
// selectedEpisodes: []int{10, 11, 12}, // should select 10, 11, 12 in season 2
// client: QbittorrentClient,
// },
// {
// name: "Spy x Family",
// mediaId: 140960,
// url: "https://nyaa.si/view/1661695", // spy x family (01-25)
// selectedEpisodes: []int{10, 11, 12}, // should select 10, 11, 12
// client: QbittorrentClient,
// },
// {
// name: "Spy x Family Part 2",
// mediaId: 142838,
// url: "https://nyaa.si/view/1661695", // spy x family (01-25)
// selectedEpisodes: []int{10, 11, 12, 13}, // should select 22, 23, 24, 25
// client: QbittorrentClient,
// },
// {
// name: "Kakegurui xx (Season 2)",
// mediaId: 100876,
// url: "https://nyaa.si/view/1553978", // kakegurui season 1 + season 2
// selectedEpisodes: []int{10, 11, 12}, // should select 10, 11, 12 in season 2
// client: TransmissionClient,
// },
// {
// name: "Spy x Family",
// mediaId: 140960,
// url: "https://nyaa.si/view/1661695", // spy x family (01-25)
// selectedEpisodes: []int{10, 11, 12}, // should select 10, 11, 12
// client: TransmissionClient,
// },
// {
// name: "Spy x Family Part 2",
// mediaId: 142838,
// url: "https://nyaa.si/view/1661695", // spy x family (01-25)
// selectedEpisodes: []int{10, 11, 12, 13}, // should select 22, 23, 24, 25
// client: TransmissionClient,
// },
// }
//
// for _, tt := range tests {
//
// t.Run(tt.name, func(t *testing.T) {
//
// repo := getTestRepo(t, tt.client)
//
// ok := repo.Start()
// if !assert.True(t, ok) {
// return
// }
//
// })
//
// }
//
//}

View File

@@ -0,0 +1,164 @@
package torrent_client
import (
"seanime/internal/torrent_clients/qbittorrent/model"
"seanime/internal/util"
"github.com/hekmon/transmissionrpc/v3"
)
const (
TorrentStatusDownloading TorrentStatus = "downloading"
TorrentStatusSeeding TorrentStatus = "seeding"
TorrentStatusPaused TorrentStatus = "paused"
TorrentStatusOther TorrentStatus = "other"
TorrentStatusStopped TorrentStatus = "stopped"
)
type (
Torrent struct {
Name string `json:"name"`
Hash string `json:"hash"`
Seeds int `json:"seeds"`
UpSpeed string `json:"upSpeed"`
DownSpeed string `json:"downSpeed"`
Progress float64 `json:"progress"`
Size string `json:"size"`
Eta string `json:"eta"`
Status TorrentStatus `json:"status"`
ContentPath string `json:"contentPath"`
}
TorrentStatus string
)
//var torrentPool = util.NewPool[*Torrent](func() *Torrent {
// return &Torrent{}
//})
func (r *Repository) FromTransmissionTorrents(t []transmissionrpc.Torrent) []*Torrent {
ret := make([]*Torrent, 0, len(t))
for _, t := range t {
ret = append(ret, r.FromTransmissionTorrent(&t))
}
return ret
}
func (r *Repository) FromTransmissionTorrent(t *transmissionrpc.Torrent) *Torrent {
torrent := &Torrent{}
torrent.Name = "N/A"
if t.Name != nil {
torrent.Name = *t.Name
}
torrent.Hash = "N/A"
if t.HashString != nil {
torrent.Hash = *t.HashString
}
torrent.Seeds = 0
if t.PeersSendingToUs != nil {
torrent.Seeds = int(*t.PeersSendingToUs)
}
torrent.UpSpeed = "0 KB/s"
if t.RateUpload != nil {
torrent.UpSpeed = util.ToHumanReadableSpeed(int(*t.RateUpload))
}
torrent.DownSpeed = "0 KB/s"
if t.RateDownload != nil {
torrent.DownSpeed = util.ToHumanReadableSpeed(int(*t.RateDownload))
}
torrent.Progress = 0.0
if t.PercentDone != nil {
torrent.Progress = *t.PercentDone
}
torrent.Size = "N/A"
if t.TotalSize != nil {
torrent.Size = util.Bytes(uint64(*t.TotalSize))
}
torrent.Eta = "???"
if t.ETA != nil {
torrent.Eta = util.FormatETA(int(*t.ETA))
}
torrent.ContentPath = ""
if t.DownloadDir != nil {
torrent.ContentPath = *t.DownloadDir
}
torrent.Status = TorrentStatusOther
if t.Status != nil && t.IsFinished != nil {
torrent.Status = fromTransmissionTorrentStatus(*t.Status, *t.IsFinished)
}
return torrent
}
// fromTransmissionTorrentStatus returns a normalized status for the torrent.
func fromTransmissionTorrentStatus(st transmissionrpc.TorrentStatus, isFinished bool) TorrentStatus {
if st == transmissionrpc.TorrentStatusSeed || st == transmissionrpc.TorrentStatusSeedWait {
return TorrentStatusSeeding
} else if st == transmissionrpc.TorrentStatusStopped && isFinished {
return TorrentStatusStopped
} else if st == transmissionrpc.TorrentStatusStopped && !isFinished {
return TorrentStatusPaused
} else if st == transmissionrpc.TorrentStatusDownload || st == transmissionrpc.TorrentStatusDownloadWait {
return TorrentStatusDownloading
} else {
return TorrentStatusOther
}
}
func (r *Repository) FromQbitTorrents(t []*qbittorrent_model.Torrent) []*Torrent {
ret := make([]*Torrent, 0, len(t))
for _, t := range t {
ret = append(ret, r.FromQbitTorrent(t))
}
return ret
}
func (r *Repository) FromQbitTorrent(t *qbittorrent_model.Torrent) *Torrent {
torrent := &Torrent{}
torrent.Name = t.Name
torrent.Hash = t.Hash
torrent.Seeds = t.NumSeeds
torrent.UpSpeed = util.ToHumanReadableSpeed(t.Upspeed)
torrent.DownSpeed = util.ToHumanReadableSpeed(t.Dlspeed)
torrent.Progress = t.Progress
torrent.Size = util.Bytes(uint64(t.Size))
torrent.Eta = util.FormatETA(t.Eta)
torrent.ContentPath = t.ContentPath
torrent.Status = fromQbitTorrentStatus(t.State)
return torrent
}
// fromQbitTorrentStatus returns a normalized status for the torrent.
func fromQbitTorrentStatus(st qbittorrent_model.TorrentState) TorrentStatus {
if st == qbittorrent_model.StateQueuedUP ||
st == qbittorrent_model.StateStalledUP ||
st == qbittorrent_model.StateForcedUP ||
st == qbittorrent_model.StateCheckingUP ||
st == qbittorrent_model.StateUploading {
return TorrentStatusSeeding
} else if st == qbittorrent_model.StatePausedDL || st == qbittorrent_model.StateStoppedDL {
return TorrentStatusPaused
} else if st == qbittorrent_model.StateDownloading ||
st == qbittorrent_model.StateCheckingDL ||
st == qbittorrent_model.StateStalledDL ||
st == qbittorrent_model.StateQueuedDL ||
st == qbittorrent_model.StateMetaDL ||
st == qbittorrent_model.StateAllocating ||
st == qbittorrent_model.StateForceDL {
return TorrentStatusDownloading
} else if st == qbittorrent_model.StatePausedUP || st == qbittorrent_model.StateStoppedUP {
return TorrentStatusStopped
} else {
return TorrentStatusOther
}
}