Merge branch 'beta' of github.com:sirrobot01/debrid-blackhole into beta

This commit is contained in:
Mukhtar Akere
2025-02-01 04:29:27 +01:00
3 changed files with 112 additions and 70 deletions

View File

@@ -2,11 +2,11 @@ package common
import (
"bufio"
"bytes"
"context"
"encoding/base32"
"encoding/hex"
"fmt"
"github.com/anacrolix/torrent/metainfo"
"io"
"log"
"math/rand"
@@ -17,6 +17,8 @@ import (
"regexp"
"strings"
"time"
"github.com/anacrolix/torrent/metainfo"
)
type Magnet struct {
@@ -28,23 +30,11 @@ type Magnet struct {
func GetMagnetFromFile(file io.Reader, filePath string) (*Magnet, error) {
if filepath.Ext(filePath) == ".torrent" {
mi, err := metainfo.Load(file)
torrentData, err := io.ReadAll(file)
if err != nil {
return nil, err
}
hash := mi.HashInfoBytes()
infoHash := hash.HexString()
info, err := mi.UnmarshalInfo()
if err != nil {
return nil, err
}
magnet := &Magnet{
InfoHash: infoHash,
Name: info.Name,
Size: info.Length,
Link: mi.Magnet(&hash, &info).String(),
}
return magnet, nil
return GetMagnetFromBytes(torrentData)
} else {
// .magnet file
magnetLink := ReadMagnetFile(file)
@@ -61,6 +51,28 @@ func GetMagnetFromUrl(url string) (*Magnet, error) {
return nil, fmt.Errorf("invalid url")
}
func GetMagnetFromBytes(torrentData []byte) (*Magnet, error) {
// Create a scanner to read the file line by line
mi, err := metainfo.Load(bytes.NewReader(torrentData))
if err != nil {
return nil, err
}
hash := mi.HashInfoBytes()
infoHash := hash.HexString()
info, err := mi.UnmarshalInfo()
if err != nil {
return nil, err
}
log.Println("InfoHash: ", infoHash)
magnet := &Magnet{
InfoHash: infoHash,
Name: info.Name,
Size: info.Length,
Link: mi.Magnet(&hash, &info).String(),
}
return magnet, nil
}
func OpenMagnetFile(filePath string) string {
file, err := os.Open(filePath)
if err != nil {
@@ -103,27 +115,11 @@ func OpenMagnetHttpURL(magnetLink string) (*Magnet, error) {
return
}
}(resp) // Ensure the response is closed after the function ends
// Create a scanner to read the file line by line
mi, err := metainfo.Load(resp.Body)
torrentData, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
return nil, fmt.Errorf("error reading response body: %v", err)
}
hash := mi.HashInfoBytes()
infoHash := hash.HexString()
info, err := mi.UnmarshalInfo()
if err != nil {
return nil, err
}
log.Println("InfoHash: ", infoHash)
magnet := &Magnet{
InfoHash: infoHash,
Name: info.Name,
Size: info.Length,
Link: mi.Magnet(&hash, &info).String(),
}
return magnet, nil
return GetMagnetFromBytes(torrentData)
}
func GetMagnetInfo(magnetLink string) (*Magnet, error) {

View File

@@ -5,20 +5,26 @@
<h4 class="mb-0"><i class="bi bi-cloud-download me-2"></i>Add New Download</h4>
</div>
<div class="card-body">
<form id="downloadForm">
<div class="mb-3">
<label for="magnetURI" class="form-label">Magnet Link(s) or Torrent URL(s)</label>
<textarea class="form-control" id="magnetURI" rows="8" placeholder="Paste your magnet links or torrent URLs here, one per line..."></textarea>
<form id="downloadForm" enctype="multipart/form-data">
<div class="mb-2">
<label for="magnetURI" class="form-label">Torrent(s)</label>
<textarea class="form-control" id="magnetURI" name="urls" rows="8" placeholder="Paste your magnet links or torrent URLs here, one per line..."></textarea>
</div>
<div class="mb-3">
<input type="file" class="form-control" id="torrentFiles" name="torrents" multiple accept=".torrent,.magnet">
</div>
<hr />
<div class="mb-3">
<label for="category" class="form-label">Enter Category</label>
<input type="text" class="form-control" id="category" placeholder="Enter Category(e.g sonarr, radarr, radarr4k)">
<input type="text" class="form-control" id="category" name="arr" placeholder="Enter Category (e.g sonarr, radarr, radarr4k)">
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="isSymlink">
<input class="form-check-input" type="checkbox" id="isSymlink" name="notSymlink">
<label class="form-check-label" for="isSymlink">
Download real files instead of symlinks
</label>
@@ -62,31 +68,40 @@
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Adding...';
try {
const formData = new FormData();
// Add URLs if present
const urls = document.getElementById('magnetURI').value
.split('\n')
.map(url => url.trim())
.filter(url => url.length > 0);
if (urls.length === 0) {
if (urls.length > 0) {
formData.append('urls', urls.join('\n'));
}
// Add torrent files if present
const fileInput = document.getElementById('torrentFiles');
for (let i = 0; i < fileInput.files.length; i++) {
formData.append('files', fileInput.files[i]);
}
if (urls.length + fileInput.files.length === 0) {
createToast('Please submit at least one torrent', 'warning');
return;
}
if (urls.length >= 100) {
createToast('Please submit less than 100 torrents at a time', 'warning');
if (urls.length + fileInput.files.length > 100) {
createToast('Please submit up to 100 torrents at a time', 'warning');
return;
}
formData.append('arr', document.getElementById('category').value);
formData.append('notSymlink', document.getElementById('isSymlink').checked);
const response = await fetch('/internal/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
urls: urls,
arr: document.getElementById('category').value,
notSymlink: document.getElementById('isSymlink').checked
})
body: formData
});
const result = await response.json();
@@ -100,6 +115,9 @@
} else {
createToast(`Successfully added ${result.results.length} torrents!`);
}
document.getElementById('magnetURI').value = '';
document.getElementById('torrentFiles').value = '';
} catch (error) {
createToast(`Error adding downloads: ${error.message}`, 'error');
} finally {

View File

@@ -117,37 +117,65 @@ func (u *uiHandler) handleGetArrs(w http.ResponseWriter, r *http.Request) {
}
func (u *uiHandler) handleAddContent(w http.ResponseWriter, r *http.Request) {
var req struct {
URLs []string `json:"urls"`
Arr string `json:"arr"`
NotSymlink bool `json:"notSymlink"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
if err := r.ParseMultipartForm(32 << 20); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
results := make([]*ImportRequest, 0, len(req.URLs))
results := make([]*ImportRequest, 0)
errs := make([]string, 0)
_arr := u.qbit.Arrs.Get(req.Arr)
arrName := r.FormValue("arr")
notSymlink := r.FormValue("notSymlink") == "true"
_arr := u.qbit.Arrs.Get(arrName)
if _arr == nil {
_arr = arr.NewArr(req.Arr, "", "", arr.Sonarr)
_arr = arr.NewArr(arrName, "", "", arr.Sonarr)
}
for _, url := range req.URLs {
if url == "" {
continue
// Handle URLs
if urls := r.FormValue("urls"); urls != "" {
var urlList []string
for _, u := range strings.Split(urls, "\n") {
if trimmed := strings.TrimSpace(u); trimmed != "" {
urlList = append(urlList, trimmed)
}
}
importReq := NewImportRequest(url, _arr, !req.NotSymlink)
err := importReq.Process(u.qbit)
if err != nil {
errs = append(errs, fmt.Sprintf("URL %s: %v", url, err))
continue
for _, url := range urlList {
importReq := NewImportRequest(url, _arr, !notSymlink)
err := importReq.Process(u.qbit)
if err != nil {
errs = append(errs, fmt.Sprintf("URL %s: %v", url, err))
continue
}
results = append(results, importReq)
}
}
// Handle torrent/magnet files
if files := r.MultipartForm.File["files"]; len(files) > 0 {
for _, fileHeader := range files {
file, err := fileHeader.Open()
if err != nil {
errs = append(errs, fmt.Sprintf("Failed to open file %s: %v", fileHeader.Filename, err))
continue
}
magnet, err := common.GetMagnetFromFile(file, fileHeader.Filename)
if err != nil {
errs = append(errs, fmt.Sprintf("Failed to parse torrent file %s: %v", fileHeader.Filename, err))
continue
}
importReq := NewImportRequest(magnet.Link, _arr, !notSymlink)
err = importReq.Process(u.qbit)
if err != nil {
errs = append(errs, fmt.Sprintf("File %s: %v", fileHeader.Filename, err))
continue
}
results = append(results, importReq)
}
results = append(results, importReq)
}
common.JSONResponse(w, struct {