Pause imp

This commit is contained in:
Joren 2024-09-13 22:29:03 +02:00
parent dfe21445e5
commit 2f9552e771
Signed by: Joren
GPG Key ID: 280E33DFBC0F1B55
6 changed files with 187 additions and 31 deletions

View File

@ -115,6 +115,9 @@ func downloadFile(item Item, jobInfo *JobInfo) error {
} }
return fmt.Errorf("download aborted") return fmt.Errorf("download aborted")
case err := <-done: case err := <-done:
if jobInfo.Paused {
return fmt.Errorf("download paused")
}
if err != nil { if err != nil {
return fmt.Errorf("error executing download command: %v", err) return fmt.Errorf("error executing download command: %v", err)
} }

1
go.mod
View File

@ -11,7 +11,6 @@ require (
require ( require (
github.com/asticode/go-astikit v0.20.0 // indirect github.com/asticode/go-astikit v0.20.0 // indirect
github.com/asticode/go-astits v1.8.0 // indirect github.com/asticode/go-astits v1.8.0 // indirect
github.com/pkg/exec v0.0.0-20150614095509-0bd164ad2a5a
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
golang.org/x/text v0.3.2 // indirect golang.org/x/text v0.3.2 // indirect
) )

View File

@ -135,3 +135,73 @@ func handleProgress(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }
} }
func handlePause(w http.ResponseWriter, r *http.Request) {
filename := r.URL.Query().Get("filename")
if filename == "" {
http.Error(w, "Filename is required", http.StatusBadRequest)
return
}
jobsMutex.Lock()
jobInfo, exists := jobs[filename]
jobsMutex.Unlock()
if !exists {
http.Error(w, "Job not found", http.StatusNotFound)
return
}
jobInfo.Paused = true
if jobInfo.Cmd != nil && jobInfo.Cmd.Process != nil {
jobInfo.Cmd.Process.Kill()
}
fmt.Fprintf(w, "Pause signal sent for %s", filename)
}
func handleResume(w http.ResponseWriter, r *http.Request) {
filename := r.URL.Query().Get("filename")
if filename == "" {
http.Error(w, "Filename is required", http.StatusBadRequest)
return
}
jobsMutex.Lock()
jobInfo, exists := jobs[filename]
jobsMutex.Unlock()
if !exists {
http.Error(w, "Job not found", http.StatusNotFound)
return
}
jobInfo.Paused = false
jobInfo.ResumeChan <- struct{}{}
fmt.Fprintf(w, "Resume signal sent for %s", filename)
}
func handleAbort(w http.ResponseWriter, r *http.Request) {
filename := r.URL.Query().Get("filename")
if filename == "" {
http.Error(w, "Filename is required", http.StatusBadRequest)
return
}
jobsMutex.Lock()
jobInfo, exists := jobs[filename]
jobsMutex.Unlock()
if !exists {
http.Error(w, "Job not found", http.StatusNotFound)
return
}
close(jobInfo.AbortChan)
if jobInfo.Cmd != nil && jobInfo.Cmd.Process != nil {
jobInfo.Cmd.Process.Kill()
}
fmt.Fprintf(w, "Abort signal sent for %s", filename)
}

26
main.go
View File

@ -79,6 +79,8 @@ func startWebServer() {
http.HandleFunc("/process", handleProcess) http.HandleFunc("/process", handleProcess)
http.HandleFunc("/progress", handleProgress) http.HandleFunc("/progress", handleProgress)
http.HandleFunc("/abort", handleAbort) http.HandleFunc("/abort", handleAbort)
http.HandleFunc("/pause", handlePause)
http.HandleFunc("/resume", handleResume)
fmt.Println("Starting web server on http://0.0.0.0:8080") fmt.Println("Starting web server on http://0.0.0.0:8080")
http.ListenAndServe(":8080", nil) http.ListenAndServe(":8080", nil)
@ -115,27 +117,3 @@ func parseMetadata(metadata string) Metadata {
Season: "S" + strings.TrimSpace(parts[2]), Season: "S" + strings.TrimSpace(parts[2]),
} }
} }
func handleAbort(w http.ResponseWriter, r *http.Request) {
filename := r.URL.Query().Get("filename")
if filename == "" {
http.Error(w, "Filename is required", http.StatusBadRequest)
return
}
jobsMutex.Lock()
jobInfo, exists := jobs[filename]
jobsMutex.Unlock()
if !exists {
http.Error(w, "Job not found", http.StatusNotFound)
return
}
close(jobInfo.AbortChan)
if jobInfo.Cmd != nil && jobInfo.Cmd.Process != nil {
jobInfo.Cmd.Process.Kill()
}
fmt.Fprintf(w, "Abort signal sent for %s", filename)
}

View File

