fix mutli dowload

This commit is contained in:
cangui 2025-09-28 15:44:15 +02:00
parent d0eebe3ffc
commit b52729630a

View File

@ -25,7 +25,27 @@ var seriesRegex = regexp.MustCompile(`^(.+?)\.S\d{2}E\d{2}`)
var ( var (
jobs = make(map[string]*runner.DownloadJob) jobs = make(map[string]*runner.DownloadJob)
jobsMu sync.Mutex jobsMu sync.Mutex
// --- Ajouts ---
inFlightMu sync.Mutex
inFlight = map[string]struct{}{} // set des jobs déjà en cours (par id)
parallelism = 4 // <— ajuste le nombre de DL en parallèle
sem = make(chan struct{}, parallelism)
) )
func tryMarkInFlight(id string) bool {
inFlightMu.Lock()
defer inFlightMu.Unlock()
if _, ok := inFlight[id]; ok {
return false
}
inFlight[id] = struct{}{}
return true
}
func unmarkInFlight(id string) {
inFlightMu.Lock()
delete(inFlight, id)
inFlightMu.Unlock()
}
func HandleAddJobsMultiple(db *gorm.DB) gin.HandlerFunc { func HandleAddJobsMultiple(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
// 1. Parsing des champs du formulaire // 1. Parsing des champs du formulaire
@ -167,43 +187,62 @@ func HandleAddJobsMultiple(db *gorm.DB) gin.HandlerFunc {
func HandleStartJob(db *gorm.DB) gin.HandlerFunc { func HandleStartJob(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
log.Printf("[StartJob] ID = %s", id) log.Printf("[StartJob] ID = %s", id)
// 1. Vérifie si le job est déjà en mémoire // Empêche un job déjà en cours d'être relancé
jobsMu.Lock() if !tryMarkInFlight(id) {
job, exists := jobs[id] log.Printf("[StartJob] Job %s déjà en cours, on ignore", id)
jobsMu.Unlock() c.Status(http.StatusNoContent)
return
}
// 2. Sinon, récupère depuis la BDD // Récupération du job depuis la mémoire ou la DB
if !exists { jobsMu.Lock()
var j runner.DownloadJob job, exists := jobs[id]
if err := db.First(&j, "id = ?", id).Error; err != nil { jobsMu.Unlock()
c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"}) if !exists {
return var j runner.DownloadJob
} if err := db.First(&j, "id = ?", id).Error; err != nil {
jobCopy := j unmarkInFlight(id)
jobsMu.Lock() c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"})
jobs[id] = &jobCopy return
job = &jobCopy }
jobsMu.Unlock() jobCopy := j
} jobsMu.Lock()
jobs[id] = &jobCopy
job = &jobCopy
jobsMu.Unlock()
}
clt:= client.NewClient(db) // Init client Debrid-Link
account := runner.GetFirstActiveAccount(clt) clt := client.NewClient(db)
if account == nil { account := runner.GetFirstActiveAccount(clt)
c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte Debrid-Link actif"}) if account == nil {
return unmarkInFlight(id)
} c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte Debrid-Link actif"})
clt.SetAccount(account) return
}
clt.SetAccount(account)
go runner.StartDownload(job, job.Link, clt, db) // Lancement asynchrone avec limite de parallélisme
runner.Broadcast() go func(job *runner.DownloadJob) {
sem <- struct{}{} // bloque si trop de jobs en cours
defer func() {
<-sem
unmarkInFlight(job.ID)
runner.Broadcast()
}()
c.Status(http.StatusNoContent) runner.StartDownload(job, job.Link, clt, db)
} }(job)
runner.Broadcast()
c.Status(http.StatusNoContent)
}
} }
func HandlePauseJob() gin.HandlerFunc { func HandlePauseJob() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
@ -214,39 +253,60 @@ func HandlePauseJob() gin.HandlerFunc {
} }
} }
func HandleResumeJob(db *gorm.DB) gin.HandlerFunc { func HandleResumeJob(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
log.Printf("[ResumeJob] ID = %s", id)
jobsMu.Lock() // Empêche un job déjà en cours d'être relancé
job, exists := jobs[id] if !tryMarkInFlight(id) {
jobsMu.Unlock() log.Printf("[ResumeJob] Job %s déjà en cours, on ignore", id)
c.Status(http.StatusNoContent)
return
}
if !exists { // Récupération du job depuis la mémoire ou la DB
var j runner.DownloadJob jobsMu.Lock()
if err := db.First(&j, "id = ?", id).Error; err != nil { job, exists := jobs[id]
c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"}) jobsMu.Unlock()
return if !exists {
} var j runner.DownloadJob
jobCopy := j if err := db.First(&j, "id = ?", id).Error; err != nil {
jobsMu.Lock() unmarkInFlight(id)
jobs[id] = &jobCopy c.JSON(http.StatusNotFound, gin.H{"error": "Job introuvable"})
job = &jobCopy return
jobsMu.Unlock() }
} jobCopy := j
jobsMu.Lock()
jobs[id] = &jobCopy
job = &jobCopy
jobsMu.Unlock()
}
clt := client.NewClient(db) // Init client Debrid-Link
account := runner.GetFirstActiveAccount(clt) clt := client.NewClient(db)
if account == nil { account := runner.GetFirstActiveAccount(clt)
c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte actif"}) if account == nil {
return unmarkInFlight(id)
} c.JSON(http.StatusBadRequest, gin.H{"error": "Aucun compte Debrid-Link actif"})
clt.SetAccount(account) return
}
clt.SetAccount(account)
go runner.StartDownload(job, job.Link, clt, db) // Reprise asynchrone avec limite de parallélisme
runner.Broadcast() go func(job *runner.DownloadJob) {
sem <- struct{}{} // bloque si trop de jobs en cours
defer func() {
<-sem
unmarkInFlight(job.ID)
runner.Broadcast()
}()
c.Status(http.StatusNoContent) runner.StartDownload(job, job.Link, clt, db)
} }(job)
runner.Broadcast()
c.Status(http.StatusNoContent)
}
} }
func HandleDeleteJob(db *gorm.DB) gin.HandlerFunc { func HandleDeleteJob(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {