Add proper logging

This commit is contained in:
Ben Sarmiento
2023-11-05 01:23:41 +01:00
parent 1b116c2194
commit a9c71a3e93
11 changed files with 103 additions and 96 deletions

View File

@@ -52,7 +52,7 @@ func main() {
log.Errorf("Server panic: %v\n", r) log.Errorf("Server panic: %v\n", r)
} }
}() }()
log.Infof("Starting server on %s\n", addr) log.Infof("Starting server on %s", addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Panicf("Failed to start server: %v", err) log.Panicf("Failed to start server: %v", err)
} }
@@ -65,7 +65,7 @@ func main() {
log.Errorf("Mount panic: %v\n", r) log.Errorf("Mount panic: %v\n", r)
} }
}() }()
log.Infof("Mounting on %s\n", mountPoint) log.Infof("Mounting on %s", mountPoint)
if err := zfs.Mount(mountPoint); err != nil { if err := zfs.Mount(mountPoint); err != nil {
log.Panicf("Failed to mount: %v", err) log.Panicf("Failed to mount: %v", err)
} }

View File

@@ -10,14 +10,10 @@ import (
) )
func loadV1Config(content []byte) (*ZurgConfigV1, error) { func loadV1Config(content []byte) (*ZurgConfigV1, error) {
rlog := logutil.NewLogger()
log := rlog.Named("config")
var configV1 ZurgConfigV1 var configV1 ZurgConfigV1
if err := yaml.Unmarshal(content, &configV1); err != nil { if err := yaml.Unmarshal(content, &configV1); err != nil {
return nil, err return nil, err
} }
log.Debug("V1 config loaded successfully")
return &configV1, nil return &configV1, nil
} }
@@ -27,13 +23,9 @@ func (z *ZurgConfigV1) GetVersion() string {
} }
func (z *ZurgConfigV1) GetDirectories() []string { func (z *ZurgConfigV1) GetDirectories() []string {
rlog := logutil.NewLogger()
log := rlog.Named("config")
rootDirectories := make([]string, len(z.Directories)) rootDirectories := make([]string, len(z.Directories))
i := 0 i := 0
for directory := range z.Directories { for directory := range z.Directories {
log.Debug("Adding to rootDirectories", directory)
rootDirectories[i] = directory rootDirectories[i] = directory
i++ i++
} }
@@ -41,12 +33,9 @@ func (z *ZurgConfigV1) GetDirectories() []string {
} }
func (z *ZurgConfigV1) GetGroupMap() map[string][]string { func (z *ZurgConfigV1) GetGroupMap() map[string][]string {
// Obtain a new sugared logger instance
rlog := logutil.NewLogger() rlog := logutil.NewLogger()
log := rlog.Named("config") log := rlog.Named("config")
log.Debug("Starting to build the group map from directories")
var groupMap = make(map[string][]string) var groupMap = make(map[string][]string)
var groupOrderMap = make(map[string]int) // To store GroupOrder for each directory var groupOrderMap = make(map[string]int) // To store GroupOrder for each directory
@@ -54,7 +43,7 @@ func (z *ZurgConfigV1) GetGroupMap() map[string][]string {
for directory, val := range z.Directories { for directory, val := range z.Directories {
groupMap[val.Group] = append(groupMap[val.Group], directory) groupMap[val.Group] = append(groupMap[val.Group], directory)
groupOrderMap[directory] = val.GroupOrder groupOrderMap[directory] = val.GroupOrder
log.Debugf("Added directory to group: %s, group: %s, groupOrder: %d", directory, val.Group, val.GroupOrder) log.Debugf("Added directory to group: %s, group: %s, order: %d", directory, val.Group, val.GroupOrder)
} }
// Sort the slice based on GroupOrder and then directory name for deterministic order // Sort the slice based on GroupOrder and then directory name for deterministic order
@@ -66,7 +55,7 @@ func (z *ZurgConfigV1) GetGroupMap() map[string][]string {
return groupOrderMap[dirs[i]] < groupOrderMap[dirs[j]] return groupOrderMap[dirs[i]] < groupOrderMap[dirs[j]]
}) })
groupMap[group] = dirs groupMap[group] = dirs
log.Debugf("Sorted directories within a group: %s, sortedDirectories: %v", group, dirs) log.Debugf("Sorted directories within a group: %s %v", group, dirs)
} }
// Return a deep copy of the map // Return a deep copy of the map

View File