@ -68,6 +68,21 @@
#abort-button:hover { #abort-button:hover {
background-color: #d32f2f; background-color: #d32f2f;
} }
#pause-button, #resume-button {
background-color: #2196F3;
color: white;
border: none;
padding: 10px 15px;
margin-top: 10px;
border-radius: 4px;
cursor: pointer;
}
#pause-button:hover, #resume-button:hover {
background-color: #1976D2;
}
#resume-button {
display: none;
}
@media (max-width: 600px) { @media (max-width: 600px) {
body { body {
padding: 10px; padding: 10px;
@ -97,6 +112,8 @@
<div id="currentFile"></div> <div id="currentFile"></div>
</div> </div>
<button id="abort-button" onclick="abortDownload()">Abort Download</button> <button id="abort-button" onclick="abortDownload()">Abort Download</button>
<button id="pause-button" onclick="pauseDownload()">Pause Download</button>
<button id="resume-button" onclick="resumeDownload()">Resume Download</button>
<script> <script>
function updateProgress() { function updateProgress() {
fetch('/progress?filename={{.Filename}}', { fetch('/progress?filename={{.Filename}}', {
@ -127,6 +144,32 @@
} }
}); });
} }
function pauseDownload() {
fetch('/pause?filename={{.Filename}}', { method: 'POST' })
.then(response => {
if (response.ok) {
alert('Pause signal sent. The download will pause soon.');
document.getElementById('pause-button').style.display = 'none';
document.getElementById('resume-button').style.display = 'inline-block';
} else {
alert('Failed to pause the download.');
}
});
}
function resumeDownload() {
fetch('/resume?filename={{.Filename}}', { method: 'POST' })
.then(response => {
if (response.ok) {
alert('Resume signal sent. The download will resume soon.');
document.getElementById('resume-button').style.display = 'none';
document.getElementById('pause-button').style.display = 'inline-block';
} else {
alert('Failed to resume the download.');
}
});
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -7,6 +7,7 @@ import (
"net/url" "net/url"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -17,7 +18,9 @@ import (
type JobInfo struct { type JobInfo struct {
AbortChan chan struct{} AbortChan chan struct{}
ResumeChan chan struct{}
Cmd *exec.Cmd Cmd *exec.Cmd
Paused bool
} }
var ( var (
@ -188,6 +191,7 @@ func processItems(filename string, items []Item) error {
jobsMutex.Lock() jobsMutex.Lock()
jobInfo := &JobInfo{ jobInfo := &JobInfo{
AbortChan: make(chan struct{}), AbortChan: make(chan struct{}),
ResumeChan: make(chan struct{}),
} }
jobs[filename] = jobInfo jobs[filename] = jobInfo
jobsMutex.Unlock() jobsMutex.Unlock()
@ -198,15 +202,30 @@ func processItems(filename string, items []Item) error {
jobsMutex.Unlock() jobsMutex.Unlock()
}() }()
for i, item := range items { for i := 0; i < len(items); i++ {
select { select {
case <-jobInfo.AbortChan: case <-jobInfo.AbortChan:
updateProgress(filename, 100, "Aborted") updateProgress(filename, 100, "Aborted")
return fmt.Errorf("download aborted") return fmt.Errorf("download aborted")
default: default:
updateProgress(filename, float64(i)/float64(len(items))*100, item.Filename) if jobInfo.Paused {
err := downloadFile(item, jobInfo) select {
case <-jobInfo.ResumeChan:
jobInfo.Paused = false
fmt.Printf("Resuming download for %s\n", filename)
case <-jobInfo.AbortChan:
updateProgress(filename, 100, "Aborted")
return fmt.Errorf("download aborted")
}
}
updateProgress(filename, float64(i)/float64(len(items))*100, items[i].Filename)
err := downloadFile(items[i], jobInfo)
if err != nil { if err != nil {
if err.Error() == "download paused" {
removeCompletedEpisodes(filename, items[:i])
i--
continue
}
fmt.Printf("Error downloading file: %v\n", err) fmt.Printf("Error downloading file: %v\n", err)
} }
} }
@ -214,3 +233,47 @@ func processItems(filename string, items []Item) error {
updateProgress(filename, 100, "") updateProgress(filename, 100, "")
return nil return nil
} }
func removeCompletedEpisodes(filename string, completedItems []Item) error {
inputFile := filepath.Join(uploadDir, filename)
items, err := parseInputFile(inputFile)
if err != nil {
return fmt.Errorf("error parsing input file: %v", err)
}
remainingItems := make([]Item, 0)
for _, item := range items {
if !isItemCompleted(item, completedItems) || isLastCompletedItem(item, completedItems) {
remainingItems = append(remainingItems, item)
}
}
updatedItems := Items{Items: remainingItems}
jsonData, err := json.MarshalIndent(updatedItems, "", " ")
if err != nil {
return fmt.Errorf("error marshaling updated items: %v", err)
}
err = os.WriteFile(inputFile, jsonData, 0644)
if err != nil {
return fmt.Errorf("error writing updated DRMD file: %v", err)
}
return nil
}
func isItemCompleted(item Item, completedItems []Item) bool {
for _, completedItem := range completedItems {
if item.Filename == completedItem.Filename {
return true
}
}
return false
}
func isLastCompletedItem(item Item, completedItems []Item) bool {
if len(completedItems) == 0 {
return false
}
return item.Filename == completedItems[len(completedItems)-1].Filename
}