This commit is contained in:
cangui 2025-08-18 22:01:17 +02:00
parent 91d1d97361
commit e1955871b2

View File

@ -184,12 +184,22 @@ func ListJobs(db *gorm.DB) []*DownloadJob {
func StartDownload(job *DownloadJob, downloadURL string, client *client.Client, db *gorm.DB) { func StartDownload(job *DownloadJob, downloadURL string, client *client.Client, db *gorm.DB) {
UpdateJobStatus(job.ID, "downloading", db) UpdateJobStatus(job.ID, "downloading", db)
var path models.PathDownload // Récupère l'entrée (Path="Film" / "Série" / ...)
if err := db.First(&path, job.PathID).Error; err != nil { var p models.PathDownload
if err := db.First(&p, job.PathID).Error; err != nil {
UpdateJobStatus(job.ID, "failed", db) UpdateJobStatus(job.ID, "failed", db)
return return
} }
// ★ Chemin physique = upload/<Path>
diskBase := filepath.Join("upload", p.Path)
if err := os.MkdirAll(diskBase, 0o755); err != nil {
log.Printf("[ERROR] Création dossier '%s' : %v", diskBase, err)
UpdateJobStatus(job.ID, "failed", db)
return
}
// HEAD pour taille + Accept-Ranges
resp, err := http.Head(downloadURL) resp, err := http.Head(downloadURL)
if err != nil || resp.StatusCode != http.StatusOK { if err != nil || resp.StatusCode != http.StatusOK {
UpdateJobStatus(job.ID, "failed", db) UpdateJobStatus(job.ID, "failed", db)
@ -201,10 +211,11 @@ func StartDownload(job *DownloadJob, downloadURL string, client *client.Client,
return return
} }
acceptRanges := resp.Header.Get("Accept-Ranges") acceptRanges := strings.ToLower(resp.Header.Get("Accept-Ranges"))
if acceptRanges != "bytes" { if acceptRanges != "bytes" {
log.Println("[INFO] Serveur ne supporte pas Range, fallback single thread") log.Println("[INFO] Serveur ne supporte pas Range, fallback single thread")
StartDownloadSingleThread(job, downloadURL, db, path.Path) // ★ passer le chemin physique
StartDownloadSingleThread(job, downloadURL, db, diskBase)
return return
} }
@ -220,7 +231,6 @@ func StartDownload(job *DownloadJob, downloadURL string, client *client.Client,
go func() { go func() {
var lastTotal int64 = 0 var lastTotal int64 = 0
lastUpdate := time.Now() lastUpdate := time.Now()
ticker := time.NewTicker(1 * time.Second) ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() defer ticker.Stop()
@ -231,12 +241,11 @@ func StartDownload(job *DownloadJob, downloadURL string, client *client.Client,
case <-ticker.C: case <-ticker.C:
elapsed := time.Since(lastUpdate).Seconds() elapsed := time.Since(lastUpdate).Seconds()
if elapsed > 0 { if elapsed > 0 {
speed := int(float64(downloaded-lastTotal) / elapsed / 1024) // en Ko/s speed := int(float64(downloaded-lastTotal) / elapsed / 1024) // Ko/s
lastTotal = downloaded lastTotal = downloaded
lastUpdate = time.Now() lastUpdate = time.Now()
progress := int((downloaded * 100) / size) progress := int((downloaded * 100) / size)
// Update en base
db.Model(&DownloadJob{}).Where("id = ?", job.ID).Updates(map[string]interface{}{ db.Model(&DownloadJob{}).Where("id = ?", job.ID).Updates(map[string]interface{}{
"progress": progress, "progress": progress,
"speed": speed, "speed": speed,
@ -257,14 +266,13 @@ func StartDownload(job *DownloadJob, downloadURL string, client *client.Client,
end = size - 1 end = size - 1
} }
tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("%s.part%d", job.ID, i)) tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v.part%d", job.ID, i))
tmpFiles[i] = tmpPath tmpFiles[i] = tmpPath
wg.Add(1) wg.Add(1)
go func(start, end int64, tmpPath string) { go func(start, end int64, tmpPath string) {
defer wg.Done() defer wg.Done()
err := downloadSegment(downloadURL, start, end, tmpPath, progressChan) if err := downloadSegment(downloadURL, start, end, tmpPath, progressChan); err != nil {
if err != nil {
log.Printf("[ERROR] Segment %d-%d échoué : %v\n", start, end, err) log.Printf("[ERROR] Segment %d-%d échoué : %v\n", start, end, err)
} }
}(start, end, tmpPath) }(start, end, tmpPath)
@ -273,9 +281,10 @@ func StartDownload(job *DownloadJob, downloadURL string, client *client.Client,
wg.Wait() wg.Wait()
close(done) close(done)
// Fusion // Fusion vers destination finale
safeName := SanitizeFileName(job.Name) safeName := SanitizeFileName(job.Name)
finalPath := generateUniqueFilePath(path.Path, safeName) // ★ utilise le dossier physique
finalPath := generateUniqueFilePath(diskBase, safeName)
out, err := os.Create(finalPath) out, err := os.Create(finalPath)
if err != nil { if err != nil {
@ -290,24 +299,26 @@ func StartDownload(job *DownloadJob, downloadURL string, client *client.Client,
UpdateJobStatus(job.ID, "failed", db) UpdateJobStatus(job.ID, "failed", db)
return return
} }
io.Copy(out, part) _, _ = io.Copy(out, part)
part.Close() _ = part.Close()
os.Remove(tmpPath) _ = os.Remove(tmpPath)
} }
// Post-traitement selon extension
ext := strings.ToLower(filepath.Ext(finalPath)) ext := strings.ToLower(filepath.Ext(finalPath))
videoExts := map[string]bool{".mkv": true, ".avi": true, ".mp4": true} videoExts := map[string]bool{".mkv": true, ".avi": true, ".mp4": true}
if !videoExts[ext] { if !videoExts[ext] {
switch ext { switch ext {
case ".zip": case ".zip":
if err := unzip(finalPath, path.Path); err != nil { // ★ extraire dans le dossier physique
if err := unzip(finalPath, diskBase); err != nil {
log.Printf("[ERROR] Décompression ZIP échouée : %v\n", err) log.Printf("[ERROR] Décompression ZIP échouée : %v\n", err)
UpdateJobStatus(job.ID, "failed", db) UpdateJobStatus(job.ID, "failed", db)
return return
} }
case ".rar": case ".rar":
if err := unrarExtract(finalPath, path.Path); err != nil { if err := unrarExtract(finalPath, diskBase); err != nil {
log.Printf("[ERROR] Décompression RAR échouée : %v\n", err) log.Printf("[ERROR] Décompression RAR échouée : %v\n", err)
UpdateJobStatus(job.ID, "failed", db) UpdateJobStatus(job.ID, "failed", db)
return return
@ -322,6 +333,7 @@ func StartDownload(job *DownloadJob, downloadURL string, client *client.Client,
log.Printf("[OK] Fichier téléchargé : %s\n", finalPath) log.Printf("[OK] Fichier téléchargé : %s\n", finalPath)
} }
// generateUniqueFilePath ajoute un suffixe si le fichier existe déjà // generateUniqueFilePath ajoute un suffixe si le fichier existe déjà
func generateUniqueFilePath(basePath, fileName string) string { func generateUniqueFilePath(basePath, fileName string) string {
finalPath := filepath.Join(basePath, fileName) finalPath := filepath.Join(basePath, fileName)
@ -346,6 +358,25 @@ func generateUniqueFilePath(basePath, fileName string) string {
func StartDownloadSingleThread(job *DownloadJob, downloadURL string, db *gorm.DB, basePath string) { func StartDownloadSingleThread(job *DownloadJob, downloadURL string, db *gorm.DB, basePath string) {
UpdateJobStatus(job.ID, "running", db) UpdateJobStatus(job.ID, "running", db)
// --- Normalise le chemin disque ---
// Si basePath est "Film" (logique BDD), on le préfixe par "upload/".
// Si on te passe déjà "upload/Film" ou un chemin absolu, on le garde.
diskBase := basePath
clean := filepath.Clean(basePath)
if !filepath.IsAbs(clean) &&
clean != "upload" &&
!strings.HasPrefix(clean, "upload"+string(os.PathSeparator)) {
diskBase = filepath.Join("upload", clean)
}
// Créer le répertoire si nécessaire
if err := os.MkdirAll(diskBase, 0o755); err != nil {
log.Printf("[ERROR] Création du dossier %s échouée : %v\n", diskBase, err)
UpdateJobStatus(job.ID, "failed", db)
return
}
// Lance le téléchargement
resp, err := http.Get(downloadURL) resp, err := http.Get(downloadURL)
if err != nil { if err != nil {
log.Printf("[ERROR] Téléchargement échoué : %v\n", err) log.Printf("[ERROR] Téléchargement échoué : %v\n", err)
@ -360,14 +391,7 @@ func StartDownloadSingleThread(job *DownloadJob, downloadURL string, db *gorm.DB
return return
} }
// Créer le répertoire si nécessaire destPath := filepath.Join(diskBase, SanitizeFileName(job.Name))
if err := os.MkdirAll(basePath, os.ModePerm); err != nil {
log.Printf("[ERROR] Création du dossier %s échouée : %v\n", basePath, err)
UpdateJobStatus(job.ID, "failed", db)
return
}
destPath := filepath.Join(basePath, SanitizeFileName(job.Name))
outFile, err := os.Create(destPath) outFile, err := os.Create(destPath)
if err != nil { if err != nil {
log.Printf("[ERROR] Impossible de créer le fichier : %v\n", err) log.Printf("[ERROR] Impossible de créer le fichier : %v\n", err)
@ -376,7 +400,7 @@ func StartDownloadSingleThread(job *DownloadJob, downloadURL string, db *gorm.DB
} }
defer outFile.Close() defer outFile.Close()
// Calcul taille totale // Taille totale pour progression
totalSize := resp.ContentLength totalSize := resp.ContentLength
if totalSize <= 0 && job.Size > 0 { if totalSize <= 0 && job.Size > 0 {
totalSize = job.Size totalSize = job.Size
@ -387,7 +411,7 @@ func StartDownloadSingleThread(job *DownloadJob, downloadURL string, db *gorm.DB
lastUpdate := time.Now() lastUpdate := time.Now()
for { for {
n, err := resp.Body.Read(buf) n, readErr := resp.Body.Read(buf)
if n > 0 { if n > 0 {
if _, writeErr := outFile.Write(buf[:n]); writeErr != nil { if _, writeErr := outFile.Write(buf[:n]); writeErr != nil {
log.Printf("[ERROR] Écriture échouée : %v\n", writeErr) log.Printf("[ERROR] Écriture échouée : %v\n", writeErr)
@ -396,17 +420,17 @@ func StartDownloadSingleThread(job *DownloadJob, downloadURL string, db *gorm.DB
} }
downloaded += int64(n) downloaded += int64(n)
} }
if err != nil { if readErr != nil {
if err == io.EOF { if readErr == io.EOF {
break break
} }
log.Printf("[ERROR] Erreur de lecture : %v\n", err) log.Printf("[ERROR] Erreur de lecture : %v\n", readErr)
UpdateJobStatus(job.ID, "failed", db) UpdateJobStatus(job.ID, "failed", db)
return return
} }
// Mise à jour de la progression // Mise à jour de la progression (toutes les 500ms)
if time.Since(lastUpdate) > 500*time.Millisecond && totalSize > 0 { if totalSize > 0 && time.Since(lastUpdate) > 500*time.Millisecond {
progress := int((downloaded * 100) / totalSize) progress := int((downloaded * 100) / totalSize)
UpdateJobProgress(job.ID, progress, db) UpdateJobProgress(job.ID, progress, db)
lastUpdate = time.Now() lastUpdate = time.Now()
@ -418,6 +442,7 @@ func StartDownloadSingleThread(job *DownloadJob, downloadURL string, db *gorm.DB
log.Printf("[OK] Fichier téléchargé (single) : %s\n", destPath) log.Printf("[OK] Fichier téléchargé (single) : %s\n", destPath)
} }
func downloadSegment(url string, start, end int64, dest string, progressChan chan<- int64) error { func downloadSegment(url string, start, end int64, dest string, progressChan chan<- int64) error {
req, _ := http.NewRequest("GET", url, nil) req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end)) req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))