@@ -3,7 +3,6 @@ package dav
import ( import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"log"
"net/http" "net/http"
"path" "path"
"strings" "strings"
@@ -11,10 +10,14 @@ import (
"github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/config"
"github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/internal/torrent"
"github.com/debridmediamanager.com/zurg/pkg/dav" "github.com/debridmediamanager.com/zurg/pkg/dav"
"github.com/debridmediamanager.com/zurg/pkg/logutil"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
) )
func HandlePropfindRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) { func HandlePropfindRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) {
rlog := logutil.NewLogger()
log := rlog.Named("dav")
requestPath := path.Clean(r.URL.Path) requestPath := path.Clean(r.URL.Path)
requestPath = strings.Trim(requestPath, "/") requestPath = strings.Trim(requestPath, "/")
@@ -38,13 +41,13 @@ func HandlePropfindRequest(w http.ResponseWriter, r *http.Request, t *torrent.To
case len(filteredSegments) == 2: case len(filteredSegments) == 2:
output, err = handleSingleTorrent(requestPath, w, r, t) output, err = handleSingleTorrent(requestPath, w, r, t)
default: default:
log.Println("Not Found", r.Method, requestPath) log.Errorf("Request %s %s not found", r.Method, requestPath)
writeHTTPError(w, "Not Found", http.StatusNotFound) writeHTTPError(w, "Not Found", http.StatusNotFound)
return return
} }
if err != nil { if err != nil {
log.Printf("Error processing request: %v\n", err) log.Errorf("Error processing request: %v", err)
writeHTTPError(w, "Server error", http.StatusInternalServerError) writeHTTPError(w, "Server error", http.StatusInternalServerError)
return return
} }

View File

