// internal/download/jobs.go package download import ( "app/shelfly/internal/debridlink" "context" "encoding/json" "net/http" "strconv" "sync" "time" "github.com/gorilla/mux" "gorm.io/gorm" ) type DownloadJob struct { ID uint `gorm:"primaryKey"` RemoteID string `gorm:"index"` FileName string `gorm:"size:255"` Status string `gorm:"size:50"` Speed string `gorm:"size:50"` StreamURL string `gorm:"-" json:"stream_url"` ErrorMsg string `gorm:"-" json:"error_msg"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"` Link string `gorm:"size:512"` } var ( jobs = make(map[string]*DownloadJob) jobsMu sync.Mutex ) func AddJob(link string, pathID uint, db *gorm.DB) *DownloadJob { id := time.Now().UnixNano() job := &DownloadJob{ ID: uint(id), Link: link, // ← important ! RemoteID: "", FileName: extractFileName(link), Status: "added", Speed: "", CreatedAt: time.Now(), UpdatedAt: time.Now(), } jobsMu.Lock() jobs[strconv.FormatUint(uint64(id), 10)] = job jobsMu.Unlock() db.Create(job) return job } func startDownload(job *DownloadJob, link string, client *debridlink.Client, db *gorm.DB) { ctx := context.Background() job.Status = "downloading" linkResp, err := client.AddLink(ctx, link) if err != nil { job.Status = "error" job.ErrorMsg = err.Error() db.Save(job) return } job.RemoteID = linkResp.ID db.Save(job) go syncDownloadStatus(job, client, db) } func syncDownloadStatus(job *DownloadJob, client *debridlink.Client, db *gorm.DB) { ctx := context.Background() var checkCount int for { if job.Status != "downloading" { return } links, err := client.ListLinks(ctx) if err != nil { job.Status = "error" job.ErrorMsg = err.Error() db.Save(job) return } for _, l := range links { if l.ID == job.RemoteID { job.Status = l.Status checkCount++ if checkCount%2 == 0 { job.Speed = "~2 MB/s" } else { job.Speed = "~1.4 MB/s" } if l.Status == "downloaded" { files, err := client.ListFiles(ctx, l.ID) if err == nil && len(files) > 0 { transcodeID, err := client.CreateTranscode(ctx, files[0].ID, "original") if err == nil { job.StreamURL = "https://debrid-link.com/stream/" + transcodeID } } } db.Save(job) if l.Status == "downloaded" || l.Status == "error" { _ = client.RemoveLinks(ctx, []string{l.ID}) return } } } time.Sleep(2 * time.Second) } } func ListJobs() []*DownloadJob { jobsMu.Lock() defer jobsMu.Unlock() var list []*DownloadJob for _, job := range jobs { list = append(list, job) } return list } func PauseJob(id string) { jobsMu.Lock() defer jobsMu.Unlock() if job, ok := jobs[id]; ok && job.Status == "downloading" { job.Status = "paused" } } func ResumeJob(id string, client *debridlink.Client, db *gorm.DB) { jobsMu.Lock() defer jobsMu.Unlock() if job, ok := jobs[id]; ok && job.Status == "paused" { job.Status = "downloading" db.Save(job) go syncDownloadStatus(job, client, db) } } func DeleteJob(id string) { jobsMu.Lock() defer jobsMu.Unlock() delete(jobs, id) } func generateID() string { return time.Now().Format("20060102150405.000") } func extractFileName(link string) string { return "file_from_" + link } func HandleListJobs(w http.ResponseWriter, r *http.Request) { jobs := ListJobs() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(jobs) } func HandlePauseJob(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] PauseJob(id) w.WriteHeader(http.StatusNoContent) } func HandleResumeJob(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] client := debridlink.NewClient(db) account := getFirstActiveAccount(client) if account == nil { http.Error(w, "Aucun compte actif", http.StatusBadRequest) return } client.SetAccount(account) ResumeJob(id, client, db) w.WriteHeader(http.StatusNoContent) } } func HandleDeleteJob(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] DeleteJob(id) w.WriteHeader(http.StatusNoContent) } func getFirstActiveAccount(client *debridlink.Client) *debridlink.DebridAccount { ctx := context.Background() accounts, err := client.ListDebridAccounts(ctx) if err != nil { return nil } for _, acc := range accounts { if acc.IsActive { return &acc } } return nil } func HandleStartJob(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] jobsMu.Lock() job, exists := jobs[id] jobsMu.Unlock() if !exists { http.Error(w, "Job not found", http.StatusNotFound) return } client := debridlink.NewClient(db) account := getFirstActiveAccount(client) if account == nil { http.Error(w, "Aucun compte actif", http.StatusBadRequest) return } client.SetAccount(account) go startDownload(job, job.Link, client, db) w.WriteHeader(http.StatusNoContent) } }