wraps up duplicate names implementation
This commit is contained in:
@@ -27,6 +27,7 @@ DecyphArr includes several advanced features that extend its capabilities:
|
|||||||
|
|
||||||
- [Repair Worker](repair-worker.md): Identifies and fixes issues with your media files
|
- [Repair Worker](repair-worker.md): Identifies and fixes issues with your media files
|
||||||
- [WebDAV Server](webdav.md): Provides direct access to your Debrid files
|
- [WebDAV Server](webdav.md): Provides direct access to your Debrid files
|
||||||
|
- [Using with Rclone](rclone.md): Mount the WebDAV server locally for easier access
|
||||||
|
|
||||||
## Supported Debrid Providers
|
## Supported Debrid Providers
|
||||||
|
|
||||||
|
|||||||
@@ -32,12 +32,11 @@ services:
|
|||||||
image: cy01/blackhole:latest # or cy01/blackhole:beta
|
image: cy01/blackhole:latest # or cy01/blackhole:beta
|
||||||
container_name: decypharr
|
container_name: decypharr
|
||||||
ports:
|
ports:
|
||||||
- "8282:8282" # qBittorrent
|
- "8282:8282"
|
||||||
- "8181:8181" # Proxy
|
|
||||||
user: "1000:1000"
|
user: "1000:1000"
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/:/mnt
|
- /mnt/:/mnt
|
||||||
- ./configs/:/app # config.json must be in this directory
|
- ./config/:/app # config.json must be in this directory
|
||||||
environment:
|
environment:
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
@@ -65,7 +64,31 @@ Create a configuration file (see Configuration)
|
|||||||
Run the binary:
|
Run the binary:
|
||||||
```bash
|
```bash
|
||||||
chmod +x decypharr
|
chmod +x decypharr
|
||||||
./decypharr --config /path/to/config
|
./decypharr --config /path/to/config/folder
|
||||||
```
|
```
|
||||||
|
|
||||||
The config directory should contain your config.json file.
|
The config directory should contain your config.json file.
|
||||||
|
|
||||||
|
## config.json
|
||||||
|
|
||||||
|
The `config.json` file is where you configure DecyphArr. You can find a sample configuration file in the `configs` directory of the repository.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"debrids": [
|
||||||
|
{
|
||||||
|
"name": "realdebrid",
|
||||||
|
"api_key": "your_api_key_here",
|
||||||
|
"folder": "/mnt/remote/realdebrid/__all__/",
|
||||||
|
"use_webdav": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"qbittorrent": {
|
||||||
|
"download_folder": "/mnt/symlinks/",
|
||||||
|
"categories": ["sonarr", "radarr"]
|
||||||
|
},
|
||||||
|
"use_auth": false,
|
||||||
|
"log_level": "info",
|
||||||
|
"port": "8282"
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -122,7 +122,7 @@ func getAlldebridStatus(statusCode int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func flattenFiles(files []MagnetFile, parentPath string, index *int) map[string]types.File {
|
func flattenFiles(torrentId string, files []MagnetFile, parentPath string, index *int) map[string]types.File {
|
||||||
result := make(map[string]types.File)
|
result := make(map[string]types.File)
|
||||||
|
|
||||||
cfg := config.Get()
|
cfg := config.Get()
|
||||||
@@ -135,7 +135,7 @@ func flattenFiles(files []MagnetFile, parentPath string, index *int) map[string]
|
|||||||
|
|
||||||
if f.Elements != nil {
|
if f.Elements != nil {
|
||||||
// This is a folder, recurse into it
|
// This is a folder, recurse into it
|
||||||
subFiles := flattenFiles(f.Elements, currentPath, index)
|
subFiles := flattenFiles(torrentId, f.Elements, currentPath, index)
|
||||||
for k, v := range subFiles {
|
for k, v := range subFiles {
|
||||||
if _, ok := result[k]; ok {
|
if _, ok := result[k]; ok {
|
||||||
// File already exists, use path as key
|
// File already exists, use path as key
|
||||||
@@ -162,11 +162,12 @@ func flattenFiles(files []MagnetFile, parentPath string, index *int) map[string]
|
|||||||
|
|
||||||
*index++
|
*index++
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Id: strconv.Itoa(*index),
|
TorrentId: torrentId,
|
||||||
Name: fileName,
|
Id: strconv.Itoa(*index),
|
||||||
Size: f.Size,
|
Name: fileName,
|
||||||
Path: currentPath,
|
Size: f.Size,
|
||||||
Link: f.Link,
|
Path: currentPath,
|
||||||
|
Link: f.Link,
|
||||||
}
|
}
|
||||||
result[file.Name] = file
|
result[file.Name] = file
|
||||||
}
|
}
|
||||||
@@ -203,7 +204,7 @@ func (ad *AllDebrid) UpdateTorrent(t *types.Torrent) error {
|
|||||||
if status == "downloaded" {
|
if status == "downloaded" {
|
||||||
t.Progress = 100
|
t.Progress = 100
|
||||||
index := -1
|
index := -1
|
||||||
files := flattenFiles(data.Files, "", &index)
|
files := flattenFiles(t.Id, data.Files, "", &index)
|
||||||
t.Files = files
|
t.Files = files
|
||||||
} else {
|
} else {
|
||||||
t.Progress = float64(data.Downloaded) / float64(data.Size) * 100
|
t.Progress = float64(data.Downloaded) / float64(data.Size) * 100
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ func (c *Cache) load() (map[string]*CachedTorrent, error) {
|
|||||||
if len(ct.Files) != 0 {
|
if len(ct.Files) != 0 {
|
||||||
// Check if all files are valid, if not, delete the file.json and remove from cache.
|
// Check if all files are valid, if not, delete the file.json and remove from cache.
|
||||||
for _, f := range ct.Files {
|
for _, f := range ct.Files {
|
||||||
|
f.TorrentId = ct.Id
|
||||||
if f.Link == "" {
|
if f.Link == "" {
|
||||||
isComplete = false
|
isComplete = false
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -134,7 +134,9 @@ func (c *Cache) reInsertTorrent(ct *CachedTorrent) (*CachedTorrent, error) {
|
|||||||
torrent, err = c.client.CheckStatus(torrent, true)
|
torrent, err = c.client.CheckStatus(torrent, true)
|
||||||
if err != nil && torrent != nil {
|
if err != nil && torrent != nil {
|
||||||
// Torrent is likely uncached, delete it
|
// Torrent is likely uncached, delete it
|
||||||
_ = c.client.DeleteTorrent(torrent.Id) // Delete the newly added un-cached torrent
|
if err := c.client.DeleteTorrent(torrent.Id); err != nil {
|
||||||
|
c.logger.Error().Err(err).Str("torrentId", torrent.Id).Msg("Failed to delete torrent")
|
||||||
|
} // Delete the newly added un-cached torrent
|
||||||
return ct, fmt.Errorf("failed to check status: %w", err)
|
return ct, fmt.Errorf("failed to check status: %w", err)
|
||||||
}
|
}
|
||||||
if torrent == nil {
|
if torrent == nil {
|
||||||
@@ -153,7 +155,7 @@ func (c *Cache) reInsertTorrent(ct *CachedTorrent) (*CachedTorrent, error) {
|
|||||||
return ct, fmt.Errorf("failed to reinsert torrent: empty link")
|
return ct, fmt.Errorf("failed to reinsert torrent: empty link")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can safely delete the old torrent here
|
// We can safely delete the old torrent here
|
||||||
if oldID != "" {
|
if oldID != "" {
|
||||||
if err := c.DeleteTorrent(oldID); err != nil {
|
if err := c.DeleteTorrent(oldID); err != nil {
|
||||||
|
|||||||
@@ -137,10 +137,11 @@ func (dl *DebridLink) UpdateTorrent(t *types.Torrent) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Id: f.ID,
|
TorrentId: t.Id,
|
||||||
Name: f.Name,
|
Id: f.ID,
|
||||||
Size: f.Size,
|
Name: f.Name,
|
||||||
Path: f.Name,
|
Size: f.Size,
|
||||||
|
Path: f.Name,
|
||||||
DownloadLink: &types.DownloadLink{
|
DownloadLink: &types.DownloadLink{
|
||||||
Filename: f.Name,
|
Filename: f.Name,
|
||||||
Link: f.DownloadURL,
|
Link: f.DownloadURL,
|
||||||
@@ -189,11 +190,12 @@ func (dl *DebridLink) SubmitMagnet(t *types.Torrent) (*types.Torrent, error) {
|
|||||||
t.Debrid = dl.Name
|
t.Debrid = dl.Name
|
||||||
for _, f := range data.Files {
|
for _, f := range data.Files {
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Id: f.ID,
|
TorrentId: t.Id,
|
||||||
Name: f.Name,
|
Id: f.ID,
|
||||||
Size: f.Size,
|
Name: f.Name,
|
||||||
Path: f.Name,
|
Size: f.Size,
|
||||||
Link: f.DownloadURL,
|
Path: f.Name,
|
||||||
|
Link: f.DownloadURL,
|
||||||
DownloadLink: &types.DownloadLink{
|
DownloadLink: &types.DownloadLink{
|
||||||
Filename: f.Name,
|
Filename: f.Name,
|
||||||
Link: f.DownloadURL,
|
Link: f.DownloadURL,
|
||||||
@@ -370,10 +372,11 @@ func (dl *DebridLink) getTorrents(page, perPage int) ([]*types.Torrent, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Id: f.ID,
|
TorrentId: torrent.Id,
|
||||||
Name: f.Name,
|
Id: f.ID,
|
||||||
Size: f.Size,
|
Name: f.Name,
|
||||||
Path: f.Name,
|
Size: f.Size,
|
||||||
|
Path: f.Name,
|
||||||
DownloadLink: &types.DownloadLink{
|
DownloadLink: &types.DownloadLink{
|
||||||
Filename: f.Name,
|
Filename: f.Name,
|
||||||
Link: f.DownloadURL,
|
Link: f.DownloadURL,
|
||||||
|
|||||||
@@ -111,10 +111,11 @@ func getSelectedFiles(t *types.Torrent, data torrentInfo) map[string]types.File
|
|||||||
if f.Selected == 1 {
|
if f.Selected == 1 {
|
||||||
name := filepath.Base(f.Path)
|
name := filepath.Base(f.Path)
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Name: name,
|
TorrentId: t.Id,
|
||||||
Path: name,
|
Name: name,
|
||||||
Size: f.Bytes,
|
Path: name,
|
||||||
Id: strconv.Itoa(f.ID),
|
Size: f.Bytes,
|
||||||
|
Id: strconv.Itoa(f.ID),
|
||||||
}
|
}
|
||||||
selectedFiles = append(selectedFiles, file)
|
selectedFiles = append(selectedFiles, file)
|
||||||
}
|
}
|
||||||
@@ -153,10 +154,11 @@ func getTorrentFiles(t *types.Torrent, data torrentInfo) map[string]types.File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Name: name,
|
TorrentId: t.Id,
|
||||||
Path: name,
|
Name: name,
|
||||||
Size: f.Bytes,
|
Path: name,
|
||||||
Id: strconv.Itoa(f.ID),
|
Size: f.Bytes,
|
||||||
|
Id: strconv.Itoa(f.ID),
|
||||||
}
|
}
|
||||||
files[name] = file
|
files[name] = file
|
||||||
idx++
|
idx++
|
||||||
|
|||||||
@@ -220,10 +220,11 @@ func (tb *Torbox) UpdateTorrent(t *types.Torrent) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
file := types.File{
|
file := types.File{
|
||||||
Id: strconv.Itoa(f.Id),
|
TorrentId: t.Id,
|
||||||
Name: fileName,
|
Id: strconv.Itoa(f.Id),
|
||||||
Size: f.Size,
|
Name: fileName,
|
||||||
Path: fileName,
|
Size: f.Size,
|
||||||
|
Path: fileName,
|
||||||
}
|
}
|
||||||
t.Files[fileName] = file
|
t.Files[fileName] = file
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,9 +183,9 @@ func (h *Handler) OpenFile(ctx context.Context, name string, flag int, perm os.F
|
|||||||
// Torrent file level
|
// Torrent file level
|
||||||
filename := strings.Join(parts[2:], "/")
|
filename := strings.Join(parts[2:], "/")
|
||||||
if file, ok := cachedTorrent.Files[filename]; ok {
|
if file, ok := cachedTorrent.Files[filename]; ok {
|
||||||
fi := &File{
|
return &File{
|
||||||
cache: h.cache,
|
cache: h.cache,
|
||||||
torrentId: cachedTorrent.Id,
|
torrentId: file.TorrentId,
|
||||||
fileId: file.Id,
|
fileId: file.Id,
|
||||||
isDir: false,
|
isDir: false,
|
||||||
name: file.Name,
|
name: file.Name,
|
||||||
@@ -193,8 +193,7 @@ func (h *Handler) OpenFile(ctx context.Context, name string, flag int, perm os.F
|
|||||||
link: file.Link,
|
link: file.Link,
|
||||||
metadataOnly: metadataOnly,
|
metadataOnly: metadataOnly,
|
||||||
modTime: cachedTorrent.AddedOn,
|
modTime: cachedTorrent.AddedOn,
|
||||||
}
|
}, nil
|
||||||
return fi, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user