Merge branch 'beta' of github.com:sirrobot01/debrid-blackhole into beta
This commit is contained in:
@@ -2,11 +2,11 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anacrolix/torrent/metainfo"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Magnet struct {
|
type Magnet struct {
|
||||||
@@ -28,23 +30,11 @@ type Magnet struct {
|
|||||||
|
|
||||||
func GetMagnetFromFile(file io.Reader, filePath string) (*Magnet, error) {
|
func GetMagnetFromFile(file io.Reader, filePath string) (*Magnet, error) {
|
||||||
if filepath.Ext(filePath) == ".torrent" {
|
if filepath.Ext(filePath) == ".torrent" {
|
||||||
mi, err := metainfo.Load(file)
|
torrentData, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
hash := mi.HashInfoBytes()
|
return GetMagnetFromBytes(torrentData)
|
||||||
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
|
|
||||||
} else {
|
} else {
|
||||||
// .magnet file
|
// .magnet file
|
||||||
magnetLink := ReadMagnetFile(file)
|
magnetLink := ReadMagnetFile(file)
|
||||||
@@ -61,6 +51,28 @@ func GetMagnetFromUrl(url string) (*Magnet, error) {
|
|||||||
return nil, fmt.Errorf("invalid url")
|
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 {
|
func OpenMagnetFile(filePath string) string {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -103,27 +115,11 @@ func OpenMagnetHttpURL(magnetLink string) (*Magnet, error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}(resp) // Ensure the response is closed after the function ends
|
}(resp) // Ensure the response is closed after the function ends
|
||||||
|
torrentData, err := io.ReadAll(resp.Body)
|
||||||
// Create a scanner to read the file line by line
|
|
||||||
|
|
||||||
mi, err := metainfo.Load(resp.Body)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("error reading response body: %v", err)
|
||||||
}
|
}
|
||||||
hash := mi.HashInfoBytes()
|
return GetMagnetFromBytes(torrentData)
|
||||||
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 GetMagnetInfo(magnetLink string) (*Magnet, error) {
|
func GetMagnetInfo(magnetLink string) (*Magnet, error) {
|
||||||
|
|||||||
@@ -5,20 +5,26 @@
|
|||||||
<h4 class="mb-0"><i class="bi bi-cloud-download me-2"></i>Add New Download</h4>
|
<h4 class="mb-0"><i class="bi bi-cloud-download me-2"></i>Add New Download</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form id="downloadForm">
|
<form id="downloadForm" enctype="multipart/form-data">
|
||||||
<div class="mb-3">
|
<div class="mb-2">
|
||||||
<label for="magnetURI" class="form-label">Magnet Link(s) or Torrent URL(s)</label>
|
<label for="magnetURI" class="form-label">Torrent(s)</label>
|
||||||
<textarea class="form-control" id="magnetURI" rows="8" placeholder="Paste your magnet links or torrent URLs here, one per line..."></textarea>
|
<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>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="file" class="form-control" id="torrentFiles" name="torrents" multiple accept=".torrent,.magnet">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="category" class="form-label">Enter Category</label>
|
<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>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="form-check">
|
<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">
|
<label class="form-check-label" for="isSymlink">
|
||||||
Download real files instead of symlinks
|
Download real files instead of symlinks
|
||||||
</label>
|
</label>
|
||||||
@@ -62,31 +68,40 @@
|
|||||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Adding...';
|
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Adding...';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
// Add URLs if present
|
||||||
const urls = document.getElementById('magnetURI').value
|
const urls = document.getElementById('magnetURI').value
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(url => url.trim())
|
.map(url => url.trim())
|
||||||
.filter(url => url.length > 0);
|
.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');
|
createToast('Please submit at least one torrent', 'warning');
|
||||||
return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formData.append('arr', document.getElementById('category').value);
|
||||||
|
formData.append('notSymlink', document.getElementById('isSymlink').checked);
|
||||||
|
|
||||||
const response = await fetch('/internal/add', {
|
const response = await fetch('/internal/add', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
body: formData
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
urls: urls,
|
|
||||||
arr: document.getElementById('category').value,
|
|
||||||
notSymlink: document.getElementById('isSymlink').checked
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
@@ -100,6 +115,9 @@
|
|||||||
} else {
|
} else {
|
||||||
createToast(`Successfully added ${result.results.length} torrents!`);
|
createToast(`Successfully added ${result.results.length} torrents!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.getElementById('magnetURI').value = '';
|
||||||
|
document.getElementById('torrentFiles').value = '';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
createToast(`Error adding downloads: ${error.message}`, 'error');
|
createToast(`Error adding downloads: ${error.message}`, 'error');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -117,31 +117,33 @@ func (u *uiHandler) handleGetArrs(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *uiHandler) handleAddContent(w http.ResponseWriter, r *http.Request) {
|
func (u *uiHandler) handleAddContent(w http.ResponseWriter, r *http.Request) {
|
||||||
var req struct {
|
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
||||||
URLs []string `json:"urls"`
|
|
||||||
Arr string `json:"arr"`
|
|
||||||
NotSymlink bool `json:"notSymlink"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
results := make([]*ImportRequest, 0, len(req.URLs))
|
results := make([]*ImportRequest, 0)
|
||||||
errs := make([]string, 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 {
|
if _arr == nil {
|
||||||
_arr = arr.NewArr(req.Arr, "", "", arr.Sonarr)
|
_arr = arr.NewArr(arrName, "", "", arr.Sonarr)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, url := range req.URLs {
|
// Handle URLs
|
||||||
if url == "" {
|
if urls := r.FormValue("urls"); urls != "" {
|
||||||
continue
|
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)
|
for _, url := range urlList {
|
||||||
|
importReq := NewImportRequest(url, _arr, !notSymlink)
|
||||||
err := importReq.Process(u.qbit)
|
err := importReq.Process(u.qbit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("URL %s: %v", url, err))
|
errs = append(errs, fmt.Sprintf("URL %s: %v", url, err))
|
||||||
@@ -149,6 +151,32 @@ func (u *uiHandler) handleAddContent(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
results = append(results, importReq)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
common.JSONResponse(w, struct {
|
common.JSONResponse(w, struct {
|
||||||
Results []*ImportRequest `json:"results"`
|
Results []*ImportRequest `json:"results"`
|
||||||
|
|||||||
Reference in New Issue
Block a user