diff --git a/qBT/main.go b/qBT/main.go index cc2c1de..8abbd9e 100644 --- a/qBT/main.go +++ b/qBT/main.go @@ -45,6 +45,8 @@ const ( INVALID_ID ID = -1 ) +var RECENTLY_ACTIVE_TIMEOUT = 60 * time.Second + type Connection struct { addr *url.URL client *http.Client @@ -56,6 +58,7 @@ type TorrentsList struct { useSync bool items map[Hash]*TorrentInfo activity map[Hash]*time.Time + deleted map[ID]*time.Time rid int hashIds map[ID]Hash lastIndex ID @@ -108,14 +111,12 @@ func (list *TorrentsList) Slice() TorrentInfoList { } func (list *TorrentsList) GetActive() (resp TorrentInfoList) { - const timeout = 60 * time.Second - list.mutex.RLock() defer list.mutex.RUnlock() for _, item := range list.items { activity := list.activity[item.Hash] - if activity != nil && time.Since(*activity) < timeout { + if activity != nil && time.Since(*activity) < RECENTLY_ACTIVE_TIMEOUT { resp = append(resp, item) } } @@ -123,6 +124,20 @@ func (list *TorrentsList) GetActive() (resp TorrentInfoList) { return } +func (list *TorrentsList) GetRemoved() (removed []ID) { + list.mutex.RLock() + defer list.mutex.RUnlock() + + removed = make([]ID, 0, len(list.deleted)) + for id, additionTime := range list.deleted { + if additionTime != nil && time.Since(*additionTime) < RECENTLY_ACTIVE_TIMEOUT { + removed = append(removed, id) + } + } + list.maintainListOfDeleted(nil) + return +} + func (list *TorrentsList) ByID(id ID) *TorrentInfo { list.mutex.RLock() defer list.mutex.RUnlock() @@ -152,6 +167,7 @@ func (list *TorrentsList) ItemsNum() int { func (q *Connection) Init(baseUrl string, client *http.Client, useSync bool) { q.TorrentsList.items = make(map[Hash]*TorrentInfo, 0) q.TorrentsList.activity = make(map[Hash]*time.Time) + q.TorrentsList.deleted = make(map[ID]*time.Time) q.TorrentsList.hashIds = make(map[ID]Hash) q.TorrentsList.useSync = useSync q.TorrentsList.rid = 0 @@ -407,6 +423,7 @@ func (list *TorrentsList) DeleteIDsSync(deleted TorrentInfoList) { if _, exists := list.hashIds[torrent.Id]; exists { log.WithField("hash", torrent.Hash).WithField("id", torrent.Id).Info("Hash was removed from the torrent list") delete(list.hashIds, torrent.Id) + list.maintainListOfDeleted(torrent) } } } @@ -418,8 +435,22 @@ func (list *TorrentsList) DeleteIDsFullRescan() { } else { log.WithField("hash", hash).WithField("id", id).Info("Hash disappeared from the torrent list") delete(list.hashIds, id) + list.maintainListOfDeleted(torrent) + } + } +} + +func (list *TorrentsList) maintainListOfDeleted(deletedTorrent *TorrentInfo) { + for id, additionTime := range list.deleted { + if additionTime != nil && time.Since(*additionTime) > RECENTLY_ACTIVE_TIMEOUT { + delete(list.deleted, id) } } + + if deletedTorrent != nil { + now := time.Now() + list.deleted[deletedTorrent.Id] = &now + } } func (list *TorrentsList) UpdateIDs(added TorrentInfoList) { diff --git a/reflection/main.go b/reflection/main.go index 6461ec2..f1e5b71 100644 --- a/reflection/main.go +++ b/reflection/main.go @@ -565,7 +565,23 @@ func TorrentGet(args json.RawMessage) (JsonMap, string) { } resultList[i] = translated } - return JsonMap{"torrents": resultList}, "success" + response := JsonMap{"torrents": resultList} + addRemovedList(req.Ids, response) + return response, "success" +} + +func addRemovedList(idsField *json.RawMessage, resp JsonMap) { + if idsField == nil { + return + } + var ids interface{} + err := json.Unmarshal(*idsField, &ids) + Check(err) + + switch ids.(type) { + case string: + resp["removed"] = qBTConn.TorrentsList.GetRemoved() + } } func qBTEncryptionToTR(enc int) (res string) { diff --git a/reflection/main_test.go b/reflection/main_test.go index 145f22f..be3fbdc 100644 --- a/reflection/main_test.go +++ b/reflection/main_test.go @@ -1,6 +1,7 @@ package main import ( + "github.com/h31/Reflection/qBT" "github.com/hekmon/transmissionrpc" log "github.com/sirupsen/logrus" "gopkg.in/h2non/gock.v1" @@ -260,12 +261,15 @@ func TestSyncingRecentlyActive(t *testing.T) { } } -func testSyncingRecentlyActiveLong(t *testing.T) { +func TestSyncingRecentlyActiveLong(t *testing.T) { const apiAddr = "http://localhost:8080" log.SetLevel(currentLogLevel) defer gock.Off() + prevTime := qBT.RECENTLY_ACTIVE_TIMEOUT + qBT.RECENTLY_ACTIVE_TIMEOUT = 5 * time.Second + gock.Observe(gock.DumpRequest) gock.New(apiAddr). @@ -303,7 +307,7 @@ func testSyncingRecentlyActiveLong(t *testing.T) { t.Error("Number of torrents is not equal to 2") } - time.Sleep((60 + 10) * time.Second) + time.Sleep(qBT.RECENTLY_ACTIVE_TIMEOUT + 5*time.Second) gock.New(apiAddr). Post("/api/v2/torrents/resume"). @@ -320,6 +324,8 @@ func testSyncingRecentlyActiveLong(t *testing.T) { Reply(200) err = transmissionbt.TorrentStartRecentlyActive() Check(err) + + qBT.RECENTLY_ACTIVE_TIMEOUT = prevTime } func TestTorrentAdd(t *testing.T) {