diff --git a/annie.go b/annie.go deleted file mode 100644 index 9c1c1ed..0000000 --- a/annie.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "github.com/iawia002/lux/downloader" - "github.com/iawia002/lux/extractors" -) - -func download(videoURL string, wantPlaylist bool, outputPath string) bool { - - data, err := extractors.Extract(videoURL, extractors.Options{ - Playlist: wantPlaylist, - }) - if err != nil { - // if this error occurs, it means that an error occurred before actually starting to extract data - // (there is an error in the preparation step), and the data list is empty. - printError(videoURL, err) - return false - } - - var isErr bool - for _, item := range data { - if item.Err != nil { - // if this error occurs, the preparation step is normal, but the data extraction is wrong. - // the data is an empty struct. - printError(item.URL, item.Err) - isErr = true - continue - } - - err = downloader.New(downloader.Options{ - OutputPath: outputPath, - }).Download(item) - if err != nil { - printError(item.URL, err) - isErr = true - } - } - return !isErr -} diff --git a/main.go b/main.go index 00a1cd4..31b73d9 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,10 @@ import ( "syscall" "time" + "github.com/iawia002/lux/downloader" + "github.com/iawia002/lux/extractors" qt "github.com/mappu/miqt/qt6" + "github.com/mappu/miqt/qt6/mainthread" ) // GUIConfig carries all application configurations. @@ -65,12 +68,6 @@ func LoadGUIConfig() *GUIConfig { var settings *qt.QSettings var guiConfig *GUIConfig -func printError(url string, err error) { - fmt.Printf( - "Downloading %s error:\n%s\n", url, err, - ) -} - // type outputBuffer struct { @@ -105,9 +102,15 @@ func (b *outputBuffer) attachReader(r io.Reader) { }) } +func (b *outputBuffer) addLine(line string) { + mainthread.Start(func() { + b.addLine_nothread(line) + }) +} + // addLine adds a line that might end in LF, CRLF, CR, or none of the above (in // which case an LF is appended). Aware of CR and scroll position. -func (b *outputBuffer) addLine(line string) { +func (b *outputBuffer) addLine_nothread(line string) { scrollBar := b.textEdit.VerticalScrollBar() currentScroll := scrollBar.Value() userScrolledBack := currentScroll != scrollBar.Maximum() @@ -156,18 +159,6 @@ func (b *outputBuffer) readLineAndUpdate() (fullLine string, err error) { return } -func init() { - flag.Parse() - - initLanguages() - - qt.QCoreApplication_SetOrganizationName(appAuthor) - qt.QCoreApplication_SetOrganizationDomain(appAuthorDomain) - - settings = qt.NewQSettings5() - guiConfig = LoadGUIConfig() -} - func initAboutWindow(parent *qt.QWidget) *qt.QDialog { w := qt.NewQDialog(parent) label := qt.NewQLabel3(about) @@ -178,12 +169,22 @@ func initAboutWindow(parent *qt.QWidget) *qt.QDialog { return w } -func main() { - qt.NewQApplication(os.Args) +type AnnieGuiApp struct { + *qt.QMainWindow + + urlLineEdit *qt.QLineEdit + output *outputBuffer +} + +func NewAnnieGuiApp() *AnnieGuiApp { window := qt.NewQMainWindow2() window.SetWindowTitle(appName) + app := AnnieGuiApp{ + QMainWindow: window, + } + const WindowStateVersion = 0 window.OnCloseEvent(func(super func(event *qt.QCloseEvent), event *qt.QCloseEvent) { @@ -218,7 +219,7 @@ func main() { aboutWindow.Raise() }) - urlLineEdit := qt.NewQLineEdit(nil) + app.urlLineEdit = qt.NewQLineEdit(nil) folderLineEdit := qt.NewQLineEdit3(guiConfig.DestinationFolder) folderLineEdit.SetReadOnly(true) @@ -246,7 +247,7 @@ func main() { inputFormLayout := qt.NewQFormLayout(nil) inputFormLayout.SetFieldGrowthPolicy(qt.QFormLayout__AllNonFixedFieldsGrow) - inputFormLayout.AddRow3(tr("Video URL"), urlLineEdit.QWidget) + inputFormLayout.AddRow3(tr("Video URL"), app.urlLineEdit.QWidget) inputFormLayout.AddRow4(tr("Destination folder"), folderHBoxLayout.QLayout) inputFormLayout.AddRow3(tr("Download playlists"), playlistCheckBox.QWidget) @@ -258,43 +259,12 @@ func main() { monospaceFont := qt.NewQFont2("Courier") monospaceFont.SetStyleHint(qt.QFont__Monospace) outputTextEdit.SetFont(monospaceFont) - output := newOutputBuffer(outputTextEdit) - output.addLine(tr("Awaiting user input")) + + app.output = newOutputBuffer(outputTextEdit) + app.output.addLine(tr("Awaiting user input")) downloadButton := qt.NewQPushButton3(tr("Download")) - downloadButton.OnClicked(func() { - url := strings.TrimSpace(urlLineEdit.Text()) - if len(url) > 0 { - output.addLine(time.Now().Format("15:04:05 ") + tr("Download started")) - - savedStdout := os.Stdout - r, w, _ := os.Pipe() - output.attachReader(r) - os.Stdout = w - - go func() { - for { - _, err := output.readLineAndUpdate() - if err != nil { - if err == io.EOF { - break - } - log.Fatal(err) - } - // fmt.Fprint(savedStdout, line) - } - output.addLine("") - }() - - go func() { - if !download(url, guiConfig.PlaylistEnabled, guiConfig.DestinationFolder) { - fmt.Println(tr("On network errors, e.g. HTTP 403, please retry a few times.")) - } - w.Close() - os.Stdout = savedStdout - }() - } - }) + downloadButton.OnClicked(app.StartDownload) layout := qt.NewQVBoxLayout2() layout.AddLayout2(inputFormLayout.QLayout, 0) @@ -302,7 +272,99 @@ func main() { layout.AddWidget3(outputTextEdit.QWidget, 1, 0) centralWidget.SetLayout(layout.QLayout) - window.Show() + // + + return &app +} + +func (app *AnnieGuiApp) StartDownload() { + url := strings.TrimSpace(app.urlLineEdit.Text()) + if len(url) == 0 { + return + } + + app.output.addLine(time.Now().Format("15:04:05 ") + tr("Download started")) + + savedStdout := os.Stdout + r, w, _ := os.Pipe() + app.output.attachReader(r) + os.Stdout = w + + go func() { + for { + _, err := app.output.readLineAndUpdate() + if err != nil { + if err == io.EOF { + break + } + log.Fatal(err) + } + // fmt.Fprint(savedStdout, line) + } + app.output.addLine("") + }() + + printError := func(url string, err error) { + app.output.addLine(fmt.Sprintf( + "Downloading %s error:\n%s\n", url, err, + )) + } + + go func() { + + data, err := extractors.Extract(url, extractors.Options{ + Playlist: guiConfig.PlaylistEnabled, + }) + if err != nil { + // if this error occurs, it means that an error occurred before actually starting to extract data + // (there is an error in the preparation step), and the data list is empty. + printError(url, err) + return + } + + var isErr bool + for _, item := range data { + if item.Err != nil { + // if this error occurs, the preparation step is normal, but the data extraction is wrong. + // the data is an empty struct. + printError(item.URL, item.Err) + isErr = true + continue + } + + err = downloader.New(downloader.Options{ + OutputPath: guiConfig.DestinationFolder, + }).Download(item) + if err != nil { + printError(item.URL, err) + isErr = true + } + } + + if isErr { + app.output.addLine(tr("On network errors, e.g. HTTP 403, please retry a few times.")) + } + w.Close() + os.Stdout = savedStdout + }() +} + +func main() { + flag.Parse() + + initLanguages() + + qt.QCoreApplication_SetOrganizationName(appAuthor) + qt.QCoreApplication_SetOrganizationDomain(appAuthorDomain) + + settings = qt.NewQSettings5() + guiConfig = LoadGUIConfig() + + qt.NewQApplication(os.Args) + + app := NewAnnieGuiApp() + + app.Show() sigs := make(chan os.Signal) signal.Notify(sigs, syscall.SIGSEGV, syscall.SIGABRT)