From 0bae45a824c5b97d05c9b6a3d9cbcbd54f705287 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 23 Sep 2024 16:50:04 +0200 Subject: [PATCH 1/7] Display Console --- src/downloaders.go | 19 ++++++++++++++----- src/go.mod | 1 + src/go.sum | 2 ++ src/handlers.go | 42 ++++++++++++++++++++++++++++++++++++++++++ src/main.go | 1 + src/templates/progress | 33 +++++++++++++++++++++++++++++++-- 6 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/downloaders.go b/src/downloaders.go index eef7fdb..647aeae 100644 --- a/src/downloaders.go +++ b/src/downloaders.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/base64" "fmt" "io" @@ -9,6 +10,7 @@ import ( "os/exec" "path/filepath" "strings" + "time" ) func removeBOM(input []byte) []byte { @@ -99,11 +101,8 @@ func downloadFile(item Item, jobInfo *JobInfo) error { cmd := exec.Command("bash", "-c", command) - jobsMutex.Lock() - jobInfo.Cmd = cmd - jobsMutex.Unlock() - - cmd.Stdout = os.Stdout + var outputBuffer bytes.Buffer + cmd.Stdout = io.MultiWriter(os.Stdout, &outputBuffer) cmd.Stderr = os.Stderr err = cmd.Start() @@ -116,6 +115,16 @@ func downloadFile(item Item, jobInfo *JobInfo) error { done <- cmd.Wait() }() + go func() { + for { + if outputBuffer.Len() > 0 { + broadcast(outputBuffer.Bytes()) + outputBuffer.Reset() + } + time.Sleep(1 * time.Second) + } + }() + select { case <-jobInfo.AbortChan: if cmd.Process != nil { diff --git a/src/go.mod b/src/go.mod index bc9873f..38e8b9d 100644 --- a/src/go.mod +++ b/src/go.mod @@ -11,6 +11,7 @@ require ( require ( github.com/asticode/go-astikit v0.20.0 // indirect github.com/asticode/go-astits v1.8.0 // indirect + github.com/gorilla/websocket v1.5.3 golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect golang.org/x/text v0.3.2 // indirect ) diff --git a/src/go.sum b/src/go.sum index 97fe73a..68a64b8 100644 --- a/src/go.sum +++ b/src/go.sum @@ -10,6 +10,8 @@ github.com/beevik/etree v1.4.1 h1:PmQJDDYahBGNKDcpdX8uPy1xRCwoCGVUiW669MEirVI= github.com/beevik/etree v1.4.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/pkg/exec v0.0.0-20150614095509-0bd164ad2a5a h1:EN123kAtAAE2pg/+TvBsUBZfHCWNNFyL2ZBPPfNWAc0= github.com/pkg/exec v0.0.0-20150614095509-0bd164ad2a5a/go.mod h1:b95YoNrAnScjaWG+asr8lxqlrsPUcT2ZEBcjvVGshMo= github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= diff --git a/src/handlers.go b/src/handlers.go index c5e2e5f..ee72dae 100644 --- a/src/handlers.go +++ b/src/handlers.go @@ -9,6 +9,9 @@ import ( "os" "path/filepath" "strings" + "sync" + + "github.com/gorilla/websocket" ) type ProgressInfo struct { @@ -354,3 +357,42 @@ func updateProgress(filename string, value float64, currentFile string) { } } } + +var upgrader = websocket.Upgrader{} +var clients = make(map[*websocket.Conn]bool) +var mu sync.Mutex + +func handleWebSocket(w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + fmt.Println("Error while upgrading connection:", err) + return + } + defer conn.Close() + + mu.Lock() + clients[conn] = true + mu.Unlock() + + for { + if _, _, err := conn.NextReader(); err != nil { + break + } + } + + mu.Lock() + delete(clients, conn) + mu.Unlock() +} + +func broadcast(message []byte) { + mu.Lock() + defer mu.Unlock() + + for client := range clients { + if err := client.WriteMessage(websocket.TextMessage, message); err != nil { + client.Close() + delete(clients, client) + } + } +} diff --git a/src/main.go b/src/main.go index 7feea87..8f955e1 100644 --- a/src/main.go +++ b/src/main.go @@ -77,6 +77,7 @@ func startWebServer() { http.HandleFunc("/pause", handlePause) http.HandleFunc("/resume", handleResume) http.HandleFunc("/clear-completed", handleClearCompleted) + http.HandleFunc("/ws", handleWebSocket) fmt.Println("Starting web server on http://0.0.0.0:8080") http.ListenAndServe(":8080", nil) diff --git a/src/templates/progress b/src/templates/progress index f3c710d..74e23e9 100644 --- a/src/templates/progress +++ b/src/templates/progress @@ -68,7 +68,7 @@ #abort-button:hover { background-color: #d32f2f; } - #pause-button, #resume-button { + #pause-button, #resume-button, #toggle-console { background-color: #4CAF50; color: white; border: none; @@ -77,7 +77,7 @@ border-radius: 4px; cursor: pointer; } - #pause-button:hover, #resume-button:hover { + #pause-button:hover, #resume-button:hover, #toggle-console:hover { background-color: #45a049; } #resume-button { @@ -96,6 +96,17 @@ #back-button:hover { background-color: #1976D2; } + #console { + display: none; /* Initially hidden */ + background-color: black; + color: white; + height: 300px; /* Adjust height as needed */ + overflow-y: scroll; + white-space: pre; /* Preserve whitespace */ + font-family: monospace; /* Use monospace font */ + margin-top: 10px; + border: 1px solid #ccc; + } @media (max-width: 600px) { body { padding: 10px; @@ -128,8 +139,10 @@ + +
From da03138d5cfe24c2919f28ce186d2edf9f5cc544 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 23 Sep 2024 17:23:22 +0200 Subject: [PATCH 2/7] Implement logging, console window --- src/downloaders.go | 26 +++++++++++++++++++++----- src/handlers.go | 23 +++++++++++++++++++++++ src/logger.go | 36 ++++++++++++++++++++++++++++++++++++ src/main.go | 6 +++++- src/subtitles.go | 9 +++++++++ 5 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/logger.go diff --git a/src/downloaders.go b/src/downloaders.go index 647aeae..7678bbc 100644 --- a/src/downloaders.go +++ b/src/downloaders.go @@ -21,11 +21,12 @@ func removeBOM(input []byte) []byte { } func downloadFile(item Item, jobInfo *JobInfo) error { - fmt.Println("Downloading:", item.Filename) + logger.LogInfo("Download File", fmt.Sprintf("Starting download for: %s", item.Filename)) tempDir := filepath.Join(config.TempBaseDir, sanitizeFilename(item.Filename)) err := os.MkdirAll(tempDir, 0755) if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error creating temporary directory: %v", err)) return fmt.Errorf("error creating temporary directory: %v", err) } @@ -35,19 +36,23 @@ func downloadFile(item Item, jobInfo *JobInfo) error { if !isValidURL(item.MPD) { decodedMPD, err := base64.StdEncoding.DecodeString(item.MPD) if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error decoding base64 MPD: %v", err)) return fmt.Errorf("error decoding base64 MPD: %v", err) } tempFile, err := os.CreateTemp("", "temp_mpd_*.mpd") if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error creating temporary MPD file: %v", err)) return fmt.Errorf("error creating temporary MPD file: %v", err) } defer os.Remove(tempFile.Name()) if _, err := tempFile.Write(decodedMPD); err != nil { + logger.LogError("Download File", fmt.Sprintf("Error writing to temporary MPD file: %v", err)) return fmt.Errorf("error writing to temporary MPD file: %v", err) } if err := tempFile.Close(); err != nil { + logger.LogError("Download File", fmt.Sprintf("Error closing temporary MPD file: %v", err)) return fmt.Errorf("error closing temporary MPD file: %v", err) } @@ -55,30 +60,36 @@ func downloadFile(item Item, jobInfo *JobInfo) error { } else if strings.HasPrefix(item.MPD, "https://pubads.g.doubleclick.net") { resp, err := http.Get(item.MPD) if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error downloading MPD: %v", err)) return fmt.Errorf("error downloading MPD: %v", err) } defer resp.Body.Close() mpdContent, err := io.ReadAll(resp.Body) if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error reading MPD content: %v", err)) return fmt.Errorf("error reading MPD content: %v", err) } fixedMPDContent, err := fixGoPlay(string(mpdContent)) if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error fixing MPD content: %v", err)) return fmt.Errorf("error fixing MPD content: %v", err) } tempFile, err := os.CreateTemp("", "fixed_mpd_*.mpd") if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error creating temporary MPD file: %v", err)) return fmt.Errorf("error creating temporary MPD file: %v", err) } defer os.Remove(tempFile.Name()) if _, err := tempFile.WriteString(fixedMPDContent); err != nil { + logger.LogError("Download File", fmt.Sprintf("Error writing to temporary MPD file: %v", err)) return fmt.Errorf("error writing to temporary MPD file: %v", err) } if err := tempFile.Close(); err != nil { + logger.LogError("Download File", fmt.Sprintf("Error closing temporary MPD file: %v", err)) return fmt.Errorf("error closing temporary MPD file: %v", err) } @@ -90,23 +101,25 @@ func downloadFile(item Item, jobInfo *JobInfo) error { if item.Subtitles != "" { subtitlePaths, err := downloadAndConvertSubtitles(item.Subtitles) if err != nil { - fmt.Printf("Error processing subtitles: %v\n", err) + logger.LogError("Download File", fmt.Sprintf("Error processing subtitles: %v", err)) } else { for _, path := range subtitlePaths { - fmt.Println("Adding subtitle:", path) + logger.LogInfo("Download File", fmt.Sprintf("Adding subtitle: %s", path)) command += fmt.Sprintf(" --mux-import \"path=%s:lang=nl:name=Nederlands\"", path) } } } cmd := exec.Command("bash", "-c", command) + jobInfo.Cmd = cmd var outputBuffer bytes.Buffer - cmd.Stdout = io.MultiWriter(os.Stdout, &outputBuffer) + cmd.Stdout = io.MultiWriter(&outputBuffer) cmd.Stderr = os.Stderr err = cmd.Start() if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error starting download command: %v", err)) return fmt.Errorf("error starting download command: %v", err) } @@ -131,17 +144,20 @@ func downloadFile(item Item, jobInfo *JobInfo) error { cmd.Process.Kill() } os.RemoveAll(tempDir) + logger.LogInfo("Download File", "Download aborted") return fmt.Errorf("download aborted") case err := <-done: if jobInfo.Paused { + logger.LogInfo("Download File", "Download paused") return fmt.Errorf("download paused") } if err != nil { + logger.LogError("Download File", fmt.Sprintf("Error executing download command: %v", err)) return fmt.Errorf("error executing download command: %v", err) } } - fmt.Println("Download completed successfully") + logger.LogInfo("Download File", "Download completed successfully") return nil } diff --git a/src/handlers.go b/src/handlers.go index ee72dae..0590199 100644 --- a/src/handlers.go +++ b/src/handlers.go @@ -50,19 +50,23 @@ func handleRoot(w http.ResponseWriter, r *http.Request) { } }{jobsInfo}) if err != nil { + logger.LogError("Handle Root", fmt.Sprintf("Error executing template: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) } } func handleUpload(w http.ResponseWriter, r *http.Request) { + logger.LogInfo("Handle Upload", "Starting file upload") err := r.ParseMultipartForm(32 << 20) if err != nil { + logger.LogError("Handle Upload", fmt.Sprintf("Error parsing multipart form: %v", err)) http.Error(w, err.Error(), http.StatusBadRequest) return } files := r.MultipartForm.File["files"] if len(files) == 0 { + logger.LogError("Handle Upload", "No files uploaded") http.Error(w, "No files uploaded", http.StatusBadRequest) return } @@ -72,6 +76,7 @@ func handleUpload(w http.ResponseWriter, r *http.Request) { for _, fileHeader := range files { file, err := fileHeader.Open() if err != nil { + logger.LogError("Handle Upload", fmt.Sprintf("Error opening file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -79,6 +84,7 @@ func handleUpload(w http.ResponseWriter, r *http.Request) { tempFile, err := os.CreateTemp(uploadDir, fileHeader.Filename) if err != nil { + logger.LogError("Handle Upload", fmt.Sprintf("Error creating temporary file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -86,6 +92,7 @@ func handleUpload(w http.ResponseWriter, r *http.Request) { _, err = io.Copy(tempFile, file) if err != nil { + logger.LogError("Handle Upload", fmt.Sprintf("Error copying file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -94,6 +101,7 @@ func handleUpload(w http.ResponseWriter, r *http.Request) { _, err = parseInputFile(tempFile.Name()) if err != nil { + logger.LogError("Handle Upload", fmt.Sprintf("Error parsing input file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -107,10 +115,12 @@ func handleUpload(w http.ResponseWriter, r *http.Request) { } if len(validFiles) == 0 { + logger.LogError("Handle Upload", "No valid files were uploaded") http.Error(w, "No valid files were uploaded", http.StatusBadRequest) return } + logger.LogInfo("Handle Upload", fmt.Sprintf("Redirecting to select with files: %v", validFiles)) http.Redirect(w, r, "/select?files="+url.QueryEscape(strings.Join(validFiles, ",")), http.StatusSeeOther) } @@ -127,11 +137,13 @@ func handleSelect(w http.ResponseWriter, r *http.Request) { fullPath := filepath.Join(uploadDir, filename) if _, err := os.Stat(fullPath); os.IsNotExist(err) { + logger.LogError("Handle Select", fmt.Sprintf("File does not exist: %s", fullPath)) continue } items, err := parseInputFile(fullPath) if err != nil { + logger.LogError("Handle Select", fmt.Sprintf("Error parsing input file: %v", err)) continue } @@ -142,6 +154,7 @@ func handleSelect(w http.ResponseWriter, r *http.Request) { } if len(allItems) == 0 { + logger.LogError("Handle Select", "No valid files were processed") http.Error(w, "No valid files were processed", http.StatusBadRequest) return } @@ -154,18 +167,22 @@ func handleSelect(w http.ResponseWriter, r *http.Request) { AllItems: allItems, }) if err != nil { + logger.LogError("Handle Select", fmt.Sprintf("Error executing template: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) } } func handleProcess(w http.ResponseWriter, r *http.Request) { + logger.LogInfo("Handle Process", "Starting process") if err := r.ParseForm(); err != nil { + logger.LogError("Handle Process", fmt.Sprintf("Error parsing form: %v", err)) http.Error(w, err.Error(), http.StatusBadRequest) return } selectedItems := r.Form["items"] if len(selectedItems) == 0 { + logger.LogError("Handle Process", "No items selected") http.Error(w, "No items selected", http.StatusBadRequest) return } @@ -174,6 +191,7 @@ func handleProcess(w http.ResponseWriter, r *http.Request) { for _, item := range selectedItems { parts := strings.SplitN(item, ":", 2) if len(parts) != 2 { + logger.LogError("Handle Process", "Invalid item format") continue } filename, itemName := parts[0], parts[1] @@ -181,9 +199,11 @@ func handleProcess(w http.ResponseWriter, r *http.Request) { } for filename, items := range itemsByFile { + logger.LogInfo("Handle Process", fmt.Sprintf("Processing file: %s", filename)) fullPath := filepath.Join(uploadDir, filename) allItems, err := parseInputFile(fullPath) if err != nil { + logger.LogError("Handle Process", fmt.Sprintf("Error parsing input file: %v", err)) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -226,6 +246,7 @@ func handleProgress(w http.ResponseWriter, r *http.Request) { func handlePause(w http.ResponseWriter, r *http.Request) { filename := r.URL.Query().Get("filename") if filename == "" { + logger.LogError("Pause Handler", "Filename is required") http.Error(w, "Filename is required", http.StatusBadRequest) return } @@ -235,12 +256,14 @@ func handlePause(w http.ResponseWriter, r *http.Request) { jobsMutex.Unlock() if !exists { + logger.LogError("Pause Handler", "Job not found") http.Error(w, "Job not found", http.StatusNotFound) return } jobInfo.Paused = true if jobInfo.Cmd != nil && jobInfo.Cmd.Process != nil { + logger.LogJobState(filename, "pausing") jobInfo.Cmd.Process.Kill() } diff --git a/src/logger.go b/src/logger.go new file mode 100644 index 0000000..4375a0f --- /dev/null +++ b/src/logger.go @@ -0,0 +1,36 @@ +package main + +import ( + "log" + "os" +) + +type Logger struct { + *log.Logger +} + +const ( + Reset = "\033[0m" + Red = "\033[31m" + Green = "\033[32m" + Yellow = "\033[33m" + Blue = "\033[34m" +) + +func NewLogger(prefix string) *Logger { + return &Logger{ + Logger: log.New(os.Stdout, prefix, log.Ldate|log.Ltime|log.Lshortfile), + } +} + +func (l *Logger) LogInfo(jobName, message string) { + l.Printf("%s[INFO] [%s] %s%s", Green, jobName, message, Reset) +} + +func (l *Logger) LogError(jobName, message string) { + l.Printf("%s[ERROR] [%s] %s%s", Red, jobName, message, Reset) +} + +func (l *Logger) LogJobState(jobName, state string) { + l.Printf("%s[JOB STATE] [%s] %s%s", Yellow, jobName, state, Reset) +} diff --git a/src/main.go b/src/main.go index 8f955e1..d9434f4 100644 --- a/src/main.go +++ b/src/main.go @@ -12,6 +12,8 @@ import ( "embed" ) +var logger *Logger + type Item struct { MPD string Keys string @@ -48,6 +50,8 @@ func init() { } templates = template.Must(template.ParseFS(templateFS, "templates/*")) + + logger = NewLogger("") } func main() { @@ -60,7 +64,7 @@ func main() { } else { items, err := parseInputFile(*inputFile) if err != nil { - fmt.Printf("Error parsing input file: %v\n", err) + logger.LogError("Main", fmt.Sprintf("Error parsing input file: %v", err)) return } processItems(*inputFile, items) diff --git a/src/subtitles.go b/src/subtitles.go index c30bd81..91b22b8 100644 --- a/src/subtitles.go +++ b/src/subtitles.go @@ -15,13 +15,16 @@ func downloadAndConvertSubtitles(subtitlesURLs string) ([]string, error) { urls := strings.Split(subtitlesURLs, ",") for _, url := range urls { + logger.LogInfo("Subtitle Download", fmt.Sprintf("Downloading subtitle from %s", url)) vttPath, err := downloadSubtitle(url) if err != nil { + logger.LogError("Subtitle Download", fmt.Sprintf("Error downloading subtitle: %v", err)) return nil, fmt.Errorf("error downloading subtitle: %v", err) } srtPath, err := convertVTTtoSRT(vttPath) if err != nil { + logger.LogError("Subtitle Download", fmt.Sprintf("Error converting subtitle: %v", err)) return nil, fmt.Errorf("error converting subtitle: %v", err) } @@ -32,23 +35,28 @@ func downloadAndConvertSubtitles(subtitlesURLs string) ([]string, error) { } func downloadSubtitle(url string) (string, error) { + logger.LogInfo("Download Subtitle", fmt.Sprintf("Starting download from %s", url)) resp, err := http.Get(url) if err != nil { + logger.LogError("Download Subtitle", fmt.Sprintf("Error getting subtitle URL: %v", err)) return "", err } defer resp.Body.Close() tempFile, err := os.CreateTemp("", "subtitle_*.vtt") if err != nil { + logger.LogError("Download Subtitle", fmt.Sprintf("Error creating temp file: %v", err)) return "", err } defer tempFile.Close() _, err = io.Copy(tempFile, resp.Body) if err != nil { + logger.LogError("Download Subtitle", fmt.Sprintf("Error copying to temp file: %v", err)) return "", err } + logger.LogInfo("Download Subtitle", "Subtitle downloaded successfully") return tempFile.Name(), nil } @@ -56,5 +64,6 @@ func convertVTTtoSRT(vttPath string) (string, error) { srtPath := strings.TrimSuffix(vttPath, ".vtt") + ".srt" s1, _ := astisub.OpenFile(vttPath) s1.Write(srtPath) + logger.LogInfo("Convert VTT to SRT", fmt.Sprintf("Converted %s to %s", vttPath, srtPath)) return srtPath, nil } From 8a63f738399f13e439b707030204abcad6197c58 Mon Sep 17 00:00:00 2001 From: Joren Date: Mon, 23 Sep 2024 17:24:03 +0200 Subject: [PATCH 3/7] Fix: Console display should be set to none --- src/templates/progress | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/progress b/src/templates/progress index 74e23e9..ad6f1ca 100644 --- a/src/templates/progress +++ b/src/templates/progress @@ -142,7 +142,7 @@ -
+