shelfy-v2/internal/controllers/downloadController.go

276 lines
7.0 KiB
Go
Raw Normal View History

2025-07-27 14:26:30 +00:00
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
2025-08-18 19:26:33 +00:00
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)
2025-07-27 14:26:30 +00:00
// 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,
})
}
}