From b40dd218f192588bf6e8ffe4545ddb8eeb97637a Mon Sep 17 00:00:00 2001 From: Justin Hawkins Date: Mon, 13 Mar 2023 10:32:20 +1030 Subject: [PATCH] Add move to destination functionality --- CHANGELOG.md | 1 + download/download.go | 77 ++++++++++++++++++++++++++++++++++++-------- main.go | 7 ++-- web/layout.tmpl | 3 ++ web/popup.html | 4 +++ 5 files changed, 74 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f4c2e6..414f0e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file. - Fixes and improvements to capturing output info and showing it in the UI - Fixes to handling of queued downloads - Fix portable mode to look in binary directory, not current directory +- Allow custom destinations to be configured, and chosen on a per-download basis ## [v0.5.5] - 2022-04-09 diff --git a/download/download.go b/download/download.go index b997e70..407176a 100644 --- a/download/download.go +++ b/download/download.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "os/exec" + "path/filepath" "regexp" "strconv" "strings" @@ -24,7 +25,7 @@ type Download struct { PopupUrl string `json:"popup_url"` Process *os.Process `json:"-"` ExitCode int `json:"exit_code"` - State string `json:"state"` + State State `json:"state"` DownloadProfile config.DownloadProfile `json:"download_profile"` Destination *config.Destination `json:"destination"` Finished bool `json:"finished"` @@ -46,6 +47,18 @@ type Manager struct { Lock sync.Mutex } +type State string + +const ( + STATE_PREPARING State = "Preparing to start" + STATE_QUEUED = "Queued" + STATE_DOWNLOADING = "Downloading" + STATE_DOWNLOADING_METADATA = "Downloading metadata" + STATE_FAILED = "Failed" + STATE_COMPLETE = "Complete" + STATE_MOVED = "Moved" +) + var CanStopDownload = false var downloadId int32 = 0 @@ -55,6 +68,7 @@ func (m *Manager) ManageQueue() { m.Lock.Lock() m.startQueued(m.MaxPerDomain) + m.moveToDest() // m.cleanup() m.Lock.Unlock() @@ -74,6 +88,32 @@ func (m *Manager) DownloadsAsJSON() ([]byte, error) { return b, err } +func (m *Manager) moveToDest() { + + // move any downloads that are complete and have a dest + for _, dl := range m.Downloads { + + dl.Lock.Lock() + if dl.Destination != nil && dl.State == STATE_COMPLETE { + dl.State = STATE_MOVED + for _, fn := range dl.Files { + src := filepath.Join(dl.Config.Server.DownloadPath, fn) + dst := filepath.Join(dl.Destination.Path, fn) + err := os.Rename(src, dst) + if err != nil { + log.Printf("%s", err) + dl.Log = append(dl.Log, fmt.Sprintf("Could not move %s to %s - %s", fn, dl.Destination.Path, err)) + break + } else { + dl.Log = append(dl.Log, fmt.Sprintf("Moved %s to %s", fn, dl.Destination.Path)) + + } + } + } + dl.Lock.Unlock() + } +} + // startQueued starts any downloads that have been queued, we would not exceed // maxRunning. If maxRunning is 0, there is no limit. func (m *Manager) startQueued(maxRunning int) { @@ -83,7 +123,7 @@ func (m *Manager) startQueued(maxRunning int) { for _, dl := range m.Downloads { dl.Lock.Lock() - if dl.State == "downloading" || dl.State == "preparing to start" { + if dl.State == STATE_DOWNLOADING || dl.State == STATE_PREPARING { active[dl.domain()]++ } dl.Lock.Unlock() @@ -94,8 +134,8 @@ func (m *Manager) startQueued(maxRunning int) { dl.Lock.Lock() - if dl.State == "queued" && (maxRunning == 0 || active[dl.domain()] < maxRunning) { - dl.State = "preparing to start" + if dl.State == STATE_QUEUED && (maxRunning == 0 || active[dl.domain()] < maxRunning) { + dl.State = STATE_PREPARING active[dl.domain()]++ log.Printf("Starting download for id:%d (%s)", dl.Id, dl.Url) @@ -144,7 +184,18 @@ func (m *Manager) GetDlById(id int) (*Download, error) { func (m *Manager) Queue(dl *Download) { dl.Lock.Lock() defer dl.Lock.Unlock() - dl.State = "queued" + dl.State = STATE_QUEUED +} + +func (m *Manager) ChangeDestination(dl *Download, dest *config.Destination) { + dl.Lock.Lock() + // we can only change destination is certain cases... + if dl.State != STATE_FAILED && dl.State != STATE_MOVED { + dl.Destination = dest + } + + dl.Lock.Unlock() + } func NewDownload(url string, conf *config.Config) *Download { @@ -206,7 +257,7 @@ func (dl *Download) domain() string { func (dl *Download) Begin() { dl.Lock.Lock() - dl.State = "downloading" + dl.State = STATE_DOWNLOADING cmdSlice := []string{} cmdSlice = append(cmdSlice, dl.DownloadProfile.Args...) @@ -220,7 +271,7 @@ func (dl *Download) Begin() { stdout, err := cmd.StdoutPipe() if err != nil { - dl.State = "failed" + dl.State = STATE_FAILED dl.Finished = true dl.FinishedTS = time.Now() dl.Log = append(dl.Log, fmt.Sprintf("error setting up stdout pipe: %v", err)) @@ -231,7 +282,7 @@ func (dl *Download) Begin() { stderr, err := cmd.StderrPipe() if err != nil { - dl.State = "failed" + dl.State = STATE_FAILED dl.Finished = true dl.FinishedTS = time.Now() dl.Log = append(dl.Log, fmt.Sprintf("error setting up stderr pipe: %v", err)) @@ -243,7 +294,7 @@ func (dl *Download) Begin() { log.Printf("Executing command: %v", cmd) err = cmd.Start() if err != nil { - dl.State = "failed" + dl.State = STATE_FAILED dl.Finished = true dl.FinishedTS = time.Now() dl.Log = append(dl.Log, fmt.Sprintf("error starting command '%s': %v", dl.DownloadProfile.Command, err)) @@ -276,13 +327,13 @@ func (dl *Download) Begin() { log.Printf("Process finished for id: %d (%v)", dl.Id, cmd) - dl.State = "complete" + dl.State = STATE_COMPLETE dl.Finished = true dl.FinishedTS = time.Now() dl.ExitCode = cmd.ProcessState.ExitCode() if dl.ExitCode != 0 { - dl.State = "failed" + dl.State = STATE_FAILED } dl.Lock.Unlock() @@ -330,7 +381,7 @@ func (dl *Download) updateMetadata(s string) { matches := etaRE.FindStringSubmatch(s) if len(matches) == 2 { dl.Eta = matches[1] - dl.State = "downloading" + dl.State = STATE_DOWNLOADING } @@ -391,7 +442,7 @@ func (dl *Download) updateMetadata(s string) { metadataDL := regexp.MustCompile(`Downloading JSON metadata page (\d+)`) matches = metadataDL.FindStringSubmatch(s) if len(matches) == 2 { - dl.State = "Downloading metadata, page " + matches[1] + dl.State = STATE_DOWNLOADING_METADATA } // [FixupM3u8] Fixing MPEG-TS in MP4 container of "file [-168849776_456239489].mp4" diff --git a/main.go b/main.go index afbce04..e6570f3 100644 --- a/main.go +++ b/main.go @@ -285,12 +285,9 @@ func fetchInfoOneRESTHandler(w http.ResponseWriter, r *http.Request) { // nil means (probably) that they chose "don't move" - which is fine, // and maps to nil on the Download (the default state). destination := configService.Config.DestinationCalled(thisReq.Destination) + dm.ChangeDestination(thisDownload, destination) - thisDownload.Lock.Lock() - thisDownload.Destination = destination - thisDownload.Lock.Unlock() - - log.Printf("%#v", thisDownload) + // log.Printf("%#v", thisDownload) succRes := successResponse{Success: true, Message: "destination changed"} succResB, _ := json.Marshal(succRes) diff --git a/web/layout.tmpl b/web/layout.tmpl index 89e1ae7..4ad5c0c 100644 --- a/web/layout.tmpl +++ b/web/layout.tmpl @@ -33,6 +33,9 @@ .state-downloading { color: blue; } + .state-moved { + color: green; + } .state-complete { color: green; } diff --git a/web/popup.html b/web/popup.html index e448c95..bcaceb7 100644 --- a/web/popup.html +++ b/web/popup.html @@ -101,6 +101,10 @@ this.state = info.state; this.playlist_current = info.playlist_current; this.playlist_total = info.playlist_total; + this.destination_chosen = null; + if (info.destination) { + this.destination_chosen = info.destination.name; + } if (this.state != 'choose profile') { this.profile_chosen = true; }