Changelog v0.4.0

This commit is contained in:
Mukhtar Akere
2025-01-18 03:49:05 +01:00
parent ea73572557
commit 3841b7751e
30 changed files with 298 additions and 195 deletions
+7 -7
View File
@@ -4,10 +4,10 @@ import (
"context"
"encoding/base64"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog"
"github.com/sirrobot01/debrid-blackhole/common"
"github.com/sirrobot01/debrid-blackhole/pkg/arr"
"github.com/sirrobot01/debrid-blackhole/pkg/qbit/shared"
"log"
"net/http"
"path/filepath"
"strings"
@@ -15,7 +15,7 @@ import (
type qbitHandler struct {
qbit *shared.QBit
logger *log.Logger
logger zerolog.Logger
debug bool
}
@@ -148,21 +148,21 @@ func (q *qbitHandler) handleTorrentsAdd(w http.ResponseWriter, r *http.Request)
case "multipart/form-data":
err := r.ParseMultipartForm(32 << 20) // 32MB max memory
if err != nil {
q.logger.Printf("Error parsing form: %v\n", err)
q.logger.Info().Msgf("Error parsing form: %v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
case "application/x-www-form-urlencoded":
err := r.ParseForm()
if err != nil {
q.logger.Printf("Error parsing form: %v\n", err)
q.logger.Info().Msgf("Error parsing form: %v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
isSymlink := strings.ToLower(r.FormValue("sequentialDownload")) != "true"
q.logger.Printf("isSymlink: %v\n", isSymlink)
q.logger.Info().Msgf("isSymlink: %v", isSymlink)
urls := r.FormValue("urls")
category := r.FormValue("category")
atleastOne := false
@@ -175,7 +175,7 @@ func (q *qbitHandler) handleTorrentsAdd(w http.ResponseWriter, r *http.Request)
ctx = context.WithValue(ctx, "isSymlink", isSymlink)
for _, url := range urlList {
if err := q.qbit.AddMagnet(ctx, url, category); err != nil {
q.logger.Printf("Error adding magnet: %v\n", err)
q.logger.Info().Msgf("Error adding magnet: %v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
@@ -186,7 +186,7 @@ func (q *qbitHandler) handleTorrentsAdd(w http.ResponseWriter, r *http.Request)
files := r.MultipartForm.File["torrents"]
for _, fileHeader := range files {
if err := q.qbit.AddTorrent(ctx, fileHeader, category); err != nil {
q.logger.Printf("Error adding torrent: %v\n", err)
q.logger.Info().Msgf("Error adding torrent: %v", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
+6 -7
View File
@@ -2,15 +2,14 @@ package server
import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"net/http"
)
func (q *qbitHandler) Routes(r chi.Router) http.Handler {
r.Route("/api/v2", func(r chi.Router) {
if q.debug {
r.Use(middleware.Logger)
}
//if q.debug {
// r.Use(middleware.Logger)
//}
r.Use(q.CategoryContext)
r.Post("/auth/login", q.handleLogin)
@@ -46,9 +45,9 @@ func (q *qbitHandler) Routes(r chi.Router) http.Handler {
func (u *uiHandler) Routes(r chi.Router) http.Handler {
r.Group(func(r chi.Router) {
if u.debug {
r.Use(middleware.Logger)
}
//if u.debug {
// r.Use(middleware.Logger)
//}
r.Get("/", u.IndexHandler)
r.Get("/download", u.DownloadHandler)
r.Get("/repair", u.RepairHandler)
+10 -10
View File
@@ -6,11 +6,11 @@ import (
"fmt"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/rs/zerolog"
"github.com/sirrobot01/debrid-blackhole/common"
"github.com/sirrobot01/debrid-blackhole/pkg/arr"
"github.com/sirrobot01/debrid-blackhole/pkg/debrid"
"github.com/sirrobot01/debrid-blackhole/pkg/qbit/shared"
"log"
"net/http"
"os"
"os/signal"
@@ -19,17 +19,15 @@ import (
type Server struct {
qbit *shared.QBit
logger *log.Logger
debug bool
logger zerolog.Logger
}
func NewServer(config *common.Config, deb *debrid.DebridService, arrs *arr.Storage) *Server {
logger := common.NewLogger("QBit", os.Stdout)
logger := common.NewLogger("QBit", config.QBitTorrent.LogLevel, os.Stdout)
q := shared.NewQBit(config, deb, logger, arrs)
return &Server{
qbit: q,
logger: logger,
debug: config.QBitTorrent.Debug,
}
}
@@ -37,8 +35,10 @@ func (s *Server) Start(ctx context.Context) error {
r := chi.NewRouter()
r.Use(middleware.Recoverer)
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
q := qbitHandler{qbit: s.qbit, logger: s.logger}
ui := uiHandler{qbit: s.qbit, logger: common.NewLogger("UI", os.Stdout)}
logLevel := s.logger.GetLevel().String()
debug := logLevel == "debug"
q := qbitHandler{qbit: s.qbit, logger: s.logger, debug: debug}
ui := uiHandler{qbit: s.qbit, logger: common.NewLogger("UI", s.logger.GetLevel().String(), os.Stdout), debug: debug}
// Register routes
q.Routes(r)
@@ -46,7 +46,7 @@ func (s *Server) Start(ctx context.Context) error {
go s.qbit.StartWorker(context.Background())
s.logger.Printf("Starting QBit server on :%s", s.qbit.Port)
s.logger.Info().Msgf("Starting QBit server on :%s", s.qbit.Port)
port := fmt.Sprintf(":%s", s.qbit.Port)
srv := &http.Server{
Addr: port,
@@ -58,12 +58,12 @@ func (s *Server) Start(ctx context.Context) error {
go func() {
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
fmt.Printf("Error starting server: %v\n", err)
s.logger.Info().Msgf("Error starting server: %v", err)
stop()
}
}()
<-ctx.Done()
fmt.Println("Shutting down gracefully...")
s.logger.Info().Msg("Shutting down gracefully...")
return srv.Shutdown(context.Background())
}
+32 -4
View File
@@ -6,6 +6,23 @@
</div>
<div class="card-body">
<form id="configForm">
<div class="section mb-5">
<h5 class="border-bottom pb-2">General Configuration</h5>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="qbitDebug">Log Level</label>
<select class="form-select" name="qbit.log_level" id="log-level" disabled>
<option value="info">Info</option>
<option value="debug">Debug</option>
<option value="warn">Warning</option>
<option value="error">Error</option>
<option value="trace">Trace</option>
</select>
</div>
</div>
</div>
</div>
<!-- Debrid Configuration -->
<div class="section mb-5">
<h5 class="border-bottom pb-2">Debrid Configuration</h5>
@@ -37,9 +54,15 @@
<input type="number" class="form-control" name="qbit.refresh_interval">
</div>
<div class="col-12 mb-3">
<div class="form-check">
<input type="checkbox" disabled class="form-check-input" name="qbit.debug" id="qbitDebug">
<label class="form-check-label" for="qbitDebug">Enable Debug Mode</label>
<div class="form-group">
<label for="qbitDebug">Log Level</label>
<select class="form-select" name="qbit.log_level" id="qbitDebug" disabled>
<option value="info">Info</option>
<option value="debug">Debug</option>
<option value="warn">Warning</option>
<option value="error">Error</option>
<option value="trace">Trace</option>
</select>
</div>
</div>
</div>
@@ -142,7 +165,6 @@
fetch('/internal/config')
.then(response => response.json())
.then(config => {
console.log(config)
// Load Debrid configs
config.debrids?.forEach(debrid => {
addDebridConfig(debrid);
@@ -180,6 +202,12 @@
}
});
}
// Load general config
const logLevel = document.getElementById('log-level');
logLevel.value = config.log_level;
});
// Handle form submission
+2 -2
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DebridArr - {{.Title}}</title>
<title>DecyphArr - {{.Title}}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet"/>
@@ -60,7 +60,7 @@
<nav class="navbar navbar-expand-lg navbar-light mb-4">
<div class="container">
<a class="navbar-brand" href="/">
<i class="bi bi-cloud-download me-2"></i>DebridArr
<i class="bi bi-cloud-download me-2"></i>DecyphArr
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
+3 -3
View File
@@ -6,13 +6,13 @@ import (
"errors"
"fmt"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog"
"github.com/sirrobot01/debrid-blackhole/common"
"github.com/sirrobot01/debrid-blackhole/pkg/arr"
"github.com/sirrobot01/debrid-blackhole/pkg/debrid"
"github.com/sirrobot01/debrid-blackhole/pkg/qbit/shared"
"github.com/sirrobot01/debrid-blackhole/pkg/version"
"html/template"
"log"
"net/http"
"strings"
)
@@ -50,7 +50,7 @@ var content embed.FS
type uiHandler struct {
qbit *shared.QBit
logger *log.Logger
logger zerolog.Logger
debug bool
}
@@ -198,7 +198,7 @@ func (u *uiHandler) handleRepairMedia(w http.ResponseWriter, r *http.Request) {
go func() {
err := a.Repair(tvId)
if err != nil {
u.logger.Printf("Failed to repair: %v", err)
u.logger.Info().Msgf("Failed to repair: %v", err)
}
}()
}
+19 -14
View File
@@ -12,7 +12,7 @@ import (
)
func (q *QBit) processManualFiles(debridTorrent *debrid.Torrent) (string, error) {
q.logger.Printf("Downloading %d files...", len(debridTorrent.DownloadLinks))
q.logger.Info().Msgf("Downloading %d files...", len(debridTorrent.DownloadLinks))
torrentPath := common.RemoveExtension(debridTorrent.OriginalFilename)
parent := common.RemoveInvalidChars(filepath.Join(q.DownloadFolder, debridTorrent.Arr.Name, torrentPath))
err := os.MkdirAll(parent, os.ModePerm)
@@ -30,7 +30,7 @@ func (q *QBit) downloadFiles(debridTorrent *debrid.Torrent, parent string) {
client := downloaders.GetHTTPClient()
for _, link := range debridTorrent.DownloadLinks {
if link.DownloadLink == "" {
q.logger.Printf("No download link found for %s\n", link.Filename)
q.logger.Info().Msgf("No download link found for %s", link.Filename)
continue
}
wg.Add(1)
@@ -40,14 +40,14 @@ func (q *QBit) downloadFiles(debridTorrent *debrid.Torrent, parent string) {
defer func() { <-semaphore }()
err := downloaders.NormalHTTP(client, link.DownloadLink, filepath.Join(parent, link.Filename))
if err != nil {
q.logger.Printf("Error downloading %s: %v\n", link.DownloadLink, err)
q.logger.Info().Msgf("Error downloading %s: %v", link.DownloadLink, err)
} else {
q.logger.Printf("Downloaded %s successfully\n", link.DownloadLink)
q.logger.Info().Msgf("Downloaded %s successfully", link.DownloadLink)
}
}(link)
}
wg.Wait()
q.logger.Printf("Downloaded all files for %s\n", debridTorrent.Name)
q.logger.Info().Msgf("Downloaded all files for %s", debridTorrent.Name)
}
func (q *QBit) ProcessSymlink(debridTorrent *debrid.Torrent) (string, error) {
@@ -57,18 +57,25 @@ func (q *QBit) ProcessSymlink(debridTorrent *debrid.Torrent) (string, error) {
if len(files) == 0 {
return "", fmt.Errorf("no video files found")
}
q.logger.Printf("Checking %d files...", len(files))
q.logger.Info().Msgf("Checking %d files...", len(files))
rCloneBase := debridTorrent.Debrid.GetMountPath()
torrentPath, err := q.getTorrentPath(rCloneBase, debridTorrent) // /MyTVShow/
if err != nil {
return "", fmt.Errorf("failed to get torrent path: %v", err)
}
torrentSymlinkPath := filepath.Join(q.DownloadFolder, debridTorrent.Arr.Name, torrentPath) // /mnt/symlinks/{category}/MyTVShow/
// Fix for alldebrid
newTorrentPath := torrentPath
if newTorrentPath == "" {
// Alldebrid at times doesn't return the parent folder for single file torrents
newTorrentPath = common.RemoveExtension(debridTorrent.Name) // MyTVShow
}
torrentSymlinkPath := filepath.Join(q.DownloadFolder, debridTorrent.Arr.Name, newTorrentPath) // /mnt/symlinks/{category}/MyTVShow/
err = os.MkdirAll(torrentSymlinkPath, os.ModePerm)
if err != nil {
return "", fmt.Errorf("failed to create directory: %s: %v", torrentSymlinkPath, err)
}
torrentRclonePath := filepath.Join(rCloneBase, torrentPath)
torrentRclonePath := filepath.Join(rCloneBase, torrentPath) // leave it as is
q.logger.Debug().Msgf("Debrid torrent path: %s\nSymlink Path: %s", torrentRclonePath, torrentSymlinkPath)
for _, file := range files {
wg.Add(1)
go checkFileLoop(&wg, torrentRclonePath, file, ready)
@@ -80,7 +87,7 @@ func (q *QBit) ProcessSymlink(debridTorrent *debrid.Torrent) (string, error) {
}()
for f := range ready {
q.logger.Println("File is ready:", f.Path)
q.logger.Info().Msgf("File is ready: %s", f.Path)
q.createSymLink(torrentSymlinkPath, torrentRclonePath, f)
}
return torrentPath, nil
@@ -88,8 +95,10 @@ func (q *QBit) ProcessSymlink(debridTorrent *debrid.Torrent) (string, error) {
func (q *QBit) getTorrentPath(rclonePath string, debridTorrent *debrid.Torrent) (string, error) {
for {
q.logger.Debug().Msgf("Checking for torrent path: %s", rclonePath)
torrentPath, err := debridTorrent.GetMountFolder(rclonePath)
if err == nil {
q.logger.Debug().Msgf("Found torrent path: %s", torrentPath)
return torrentPath, err
}
time.Sleep(time.Second)
@@ -104,10 +113,6 @@ func (q *QBit) createSymLink(path string, torrentMountPath string, file debrid.T
torrentFilePath := filepath.Join(torrentMountPath, file.Name) // debridFolder/MyTVShow/MyTVShow.S01E01.720p.mkv
err := os.Symlink(torrentFilePath, fullPath)
if err != nil {
q.logger.Printf("Failed to create symlink: %s: %v\n", fullPath, err)
q.logger.Info().Msgf("Failed to create symlink: %s: %v", fullPath, err)
}
// Check if the file exists
//if !common.FileReady(fullPath) {
// q.logger.Printf("Symlink not ready: %s\n", fullPath)
//}
}
+3 -4
View File
@@ -2,10 +2,10 @@ package shared
import (
"cmp"
"github.com/rs/zerolog"
"github.com/sirrobot01/debrid-blackhole/common"
"github.com/sirrobot01/debrid-blackhole/pkg/arr"
"github.com/sirrobot01/debrid-blackhole/pkg/debrid"
"log"
"os"
)
@@ -18,12 +18,12 @@ type QBit struct {
Debrid *debrid.DebridService
Storage *TorrentStorage
debug bool
logger *log.Logger
logger zerolog.Logger
Arrs *arr.Storage
RefreshInterval int
}
func NewQBit(config *common.Config, deb *debrid.DebridService, logger *log.Logger, arrs *arr.Storage) *QBit {
func NewQBit(config *common.Config, deb *debrid.DebridService, logger zerolog.Logger, arrs *arr.Storage) *QBit {
cfg := config.QBitTorrent
port := cmp.Or(cfg.Port, os.Getenv("QBIT_PORT"), "8182")
refreshInterval := cmp.Or(cfg.RefreshInterval, 10)
@@ -34,7 +34,6 @@ func NewQBit(config *common.Config, deb *debrid.DebridService, logger *log.Logge
DownloadFolder: cfg.DownloadFolder,
Categories: cfg.Categories,
Debrid: deb,
debug: cfg.Debug,
Storage: NewTorrentStorage("torrents.json"),
logger: logger,
Arrs: arrs,
+5 -5
View File
@@ -92,11 +92,11 @@ func (q *QBit) CreateTorrentFromMagnet(magnet *common.Magnet, category, source s
func (q *QBit) ProcessFiles(torrent *Torrent, debridTorrent *debrid.Torrent, arr *arr.Arr, isSymlink bool) {
for debridTorrent.Status != "downloaded" {
progress := debridTorrent.Progress
q.logger.Printf("%s Download Progress: %.2f%%", debridTorrent.Debrid.GetName(), progress)
time.Sleep(4 * time.Second)
q.logger.Debug().Msgf("%s -> (%s) Download Progress: %.2f%%", debridTorrent.Debrid.GetName(), debridTorrent.Name, progress)
time.Sleep(10 * time.Second)
dbT, err := debridTorrent.Debrid.CheckStatus(debridTorrent, isSymlink)
if err != nil {
q.logger.Printf("Error checking status: %v", err)
q.logger.Error().Msgf("Error checking status: %v", err)
go debridTorrent.Delete()
q.MarkAsFailed(torrent)
_ = arr.Refresh()
@@ -118,7 +118,7 @@ func (q *QBit) ProcessFiles(torrent *Torrent, debridTorrent *debrid.Torrent, arr
if err != nil {
q.MarkAsFailed(torrent)
go debridTorrent.Delete()
q.logger.Printf("Error: %v", err)
q.logger.Info().Msgf("Error: %v", err)
return
}
torrent.TorrentPath = filepath.Base(torrentPath)
@@ -181,7 +181,7 @@ func (q *QBit) UpdateTorrent(t *Torrent, debridTorrent *debrid.Torrent) *Torrent
debridTorrent, _ = db.GetTorrent(t.ID)
}
if debridTorrent == nil {
q.logger.Printf("Torrent with ID %s not found in %s", t.ID, db.GetName())
q.logger.Info().Msgf("Torrent with ID %s not found in %s", t.ID, db.GetName())
return t
}
if debridTorrent.Status != "downloaded" {
+2 -2
View File
@@ -6,7 +6,7 @@ import (
)
func (q *QBit) StartWorker(ctx context.Context) {
q.logger.Println("Qbit Worker started")
q.logger.Info().Msg("Qbit Worker started")
q.StartRefreshWorker(ctx)
}
@@ -16,7 +16,7 @@ func (q *QBit) StartRefreshWorker(ctx context.Context) {
for {
select {
case <-refreshCtx.Done():
q.logger.Println("Qbit Refresh Worker stopped")
q.logger.Info().Msg("Qbit Refresh Worker stopped")
return
case <-refreshTicker.C:
torrents := q.Storage.GetAll("", "", nil)