diff --git a/config.example.yml b/config.example.yml index 2d7780f..00eb1c0 100644 --- a/config.example.yml +++ b/config.example.yml @@ -12,7 +12,10 @@ info_cache_time_hours: 12 enable_repair: true # BEWARE! THERE CAN ONLY BE 1 INSTANCE OF ZURG THAT SHOULD REPAIR YOUR TORRENTS retain_folder_name_extension: false # if true, zurg won't modify the filenames from real-debrid on_library_update: | - echo "hook" + for arg in "$@" + do + echo "detected update on: $arg" + done network_buffer_size: 1048576 # 1 MiB preferred_hosts: # Run the script here https://github.com/debridmediamanager/real-debrid-network-test diff --git a/internal/torrent/hooks.go b/internal/torrent/hooks.go index 114a6a8..ff15e9c 100644 --- a/internal/torrent/hooks.go +++ b/internal/torrent/hooks.go @@ -19,6 +19,8 @@ func (se *ScriptExecutor) Execute() (string, error) { return "", nil } cmd := exec.Command("/bin/sh", "-c", se.Script) + cmd.Args = append(cmd.Args, "zurg") + cmd.Args = append(cmd.Args, se.Args...) var out bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &out diff --git a/internal/torrent/manager.go b/internal/torrent/manager.go index e10433c..ee4205b 100644 --- a/internal/torrent/manager.go +++ b/internal/torrent/manager.go @@ -29,6 +29,7 @@ const ( type TorrentManager struct { DirectoryMap cmap.ConcurrentMap[string, cmap.ConcurrentMap[string, *Torrent]] // directory -> accessKey -> Torrent checksum string + latestAdded string requiredVersion string cfg config.ConfigInterface api *realdebrid.RealDebrid @@ -133,6 +134,8 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, p } go t.startRefreshJob() + t.latestAdded = newTorrents[0].Added // set the latest added to the first torrent's added + return t } @@ -268,23 +271,26 @@ func (t *TorrentManager) startRefreshJob() { t.log.Infof("Fetched info for %d torrents", len(newTorrents)) noInfoCount := 0 - allTorrents, _ := t.DirectoryMap.Get(ALL_TORRENTS) - retain := make(map[string]bool) + oldTorrents, _ := t.DirectoryMap.Get(ALL_TORRENTS) + newSet := cmap.New[*Torrent]() for info := range torrentsChan { if info == nil { noInfoCount++ continue } - retain[info.AccessKey] = true - if torrent, exists := allTorrents.Get(info.AccessKey); exists { + if torrent, exists := oldTorrents.Get(info.AccessKey); exists { mainTorrent := t.mergeToMain(torrent, info) - allTorrents.Set(info.AccessKey, mainTorrent) + oldTorrents.Set(info.AccessKey, mainTorrent) + newSet.Set(info.AccessKey, mainTorrent) } else { - allTorrents.Set(info.AccessKey, info) + oldTorrents.Set(info.AccessKey, info) + newSet.Set(info.AccessKey, info) } } - allTorrents.IterCb(func(accessKey string, torrent *Torrent) { + var updatedPaths []string + + newSet.IterCb(func(_ string, torrent *Torrent) { // get IDs var torrentIDs []string for _, instance := range torrent.Instances { @@ -301,7 +307,10 @@ func (t *TorrentManager) startRefreshJob() { for _, directory := range directories { if t.cfg.MeetsConditions(directory, torrent.AccessKey, torrentIDs, filenames) { torrents, _ := t.DirectoryMap.Get(directory) - torrents.Set(accessKey, torrent) + torrents.Set(torrent.AccessKey, torrent) + if torrent.LatestAdded > t.latestAdded { + updatedPaths = append(updatedPaths, fmt.Sprintf("%s/%s", directory, torrent.AccessKey)) + } break } } @@ -310,9 +319,9 @@ func (t *TorrentManager) startRefreshJob() { }) // delete torrents that no longer exist - accessKeys := allTorrents.Keys() - for _, oldAccessKey := range accessKeys { - if _, ok := retain[oldAccessKey]; !ok { + oldAccessKeys := oldTorrents.Keys() + for _, oldAccessKey := range oldAccessKeys { + if _, ok := newSet.Get(oldAccessKey); !ok { t.DirectoryMap.IterCb(func(_ string, torrents cmap.ConcurrentMap[string, *Torrent]) { torrents.Remove(oldAccessKey) }) @@ -320,7 +329,7 @@ func (t *TorrentManager) startRefreshJob() { } } - t.log.Infof("Compiled into %d torrents, %d were missing info", allTorrents.Count(), noInfoCount) + t.log.Infof("Compiled into %d torrents, %d were missing info", oldTorrents.Count(), noInfoCount) t.SetChecksum(t.getChecksum()) @@ -329,8 +338,9 @@ func (t *TorrentManager) startRefreshJob() { t.repairAll() t.log.Info("Finished checking for torrents to repair") } - // TODO: pass the changed directories to the hook - go OnLibraryUpdateHook([]string{}, t.cfg, t.log) + go OnLibraryUpdateHook(updatedPaths, t.cfg, t.log) + + t.latestAdded = newTorrents[0].Added } } @@ -610,11 +620,9 @@ func (t *TorrentManager) Repair(accessKey string) { } fileCopy.Link = "" // empty the links = chaos! }) - selectedFiles, isChaotic = t.organizeChaos(links, selectedFiles) // chaotic file means RD will not output the desired file selection - // // e.g. even if we select just a single mkv, it will output a rar - // var isChaotic bool - // selectedFiles, isChaotic = t.organizeChaos(info.Links, selectedFiles) + // e.g. even if we select just a single mkv, it will output a rar + selectedFiles, isChaotic = t.organizeChaos(links, selectedFiles) if isChaotic { t.log.Warnf("Torrent %s is always returning an unplayable rar file (it will no longer show up in your directories, zurg suggests you delete it)", torrent.AccessKey) t.DirectoryMap.IterCb(func(_ string, torrents cmap.ConcurrentMap[string, *Torrent]) { diff --git a/pkg/realdebrid/types.go b/pkg/realdebrid/types.go index b984f5a..13735e7 100644 --- a/pkg/realdebrid/types.go +++ b/pkg/realdebrid/types.go @@ -25,12 +25,14 @@ type Torrent struct { Name string `json:"filename"` Progress int `json:"-"` Links []string `json:"links"` + Added string `json:"-"` } func (i *Torrent) UnmarshalJSON(data []byte) error { type Alias Torrent aux := &struct { Progress float64 `json:"progress"` + Added string `json:"added"` *Alias }{ Alias: (*Alias)(i), @@ -41,6 +43,9 @@ func (i *Torrent) UnmarshalJSON(data []byte) error { } i.Progress = int(math.Round(aux.Progress)) + + i.Added = strings.Replace(aux.Added, "Z", "+01:00", 1) + return nil }