276 lines
7.0 KiB
Go
276 lines
7.0 KiB
Go
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,
|
||
})
|
||
}
|
||
}
|
||
|
||
|
||
|