shelfy-v2/internal/controllers/downloadController.go
2025-08-18 21:26:33 +02:00

276 lines
7.0 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controllers
import (
"canguidev/shelfy/internal/client"
runner "canguidev/shelfy/internal/job"
"canguidev/shelfy/internal/models"
"context"
"log"
"net/http"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func sanitizeFileName(name string) string {
return runner.SanitizeFileName(name)
}
var seriesRegex = regexp.MustCompile(`^(.+?)\.S\d{2}E\d{2}`)
var (
jobs = make(map[string]*runner.DownloadJob)
jobsMu sync.Mutex
)
func HandleAddJobsMultiple(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
// 1. Parsing des champs du formulaire
var form struct {
Links string `json:"links" form:"links" binding:"required"`
PathID string `json:"path_id" form:"path_id" binding:"required"`
}
// On tente d'abord le bind JSON
if err := c.ShouldBindJSON(&form); err != nil {
log.Printf("[DEBUG] Reçu links=%q | path_id=%q", form.Links, form.PathID)
// Si JSON échoue, on tente avec form-urlencoded
if err := c.ShouldBind(&form); err != nil {
log.Printf("[DEBUG] Reçu links=%q | path_id=%q", form.Links, form.PathID)
c.JSON(http.StatusBadRequest, gin.H{"error": "Champs requis manquants ou invalides"})
return
}
}
lines := strings.Split(form.Links, "\n")
baseID, err := strconv.ParseInt(form.PathID, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID de chemin invalide"})
return
}
// 2. Vérification du dossier principal
var paths []models.PathDownload
if err := db.Find(&paths).Error; err != nil {
log.Printf("[DEBUG] Erreur lors du Find: %v", err)
} else {
log.Printf("[DEBUG] Contenu de la table PathDownload avant recherche: %+v", paths)
}
var basePath models.PathDownload
if err := db.First(&basePath, baseID).Error; err != nil {
log.Printf("[DEBUG] Pas trouvé baseID=%v dans PathDownload", baseID)
c.JSON(http.StatusBadRequest, gin.H{"error": "Dossier principal introuvable"})
return
}
log.Printf("[DEBUG] PathDownload trouvé pour baseID=%v: %+v", baseID, basePath)
// 3. Initialisation Debrid-Link
ctx := context.Background()
clt := client.NewClient(db)
account := runner.GetFirstActiveAccount(clt)
if account == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte Debrid-Link actif"})
return
}
clt.SetAccount(account)
// 4. Résultats à retourner
var results []map[string]interface{}
// 5. Itération des liens
for _, link := range lines {
link = strings.TrimSpace(link)
if link == "" {
continue
}
links, err := clt.AddLink(ctx, link)
if err != nil {
log.Printf("Échec débridage de %s: %v", link, err)
results = append(results, gin.H{"link": link, "status": "failed", "error": err.Error()})
continue
}
for _, l := range links {
clean := sanitizeFileName(l.Name)
series := clean
if m := seriesRegex.FindStringSubmatch(clean); len(m) == 2 {
series = m[1]
}
assignID := int(basePath.ID)
if series != "" {
dirPath := filepath.Join(basePath.Path, series)
if err := os.MkdirAll(dirPath, os.ModePerm); err != nil {
log.Printf("Erreur création dossier %s: %v", dirPath, err)
}
var sub models.PathDownload
if err := db.Where("path = ?", dirPath).First(&sub).Error; err != nil {
if err == gorm.ErrRecordNotFound {
sub = models.PathDownload{Path: dirPath, PathName: series}
if err := db.Create(&sub).Error; err != nil {
log.Printf("Erreur création PathDownload: %v", err)
}
} else {
log.Printf("Erreur lecture PathDownload: %v", err)
}
}
assignID = int(sub.ID)
}
// streamInfo, err := clt.CreateTranscode(ctx, l.ID)
// if err != nil {
// log.Printf("Erreur transcode pour %s: %v", l.ID, err)
// }
job := &runner.DownloadJob{
ID: l.ID,
Link: l.DownloadURL,
Name: l.Name,
Status: "waiting",
PathID: assignID,
Size: l.Size,
Host: l.Host,
Progress: 0,
StreamURL: "",
}
// if streamInfo != nil {
// job.StreamURL = streamInfo.StreamURL
// }
if err := runner.RegisterJobWithDB(job, db); err != nil {
log.Printf("Erreur enregistrement job: %v", err)
results = append(results, gin.H{"link": l.URL, "status": "failed", "error": err.Error()})
continue
}
results = append(results, gin.H{"link": l.URL, "status": "added", "name": job.Name})
}
}
// 6. Broadcast au frontend
runner.Broadcast()
c.JSON(http.StatusOK, gin.H{
"message": "Traitement terminé",
"results": results,
})
}
}
func HandleStartJob(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
log.Printf("[StartJob] ID = %s", id)
// 1. Vérifie si le job est déjà en mémoire
jobsMu.Lock()
job, exists := jobs[id]
jobsMu.Unlock()
// 2. Sinon, récupère depuis la BDD
if !exists {
var j runner.DownloadJob
if err := db.First(&j, "id = ?", id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"})
return
}
jobCopy := j
jobsMu.Lock()
jobs[id] = &jobCopy
job = &jobCopy
jobsMu.Unlock()
}
clt:= client.NewClient(db)
account := runner.GetFirstActiveAccount(clt)
if account == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte Debrid-Link actif"})
return
}
clt.SetAccount(account)
go runner.StartDownload(job, job.Link, clt, db)
runner.Broadcast()
c.Status(http.StatusNoContent)
}
}
func HandlePauseJob() gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
runner.UpdateJobStatus(id, "paused", nil)
runner.Broadcast()
c.Status(http.StatusNoContent)
}
}
func HandleResumeJob(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
jobsMu.Lock()
job, exists := jobs[id]
jobsMu.Unlock()
if !exists {
var j runner.DownloadJob
if err := db.First(&j, "id = ?", id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"})
return
}
jobCopy := j
jobsMu.Lock()
jobs[id] = &jobCopy
job = &jobCopy
jobsMu.Unlock()
}
clt := client.NewClient(db)
account := runner.GetFirstActiveAccount(clt)
if account == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte actif"})
return
}
clt.SetAccount(account)
go runner.StartDownload(job, job.Link, clt, db)
runner.Broadcast()
c.Status(http.StatusNoContent)
}
}
func HandleDeleteJob(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
runner.DeleteJob(id, db)
go runner.Broadcast()
c.Status(http.StatusNoContent)
}
}
func HandleListJobs(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var jobs []runner.DownloadJob
// Optionnel: filtrage (par user, status, etc.)
if err := db.Find(&jobs).Error; err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{
"jobs": jobs,
})
}
}