Add support for different folder naming; minor bug fixes
This commit is contained in:
@@ -30,6 +30,7 @@ type Debrid struct {
|
||||
TorrentRefreshInterval string `json:"torrent_refresh_interval"`
|
||||
DownloadLinksRefreshInterval string `json:"downloads_refresh_interval"`
|
||||
TorrentRefreshWorkers int `json:"torrent_refresh_workers"`
|
||||
WebDavFolderNaming string `json:"webdav_folder_naming"`
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
@@ -78,6 +79,10 @@ type WebDav struct {
|
||||
DownloadLinksRefreshInterval string `json:"download_links_refresh_interval"`
|
||||
Workers int `json:"workers"`
|
||||
|
||||
// Folder
|
||||
FolderNaming string `json:"folder_naming"`
|
||||
|
||||
// Rclone
|
||||
RcUrl string `json:"rc_url"`
|
||||
RcUser string `json:"rc_user"`
|
||||
RcPass string `json:"rc_pass"`
|
||||
@@ -314,5 +319,8 @@ func (c *Config) GetDebridWebDav(d Debrid) Debrid {
|
||||
if d.TorrentRefreshWorkers == 0 {
|
||||
d.TorrentRefreshWorkers = cmp.Or(c.WebDav.Workers, 30) // 30 workers
|
||||
}
|
||||
if d.WebDavFolderNaming == "" {
|
||||
d.WebDavFolderNaming = cmp.Or(c.WebDav.FolderNaming, "original_no_ext")
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ func RemoveInvalidChars(value string) string {
|
||||
}
|
||||
|
||||
func RemoveExtension(value string) string {
|
||||
value = RemoveInvalidChars(value)
|
||||
re := regexp.MustCompile(VIDEOMATCH + "|" + MUSICMATCH)
|
||||
|
||||
// Find the last index of the matched extension
|
||||
|
||||
@@ -128,6 +128,7 @@ func flattenFiles(files []MagnetFile, parentPath string, index *int) map[string]
|
||||
Name: fileName,
|
||||
Size: f.Size,
|
||||
Path: currentPath,
|
||||
Link: f.Link,
|
||||
}
|
||||
result[file.Name] = file
|
||||
}
|
||||
@@ -239,7 +240,7 @@ func (ad *AllDebrid) GenerateDownloadLinks(t *types.Torrent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *AllDebrid) GetDownloadLink(t *types.Torrent, file *types.File) *types.File {
|
||||
func (ad *AllDebrid) GetDownloadLink(t *types.Torrent, file *types.File) (string, error) {
|
||||
url := fmt.Sprintf("%s/link/unlock", ad.Host)
|
||||
query := gourl.Values{}
|
||||
query.Add("link", file.Link)
|
||||
@@ -247,16 +248,17 @@ func (ad *AllDebrid) GetDownloadLink(t *types.Torrent, file *types.File) *types.
|
||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
resp, err := ad.client.MakeRequest(req)
|
||||
if err != nil {
|
||||
return nil
|
||||
return "", err
|
||||
}
|
||||
var data DownloadLink
|
||||
if err = json.Unmarshal(resp, &data); err != nil {
|
||||
return nil
|
||||
return "", err
|
||||
}
|
||||
link := data.Data.Link
|
||||
file.DownloadLink = link
|
||||
file.Generated = time.Now()
|
||||
return file
|
||||
if link == "" {
|
||||
return "", fmt.Errorf("error getting download links %s", data.Error.Message)
|
||||
}
|
||||
return link, nil
|
||||
}
|
||||
|
||||
func (ad *AllDebrid) GetCheckCached() bool {
|
||||
@@ -264,7 +266,35 @@ func (ad *AllDebrid) GetCheckCached() bool {
|
||||
}
|
||||
|
||||
func (ad *AllDebrid) GetTorrents() ([]*types.Torrent, error) {
|
||||
return nil, nil
|
||||
url := fmt.Sprintf("%s/magnet/status?status=ready", ad.Host)
|
||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
resp, err := ad.client.MakeRequest(req)
|
||||
torrents := make([]*types.Torrent, 0)
|
||||
if err != nil {
|
||||
return torrents, err
|
||||
}
|
||||
var res TorrentsListResponse
|
||||
err = json.Unmarshal(resp, &res)
|
||||
if err != nil {
|
||||
ad.logger.Info().Msgf("Error unmarshalling torrent info: %s", err)
|
||||
return torrents, err
|
||||
}
|
||||
for _, magnet := range res.Data.Magnets {
|
||||
torrents = append(torrents, &types.Torrent{
|
||||
Id: strconv.Itoa(magnet.Id),
|
||||
Name: magnet.Filename,
|
||||
Bytes: magnet.Size,
|
||||
Status: getAlldebridStatus(magnet.StatusCode),
|
||||
Filename: magnet.Filename,
|
||||
OriginalFilename: magnet.Filename,
|
||||
Files: make(map[string]types.File),
|
||||
InfoHash: magnet.Hash,
|
||||
Debrid: ad.Name,
|
||||
MountPath: ad.MountPath,
|
||||
})
|
||||
}
|
||||
|
||||
return torrents, nil
|
||||
}
|
||||
|
||||
func (ad *AllDebrid) GetDownloads() (map[string]types.DownloadLinks, error) {
|
||||
@@ -279,10 +309,6 @@ func (ad *AllDebrid) GetDownloadUncached() bool {
|
||||
return ad.DownloadUncached
|
||||
}
|
||||
|
||||
func (ad *AllDebrid) ConvertLinksToFiles(links []string) []types.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func New(dc config.Debrid) *AllDebrid {
|
||||
rl := request.ParseRateLimit(dc.RateLimit)
|
||||
headers := map[string]string{
|
||||
|
||||
@@ -40,6 +40,14 @@ type TorrentInfoResponse struct {
|
||||
Error *errorResponse `json:"error"`
|
||||
}
|
||||
|
||||
type TorrentsListResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
Magnets []magnetInfo `json:"magnets"`
|
||||
} `json:"data"`
|
||||
Error *errorResponse `json:"error"`
|
||||
}
|
||||
|
||||
type UploadMagnetResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/sirrobot01/debrid-blackhole/internal/config"
|
||||
"github.com/sirrobot01/debrid-blackhole/internal/logger"
|
||||
"github.com/sirrobot01/debrid-blackhole/internal/utils"
|
||||
"github.com/sirrobot01/debrid-blackhole/pkg/debrid/types"
|
||||
@@ -16,8 +17,14 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
"github.com/sirrobot01/debrid-blackhole/internal/config"
|
||||
type WebDavFolderNaming string
|
||||
|
||||
const (
|
||||
WebDavUseOriginalName WebDavFolderNaming = "original"
|
||||
WebDavUseID WebDavFolderNaming = "use_id"
|
||||
WebDavUseOriginalNameNoExt WebDavFolderNaming = "original_no_ext"
|
||||
)
|
||||
|
||||
type DownloadLinkCache struct {
|
||||
@@ -46,6 +53,7 @@ type Cache struct {
|
||||
listings atomic.Value
|
||||
downloadLinks map[string]string // key: file.Link, value: download link
|
||||
PropfindResp *xsync.MapOf[string, PropfindResponse]
|
||||
folderNaming WebDavFolderNaming
|
||||
|
||||
// config
|
||||
workers int
|
||||
@@ -62,77 +70,6 @@ type Cache struct {
|
||||
downloadLinksMutex sync.Mutex // for downloadLinks
|
||||
}
|
||||
|
||||
type fileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
isDir bool
|
||||
}
|
||||
|
||||
func (fi *fileInfo) Name() string { return fi.name }
|
||||
func (fi *fileInfo) Size() int64 { return fi.size }
|
||||
func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
|
||||
func (fi *fileInfo) ModTime() time.Time { return fi.modTime }
|
||||
func (fi *fileInfo) IsDir() bool { return fi.isDir }
|
||||
func (fi *fileInfo) Sys() interface{} { return nil }
|
||||
|
||||
func (c *Cache) setTorrent(t *CachedTorrent) {
|
||||
c.torrentsMutex.Lock()
|
||||
c.torrents[t.Id] = t
|
||||
c.torrentsNames[t.Name] = t
|
||||
c.torrentsMutex.Unlock()
|
||||
|
||||
c.refreshListings()
|
||||
|
||||
go func() {
|
||||
if err := c.SaveTorrent(t); err != nil {
|
||||
c.logger.Debug().Err(err).Msgf("Failed to save torrent %s", t.Id)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Cache) GetListing() []os.FileInfo {
|
||||
if v, ok := c.listings.Load().([]os.FileInfo); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) setTorrents(torrents map[string]*CachedTorrent) {
|
||||
c.torrentsMutex.Lock()
|
||||
for _, t := range torrents {
|
||||
c.torrents[t.Id] = t
|
||||
c.torrentsNames[t.Name] = t
|
||||
}
|
||||
|
||||
c.torrentsMutex.Unlock()
|
||||
|
||||
c.refreshListings()
|
||||
|
||||
go func() {
|
||||
if err := c.SaveTorrents(); err != nil {
|
||||
c.logger.Debug().Err(err).Msgf("Failed to save torrents")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Cache) GetTorrents() map[string]*CachedTorrent {
|
||||
c.torrentsMutex.RLock()
|
||||
defer c.torrentsMutex.RUnlock()
|
||||
result := make(map[string]*CachedTorrent, len(c.torrents))
|
||||
for k, v := range c.torrents {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Cache) GetTorrentNames() map[string]*CachedTorrent {
|
||||
c.torrentsMutex.RLock()
|
||||
defer c.torrentsMutex.RUnlock()
|
||||
return c.torrentsNames
|
||||
}
|
||||
|
||||
func NewCache(dc config.Debrid, client types.Client) *Cache {
|
||||
cfg := config.GetConfig()
|
||||
torrentRefreshInterval, err := time.ParseDuration(dc.TorrentRefreshInterval)
|
||||
@@ -154,9 +91,77 @@ func NewCache(dc config.Debrid, client types.Client) *Cache {
|
||||
torrentRefreshInterval: torrentRefreshInterval,
|
||||
downloadLinksRefreshInterval: downloadLinksRefreshInterval,
|
||||
PropfindResp: xsync.NewMapOf[string, PropfindResponse](),
|
||||
folderNaming: WebDavFolderNaming(dc.WebDavFolderNaming),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) GetTorrentFolder(torrent *types.Torrent) string {
|
||||
folderName := torrent.Name
|
||||
if c.folderNaming == WebDavUseID {
|
||||
folderName = torrent.Id
|
||||
} else if c.folderNaming == WebDavUseOriginalNameNoExt {
|
||||
folderName = utils.RemoveExtension(torrent.Name)
|
||||
}
|
||||
return folderName
|
||||
}
|
||||
|
||||
func (c *Cache) setTorrent(t *CachedTorrent) {
|
||||
c.torrentsMutex.Lock()
|
||||
c.torrents[t.Id] = t
|
||||
|
||||
c.torrentsNames[c.GetTorrentFolder(t.Torrent)] = t
|
||||
c.torrentsMutex.Unlock()
|
||||
|
||||
c.refreshListings()
|
||||
|
||||
go func() {
|
||||
if err := c.SaveTorrent(t); err != nil {
|
||||
c.logger.Debug().Err(err).Msgf("Failed to save torrent %s", t.Id)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Cache) setTorrents(torrents map[string]*CachedTorrent) {
|
||||
c.torrentsMutex.Lock()
|
||||
for _, t := range torrents {
|
||||
c.torrents[t.Id] = t
|
||||
c.torrentsNames[c.GetTorrentFolder(t.Torrent)] = t
|
||||
}
|
||||
|
||||
c.torrentsMutex.Unlock()
|
||||
|
||||
c.refreshListings()
|
||||
|
||||
go func() {
|
||||
if err := c.SaveTorrents(); err != nil {
|
||||
c.logger.Debug().Err(err).Msgf("Failed to save torrents")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Cache) GetListing() []os.FileInfo {
|
||||
if v, ok := c.listings.Load().([]os.FileInfo); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) GetTorrents() map[string]*CachedTorrent {
|
||||
c.torrentsMutex.RLock()
|
||||
defer c.torrentsMutex.RUnlock()
|
||||
result := make(map[string]*CachedTorrent, len(c.torrents))
|
||||
for k, v := range c.torrents {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Cache) GetTorrentNames() map[string]*CachedTorrent {
|
||||
c.torrentsMutex.RLock()
|
||||
defer c.torrentsMutex.RUnlock()
|
||||
return c.torrentsNames
|
||||
}
|
||||
|
||||
func (c *Cache) Start() error {
|
||||
if err := os.MkdirAll(c.dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create cache directory: %w", err)
|
||||
@@ -220,7 +225,6 @@ func (c *Cache) load() (map[string]*CachedTorrent, error) {
|
||||
if len(ct.Files) != 0 {
|
||||
// We can assume the torrent is complete
|
||||
ct.IsComplete = true
|
||||
ct.Torrent.Name = utils.RemoveExtension(ct.Torrent.OriginalFilename) // Update the name
|
||||
torrents[ct.Id] = &ct
|
||||
}
|
||||
}
|
||||
@@ -445,19 +449,21 @@ func (c *Cache) GetDownloadLink(torrentId, filename, fileLink string) string {
|
||||
}
|
||||
|
||||
c.logger.Trace().Msgf("Getting download link for %s", ct.Name)
|
||||
f := c.client.GetDownloadLink(ct.Torrent, &file)
|
||||
if f == nil {
|
||||
link, err := c.client.GetDownloadLink(ct.Torrent, &file)
|
||||
if err != nil {
|
||||
c.logger.Error().Err(err).Msg("Failed to get download link")
|
||||
return ""
|
||||
}
|
||||
file.DownloadLink = f.DownloadLink
|
||||
file.DownloadLink = link
|
||||
file.Generated = time.Now()
|
||||
ct.Files[filename] = file
|
||||
|
||||
go c.updateDownloadLink(f)
|
||||
go c.updateDownloadLink(file)
|
||||
go c.setTorrent(ct)
|
||||
return f.DownloadLink
|
||||
return file.DownloadLink
|
||||
}
|
||||
|
||||
func (c *Cache) updateDownloadLink(file *types.File) {
|
||||
func (c *Cache) updateDownloadLink(file types.File) {
|
||||
c.downloadLinksMutex.Lock()
|
||||
defer c.downloadLinksMutex.Unlock()
|
||||
c.downloadLinks[file.Link] = file.DownloadLink
|
||||
@@ -493,7 +499,7 @@ func (c *Cache) DeleteTorrents(ids []string) {
|
||||
for _, id := range ids {
|
||||
if t, ok := c.torrents[id]; ok {
|
||||
delete(c.torrents, id)
|
||||
delete(c.torrentsNames, t.Name)
|
||||
delete(c.torrentsNames, c.GetTorrentFolder(t.Torrent))
|
||||
c.removeFromDB(id)
|
||||
}
|
||||
}
|
||||
@@ -507,6 +513,7 @@ func (c *Cache) removeFromDB(torrentId string) {
|
||||
}
|
||||
|
||||
func (c *Cache) OnRemove(torrentId string) {
|
||||
c.logger.Debug().Msgf("OnRemove triggered for %s", torrentId)
|
||||
go c.DeleteTorrent(torrentId)
|
||||
go c.refreshListings()
|
||||
}
|
||||
|
||||
@@ -15,6 +15,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type fileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
isDir bool
|
||||
}
|
||||
|
||||
func (fi *fileInfo) Name() string { return fi.name }
|
||||
func (fi *fileInfo) Size() int64 { return fi.size }
|
||||
func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
|
||||
func (fi *fileInfo) ModTime() time.Time { return fi.modTime }
|
||||
func (fi *fileInfo) IsDir() bool { return fi.isDir }
|
||||
func (fi *fileInfo) Sys() interface{} { return nil }
|
||||
|
||||
func (c *Cache) refreshListings() {
|
||||
if c.listingRefreshMu.TryLock() {
|
||||
defer c.listingRefreshMu.Unlock()
|
||||
@@ -23,11 +38,9 @@ func (c *Cache) refreshListings() {
|
||||
}
|
||||
// Copy the current torrents to avoid concurrent issues
|
||||
c.torrentsMutex.RLock()
|
||||
torrents := make([]string, 0, len(c.torrents))
|
||||
for _, t := range c.torrents {
|
||||
if t != nil && t.Torrent != nil {
|
||||
torrents = append(torrents, t.Name)
|
||||
}
|
||||
torrents := make([]string, 0, len(c.torrentsNames))
|
||||
for k, _ := range c.torrentsNames {
|
||||
torrents = append(torrents, k)
|
||||
}
|
||||
c.torrentsMutex.RUnlock()
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/beevik/etree"
|
||||
"github.com/sirrobot01/debrid-blackhole/internal/request"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
path "path/filepath"
|
||||
"time"
|
||||
@@ -19,7 +18,7 @@ func (c *Cache) RefreshXml() error {
|
||||
return fmt.Errorf("failed to refresh XML for %s: %v", parent, err)
|
||||
}
|
||||
}
|
||||
c.logger.Debug().Msgf("Refreshed XML cache for %s", c.client.GetName())
|
||||
c.logger.Trace().Msgf("Refreshed XML cache for %s", c.client.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ func (c *Cache) refreshParentXml(torrents []os.FileInfo, parent string) error {
|
||||
torrentPath := fmt.Sprintf("/webdav/%s/%s/%s/",
|
||||
c.client.GetName(),
|
||||
parent,
|
||||
url.PathEscape(name),
|
||||
name,
|
||||
)
|
||||
|
||||
addDirectoryResponse(multistatus, torrentPath, name, currentTime)
|
||||
|
||||
@@ -242,8 +242,8 @@ func (dl *DebridLink) GetDownloads() (map[string]types.DownloadLinks, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (dl *DebridLink) GetDownloadLink(t *types.Torrent, file *types.File) *types.File {
|
||||
return file
|
||||
func (dl *DebridLink) GetDownloadLink(t *types.Torrent, file *types.File) (string, error) {
|
||||
return file.DownloadLink, nil
|
||||
}
|
||||
|
||||
func (dl *DebridLink) GetDownloadingStatus() []string {
|
||||
@@ -281,9 +281,75 @@ func New(dc config.Debrid) *DebridLink {
|
||||
}
|
||||
|
||||
func (dl *DebridLink) GetTorrents() ([]*types.Torrent, error) {
|
||||
return nil, nil
|
||||
page := 0
|
||||
perPage := 100
|
||||
torrents := make([]*types.Torrent, 0)
|
||||
for {
|
||||
t, err := dl.getTorrents(page, perPage)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if len(t) == 0 {
|
||||
break
|
||||
}
|
||||
torrents = append(torrents, t...)
|
||||
page++
|
||||
}
|
||||
return torrents, nil
|
||||
}
|
||||
|
||||
func (dl *DebridLink) ConvertLinksToFiles(links []string) []types.File {
|
||||
return nil
|
||||
func (dl *DebridLink) getTorrents(page, perPage int) ([]*types.Torrent, error) {
|
||||
url := fmt.Sprintf("%s/seedbox/list?page=%d&perPage=%d", dl.Host, page, perPage)
|
||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
resp, err := dl.client.MakeRequest(req)
|
||||
torrents := make([]*types.Torrent, 0)
|
||||
if err != nil {
|
||||
return torrents, err
|
||||
}
|
||||
var res TorrentInfo
|
||||
err = json.Unmarshal(resp, &res)
|
||||
if err != nil {
|
||||
dl.logger.Info().Msgf("Error unmarshalling torrent info: %s", err)
|
||||
return torrents, err
|
||||
}
|
||||
|
||||
data := *res.Value
|
||||
|
||||
if len(data) == 0 {
|
||||
return torrents, nil
|
||||
}
|
||||
for _, t := range data {
|
||||
if t.Status != 100 {
|
||||
continue
|
||||
}
|
||||
torrent := &types.Torrent{
|
||||
Id: t.ID,
|
||||
Name: t.Name,
|
||||
Bytes: t.TotalSize,
|
||||
Status: "downloaded",
|
||||
Filename: t.Name,
|
||||
OriginalFilename: t.Name,
|
||||
InfoHash: t.HashString,
|
||||
Files: make(map[string]types.File),
|
||||
Debrid: dl.Name,
|
||||
MountPath: dl.MountPath,
|
||||
}
|
||||
cfg := config.GetConfig()
|
||||
for _, f := range t.Files {
|
||||
if !cfg.IsSizeAllowed(f.Size) {
|
||||
continue
|
||||
}
|
||||
file := types.File{
|
||||
Id: f.ID,
|
||||
Name: f.Name,
|
||||
Size: f.Size,
|
||||
Path: f.Name,
|
||||
DownloadLink: f.DownloadURL,
|
||||
Link: f.DownloadURL,
|
||||
}
|
||||
torrent.Files[f.Name] = file
|
||||
}
|
||||
torrents = append(torrents, torrent)
|
||||
}
|
||||
return torrents, nil
|
||||
}
|
||||
|
||||
@@ -173,16 +173,15 @@ func (r *RealDebrid) UpdateTorrent(t *types.Torrent) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := utils.RemoveExtension(data.OriginalFilename)
|
||||
t.Name = name
|
||||
t.Name = data.OriginalFilename
|
||||
t.Bytes = data.Bytes
|
||||
t.Folder = name
|
||||
t.Folder = data.OriginalFilename
|
||||
t.Progress = data.Progress
|
||||
t.Status = data.Status
|
||||
t.Speed = data.Speed
|
||||
t.Seeders = data.Seeders
|
||||
t.Filename = data.Filename
|
||||
t.OriginalFilename = name
|
||||
t.OriginalFilename = data.OriginalFilename
|
||||
t.Links = data.Links
|
||||
t.MountPath = r.MountPath
|
||||
t.Debrid = r.Name
|
||||
@@ -204,11 +203,10 @@ func (r *RealDebrid) CheckStatus(t *types.Torrent, isSymlink bool) (*types.Torre
|
||||
return t, err
|
||||
}
|
||||
status := data.Status
|
||||
name := utils.RemoveExtension(data.OriginalFilename)
|
||||
t.Name = name // Important because some magnet changes the name
|
||||
t.Folder = name
|
||||
t.Name = data.Filename // Important because some magnet changes the name
|
||||
t.Folder = data.OriginalFilename
|
||||
t.Filename = data.Filename
|
||||
t.OriginalFilename = name
|
||||
t.OriginalFilename = data.OriginalFilename
|
||||
t.Bytes = data.Bytes
|
||||
t.Progress = data.Progress
|
||||
t.Speed = data.Speed
|
||||
@@ -294,34 +292,7 @@ func (r *RealDebrid) GenerateDownloadLinks(t *types.Torrent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RealDebrid) ConvertLinksToFiles(links []string) []types.File {
|
||||
files := make([]types.File, 0)
|
||||
for _, l := range links {
|
||||
url := fmt.Sprintf("%s/unrestrict/link/", r.Host)
|
||||
payload := gourl.Values{
|
||||
"link": {l},
|
||||
}
|
||||
req, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(payload.Encode()))
|
||||
resp, err := r.client.MakeRequest(req)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var data UnrestrictResponse
|
||||
if err = json.Unmarshal(resp, &data); err != nil {
|
||||
continue
|
||||
}
|
||||
files = append(files, types.File{
|
||||
Name: data.Filename,
|
||||
Size: data.Filesize,
|
||||
Link: l,
|
||||
DownloadLink: data.Download,
|
||||
Generated: time.Now(),
|
||||
})
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func (r *RealDebrid) GetDownloadLink(t *types.Torrent, file *types.File) *types.File {
|
||||
func (r *RealDebrid) GetDownloadLink(t *types.Torrent, file *types.File) (string, error) {
|
||||
url := fmt.Sprintf("%s/unrestrict/link/", r.Host)
|
||||
payload := gourl.Values{
|
||||
"link": {file.Link},
|
||||
@@ -329,15 +300,13 @@ func (r *RealDebrid) GetDownloadLink(t *types.Torrent, file *types.File) *types.
|
||||
req, _ := http.NewRequest(http.MethodPost, url, strings.NewReader(payload.Encode()))
|
||||
resp, err := r.client.MakeRequest(req)
|
||||
if err != nil {
|
||||
return nil
|
||||
return "", err
|
||||
}
|
||||
var data UnrestrictResponse
|
||||
if err = json.Unmarshal(resp, &data); err != nil {
|
||||
return nil
|
||||
return "", err
|
||||
}
|
||||
file.DownloadLink = data.Download
|
||||
file.Generated = time.Now()
|
||||
return file
|
||||
return data.Download, nil
|
||||
}
|
||||
|
||||
func (r *RealDebrid) GetCheckCached() bool {
|
||||
|
||||
@@ -273,7 +273,7 @@ func (tb *Torbox) GenerateDownloadLinks(t *types.Torrent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tb *Torbox) GetDownloadLink(t *types.Torrent, file *types.File) *types.File {
|
||||
func (tb *Torbox) GetDownloadLink(t *types.Torrent, file *types.File) (string, error) {
|
||||
url := fmt.Sprintf("%s/api/torrents/requestdl/", tb.Host)
|
||||
query := gourl.Values{}
|
||||
query.Add("torrent_id", t.Id)
|
||||
@@ -283,19 +283,17 @@ func (tb *Torbox) GetDownloadLink(t *types.Torrent, file *types.File) *types.Fil
|
||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
resp, err := tb.client.MakeRequest(req)
|
||||
if err != nil {
|
||||
return nil
|
||||
return "", err
|
||||
}
|
||||
var data DownloadLinksResponse
|
||||
if err = json.Unmarshal(resp, &data); err != nil {
|
||||
return nil
|
||||
return "", err
|
||||
}
|
||||
if data.Data == nil {
|
||||
return nil
|
||||
return "", fmt.Errorf("error getting download links")
|
||||
}
|
||||
link := *data.Data
|
||||
file.DownloadLink = link
|
||||
file.Generated = time.Now()
|
||||
return file
|
||||
return link, nil
|
||||
}
|
||||
|
||||
func (tb *Torbox) GetDownloadingStatus() []string {
|
||||
@@ -336,10 +334,6 @@ func New(dc config.Debrid) *Torbox {
|
||||
}
|
||||
}
|
||||
|
||||
func (tb *Torbox) ConvertLinksToFiles(links []string) []types.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tb *Torbox) GetDownloads() (map[string]types.DownloadLinks, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ type Client interface {
|
||||
SubmitMagnet(tr *Torrent) (*Torrent, error)
|
||||
CheckStatus(tr *Torrent, isSymlink bool) (*Torrent, error)
|
||||
GenerateDownloadLinks(tr *Torrent) error
|
||||
GetDownloadLink(tr *Torrent, file *File) *File
|
||||
ConvertLinksToFiles(links []string) []File
|
||||
GetDownloadLink(tr *Torrent, file *File) (string, error)
|
||||
DeleteTorrent(torrentId string)
|
||||
IsAvailable(infohashes []string) map[string]bool
|
||||
GetCheckCached() bool
|
||||
|
||||
@@ -116,7 +116,7 @@ func (q *QBit) ProcessFiles(torrent *Torrent, debridTorrent *debrid.Torrent, arr
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rclonePath := filepath.Join(debridTorrent.MountPath, debridTorrent.Name)
|
||||
rclonePath := filepath.Join(debridTorrent.MountPath, cache.GetTorrentFolder(debridTorrent))
|
||||
torrentSymlinkPath, err = q.createSymlinks(debridTorrent, rclonePath, debridTorrent.Name)
|
||||
|
||||
} else {
|
||||
|
||||
@@ -73,7 +73,6 @@ func (f *File) Read(p []byte) (n int, err error) {
|
||||
if f.metadataOnly {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// If file content is preloaded, read from memory.
|
||||
if f.content != nil {
|
||||
if f.offset >= int64(len(f.content)) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package webdav
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/sirrobot01/debrid-blackhole/internal/request"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"golang.org/x/net/webdav"
|
||||
"html/template"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -229,6 +227,7 @@ func (h *Handler) getFileInfos(torrent *types.Torrent) []os.FileInfo {
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Handle OPTIONS
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
@@ -473,50 +472,3 @@ func (h *Handler) serveDirectory(w http.ResponseWriter, r *http.Request, file we
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) ioCopy(reader io.Reader, w io.Writer) (int64, error) {
|
||||
// Start with a smaller buffer for faster first byte delivery.
|
||||
buf := make([]byte, 4*1024) // 8KB initial buffer
|
||||
totalWritten := int64(0)
|
||||
firstChunk := true
|
||||
|
||||
for {
|
||||
n, err := reader.Read(buf)
|
||||
if n > 0 {
|
||||
nw, ew := w.Write(buf[:n])
|
||||
if ew != nil {
|
||||
var opErr *net.OpError
|
||||
if errors.As(ew, &opErr) && opErr.Err.Error() == "write: broken pipe" {
|
||||
h.logger.Debug().Msg("Client closed connection (normal for streaming)")
|
||||
return totalWritten, ew
|
||||
}
|
||||
return totalWritten, ew
|
||||
}
|
||||
totalWritten += int64(nw)
|
||||
|
||||
// Flush immediately after the first chunk.
|
||||
if firstChunk {
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
firstChunk = false
|
||||
// Increase buffer size for subsequent reads.
|
||||
buf = make([]byte, 512*1024) // 64KB buffer after first chunk
|
||||
} else if totalWritten%(2*1024*1024) < int64(n) {
|
||||
// Flush roughly every 2MB of data transferred.
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
h.logger.Error().Err(err).Msg("Error reading from file")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return totalWritten, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user