up
This commit is contained in:
parent
91d1d97361
commit
e1955871b2
@ -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))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user