- Add more indepth stats like number of torrents, profile details etc

- Add torrent ingest endpoints
- Add issue template
This commit is contained in:
Mukhtar Akere
2025-05-29 04:05:44 +01:00
parent f9c49cbbef
commit 1cd09239f9
25 changed files with 411 additions and 369 deletions

View File

@@ -31,7 +31,11 @@ type AllDebrid struct {
addSamples bool
}
func New(dc config.Debrid) *AllDebrid {
func (ad *AllDebrid) GetProfile() (*types.Profile, error) {
return nil, nil
}
func New(dc config.Debrid) (*AllDebrid, error) {
rl := request.ParseRateLimit(dc.RateLimit)
headers := map[string]string{
@@ -65,7 +69,7 @@ func New(dc config.Debrid) *AllDebrid {
logger: logger.New(dc.Name),
checkCached: dc.CheckCached,
addSamples: dc.AddSamples,
}
}, nil
}
func (ad *AllDebrid) GetName() string {

View File

@@ -13,7 +13,7 @@ import (
"strings"
)
func createDebridClient(dc config.Debrid) types.Client {
func createDebridClient(dc config.Debrid) (types.Client, error) {
switch dc.Name {
case "realdebrid":
return realdebrid.New(dc)

View File

@@ -52,6 +52,12 @@ func (c *downloadLinkCache) Delete(key string) {
delete(c.data, key)
}
func (c *downloadLinkCache) Len() int {
c.mu.Lock()
defer c.mu.Unlock()
return len(c.data)
}
type downloadLinkRequest struct {
result string
err error
@@ -245,3 +251,7 @@ func (c *Cache) GetDownloadByteRange(torrentName, filename string) (*[2]int64, e
file := ct.Files[filename]
return file.ByteRange, nil
}
func (c *Cache) GetTotalActiveDownloadLinks() int {
return c.downloadLinks.Len()
}

View File

@@ -2,6 +2,7 @@ package debrid
import (
"github.com/sirrobot01/decypharr/internal/config"
"github.com/sirrobot01/decypharr/internal/logger"
"github.com/sirrobot01/decypharr/pkg/debrid/types"
"sync"
)
@@ -10,7 +11,7 @@ type Engine struct {
Clients map[string]types.Client
clientsMu sync.Mutex
Caches map[string]*Cache
CacheMu sync.Mutex
cacheMu sync.Mutex
LastUsed string
}
@@ -18,16 +19,22 @@ func NewEngine() *Engine {
cfg := config.Get()
clients := make(map[string]types.Client)
_logger := logger.Default()
caches := make(map[string]*Cache)
for _, dc := range cfg.Debrids {
client := createDebridClient(dc)
logger := client.GetLogger()
client, err := createDebridClient(dc)
if err != nil {
_logger.Error().Err(err).Str("Debrid", dc.Name).Msg("failed to connect to debrid client")
continue
}
_log := client.GetLogger()
if dc.UseWebDav {
caches[dc.Name] = New(dc, client)
logger.Info().Msg("Debrid Service started with WebDAV")
_log.Info().Msg("Debrid Service started with WebDAV")
} else {
logger.Info().Msg("Debrid Service started")
_log.Info().Msg("Debrid Service started")
}
clients[dc.Name] = client
}
@@ -51,9 +58,9 @@ func (d *Engine) Reset() {
d.Clients = make(map[string]types.Client)
d.clientsMu.Unlock()
d.CacheMu.Lock()
d.cacheMu.Lock()
d.Caches = make(map[string]*Cache)
d.CacheMu.Unlock()
d.cacheMu.Unlock()
}
func (d *Engine) GetDebrids() map[string]types.Client {

View File

@@ -25,3 +25,18 @@ func mergeFiles(torrents ...CachedTorrent) map[string]types.File {
}
return merged
}
func (c *Cache) GetIngests() ([]types.IngestData, error) {
torrents := c.GetTorrents()
debridName := c.client.GetName()
var ingests []types.IngestData
for _, torrent := range torrents {
ingests = append(ingests, types.IngestData{
Debrid: debridName,
Name: torrent.Filename,
Hash: torrent.InfoHash,
Size: torrent.Bytes,
})
}
return ingests, nil
}

View File

@@ -31,6 +31,48 @@ type DebridLink struct {
addSamples bool
}
func New(dc config.Debrid) (*DebridLink, error) {
rl := request.ParseRateLimit(dc.RateLimit)
headers := map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", dc.APIKey),
"Content-Type": "application/json",
}
_log := logger.New(dc.Name)
client := request.New(
request.WithHeaders(headers),
request.WithLogger(_log),
request.WithRateLimiter(rl),
request.WithProxy(dc.Proxy),
)
accounts := make(map[string]types.Account)
for idx, key := range dc.DownloadAPIKeys {
id := strconv.Itoa(idx)
accounts[id] = types.Account{
Name: key,
ID: id,
Token: key,
}
}
return &DebridLink{
Name: "debridlink",
Host: "https://debrid-link.com/api/v2",
APIKey: dc.APIKey,
accounts: accounts,
DownloadUncached: dc.DownloadUncached,
client: client,
MountPath: dc.Folder,
logger: logger.New(dc.Name),
checkCached: dc.CheckCached,
addSamples: dc.AddSamples,
}, nil
}
func (dl *DebridLink) GetProfile() (*types.Profile, error) {
return nil, nil
}
func (dl *DebridLink) GetName() string {
return dl.Name
}
@@ -335,44 +377,6 @@ func (dl *DebridLink) GetDownloadUncached() bool {
return dl.DownloadUncached
}
func New(dc config.Debrid) *DebridLink {
rl := request.ParseRateLimit(dc.RateLimit)
headers := map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", dc.APIKey),
"Content-Type": "application/json",
}
_log := logger.New(dc.Name)
client := request.New(
request.WithHeaders(headers),
request.WithLogger(_log),
request.WithRateLimiter(rl),
request.WithProxy(dc.Proxy),
)
accounts := make(map[string]types.Account)
for idx, key := range dc.DownloadAPIKeys {
id := strconv.Itoa(idx)
accounts[id] = types.Account{
Name: key,
ID: id,
Token: key,
}
}
return &DebridLink{
Name: "debridlink",
Host: "https://debrid-link.com/api/v2",
APIKey: dc.APIKey,
accounts: accounts,
DownloadUncached: dc.DownloadUncached,
client: client,
MountPath: dc.Folder,
logger: logger.New(dc.Name),
checkCached: dc.CheckCached,
addSamples: dc.AddSamples,
}
}
func (dl *DebridLink) GetTorrents() ([]*types.Torrent, error) {
page := 0
perPage := 100

View File

@@ -45,9 +45,10 @@ type RealDebrid struct {
rarSemaphore chan struct{}
checkCached bool
addSamples bool
Profile *types.Profile
}
func New(dc config.Debrid) *RealDebrid {
func New(dc config.Debrid) (*RealDebrid, error) {
rl := request.ParseRateLimit(dc.RateLimit)
headers := map[string]string{
@@ -70,7 +71,7 @@ func New(dc config.Debrid) *RealDebrid {
"Authorization": fmt.Sprintf("Bearer %s", currentDownloadKey),
}
return &RealDebrid{
r := &RealDebrid{
Name: "realdebrid",
Host: "https://api.real-debrid.com/rest/1.0",
APIKey: dc.APIKey,
@@ -99,6 +100,12 @@ func New(dc config.Debrid) *RealDebrid {
checkCached: dc.CheckCached,
addSamples: dc.AddSamples,
}
if _, err := r.GetProfile(); err != nil {
return nil, err
} else {
return r, nil
}
}
func (r *RealDebrid) GetName() string {
@@ -908,3 +915,30 @@ func (r *RealDebrid) DeleteDownloadLink(linkId string) error {
}
return nil
}
func (r *RealDebrid) GetProfile() (*types.Profile, error) {
if r.Profile != nil {
return r.Profile, nil
}
url := fmt.Sprintf("%s/user", r.Host)
req, _ := http.NewRequest(http.MethodGet, url, nil)
resp, err := r.client.MakeRequest(req)
if err != nil {
return nil, err
}
var data profileResponse
if json.Unmarshal(resp, &data) != nil {
return nil, err
}
profile := &types.Profile{
Id: data.Id,
Username: data.Username,
Email: data.Email,
Points: data.Points,
Premium: data.Premium,
Expiration: data.Expiration,
Type: data.Type,
}
return profile, nil
}

View File

@@ -139,3 +139,15 @@ type ErrorResponse struct {
Error string `json:"error"`
ErrorCode int `json:"error_code"`
}
type profileResponse struct {
Id int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Points int64 `json:"points"`
Locale string `json:"locale"`
Avatar string `json:"avatar"`
Type string `json:"type"`
Premium int `json:"premium"`
Expiration time.Time `json:"expiration"`
}

View File

@@ -37,7 +37,11 @@ type Torbox struct {
addSamples bool
}
func New(dc config.Debrid) *Torbox {
func (tb *Torbox) GetProfile() (*types.Profile, error) {
return nil, nil
}
func New(dc config.Debrid) (*Torbox, error) {
rl := request.ParseRateLimit(dc.RateLimit)
headers := map[string]string{
@@ -73,7 +77,7 @@ func New(dc config.Debrid) *Torbox {
logger: _log,
checkCached: dc.CheckCached,
addSamples: dc.AddSamples,
}
}, nil
}
func (tb *Torbox) GetName() string {

View File

@@ -25,4 +25,5 @@ type Client interface {
DisableAccount(string)
ResetActiveDownloadKeys()
DeleteDownloadLink(linkId string) error
GetProfile() (*Profile, error)
}

View File

@@ -125,3 +125,25 @@ type Account struct {
Name string `json:"name"`
Token string `json:"token"`
}
type IngestData struct {
Debrid string `json:"debrid"`
Name string `json:"name"`
Hash string `json:"hash"`
Size int64 `json:"size"`
}
type Profile struct {
Name string `json:"name"`
Id int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Points int64 `json:"points"`
Type string `json:"type"`
Premium int `json:"premium"`
Expiration time.Time `json:"expiration"`
LibrarySize int `json:"library_size"`
BadTorrents int `json:"bad_torrents"`
ActiveLinks int `json:"active_links"`
}