Access key computation without clearing data
This commit is contained in:
12
README.md
12
README.md
@@ -1,17 +1,17 @@
|
||||
# zurg
|
||||
|
||||
A self-hosted Real-Debrid webdav server written from scratch. Together with [rclone](https://rclone.org/) it can mount your Real-Debrid torrent library into your filesystem just like Dropbox.
|
||||
A self-hosted Real-Debrid webdav server written from scratch. Together with [rclone](https://rclone.org/) it can mount your Real-Debrid torrent library into your file system like Dropbox.
|
||||
|
||||
## Download
|
||||
|
||||
### Latest version: v0.9.2-hotfix.4
|
||||
### Latest version: v0.9.3
|
||||
|
||||
[Download the binary](https://github.com/debridmediamanager/zurg-testing/tree/main/releases/v0.9.2-hotfix.4) or use docker
|
||||
[Download the binary](https://github.com/debridmediamanager/zurg-testing/tree/main/releases/v0.9.3) or use docker
|
||||
|
||||
```sh
|
||||
docker pull ghcr.io/debridmediamanager/zurg-testing:latest
|
||||
# or
|
||||
docker pull ghcr.io/debridmediamanager/zurg-testing:v0.9.2-hotfix.4
|
||||
docker pull ghcr.io/debridmediamanager/zurg-testing:v0.9.3
|
||||
```
|
||||
|
||||
## How to run zurg in 5 steps for Plex
|
||||
@@ -26,7 +26,7 @@ A webdav server is also exposed to your localhost via port `9999`.
|
||||
|
||||
## Why zurg? Why not X?
|
||||
|
||||
- Better performance than anything out there; changes in your library appear instantly (assuming Plex picks it up fast enough)
|
||||
- Better performance than anything out there; changes in your library appear instantly ([assuming Plex picks it up fast enough](./plex_update.sh))
|
||||
- You should be able to access every file even if the torrent names are the same so if you have a lot of these, you might notice that zurg will have more files compared to others (e.g. 2 torrents named "Simpsons" but have different seasons, zurg merges all contents in that directory)
|
||||
- You can configure a flexible directory structure in `config.yml`; you can select individual torrents that should appear on a directory by the ID you see in [DMM](https://debridmediamanager.com/).
|
||||
- If you've ever experienced Plex scanner being stuck on a file and thereby freezing Plex completely, it should not happen anymore because zurg does a comprehensive check if a torrent is dead or not. You can run `ps aux --sort=-time | grep "Plex Media Scanner"` to check for stuck scanner processes.
|
||||
@@ -35,6 +35,8 @@ A webdav server is also exposed to your localhost via port `9999`.
|
||||
|
||||
- [@I-am-PUID-0](https://github.com/I-am-PUID-0) - [pd_zurg](https://github.com/I-am-PUID-0/pd_zurg)
|
||||
- [@Pukabyte](https://github.com/Pukabyte) - [Guide: Zurg + RDT + Prowlarr + Arrs + Petio + Autoscan + Plex + Scannarr](https://puksthepirate.notion.site/Guide-Zurg-RDT-Prowlarr-Arrs-Petio-Autoscan-Plex-Scannarr-eebe27d130fa400c8a0536cab9d46eb3)
|
||||
- [u/pg988](https://www.reddit.com/user/pg988/) - [Windows + zurg + Plex guide](https://www.reddit.com/r/RealDebrid/comments/18so926/windows_zurg_plex_guide/)
|
||||
- [@ignamiranda](https://github.com/ignamiranda) - [Plex Debrid Zurg Windows Guide](https://github.com/ignamiranda/plex_debrid_zurg_scripts/)
|
||||
|
||||
## Please read our [wiki](https://github.com/debridmediamanager/zurg-testing/wiki) for more information!
|
||||
|
||||
|
||||
@@ -2,18 +2,44 @@
|
||||
zurg: v1
|
||||
token: YOUR_RD_API_TOKEN # https://real-debrid.com/apitoken
|
||||
|
||||
# basic functionality
|
||||
host: "[::]" # do not change this if you are running it inside a docker container
|
||||
port: 9999 # do not change this if you are running it inside a docker container
|
||||
# do not change this if you are running it inside a docker container
|
||||
host: "[::]"
|
||||
port: 9999
|
||||
|
||||
# you can protect zurg server with username+password auth
|
||||
username: yowmamasita
|
||||
password: 1234
|
||||
|
||||
# How many requests in parallel should we send to Real-Debrid API?
|
||||
concurrent_workers: 20
|
||||
|
||||
# How often should we check Real-Debrid API for file changes?
|
||||
check_for_changes_every_secs: 15
|
||||
|
||||
# misc configs
|
||||
retain_folder_name_extension: false # if true, zurg won't modify the filenames from real-debrid
|
||||
retain_rd_torrent_name: false # if true, it will strictly follow RD API torrent name property w/c should make this more compatible with rdt-client
|
||||
auto_delete_rar_torrents: false # if true, zurg will delete unstreamable rar files (these torrents will always be compressed in a rar archive no matter what files you select)
|
||||
use_download_cache: true # if true, during zurg initialization, it will fetch all downloads to unrestrict links faster
|
||||
enable_repair: true # BEWARE! THERE CAN ONLY BE 1 INSTANCE OF ZURG THAT SHOULD REPAIR YOUR TORRENTS
|
||||
# if true, you can rename directories and files; if false, saved rename info (if it exists) will be ignored
|
||||
allow_renames: true
|
||||
|
||||
# if true, it will strictly follow Real-Debrid API filename property
|
||||
# setting to true should make zurg more compatible with rdt-client
|
||||
retain_rd_torrent_name: false
|
||||
|
||||
# note: this is for cosmetic purposes only
|
||||
# if true, zurg won't drop file extensions from directories
|
||||
retain_folder_name_extension: false
|
||||
|
||||
# if true, zurg will delete Real-Debrid rar'ed torrents
|
||||
# they are always compressed in a rar archive no matter what files you select
|
||||
auto_delete_rar_torrents: false
|
||||
|
||||
# if true, during zurg initialization, it will fetch all downloads to unrestrict links faster
|
||||
# it will also mount your download links in a special directory
|
||||
use_download_cache: true
|
||||
|
||||
# BEWARE! THERE CAN ONLY BE 1 INSTANCE OF ZURG THAT SHOULD REPAIR YOUR TORRENTS
|
||||
enable_repair: true
|
||||
|
||||
# this is useful for ensuring Plex adds your new content immediately
|
||||
# uncomment the next line for triggering a partial scan
|
||||
# on_library_update: sh plex_update.sh "$@"
|
||||
on_library_update: |
|
||||
for arg in "$@"
|
||||
@@ -21,15 +47,34 @@ on_library_update: |
|
||||
echo "detected update on: $arg"
|
||||
done
|
||||
|
||||
# network configs
|
||||
# buffer size when zurg is streaming files
|
||||
network_buffer_size: 1048576 # 1 MiB
|
||||
serve_from_rclone: false # serve file data from rclone, not from zurg (zurg will only provide rclone the link to download)
|
||||
verify_download_link: true # if true, zurg will check if the link is truly streamable; only relevant if serve_from_rclone is set to true (as it already does this all the time if serve_from_rclone is false)
|
||||
force_ipv6: false # force connect to real-debrid ipv6 addresses
|
||||
rate_limit_sleep_secs: 6 # wait time after getting a 429 from Real-Debrid API
|
||||
realdebrid_timeout_secs: 60 # api timeout
|
||||
retries_until_failed: 5 # api failures until considered failed
|
||||
# preferred_hosts: # Run ./zurg network-test
|
||||
|
||||
# true = send link to rclone and rclone will stream the file
|
||||
# false = zurg will stream the file
|
||||
serve_from_rclone: false
|
||||
|
||||
# true = zurg will check if the link is really working
|
||||
# only relevant if serve_from_rclone is set to true
|
||||
# as it already does this all the time if serve_from_rclone is false
|
||||
verify_download_link: true
|
||||
|
||||
# force connect to real-debrid ipv6 addresses
|
||||
# useful if you are blocked
|
||||
force_ipv6: false
|
||||
|
||||
# sleep time after getting a 429 from Real-Debrid API
|
||||
rate_limit_sleep_secs: 6
|
||||
|
||||
# time to wait before timing out
|
||||
realdebrid_timeout_secs: 60
|
||||
|
||||
# api response failures until considered failed
|
||||
retries_until_failed: 5
|
||||
|
||||
# use the fastest hosts from your location
|
||||
# Run ./zurg network-test
|
||||
# preferred_hosts:
|
||||
# - 20.download.real-debrid.com
|
||||
# - 21.download.real-debrid.com
|
||||
# - 22.download.real-debrid.com
|
||||
|
||||
@@ -19,6 +19,7 @@ type ConfigInterface interface {
|
||||
GetNetworkBufferSize() int
|
||||
EnableRetainFolderNameExtension() bool
|
||||
EnableRetainRDTorrentName() bool
|
||||
ShouldAllowRenames() bool
|
||||
GetRandomPreferredHost() string
|
||||
ShouldServeFromRclone() bool
|
||||
ShouldVerifyDownloadLink() bool
|
||||
@@ -41,6 +42,7 @@ type ZurgConfig struct {
|
||||
NumOfWorkers int `yaml:"concurrent_workers" json:"concurrent_workers"`
|
||||
RefreshEverySeconds int `yaml:"check_for_changes_every_secs" json:"check_for_changes_every_secs"`
|
||||
|
||||
AllowRenames bool `yaml:"allow_renames" json:"allow_renames"`
|
||||
RetainRDTorrentName bool `yaml:"retain_rd_torrent_name" json:"retain_rd_torrent_name"`
|
||||
RetainFolderNameExtension bool `yaml:"retain_folder_name_extension" json:"retain_folder_name_extension"`
|
||||
|
||||
@@ -127,6 +129,10 @@ func (z *ZurgConfig) EnableRetainRDTorrentName() bool {
|
||||
return z.RetainRDTorrentName
|
||||
}
|
||||
|
||||
func (z *ZurgConfig) ShouldAllowRenames() bool {
|
||||
return z.AllowRenames
|
||||
}
|
||||
|
||||
func (z *ZurgConfig) GetRandomPreferredHost() string {
|
||||
if len(z.PreferredHosts) == 0 {
|
||||
return ""
|
||||
|
||||
@@ -47,7 +47,7 @@ func ServeTorrentsListForInfuse(directory string, torMgr *torrent.TorrentManager
|
||||
if !ok || tor.AllInProgress() {
|
||||
continue
|
||||
}
|
||||
buf.WriteString(dav.BaseDirectory(tor.AccessKey, tor.LatestAdded))
|
||||
buf.WriteString(dav.BaseDirectory(torMgr.GetKey(tor), tor.LatestAdded))
|
||||
}
|
||||
buf.WriteString("</d:multistatus>")
|
||||
return buf.Bytes(), nil
|
||||
|
||||
@@ -50,7 +50,7 @@ func ServeTorrentsList(directory string, torMgr *torrent.TorrentManager) ([]byte
|
||||
if !ok || tor.AllInProgress() {
|
||||
continue
|
||||
}
|
||||
buf.WriteString(dav.Directory(tor.AccessKey, tor.LatestAdded))
|
||||
buf.WriteString(dav.Directory(torMgr.GetKey(tor), tor.LatestAdded))
|
||||
}
|
||||
buf.WriteString("</d:multistatus>")
|
||||
return buf.Bytes(), nil
|
||||
@@ -74,7 +74,7 @@ func ServeFilesList(directory, torrentName string, torMgr *torrent.TorrentManage
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("<?xml version=\"1.0\" encoding=\"utf-8\"?><d:multistatus xmlns:d=\"DAV:\">")
|
||||
buf.WriteString(dav.BaseDirectory(filepath.Join(directory, tor.AccessKey), tor.LatestAdded))
|
||||
buf.WriteString(dav.BaseDirectory(filepath.Join(directory, torMgr.GetKey(tor)), tor.LatestAdded))
|
||||
filenames := tor.SelectedFiles.Keys()
|
||||
sort.Strings(filenames)
|
||||
for _, filename := range filenames {
|
||||
@@ -113,7 +113,7 @@ func HandleSingleFile(directory, torrentName, fileName string, torMgr *torrent.T
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("<?xml version=\"1.0\" encoding=\"utf-8\"?><d:multistatus xmlns:d=\"DAV:\">")
|
||||
buf.WriteString(dav.BaseDirectory(filepath.Join(directory, tor.AccessKey), tor.LatestAdded))
|
||||
buf.WriteString(dav.BaseDirectory(filepath.Join(directory, torMgr.GetKey(tor)), tor.LatestAdded))
|
||||
buf.WriteString(dav.File(fileName, file.Bytes, file.Ended))
|
||||
buf.WriteString("</d:multistatus>")
|
||||
return buf.Bytes(), nil
|
||||
|
||||
@@ -17,7 +17,7 @@ func HandleRenameTorrent(directory, torrentName, newName string, torMgr *torrent
|
||||
}
|
||||
torrents.Remove(torrentName)
|
||||
torrents.Set(newName, torrent)
|
||||
torrent.AccessKey = newName
|
||||
torrent.Rename = newName
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func ServeTorrentsList(directory string, torMgr *torrent.TorrentManager) ([]byte
|
||||
if !ok || tor.AllInProgress() {
|
||||
continue
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("<li><a href=\"/http/%s/\">%s</a></li>", filepath.Join(directory, url.PathEscape(tor.AccessKey)), tor.AccessKey))
|
||||
buf.WriteString(fmt.Sprintf("<li><a href=\"/http/%s/\">%s</a></li>", filepath.Join(directory, url.PathEscape(torMgr.GetKey(tor))), torMgr.GetKey(tor)))
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func (t *TorrentManager) CheckDeletedStatus(torrent *Torrent) bool {
|
||||
infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE)
|
||||
torrent.DownloadedIDs.Each(func(id string) bool {
|
||||
infoCache.Set(id, torrent)
|
||||
t.writeTorrentToFile(id, torrent)
|
||||
t.writeTorrentToFile(id, torrent, false)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, p
|
||||
|
||||
// create internal directories
|
||||
t.DirectoryMap = cmap.New[cmap.ConcurrentMap[string, *Torrent]]()
|
||||
t.DirectoryMap.Set(INT_ALL, cmap.New[*Torrent]()) // key is AccessKey
|
||||
t.DirectoryMap.Set(INT_ALL, cmap.New[*Torrent]()) // key is GetAccessKey()
|
||||
t.DirectoryMap.Set(INT_INFO_CACHE, cmap.New[*Torrent]()) // key is Torrent ID
|
||||
// create directory maps
|
||||
for _, directory := range cfg.GetDirectories() {
|
||||
@@ -165,7 +165,7 @@ func (t *TorrentManager) assignedDirectoryCb(tor *Torrent, cb func(string)) {
|
||||
cb(config.UNPLAYABLE_TORRENTS)
|
||||
break
|
||||
}
|
||||
if t.Config.MeetsConditions(directory, tor.AccessKey, tor.ComputeTotalSize(), torrentIDs, filenames, fileSizes) {
|
||||
if t.Config.MeetsConditions(directory, t.GetKey(tor), tor.ComputeTotalSize(), torrentIDs, filenames, fileSizes) {
|
||||
cb(directory)
|
||||
break
|
||||
}
|
||||
@@ -174,21 +174,24 @@ func (t *TorrentManager) assignedDirectoryCb(tor *Torrent, cb func(string)) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TorrentManager) computeAccessKey(name, originalName string) string {
|
||||
func (t *TorrentManager) GetKey(torrent *Torrent) string {
|
||||
if t.Config.ShouldAllowRenames() && torrent.Rename != "" {
|
||||
return torrent.Rename
|
||||
}
|
||||
if t.Config.EnableRetainRDTorrentName() {
|
||||
return name
|
||||
return torrent.Name
|
||||
}
|
||||
// drop the extension from the name
|
||||
if t.Config.EnableRetainFolderNameExtension() && strings.Contains(name, originalName) {
|
||||
return name
|
||||
if t.Config.EnableRetainFolderNameExtension() && strings.Contains(torrent.Name, torrent.OriginalName) {
|
||||
return torrent.Name
|
||||
} else {
|
||||
ret := strings.TrimSuffix(originalName, ".mp4")
|
||||
ret := strings.TrimSuffix(torrent.OriginalName, ".mp4")
|
||||
ret = strings.TrimSuffix(ret, ".mkv")
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TorrentManager) writeTorrentToFile(instanceID string, torrent *Torrent) {
|
||||
func (t *TorrentManager) writeTorrentToFile(instanceID string, torrent *Torrent, overwriteNames bool) {
|
||||
filePath := "data/" + instanceID + ".json"
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
@@ -197,6 +200,13 @@ func (t *TorrentManager) writeTorrentToFile(instanceID string, torrent *Torrent)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if !overwriteNames {
|
||||
infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE)
|
||||
if cachedTorrent, exists := infoCache.Get(instanceID); exists {
|
||||
torrent.Name = cachedTorrent.Name
|
||||
torrent.OriginalName = cachedTorrent.OriginalName
|
||||
}
|
||||
}
|
||||
torrent.Version = t.requiredVersion
|
||||
|
||||
jsonData, err := json.Marshal(torrent)
|
||||
|
||||
@@ -44,13 +44,13 @@ func (t *TorrentManager) RefreshTorrents() []string {
|
||||
continue
|
||||
}
|
||||
if !info.AnyInProgress() {
|
||||
freshKeys.Add(info.AccessKey)
|
||||
freshKeys.Add(t.GetKey(info))
|
||||
}
|
||||
if torrent, exists := allTorrents.Get(info.AccessKey); !exists {
|
||||
allTorrents.Set(info.AccessKey, info)
|
||||
if torrent, exists := allTorrents.Get(t.GetKey(info)); !exists {
|
||||
allTorrents.Set(t.GetKey(info), info)
|
||||
} else if !info.DownloadedIDs.Difference(torrent.DownloadedIDs).IsEmpty() {
|
||||
mainTorrent := t.mergeToMain(torrent, info)
|
||||
allTorrents.Set(info.AccessKey, &mainTorrent)
|
||||
allTorrents.Set(t.GetKey(info), &mainTorrent)
|
||||
}
|
||||
}
|
||||
t.log.Infof("Compiled into %d torrents, %d were missing info", allTorrents.Count(), noInfoCount)
|
||||
@@ -130,9 +130,10 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent {
|
||||
}
|
||||
|
||||
torrent := Torrent{
|
||||
AccessKey: t.computeAccessKey(info.Name, info.OriginalName),
|
||||
LatestAdded: info.Added,
|
||||
Hash: info.Hash,
|
||||
Name: info.Name,
|
||||
OriginalName: info.OriginalName,
|
||||
LatestAdded: info.Added,
|
||||
Hash: info.Hash,
|
||||
}
|
||||
// SelectedFiles is a subset of Files with only the selected ones
|
||||
// it also has a Link field, which can be empty
|
||||
@@ -180,21 +181,23 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent {
|
||||
torrent.InProgressIDs.Add(info.ID)
|
||||
}
|
||||
|
||||
t.writeTorrentToFile(rdTorrent.ID, &torrent)
|
||||
t.writeTorrentToFile(rdTorrent.ID, &torrent, true)
|
||||
infoCache.Set(rdTorrent.ID, &torrent)
|
||||
|
||||
return &torrent
|
||||
}
|
||||
|
||||
func (t *TorrentManager) mergeToMain(existing, toMerge *Torrent) Torrent {
|
||||
mainTorrent := Torrent{}
|
||||
|
||||
mainTorrent.AccessKey = existing.AccessKey
|
||||
mainTorrent.Hash = existing.Hash
|
||||
mainTorrent.DownloadedIDs = mapset.NewSet[string]()
|
||||
mainTorrent.InProgressIDs = mapset.NewSet[string]()
|
||||
mainTorrent.Unfixable = existing.Unfixable || toMerge.Unfixable
|
||||
mainTorrent.UnassignedLinks = existing.UnassignedLinks.Union(toMerge.UnassignedLinks)
|
||||
mainTorrent := Torrent{
|
||||
Name: existing.Name,
|
||||
OriginalName: existing.OriginalName,
|
||||
Rename: existing.Rename,
|
||||
Hash: existing.Hash,
|
||||
DownloadedIDs: mapset.NewSet[string](),
|
||||
InProgressIDs: mapset.NewSet[string](),
|
||||
Unfixable: existing.Unfixable || toMerge.Unfixable,
|
||||
UnassignedLinks: existing.UnassignedLinks.Union(toMerge.UnassignedLinks),
|
||||
}
|
||||
|
||||
// this function triggers only when we have a new DownloadedID
|
||||
toMerge.DownloadedIDs.Difference(existing.DownloadedIDs).Each(func(id string) bool {
|
||||
|
||||
@@ -96,7 +96,7 @@ func (t *TorrentManager) repairAll() {
|
||||
t.log.Debugf("Found %d broken torrents to repair in total", len(toRepair))
|
||||
for i := range toRepair {
|
||||
torrent := toRepair[i]
|
||||
t.log.Infof("Repairing %s", torrent.AccessKey)
|
||||
t.log.Infof("Repairing %s", t.GetKey(torrent))
|
||||
t.repair(torrent)
|
||||
}
|
||||
}
|
||||
@@ -105,19 +105,19 @@ func (t *TorrentManager) Repair(torrent *Torrent) {
|
||||
infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE)
|
||||
torrent.DownloadedIDs.Each(func(id string) bool {
|
||||
infoCache.Set(id, torrent)
|
||||
t.writeTorrentToFile(id, torrent)
|
||||
t.writeTorrentToFile(id, torrent, false)
|
||||
return false
|
||||
})
|
||||
_ = t.repairWorker.Submit(func() {
|
||||
t.log.Infof("Repairing torrent %s", torrent.AccessKey)
|
||||
t.log.Infof("Repairing torrent %s", t.GetKey(torrent))
|
||||
t.repair(torrent)
|
||||
t.log.Infof("Finished repairing torrent %s", torrent.AccessKey)
|
||||
t.log.Infof("Finished repairing torrent %s", t.GetKey(torrent))
|
||||
})
|
||||
}
|
||||
|
||||
func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
if torrent.AnyInProgress() {
|
||||
t.log.Infof("Torrent %s is in progress, skipping repair until download is done", torrent.AccessKey)
|
||||
t.log.Infof("Torrent %s is in progress, skipping repair until download is done", t.GetKey(torrent))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,18 +129,18 @@ func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
|
||||
if torrent.OlderThanDuration(EXPIRED_LINK_TOLERANCE_HOURS * time.Hour) {
|
||||
// first solution: reinsert with same selection
|
||||
t.log.Infof("Torrent %s is older than %d hours, reinserting it", torrent.AccessKey, EXPIRED_LINK_TOLERANCE_HOURS)
|
||||
t.log.Infof("Torrent %s is older than %d hours, reinserting it", t.GetKey(torrent), EXPIRED_LINK_TOLERANCE_HOURS)
|
||||
if t.reinsertTorrent(torrent, "") {
|
||||
t.log.Infof("Successfully downloaded torrent %s to repair it", torrent.AccessKey)
|
||||
t.log.Infof("Successfully downloaded torrent %s to repair it", t.GetKey(torrent))
|
||||
return
|
||||
} else if torrent.Unfixable {
|
||||
t.log.Warnf("Cannot repair torrent %s", torrent.AccessKey)
|
||||
t.log.Warnf("Cannot repair torrent %s", t.GetKey(torrent))
|
||||
return
|
||||
} else {
|
||||
t.log.Warnf("Failed to repair by reinserting torrent %s, will only redownload broken files...", torrent.AccessKey)
|
||||
t.log.Warnf("Failed to repair by reinserting torrent %s, will only redownload broken files...", t.GetKey(torrent))
|
||||
}
|
||||
} else {
|
||||
t.log.Warnf("Torrent %s is not older than %d hours to be repaired by reinsertion, will only redownload broken files...", torrent.AccessKey, EXPIRED_LINK_TOLERANCE_HOURS)
|
||||
t.log.Warnf("Torrent %s is not older than %d hours to be repaired by reinsertion, will only redownload broken files...", t.GetKey(torrent), EXPIRED_LINK_TOLERANCE_HOURS)
|
||||
}
|
||||
|
||||
// sleep for 30 seconds to let the torrent accumulate more broken files if scanning
|
||||
@@ -174,12 +174,12 @@ func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
})
|
||||
|
||||
if assignedCount > 0 {
|
||||
t.log.Infof("Assigned %d links to selected files for torrent %s", assignedCount, torrent.AccessKey)
|
||||
t.log.Infof("Assigned %d links to selected files for torrent %s", assignedCount, t.GetKey(torrent))
|
||||
} else if rarCount > 0 {
|
||||
// this is a rar'ed torrent, nothing we can do
|
||||
if t.Config.ShouldDeleteRarFiles() {
|
||||
t.log.Warnf("Torrent %s is rar'ed and we cannot repair it, deleting it as configured", torrent.AccessKey)
|
||||
t.Delete(torrent.AccessKey, true)
|
||||
t.log.Warnf("Torrent %s is rar'ed and we cannot repair it, deleting it as configured", t.GetKey(torrent))
|
||||
t.Delete(t.GetKey(torrent), true)
|
||||
} else {
|
||||
for _, unassigned := range unassignedDownloads {
|
||||
newFile := &File{
|
||||
@@ -208,14 +208,14 @@ func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
file.Link = "repairing"
|
||||
}
|
||||
})
|
||||
t.log.Debugf("During repair, zurg found %d broken files for torrent %s", len(brokenFiles), torrent.AccessKey)
|
||||
t.log.Debugf("During repair, zurg found %d broken files for torrent %s", len(brokenFiles), t.GetKey(torrent))
|
||||
|
||||
if len(brokenFiles) == 1 && torrent.SelectedFiles.Count() > 2 {
|
||||
// if we download a single file, it will be named differently
|
||||
// so we need to download 1 extra file to preserve the name
|
||||
// this is only relevant if we enable retain_rd_torrent_name
|
||||
// add the first file link encountered with a prefix of http
|
||||
t.log.Debugf("Torrent %s has only 1 broken file, adding 1 extra file to preserve the name", torrent.AccessKey)
|
||||
t.log.Debugf("Torrent %s has only 1 broken file, adding 1 extra file to preserve the name", t.GetKey(torrent))
|
||||
for _, file := range torrent.SelectedFiles.Items() {
|
||||
if strings.HasPrefix(file.Link, "http") {
|
||||
brokenFiles = append(brokenFiles, *file)
|
||||
@@ -225,15 +225,15 @@ func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
}
|
||||
|
||||
if len(brokenFiles) > 0 {
|
||||
t.log.Infof("Redownloading %dof%d files for torrent %s", len(brokenFiles), torrent.SelectedFiles.Count(), torrent.AccessKey)
|
||||
t.log.Infof("Redownloading %dof%d files for torrent %s", len(brokenFiles), torrent.SelectedFiles.Count(), t.GetKey(torrent))
|
||||
brokenFileIDs := strings.Join(getFileIDs(brokenFiles), ",")
|
||||
if t.reinsertTorrent(torrent, brokenFileIDs) {
|
||||
t.log.Infof("Successfully downloaded torrent %s to repair it", torrent.AccessKey)
|
||||
t.log.Infof("Successfully downloaded torrent %s to repair it", t.GetKey(torrent))
|
||||
} else {
|
||||
t.log.Warnf("Cannot repair torrent %s", torrent.AccessKey)
|
||||
t.log.Warnf("Cannot repair torrent %s", t.GetKey(torrent))
|
||||
}
|
||||
} else {
|
||||
t.log.Warnf("Torrent %s has no broken files to repair", torrent.AccessKey)
|
||||
t.log.Warnf("Torrent %s has no broken files to repair", t.GetKey(torrent))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,22 +356,22 @@ func (t *TorrentManager) canCapacityHandle() bool {
|
||||
}
|
||||
|
||||
func (t *TorrentManager) markAsUnplayable(torrent *Torrent) {
|
||||
t.log.Warnf("Marking torrent %s as unplayable", torrent.AccessKey)
|
||||
t.log.Warnf("Marking torrent %s as unplayable", t.GetKey(torrent))
|
||||
t.DirectoryMap.IterCb(func(directory string, torrents cmap.ConcurrentMap[string, *Torrent]) {
|
||||
torrents.Remove(torrent.AccessKey)
|
||||
torrents.Remove(t.GetKey(torrent))
|
||||
})
|
||||
torrents, _ := t.DirectoryMap.Get(config.UNPLAYABLE_TORRENTS)
|
||||
torrents.Set(torrent.AccessKey, torrent)
|
||||
torrents.Set(t.GetKey(torrent), torrent)
|
||||
}
|
||||
|
||||
func (t *TorrentManager) markAsUnfixable(torrent *Torrent) {
|
||||
t.log.Warnf("Marking torrent %s as unfixable", torrent.AccessKey)
|
||||
t.log.Warnf("Marking torrent %s as unfixable", t.GetKey(torrent))
|
||||
torrent.Unfixable = true
|
||||
infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE)
|
||||
torrent.DownloadedIDs.Each(func(id string) bool {
|
||||
info, _ := infoCache.Get(id)
|
||||
info.Unfixable = true
|
||||
t.writeTorrentToFile(id, torrent)
|
||||
t.writeTorrentToFile(id, torrent, false)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import (
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
|
||||
type Torrent struct {
|
||||
AccessKey string `json:"AccessKey"`
|
||||
Name string `json:"Name"`
|
||||
OriginalName string `json:"OriginalName"`
|
||||
Rename string `json:"Rename"`
|
||||
Hash string `json:"Hash"`
|
||||
SelectedFiles cmap.ConcurrentMap[string, *File] `json:"-"`
|
||||
LatestAdded string `json:"LatestAdded"`
|
||||
|
||||
Reference in New Issue
Block a user