@@ -1,7 +1,6 @@
package dav package dav
import ( import (
"log"
"time" "time"
) )
@@ -9,7 +8,6 @@ import (
func convertRFC3339toRFC1123(input string) string { func convertRFC3339toRFC1123(input string) string {
t, err := time.Parse(time.RFC3339, input) t, err := time.Parse(time.RFC3339, input)
if err != nil { if err != nil {
log.Println("Conversion error", err)
return "" return ""
} }
return t.Format("Mon, 02 Jan 2006 15:04:05 GMT") return t.Format("Mon, 02 Jan 2006 15:04:05 GMT")

View File

@@ -2,7 +2,6 @@ package http
import ( import (
"fmt" "fmt"
"log"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
@@ -10,10 +9,14 @@ import (
"github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/config"
"github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/internal/torrent"
"github.com/debridmediamanager.com/zurg/pkg/logutil"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
) )
func HandleDirectoryListing(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) { func HandleDirectoryListing(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) {
rlog := logutil.NewLogger()
log := rlog.Named("http")
requestPath := path.Clean(r.URL.Path) requestPath := path.Clean(r.URL.Path)
if data, exists := cache.Get(requestPath); exists { if data, exists := cache.Get(requestPath); exists {
@@ -35,13 +38,13 @@ func HandleDirectoryListing(w http.ResponseWriter, r *http.Request, t *torrent.T
case len(filteredSegments) == 3: case len(filteredSegments) == 3:
output, err = handleSingleTorrent(requestPath, w, r, t) output, err = handleSingleTorrent(requestPath, w, r, t)
default: default:
log.Println("Not Found (http)", r.Method, requestPath, len(filteredSegments)) log.Errorf("Request %s %s not found", r.Method, requestPath)
writeHTTPError(w, "Not Found", http.StatusNotFound) writeHTTPError(w, "Not Found", http.StatusNotFound)
return return
} }
if err != nil { if err != nil {
log.Printf("Error processing request: %v\n", err) log.Errorf("Error processing request: %v", err)
writeHTTPError(w, "Server error", http.StatusInternalServerError) writeHTTPError(w, "Server error", http.StatusInternalServerError)
return return
} }

View File

@@ -1,7 +1,6 @@
package net package net
import ( import (
"log"
"net/http" "net/http"
"path" "path"
"strings" "strings"
@@ -11,11 +10,15 @@ import (
intHttp "github.com/debridmediamanager.com/zurg/internal/http" intHttp "github.com/debridmediamanager.com/zurg/internal/http"
"github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/internal/torrent"
"github.com/debridmediamanager.com/zurg/internal/universal" "github.com/debridmediamanager.com/zurg/internal/universal"
"github.com/debridmediamanager.com/zurg/pkg/logutil"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
) )
// Router creates a WebDAV router // Router creates a WebDAV router
func Router(mux *http.ServeMux, c config.ConfigInterface, t *torrent.TorrentManager, cache *expirable.LRU[string, string]) { func Router(mux *http.ServeMux, c config.ConfigInterface, t *torrent.TorrentManager, cache *expirable.LRU[string, string]) {
rlog := logutil.NewLogger()
log := rlog.Named("net")
mux.HandleFunc("/http/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/http/", func(w http.ResponseWriter, r *http.Request) {
switch r.Method { switch r.Method {
case http.MethodGet: case http.MethodGet:
@@ -30,7 +33,7 @@ func Router(mux *http.ServeMux, c config.ConfigInterface, t *torrent.TorrentMana
universal.HandleHeadRequest(w, r, t, c, cache) universal.HandleHeadRequest(w, r, t, c, cache)
default: default:
log.Println("Method not implemented", r.Method) log.Errorf("Request %s %s not supported yet", r.Method, r.URL.Path)
http.Error(w, "Method not implemented", http.StatusMethodNotAllowed) http.Error(w, "Method not implemented", http.StatusMethodNotAllowed)
} }
}) })
@@ -47,7 +50,7 @@ func Router(mux *http.ServeMux, c config.ConfigInterface, t *torrent.TorrentMana
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
default: default:
log.Println("Method not implemented", r.Method, r.URL.Path) log.Errorf("Request %s %s not supported yet", r.Method, r.URL.Path)
http.Error(w, "Method not implemented", http.StatusMethodNotAllowed) http.Error(w, "Method not implemented", http.StatusMethodNotAllowed)
} }
}) })

View File

@@ -3,10 +3,10 @@ package torrent
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"log"
"os/exec" "os/exec"
"github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/config"
"github.com/debridmediamanager.com/zurg/pkg/logutil"
) )
type ScriptExecutor struct { type ScriptExecutor struct {
@@ -30,15 +30,18 @@ func (se *ScriptExecutor) Execute() (string, error) {
} }
func OnLibraryUpdateHook(config config.ConfigInterface) { func OnLibraryUpdateHook(config config.ConfigInterface) {
rlog := logutil.NewLogger()
log := rlog.Named("hooks")
executor := &ScriptExecutor{ executor := &ScriptExecutor{
Script: config.GetOnLibraryUpdate(), Script: config.GetOnLibraryUpdate(),
} }
output, err := executor.Execute() output, err := executor.Execute()
if err != nil { if err != nil {
log.Printf("Failed to execute hook on_library_update:\n%v\n", err) log.Errorf("Failed to execute hook on_library_update: %v", err)
return return
} }
if output != "" { if output != "" {
log.Printf("Output of hook on_library_update:\n%s\n", output) log.Infof("Output of hook on_library_update:\n%s", output)
} }
} }

View File

@@ -3,7 +3,6 @@ package torrent
import ( import (
"encoding/gob" "encoding/gob"
"fmt" "fmt"
"log"
"math" "math"
"os" "os"
"strings" "strings"
@@ -11,8 +10,10 @@ import (
"time" "time"
"github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/config"
"github.com/debridmediamanager.com/zurg/pkg/logutil"
"github.com/debridmediamanager.com/zurg/pkg/realdebrid" "github.com/debridmediamanager.com/zurg/pkg/realdebrid"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
"go.uber.org/zap"
) )
type TorrentManager struct { type TorrentManager struct {
@@ -26,6 +27,7 @@ type TorrentManager struct {
directoryMap map[string][]string directoryMap map[string][]string
processedTorrents map[string][]string processedTorrents map[string][]string
mu *sync.Mutex mu *sync.Mutex
log *zap.SugaredLogger
} }
// NewTorrentManager creates a new torrent manager // NewTorrentManager creates a new torrent manager
@@ -40,16 +42,14 @@ func NewTorrentManager(config config.ConfigInterface, cache *expirable.LRU[strin
directoryMap: make(map[string][]string), directoryMap: make(map[string][]string),
processedTorrents: make(map[string][]string), processedTorrents: make(map[string][]string),
mu: &sync.Mutex{}, mu: &sync.Mutex{},
log: logutil.NewLogger().Named("manager"),
} }
// Initialize torrents for the first time // Initialize torrents for the first time
log.Println("Initializing torrents")
t.mu.Lock() t.mu.Lock()
log.Println("Fetching torrents")
t.torrents = t.getFreshListFromAPI() t.torrents = t.getFreshListFromAPI()
t.checksum = t.getChecksum() t.checksum = t.getChecksum()
t.mu.Unlock() t.mu.Unlock()
log.Println("Finished fetching torrents")
// log.Println("First checksum", t.checksum) // log.Println("First checksum", t.checksum)
@@ -162,13 +162,13 @@ func (t *TorrentManager) getChecksum() string {
totalCount = torrentsResp.totalCount totalCount = torrentsResp.totalCount
case count = <-countChan: case count = <-countChan:
case err := <-errChan: case err := <-errChan:
log.Printf("Error: %v\n", err) t.log.Errorf("Checksum API Error: %v\n", err)
return "" return ""
} }
} }
if len(torrents) == 0 { if len(torrents) == 0 {
log.Println("Huh, no torrents returned") t.log.Error("Huh, no torrents returned")
return "" return ""
} }
@@ -178,7 +178,7 @@ func (t *TorrentManager) getChecksum() string {
// startRefreshJob periodically refreshes the torrents // startRefreshJob periodically refreshes the torrents
func (t *TorrentManager) startRefreshJob() { func (t *TorrentManager) startRefreshJob() {
log.Println("Starting periodic refresh") t.log.Info("Starting periodic refresh")
for { for {
<-time.After(time.Duration(t.config.GetRefreshEverySeconds()) * time.Second) <-time.After(time.Duration(t.config.GetRefreshEverySeconds()) * time.Second)
@@ -221,7 +221,7 @@ func (t *TorrentManager) startRefreshJob() {
func (t *TorrentManager) getFreshListFromAPI() []Torrent { func (t *TorrentManager) getFreshListFromAPI() []Torrent {
torrents, _, err := realdebrid.GetTorrents(t.config.GetToken(), 0) torrents, _, err := realdebrid.GetTorrents(t.config.GetToken(), 0)
if err != nil { if err != nil {
log.Printf("Cannot get torrents: %v\n", err) t.log.Errorf("Cannot get torrents: %v\n", err)
return nil return nil
} }
@@ -244,7 +244,7 @@ func (t *TorrentManager) getFreshListFromAPI() []Torrent {
} }
} }
log.Printf("Fetched %d torrents", len(torrentsV2)) t.log.Infof("Fetched %d torrents", len(torrentsV2))
return torrentsV2 return torrentsV2
} }
@@ -267,10 +267,10 @@ func (t *TorrentManager) addMoreInfo(torrent *Torrent) {
return return
} }
// log.Println("Getting info for", torrent.ID) // t.log.Println("Getting info for", torrent.ID)
info, err := realdebrid.GetTorrentInfo(t.config.GetToken(), torrent.ID) info, err := realdebrid.GetTorrentInfo(t.config.GetToken(), torrent.ID)
if err != nil { if err != nil {
log.Printf("Cannot get info: %v\n", err) t.log.Errorf("Cannot get info: %v\n", err)
return return
} }
@@ -298,19 +298,21 @@ func (t *TorrentManager) addMoreInfo(torrent *Torrent) {
}) })
} }
if len(selectedFiles) > len(info.Links) && info.Progress == 100 { if len(selectedFiles) > len(info.Links) && info.Progress == 100 {
log.Printf("Some links has expired for %s, %s: %d selected but only %d link(s)\n", info.ID, info.Name, len(selectedFiles), len(info.Links)) t.log.Debugf("Some links has expired for %s %s: %d selected but only %d link(s)", info.ID, info.Name, len(selectedFiles), len(info.Links))
// chaotic file means RD will not output the desired file selection // chaotic file means RD will not output the desired file selection
// e.g. even if we select just a single mkv, it will output a rar // e.g. even if we select just a single mkv, it will output a rar
var isChaotic bool var isChaotic bool
selectedFiles, isChaotic = t.organizeChaos(info, selectedFiles) selectedFiles, isChaotic = t.organizeChaos(info, selectedFiles)
if isChaotic { if isChaotic {
log.Println("This torrent is unfixable, it's always returning an unstreamable link, ignoring", info.ID, info.Name) t.log.Infof("Torrent %s %s is unfixable, it's always returning an unstreamable link, ignoring", info.ID, info.Name)
t.log.Debugf("You can try fixing it yourself magnet:?xt=urn:btih:%s", info.Hash)
} else { } else {
if len(streamableFiles) > 1 { if len(streamableFiles) > 1 {
log.Println("Marking for repair", info.ID, info.Name) t.log.Infof("Torrent %s %s marked for repair", info.ID, info.Name)
forRepair = true forRepair = true
} else { } else {
log.Println("This torrent is unfixable, the lone streamable link has expired, ignoring", info.ID, info.ID) t.log.Infof("Torrent %s %s is unfixable, the lone streamable link has expired, ignoring", info.ID, info.Name)
t.log.Debugf("You can try fixing it yourself magnet:?xt=urn:btih:%s", info.Hash)
} }
} }
} else { } else {
@@ -352,7 +354,6 @@ func (t *TorrentManager) mapToDirectories() {
// for every group, iterate over every torrent // for every group, iterate over every torrent
// and then sprinkle/distribute the torrents to the directories of the group // and then sprinkle/distribute the torrents to the directories of the group
for group, directories := range groupMap { for group, directories := range groupMap {
log.Printf("Processing directory group '%s', sequence: %s\n", group, strings.Join(directories, " > "))
counter := make(map[string]int) counter := make(map[string]int)
for i := range t.torrents { for i := range t.torrents {
// don't process torrents that are already mapped if it is not the first run // don't process torrents that are already mapped if it is not the first run
@@ -398,15 +399,14 @@ func (t *TorrentManager) mapToDirectories() {
sum += count sum += count
} }
if sum > 0 { if sum > 0 {
log.Printf("Directory group processed: %s %v %d\n", group, counter, sum) t.log.Infof("Group processing completed: %s %v total: %d", group, counter, sum)
} else { } else {
log.Println("No new additions to directory group", group) t.log.Info("No new additions to directory group", group)
} }
} }
default: default:
log.Println("Unknown config version") t.log.Error("Unknown config version")
} }
log.Println("Finished mapping to directories")
} }
// getByID returns a torrent by its ID // getByID returns a torrent by its ID
@@ -424,7 +424,7 @@ func (t *TorrentManager) writeToFile(torrent *Torrent) {
filePath := fmt.Sprintf("data/%s.bin", torrent.ID) filePath := fmt.Sprintf("data/%s.bin", torrent.ID)
file, err := os.Create(filePath) file, err := os.Create(filePath)
if err != nil { if err != nil {
log.Fatalf("Failed creating file: %s", err) t.log.Fatalf("Failed creating file: %s", err)
return return
} }
defer file.Close() defer file.Close()
@@ -468,14 +468,14 @@ func (t *TorrentManager) repairAll(wg *sync.WaitGroup) {
wg.Wait() wg.Wait()
for _, torrent := range t.torrents { for _, torrent := range t.torrents {
if torrent.ForRepair { if torrent.ForRepair {
log.Println("Issues were detected on", torrent.Name, "; fixing...") t.log.Infof("Issues were detected on %s %s; fixing...", torrent.ID, torrent.Name)
t.repair(torrent.ID, torrent.SelectedFiles, true) t.repair(torrent.ID, torrent.SelectedFiles, true)
} }
if len(torrent.Links) == 0 && torrent.Progress == 100 { if len(torrent.Links) == 0 && torrent.Progress == 100 {
// If the torrent has no links // If the torrent has no links
// and already processing repair // and already processing repair
// delete it! // delete it!
log.Println("Deleting", torrent.Name, "as it has no links") t.log.Infof("Deleting broken torrent %s %s as it doesn't contain any files", torrent.ID, torrent.Name)
realdebrid.DeleteTorrent(t.config.GetToken(), torrent.ID) realdebrid.DeleteTorrent(t.config.GetToken(), torrent.ID)
} }
} }
@@ -496,7 +496,7 @@ func (t *TorrentManager) repair(torrentID string, selectedFiles []File, tryReins
} }
} }
if found { if found {
log.Println("Repair in progress, skipping", torrentID) t.log.Infof("Repair in progress, skipping %s", torrentID)
return return
} }
@@ -519,16 +519,16 @@ func (t *TorrentManager) repair(torrentID string, selectedFiles []File, tryReins
} }
} }
if len(missingFiles) == 0 { if len(missingFiles) == 0 {
log.Println(torrent.Name, "is already repaired") t.log.Infof("Torrent %s %s is already repaired", torrent.ID, torrent.Name)
return return
} }
// then we repair it! // then we repair it!
log.Println("Repairing torrent", torrentID) t.log.Infof("Repairing torrent %s %s", torrent.ID, torrent.Name)
// check if we can still add more downloads // check if we can still add more downloads
proceed := t.canCapacityHandle() proceed := t.canCapacityHandle()
if !proceed { if !proceed {
log.Println("Cannot add more torrents, exiting") t.log.Error("Cannot add more torrents, exiting")
return return
} }
@@ -556,8 +556,7 @@ func (t *TorrentManager) repair(torrentID string, selectedFiles []File, tryReins
// we will download 1 extra streamable file to force a redownload of the missing files // we will download 1 extra streamable file to force a redownload of the missing files
// or if there's only 1 missing file, we will download 1 more to prevent a rename // or if there's only 1 missing file, we will download 1 more to prevent a rename
missingFilesPlus1 := strings.Join(getFileIDs(missingFiles), ",") missingFilesPlus1 := strings.Join(getFileIDs(missingFiles), ",")
missingFilesPlus1 += fmt.Sprintf(",%d", otherStreamableFileIDs[0]) t.log.Infof("Redownloading %d missing files", len(missingFiles))
log.Println("Trying to reinsert with 1 extra file", missingFilesPlus1)
t.reinsertTorrent(torrent, missingFilesPlus1, false) t.reinsertTorrent(torrent, missingFilesPlus1, false)
} else if len(selectedFiles) > 1 { } else if len(selectedFiles) > 1 {
// if not, last resort: add only the missing files but do it in 2 batches // if not, last resort: add only the missing files but do it in 2 batches
@@ -565,25 +564,28 @@ func (t *TorrentManager) repair(torrentID string, selectedFiles []File, tryReins
missingFiles1 := strings.Join(getFileIDs(missingFiles[:half]), ",") missingFiles1 := strings.Join(getFileIDs(missingFiles[:half]), ",")
missingFiles2 := strings.Join(getFileIDs(missingFiles[half:]), ",") missingFiles2 := strings.Join(getFileIDs(missingFiles[half:]), ",")
if missingFiles1 != "" { if missingFiles1 != "" {
log.Println("Trying to reinsert with 1/2 batches", missingFiles1) t.log.Infof("Redownloading %d missing files; batch 1 of 2", len(missingFiles1))
t.reinsertTorrent(torrent, missingFiles1, false) t.reinsertTorrent(torrent, missingFiles1, false)
} }
if missingFiles2 != "" { if missingFiles2 != "" {
log.Println("Trying to reinsert with 2/2 batches", missingFiles2) t.log.Infof("Redownloading %d missing files; batch 2 of 2", len(missingFiles2))
t.reinsertTorrent(torrent, missingFiles2, false) t.reinsertTorrent(torrent, missingFiles2, false)
} else {
t.log.Info("No other missing files left to reinsert")
} }
} else { } else {
log.Println("Cannot repair as the single link cached in RD for this torrent is already broken", torrent.ID, torrent.Name) t.log.Infof("Torrent %s %s is unfixable as the only link cached in RD is already broken", torrent.ID, torrent.Name)
t.log.Debugf("You can try fixing it yourself magnet:?xt=urn:btih:%s", torrent.Hash)
return return
} }
log.Println("Waiting for downloads to finish") t.log.Info("Waiting for downloads to finish")
} }
} }
func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string, deleteIfFailed bool) bool { func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string, deleteIfFailed bool) bool {
// if missingFiles is not provided, look for missing files // if missingFiles is not provided, look for missing files
if missingFiles == "" { if missingFiles == "" {
log.Println("Reinserting whole torrent", torrent.Name) t.log.Info("Redownloading whole torrent", torrent.Name)
var selection string var selection string
for _, file := range torrent.SelectedFiles { for _, file := range torrent.SelectedFiles {
selection += fmt.Sprintf("%d,", file.ID) selection += fmt.Sprintf("%d,", file.ID)
@@ -594,20 +596,18 @@ func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string,
if len(selection) > 0 { if len(selection) > 0 {
missingFiles = selection[:len(selection)-1] missingFiles = selection[:len(selection)-1]
} }
} else {
log.Printf("Reinserting %d missing files for %s", len(strings.Split(missingFiles, ",")), torrent.Name)
} }
// reinsert torrent // redownload torrent
resp, err := realdebrid.AddMagnetHash(t.config.GetToken(), torrent.Hash) resp, err := realdebrid.AddMagnetHash(t.config.GetToken(), torrent.Hash)
if err != nil { if err != nil {
log.Printf("Cannot reinsert torrent: %v\n", err) t.log.Errorf("Cannot redownload torrent: %v", err)
return false return false
} }
newTorrentID := resp.ID newTorrentID := resp.ID
err = realdebrid.SelectTorrentFiles(t.config.GetToken(), newTorrentID, missingFiles) err = realdebrid.SelectTorrentFiles(t.config.GetToken(), newTorrentID, missingFiles)
if err != nil { if err != nil {
log.Printf("Cannot select files on reinserted torrent: %v\n", err) t.log.Errorf("Cannot start redownloading: %v", err)
} }
if deleteIfFailed { if deleteIfFailed {
@@ -619,7 +619,7 @@ func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string,
// see if the torrent is ready // see if the torrent is ready
info, err := realdebrid.GetTorrentInfo(t.config.GetToken(), newTorrentID) info, err := realdebrid.GetTorrentInfo(t.config.GetToken(), newTorrentID)
if err != nil { if err != nil {
log.Printf("Cannot get info on reinserted torrent: %v\n", err) t.log.Errorf("Cannot get info on redownloaded torrent: %v", err)
if deleteIfFailed { if deleteIfFailed {
realdebrid.DeleteTorrent(t.config.GetToken(), newTorrentID) realdebrid.DeleteTorrent(t.config.GetToken(), newTorrentID)
} }
@@ -628,17 +628,17 @@ func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string,
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
if info.Progress != 100 { if info.Progress != 100 {
log.Printf("Torrent is not cached anymore, %d%%\n", info.Progress) t.log.Infof("Torrent is not cached anymore so we have to wait until completion, currently %d%%", info.Progress)
realdebrid.DeleteTorrent(t.config.GetToken(), newTorrentID) realdebrid.DeleteTorrent(t.config.GetToken(), newTorrentID)
return false return false
} }
if len(info.Links) != len(torrent.SelectedFiles) { if len(info.Links) != len(torrent.SelectedFiles) {
log.Printf("It doesn't fix the problem, got %d links but we need %d\n", len(info.Links), len(torrent.SelectedFiles)) t.log.Infof("It didn't fix the issue, only got %d files but we need %d, undoing", len(info.Links), len(torrent.SelectedFiles))
realdebrid.DeleteTorrent(t.config.GetToken(), newTorrentID) realdebrid.DeleteTorrent(t.config.GetToken(), newTorrentID)
return false return false
} }
log.Println("Reinsertion successful, deleting old torrent") t.log.Info("Redownload successful, deleting old torrent")
realdebrid.DeleteTorrent(t.config.GetToken(), torrent.ID) realdebrid.DeleteTorrent(t.config.GetToken(), torrent.ID)
} }
return true return true
@@ -713,9 +713,9 @@ func (t *TorrentManager) canCapacityHandle() bool {
for { for {
count, err := realdebrid.GetActiveTorrentCount(t.config.GetToken()) count, err := realdebrid.GetActiveTorrentCount(t.config.GetToken())
if err != nil { if err != nil {
log.Printf("Cannot get active torrent count: %v\n", err) t.log.Errorf("Cannot get active downloads count: %v", err)
if retryCount >= maxRetries { if retryCount >= maxRetries {
log.Println("Max retries reached. Exiting.") t.log.Error("Max retries reached. Exiting.")
return false return false
} }
delay := time.Duration(math.Pow(2, float64(retryCount))) * baseDelay delay := time.Duration(math.Pow(2, float64(retryCount))) * baseDelay
@@ -728,12 +728,12 @@ func (t *TorrentManager) canCapacityHandle() bool {
} }
if count.DownloadingCount < count.MaxNumberOfTorrents { if count.DownloadingCount < count.MaxNumberOfTorrents {
log.Printf("We can still add a new torrent, %d/%d\n", count.DownloadingCount, count.MaxNumberOfTorrents) t.log.Infof("We can still add a new torrent, we have capacity for %d more", count.MaxNumberOfTorrents-count.DownloadingCount)
return true return true
} }
if retryCount >= maxRetries { if retryCount >= maxRetries {
log.Println("Max retries reached. Exiting.") t.log.Error("Max retries reached, exiting")
return false return false
} }
delay := time.Duration(math.Pow(2, float64(retryCount))) * baseDelay delay := time.Duration(math.Pow(2, float64(retryCount))) * baseDelay

View File

@@ -2,7 +2,6 @@ package universal
import ( import (
"io" "io"
"log"
"net/http" "net/http"
"path" "path"
"path/filepath" "path/filepath"
@@ -13,12 +12,17 @@ import (
intHttp "github.com/debridmediamanager.com/zurg/internal/http" intHttp "github.com/debridmediamanager.com/zurg/internal/http"
"github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/internal/torrent"
"github.com/debridmediamanager.com/zurg/pkg/davextra" "github.com/debridmediamanager.com/zurg/pkg/davextra"
"github.com/debridmediamanager.com/zurg/pkg/logutil"
"github.com/debridmediamanager.com/zurg/pkg/realdebrid" "github.com/debridmediamanager.com/zurg/pkg/realdebrid"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
"go.uber.org/zap"
) )
// HandleGetRequest handles a GET request universally for both WebDAV and HTTP // HandleGetRequest handles a GET request universally for both WebDAV and HTTP
func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) { func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) {
rlog := logutil.NewLogger()
log := rlog.Named("uniget")
requestPath := path.Clean(r.URL.Path) requestPath := path.Clean(r.URL.Path)
isDav := true isDav := true
if strings.Contains(requestPath, "/http") { if strings.Contains(requestPath, "/http") {
@@ -39,7 +43,7 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent
return return
} }
if data, exists := cache.Get(requestPath); exists { if data, exists := cache.Get(requestPath); exists {
streamFileToResponse(data, w, r, c.GetNetworkBufferSize()) streamFileToResponse(data, w, r, c.GetNetworkBufferSize(), log)
return return
} }
@@ -49,7 +53,7 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent
torrents := t.FindAllTorrentsWithName(baseDirectory, torrentName) torrents := t.FindAllTorrentsWithName(baseDirectory, torrentName)
if torrents == nil { if torrents == nil {
log.Println("Cannot find torrent", requestPath) log.Errorf("Cannot find torrent %s in the directory %s", requestPath, baseDirectory)
http.Redirect(w, r, "https://send.nukes.wtf/tDeTd0", http.StatusFound) http.Redirect(w, r, "https://send.nukes.wtf/tDeTd0", http.StatusFound)
return return
} }
@@ -57,13 +61,13 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent
filenameV2, linkFragment := davextra.ExtractLinkFragment(filename) filenameV2, linkFragment := davextra.ExtractLinkFragment(filename)
torrent, file := getFile(torrents, filenameV2, linkFragment) torrent, file := getFile(torrents, filenameV2, linkFragment)
if file == nil { if file == nil {
log.Println("Cannot find file (get)", requestPath) log.Errorf("Cannot find file from path %s", requestPath)
http.Error(w, "Cannot find file", http.StatusNotFound) http.Error(w, "Cannot find file", http.StatusNotFound)
return return
} }
if file.Link == "" { if file.Link == "" {
log.Println("Link not found (get)", filename) // This is a dead file, serve an alternate file
http.Error(w, "Cannot find file", http.StatusNotFound) log.Errorf("File %s is no longer available", filename)
return return
} }
link := file.Link link := file.Link
@@ -74,7 +78,7 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent
resp := realdebrid.RetryUntilOk(unrestrictFn) resp := realdebrid.RetryUntilOk(unrestrictFn)
if resp == nil { if resp == nil {
if !file.Unavailable { if !file.Unavailable {
log.Println("Cannot unrestrict link", link, filenameV2) log.Errorf("Cannot unrestrict file %s %s", filenameV2, link)
t.HideTheFile(torrent, file) t.HideTheFile(torrent, file)
} }
http.Redirect(w, r, "https://send.nukes.wtf/tDeTd0", http.StatusFound) http.Redirect(w, r, "https://send.nukes.wtf/tDeTd0", http.StatusFound)
@@ -83,19 +87,19 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent
actualExt := filepath.Ext(resp.Filename) actualExt := filepath.Ext(resp.Filename)
expectedExt := filepath.Ext(filenameV2) expectedExt := filepath.Ext(filenameV2)
if actualExt != expectedExt { if actualExt != expectedExt {
log.Println("File extension mismatch", resp.Filename, filenameV2) log.Errorf("File extension mismatch: %s and %s", filenameV2, resp.Filename)
} else { } else {
log.Println("Filename mismatch", resp.Filename, filenameV2) log.Errorf("Filename mismatch: %s and %s", filenameV2, resp.Filename)
} }
} }
cache.Add(requestPath, resp.Download) cache.Add(requestPath, resp.Download)
streamFileToResponse(resp.Download, w, r, c.GetNetworkBufferSize()) streamFileToResponse(resp.Download, w, r, c.GetNetworkBufferSize(), log)
} }
func streamFileToResponse(url string, w http.ResponseWriter, r *http.Request, bufferSize int) { func streamFileToResponse(url string, w http.ResponseWriter, r *http.Request, bufferSize int, log *zap.SugaredLogger) {
req, err := http.NewRequest(http.MethodGet, url, nil) req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil { if err != nil {
log.Println("Error creating new request:", err) log.Errorf("Error creating new request %v", err)
http.Redirect(w, r, "https://send.nukes.wtf/ARjVWb", http.StatusFound) http.Redirect(w, r, "https://send.nukes.wtf/ARjVWb", http.StatusFound)
return return
} }
@@ -108,14 +112,14 @@ func streamFileToResponse(url string, w http.ResponseWriter, r *http.Request, bu
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
log.Println("Error downloading file:", err) log.Errorf("Error downloading file %v", err)
http.Redirect(w, r, "https://send.nukes.wtf/TB2u2n", http.StatusFound) http.Redirect(w, r, "https://send.nukes.wtf/TB2u2n", http.StatusFound)
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent { if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
log.Printf("Received a non-OK status code: %d", resp.StatusCode) log.Errorf("Received a nonOK status code %d", resp.StatusCode)
http.Redirect(w, r, "https://send.nukes.wtf/b5AiON", http.StatusFound) http.Redirect(w, r, "https://send.nukes.wtf/b5AiON", http.StatusFound)
return return
} }

View File

@@ -2,7 +2,6 @@ package universal
import ( import (
"fmt" "fmt"
"log"
"net/http" "net/http"
"path" "path"
"path/filepath" "path/filepath"
@@ -11,10 +10,14 @@ import (
"github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/config"
"github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/internal/torrent"
"github.com/debridmediamanager.com/zurg/pkg/davextra" "github.com/debridmediamanager.com/zurg/pkg/davextra"
"github.com/debridmediamanager.com/zurg/pkg/logutil"
"github.com/hashicorp/golang-lru/v2/expirable" "github.com/hashicorp/golang-lru/v2/expirable"
) )
func HandleHeadRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) { func HandleHeadRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) {
rlog := logutil.NewLogger()
log := rlog.Named("unihead")
requestPath := path.Clean(r.URL.Path) requestPath := path.Clean(r.URL.Path)
requestPath = strings.Replace(requestPath, "/http", "", 1) requestPath = strings.Replace(requestPath, "/http", "", 1)
if requestPath == "/favicon.ico" { if requestPath == "/favicon.ico" {
@@ -24,7 +27,7 @@ func HandleHeadRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torren
segments := strings.Split(requestPath, "/") segments := strings.Split(requestPath, "/")
// If there are less than 3 segments, return an error or adjust as needed // If there are less than 3 segments, return an error or adjust as needed
if len(segments) < 4 { if len(segments) < 4 {
log.Println("Method not implemented", r.Method, r.URL.Path) log.Errorf("Request %s %s not supported yet", r.Method, r.URL.Path)
http.Error(w, "Method not implemented", http.StatusMethodNotAllowed) http.Error(w, "Method not implemented", http.StatusMethodNotAllowed)
return return
} }
@@ -45,7 +48,7 @@ func HandleHeadRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torren
torrents := t.FindAllTorrentsWithName(baseDirectory, torrentName) torrents := t.FindAllTorrentsWithName(baseDirectory, torrentName)
if torrents == nil { if torrents == nil {
log.Println("Cannot find torrent", requestPath) log.Errorf("Cannot find torrent %s in the directory %s", requestPath, baseDirectory)
http.Error(w, "Cannot find file", http.StatusNotFound) http.Error(w, "Cannot find file", http.StatusNotFound)
return return
} }
@@ -53,12 +56,13 @@ func HandleHeadRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torren
filenameV2, linkFragment := davextra.ExtractLinkFragment(filename) filenameV2, linkFragment := davextra.ExtractLinkFragment(filename)
_, file := getFile(torrents, filenameV2, linkFragment) _, file := getFile(torrents, filenameV2, linkFragment)
if file == nil { if file == nil {
log.Println("Cannot find file (head)", requestPath) log.Errorf("Cannot find file from path %s", requestPath)
http.Error(w, "Cannot find file", http.StatusNotFound) http.Error(w, "Cannot find file", http.StatusNotFound)
return return
} }
if file.Link == "" { if file.Link == "" {
log.Println("Link not found (head)", filename) // This is a dead file, serve an alternate file
log.Errorf("File %s is no longer available", filename)
http.Error(w, "Cannot find file", http.StatusNotFound) http.Error(w, "Cannot find file", http.StatusNotFound)
return return
} }