diff --git a/.gitignore b/.gitignore index 4cb44b1..9d2b1ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/deploy +annie-miqt + diff --git a/about.go b/about.go new file mode 100644 index 0000000..d9ec254 --- /dev/null +++ b/about.go @@ -0,0 +1,17 @@ +package main + +const ( + appName = "annie-mingui" + appAuthor = "Zhiming Wang" + appAuthorDomain = "zhimingwang.org" + about = `
annie-mingui v2020.02.09
+Copyright (c) 2020 Zhiming Wang
+annie-mingui is a Qt wrapper for iawia002/annie the video downloader. Credits:
+Project URL: github.com/fanaticscripter/annie-mingui.
+` +) diff --git a/annie.go b/annie.go new file mode 100644 index 0000000..1943fec --- /dev/null +++ b/annie.go @@ -0,0 +1,139 @@ +package main + +import ( + "net/url" + + "github.com/iawia002/annie/config" + "github.com/iawia002/annie/downloader" + "github.com/iawia002/annie/extractors/bcy" + "github.com/iawia002/annie/extractors/bilibili" + "github.com/iawia002/annie/extractors/douyin" + "github.com/iawia002/annie/extractors/douyu" + "github.com/iawia002/annie/extractors/facebook" + "github.com/iawia002/annie/extractors/geekbang" + "github.com/iawia002/annie/extractors/instagram" + "github.com/iawia002/annie/extractors/iqiyi" + "github.com/iawia002/annie/extractors/mgtv" + "github.com/iawia002/annie/extractors/miaopai" + "github.com/iawia002/annie/extractors/netease" + "github.com/iawia002/annie/extractors/pixivision" + "github.com/iawia002/annie/extractors/pornhub" + "github.com/iawia002/annie/extractors/qq" + "github.com/iawia002/annie/extractors/tangdou" + "github.com/iawia002/annie/extractors/tiktok" + "github.com/iawia002/annie/extractors/tumblr" + "github.com/iawia002/annie/extractors/twitter" + "github.com/iawia002/annie/extractors/udn" + "github.com/iawia002/annie/extractors/universal" + "github.com/iawia002/annie/extractors/vimeo" + "github.com/iawia002/annie/extractors/weibo" + "github.com/iawia002/annie/extractors/xvideos" + "github.com/iawia002/annie/extractors/yinyuetai" + "github.com/iawia002/annie/extractors/youku" + "github.com/iawia002/annie/extractors/youtube" + "github.com/iawia002/annie/utils" +) + +// The following code is slightly modified from +// +// https://github.com/iawia002/annie/blob/master/main.go + +func download(videoURL string) bool { + var ( + domain string + err error + data []downloader.Data + ) + bilibiliShortLink := utils.MatchOneOf(videoURL, `^(av|ep)\d+`) + if bilibiliShortLink != nil && len(bilibiliShortLink) > 1 { + bilibiliURL := map[string]string{ + "av": "https://www.bilibili.com/video/", + "ep": "https://www.bilibili.com/bangumi/play/", + } + domain = "bilibili" + videoURL = bilibiliURL[bilibiliShortLink[1]] + videoURL + } else { + u, err := url.ParseRequestURI(videoURL) + if err != nil { + printError(videoURL, err) + return false + } + domain = utils.Domain(u.Host) + } + switch domain { + case "douyin", "iesdouyin": + data, err = douyin.Extract(videoURL) + case "bilibili": + data, err = bilibili.Extract(videoURL) + case "bcy": + data, err = bcy.Extract(videoURL) + case "pixivision": + data, err = pixivision.Extract(videoURL) + case "youku": + data, err = youku.Extract(videoURL) + case "youtube", "youtu": // youtu.be + data, err = youtube.Extract(videoURL) + case "iqiyi": + data, err = iqiyi.Extract(videoURL) + case "mgtv": + data, err = mgtv.Extract(videoURL) + case "tangdou": + data, err = tangdou.Extract(videoURL) + case "tumblr": + data, err = tumblr.Extract(videoURL) + case "vimeo": + data, err = vimeo.Extract(videoURL) + case "facebook": + data, err = facebook.Extract(videoURL) + case "douyu": + data, err = douyu.Extract(videoURL) + case "miaopai": + data, err = miaopai.Extract(videoURL) + case "163": + data, err = netease.Extract(videoURL) + case "weibo": + data, err = weibo.Extract(videoURL) + case "instagram": + data, err = instagram.Extract(videoURL) + case "twitter": + data, err = twitter.Extract(videoURL) + case "qq": + data, err = qq.Extract(videoURL) + case "yinyuetai": + data, err = yinyuetai.Extract(videoURL) + case "geekbang": + data, err = geekbang.Extract(videoURL) + case "pornhub": + data, err = pornhub.Extract(videoURL) + case "xvideos": + data, err = xvideos.Extract(videoURL) + case "udn": + data, err = udn.Extract(videoURL) + case "tiktok": + data, err = tiktok.Extract(videoURL) + default: + data, err = universal.Extract(videoURL) + } + 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.Download(item, videoURL, config.ChunkSizeMB) + if err != nil { + printError(item.URL, err) + isErr = true + } + } + return !isErr +} diff --git a/locale.go b/locale.go new file mode 100644 index 0000000..c906f08 --- /dev/null +++ b/locale.go @@ -0,0 +1,49 @@ +package main + +import ( + "github.com/cloudfoundry-attic/jibber_jabber" + "golang.org/x/text/language" +) + +var locales = map[string]map[string]string{ + "zh-Hans": { + "About": "关于", + "Application": "应用", + "Awaiting user input": "等待用户输入", + "Destination folder": "目标文件夹", + "Download": "下载", + "Download playlists": "下载完整播单", + "Download started": "下载已开始", + "On network errors, e.g. HTTP 403, please retry a few times.": "如遇HTTP 403等网络错误,请重试几次。", + "Pick another folder": "选择文件夹", + "Video URL": "视频链接", + }, +} +var locale map[string]string + +// Poor man's gettext. +func tr(s string) string { + t, ok := locale[s] + if ok { + return t + } + return s +} + +func initLanguages() { + + userLang, err := jibber_jabber.DetectIETF() + if err != nil { + userLang = "en" + } + availableLangs := []string{"en"} + availableTags := []language.Tag{language.English} + for l := range locales { + availableLangs = append(availableLangs, l) + availableTags = append(availableTags, language.Make(l)) + } + matcher := language.NewMatcher(availableTags) + _, index, _ := matcher.Match(language.Make(userLang)) + locale = locales[availableLangs[index]] + +} diff --git a/main.go b/main.go index 6e4c10e..780e58a 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( "flag" "fmt" "io" - "net/url" + "log" "os" "os/signal" "regexp" @@ -13,58 +13,10 @@ import ( "syscall" "time" - "golang.org/x/text/language" - - "github.com/cloudfoundry-attic/jibber_jabber" - "github.com/golang/glog" - - qt "github.com/mappu/miqt/qt6" - "github.com/iawia002/annie/config" - "github.com/iawia002/annie/downloader" - "github.com/iawia002/annie/extractors/bcy" - "github.com/iawia002/annie/extractors/bilibili" - "github.com/iawia002/annie/extractors/douyin" - "github.com/iawia002/annie/extractors/douyu" - "github.com/iawia002/annie/extractors/facebook" - "github.com/iawia002/annie/extractors/geekbang" - "github.com/iawia002/annie/extractors/instagram" - "github.com/iawia002/annie/extractors/iqiyi" - "github.com/iawia002/annie/extractors/mgtv" - "github.com/iawia002/annie/extractors/miaopai" - "github.com/iawia002/annie/extractors/netease" - "github.com/iawia002/annie/extractors/pixivision" - "github.com/iawia002/annie/extractors/pornhub" - "github.com/iawia002/annie/extractors/qq" - "github.com/iawia002/annie/extractors/tangdou" - "github.com/iawia002/annie/extractors/tiktok" - "github.com/iawia002/annie/extractors/tumblr" - "github.com/iawia002/annie/extractors/twitter" - "github.com/iawia002/annie/extractors/udn" - "github.com/iawia002/annie/extractors/universal" - "github.com/iawia002/annie/extractors/vimeo" - "github.com/iawia002/annie/extractors/weibo" - "github.com/iawia002/annie/extractors/xvideos" - "github.com/iawia002/annie/extractors/yinyuetai" - "github.com/iawia002/annie/extractors/youku" - "github.com/iawia002/annie/extractors/youtube" - "github.com/iawia002/annie/utils" + qt "github.com/mappu/miqt/qt6" ) -const appName = "annie-mingui" -const appAuthor = "Zhiming Wang" -const appAuthorDomain = "zhimingwang.org" -const about = `annie-mingui v2020.02.09
-Copyright (c) 2020 Zhiming Wang
-annie-mingui is a Qt wrapper for iawia002/annie the video downloader. Credits:
-Project URL: github.com/fanaticscripter/annie-mingui.
-` - // GUIConfig carries all application configurations. type GUIConfig struct { DestinationFolder string @@ -75,7 +27,7 @@ type GUIConfig struct { // configured destination folder. func (c *GUIConfig) GetDestinationFolder() string { fallback := DefaultDownloadsFolder() - val := settings.Value("destinationFolder", qt.NewQVariant12(fallback)).ToString() + val := settings.Value(*qt.NewQAnyStringView3("destinationFolder"), qt.NewQVariant11(fallback)).ToString() c.DestinationFolder = val return val } @@ -84,7 +36,7 @@ func (c *GUIConfig) GetDestinationFolder() string { // configured value of whether playlist download is enabled. func (c *GUIConfig) GetPlaylistEnabled() bool { fallback := true - val := settings.Value("playlistEnabled", qt.NewQVariant9(fallback)).ToBool() + val := settings.Value(*qt.NewQAnyStringView3("playlistEnabled"), qt.NewQVariant8(fallback)).ToBool() c.PlaylistEnabled = val return val } @@ -92,14 +44,14 @@ func (c *GUIConfig) GetPlaylistEnabled() bool { // SetDestinationFolder sets and persists the configured destination folder. func (c *GUIConfig) SetDestinationFolder(val string) { c.DestinationFolder = val - go func() { settings.SetValue("destinationFolder", qt.NewQVariant12(val)) }() + go func() { settings.SetValue(*qt.NewQAnyStringView3("destinationFolder"), qt.NewQVariant11(val)) }() } // SetPlaylistEnabled sets and persists the configured value of whether playlist // download is enabled. func (c *GUIConfig) SetPlaylistEnabled(val bool) { c.PlaylistEnabled = val - go func() { settings.SetValue("playlistEnabled", qt.NewQVariant9(val)) }() + go func() { settings.SetValue(*qt.NewQAnyStringView3("playlistEnabled"), qt.NewQVariant8(val)) }() } // LoadGUIConfig loads config values from persisted settings if possible, or @@ -114,208 +66,27 @@ func LoadGUIConfig() *GUIConfig { var settings *qt.QSettings var guiConfig *GUIConfig -var locales = map[string]map[string]string{ - "zh-Hans": { - "About": "关于", - "Application": "应用", - "Awaiting user input": "等待用户输入", - "Destination folder": "目标文件夹", - "Download": "下载", - "Download playlists": "下载完整播单", - "Download started": "下载已开始", - "On network errors, e.g. HTTP 403, please retry a few times.": "如遇HTTP 403等网络错误,请重试几次。", - "Pick another folder": "选择文件夹", - "Video URL": "视频链接", - }, -} -var locale map[string]string - -// Poor man's gettext. -func tr(s string) string { - t, ok := locale[s] - if ok { - return t - } - return s -} - -// ----- START ANNIE CODE ----- -// -// The following code is slightly modified from -// -// https://github.com/iawia002/annie/blob/master/main.go - func printError(url string, err error) { fmt.Printf( "Downloading %s error:\n%s\n", url, err, ) } -func download(videoURL string) bool { - var ( - domain string - err error - data []downloader.Data - ) - bilibiliShortLink := utils.MatchOneOf(videoURL, `^(av|ep)\d+`) - if bilibiliShortLink != nil && len(bilibiliShortLink) > 1 { - bilibiliURL := map[string]string{ - "av": "https://www.bilibili.com/video/", - "ep": "https://www.bilibili.com/bangumi/play/", - } - domain = "bilibili" - videoURL = bilibiliURL[bilibiliShortLink[1]] + videoURL - } else { - u, err := url.ParseRequestURI(videoURL) - if err != nil { - printError(videoURL, err) - return false - } - domain = utils.Domain(u.Host) - } - switch domain { - case "douyin", "iesdouyin": - data, err = douyin.Extract(videoURL) - case "bilibili": - data, err = bilibili.Extract(videoURL) - case "bcy": - data, err = bcy.Extract(videoURL) - case "pixivision": - data, err = pixivision.Extract(videoURL) - case "youku": - data, err = youku.Extract(videoURL) - case "youtube", "youtu": // youtu.be - data, err = youtube.Extract(videoURL) - case "iqiyi": - data, err = iqiyi.Extract(videoURL) - case "mgtv": - data, err = mgtv.Extract(videoURL) - case "tangdou": - data, err = tangdou.Extract(videoURL) - case "tumblr": - data, err = tumblr.Extract(videoURL) - case "vimeo": - data, err = vimeo.Extract(videoURL) - case "facebook": - data, err = facebook.Extract(videoURL) - case "douyu": - data, err = douyu.Extract(videoURL) - case "miaopai": - data, err = miaopai.Extract(videoURL) - case "163": - data, err = netease.Extract(videoURL) - case "weibo": - data, err = weibo.Extract(videoURL) - case "instagram": - data, err = instagram.Extract(videoURL) - case "twitter": - data, err = twitter.Extract(videoURL) - case "qq": - data, err = qq.Extract(videoURL) - case "yinyuetai": - data, err = yinyuetai.Extract(videoURL) - case "geekbang": - data, err = geekbang.Extract(videoURL) - case "pornhub": - data, err = pornhub.Extract(videoURL) - case "xvideos": - data, err = xvideos.Extract(videoURL) - case "udn": - data, err = udn.Extract(videoURL) - case "tiktok": - data, err = tiktok.Extract(videoURL) - default: - data, err = universal.Extract(videoURL) - } - 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.Download(item, videoURL, config.ChunkSizeMB) - if err != nil { - printError(item.URL, err) - isErr = true - } - } - return !isErr -} - -// ----- END ANNIE CODE ----- - -// PlainTextEdit extends QPlainTextEdit. -type PlainTextEdit struct { - qt.QPlainTextEdit - - _ func() `constructor:"init"` - - _ func(string) `slot:"addLine"` +// +type outputBuffer struct { + textEdit *qt.QPlainTextEdit + reader *bufio.Reader + scanner *bufio.Scanner carriageReturnInAction bool } -func (p *PlainTextEdit) init() { - p.carriageReturnInAction = false - p.ConnectAddLine(p.addLine) -} - -// addLines 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 (p *PlainTextEdit) addLine(line string) { - scrollBar := p.VerticalScrollBar() - currentScroll := scrollBar.Value() - userScrolledBack := currentScroll != scrollBar.Maximum() - - cursor := p.TextCursor() - cursor.MovePosition(qt.QTextCursor__End, qt.QTextCursor__MoveAnchor, 1) - if p.carriageReturnInAction { - // Remove last line. - cursor.Select(qt.QTextCursor__LineUnderCursor) - cursor.RemoveSelectedText() - } - p.carriageReturnInAction = false - if len(line) > 0 { - switch lastCh := line[len(line)-1]; lastCh { - case '\n': - cursor.InsertText(line) - case '\r': - cursor.InsertText(line[:len(line)-1]) - p.carriageReturnInAction = true - default: - cursor.InsertText(line + "\n") - } - } else { - cursor.InsertText("\n") - } - - if userScrolledBack { - scrollBar.SetValue(currentScroll) - } else { - scrollBar.SetValue(scrollBar.Maximum()) - } -} - -type outputBuffer struct { - textEdit *PlainTextEdit - reader *bufio.Reader - scanner *bufio.Scanner -} - -func newOutputBuffer(textEdit *PlainTextEdit) *outputBuffer { +func newOutputBuffer(textEdit *qt.QPlainTextEdit) *outputBuffer { return &outputBuffer{ - textEdit: textEdit, - reader: nil, - scanner: nil, + textEdit: textEdit, + reader: nil, + scanner: nil, + carriageReturnInAction: false, } } @@ -335,8 +106,40 @@ func (b *outputBuffer) attachReader(r io.Reader) { }) } +// 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) { - b.textEdit.AddLine(line) + scrollBar := b.textEdit.VerticalScrollBar() + currentScroll := scrollBar.Value() + userScrolledBack := currentScroll != scrollBar.Maximum() + + cursor := b.textEdit.TextCursor() + cursor.MovePosition3(qt.QTextCursor__End, qt.QTextCursor__MoveAnchor, 1) + if b.carriageReturnInAction { + // Remove last line. + cursor.Select(qt.QTextCursor__LineUnderCursor) + cursor.RemoveSelectedText() + } + b.carriageReturnInAction = false + if len(line) > 0 { + switch lastCh := line[len(line)-1]; lastCh { + case '\n': + cursor.InsertText(line) + case '\r': + cursor.InsertText(line[:len(line)-1]) + b.carriageReturnInAction = true + default: + cursor.InsertText(line + "\n") + } + } else { + cursor.InsertText("\n") + } + + if userScrolledBack { + scrollBar.SetValue(currentScroll) + } else { + scrollBar.SetValue(scrollBar.Maximum()) + } } func (b *outputBuffer) readLineAndUpdate() (fullLine string, err error) { @@ -357,115 +160,117 @@ func (b *outputBuffer) readLineAndUpdate() (fullLine string, err error) { func init() { flag.Parse() - userLang, err := jibber_jabber.DetectIETF() - if err != nil { - userLang = "en" - } - availableLangs := []string{"en"} - availableTags := []language.Tag{language.English} - for l := range locales { - availableLangs = append(availableLangs, l) - availableTags = append(availableTags, language.Make(l)) - } - matcher := language.NewMatcher(availableTags) - _, index, _ := matcher.Match(language.Make(userLang)) - locale = locales[availableLangs[index]] + initLanguages() qt.QCoreApplication_SetOrganizationName(appAuthor) qt.QCoreApplication_SetOrganizationDomain(appAuthorDomain) - settings = qt.NewQSettings5(nil) + settings = qt.NewQSettings5() guiConfig = LoadGUIConfig() } -func initAboutWindow() *qt.QDialog { - w := qt.NewQDialog(nil, 0) - label := qt.NewQLabel2(about, nil, 0) +func initAboutWindow(parent *qt.QWidget) *qt.QDialog { + w := qt.NewQDialog(parent) + label := qt.NewQLabel3(about) label.SetOpenExternalLinks(true) - layout := qt.NewQVBoxLayout() - layout.AddWidget(label, 0, 0) - w.SetLayout(layout) + layout := qt.NewQVBoxLayout2() + layout.AddWidget(label.QWidget) + w.SetLayout(layout.QLayout) return w } func main() { - qt.NewQApplication(len(os.Args), os.Args) + qt.NewQApplication(os.Args) - window := qt.NewQMainWindow(nil, 0) + window := qt.NewQMainWindow2() window.SetWindowTitle(appName) - window.ConnectCloseEvent(func(*qt.QCloseEvent) { - settings.SetValue("_geometry", qt.NewQVariant13(window.SaveGeometry())) - settings.SetValue("_windowState", qt.NewQVariant13(window.SaveState(0))) + + const WindowStateVersion = 0 + + window.OnCloseEvent(func(super func(event *qt.QCloseEvent), event *qt.QCloseEvent) { + settings.SetValue(*qt.NewQAnyStringView3("_geometry"), qt.NewQVariant12(window.SaveGeometry())) + settings.SetValue(*qt.NewQAnyStringView3("_windowState"), qt.NewQVariant12(window.SaveState1(WindowStateVersion))) + + super(event) }) - window.RestoreGeometry(settings.Value("_geometry", qt.NewQVariant()).ToByteArray()) - window.RestoreState(settings.Value("_windowState", qt.NewQVariant()).ToByteArray(), 0) + { + prevGeometry := settings.Value(*qt.NewQAnyStringView3("_geometry"), qt.NewQVariant()).ToByteArray() + if len(prevGeometry) > 0 { + window.RestoreGeometry(prevGeometry) + } - centralWidget := qt.NewQWidget(window, 0) + prevWindowState := settings.Value(*qt.NewQAnyStringView3("_windowState"), qt.NewQVariant()).ToByteArray() + if len(prevWindowState) > 0 { + window.RestoreState2(prevWindowState, WindowStateVersion) + } + } + + centralWidget := qt.NewQWidget(window.QWidget) window.SetCentralWidget(centralWidget) menuBar := window.MenuBar() - applicationMenu := menuBar.AddMenu2(tr("Application")) - aboutWindow := initAboutWindow() - aboutAction := applicationMenu.AddAction(tr("About")) + applicationMenu := menuBar.AddMenuWithTitle(tr("Application")) + aboutWindow := initAboutWindow(window.QWidget) + aboutAction := applicationMenu.AddActionWithText(tr("About")) aboutAction.SetMenuRole(qt.QAction__AboutRole) - aboutAction.ConnectTriggered(func(bool) { + aboutAction.OnTriggered(func() { aboutWindow.Show() aboutWindow.Raise() }) urlLineEdit := qt.NewQLineEdit(nil) - folderLineEdit := qt.NewQLineEdit2(guiConfig.DestinationFolder, nil) + folderLineEdit := qt.NewQLineEdit3(guiConfig.DestinationFolder) folderLineEdit.SetReadOnly(true) folderLineEdit.SetMinimumWidth(250) - folderButton := qt.NewQPushButton2(tr("Pick another folder"), nil) - folderDialog := qt.NewQFileDialog2(nil, tr("Destination folder"), guiConfig.DestinationFolder, "") - folderDialog.SetFileMode(qt.QFileDialog__DirectoryOnly) - folderButton.ConnectClicked(func(bool) { - if folderDialog.Exec() != int(qt.QDialog__Accepted) { + folderButton := qt.NewQPushButton3(tr("Pick another folder")) + folderDialog := qt.NewQFileDialog6(window.QWidget, tr("Destination folder"), guiConfig.DestinationFolder, "") + folderDialog.SetFileMode(qt.QFileDialog__Directory) + folderButton.OnClicked(func() { + if folderDialog.Exec() != int(qt.QDialog__Accepted) { // FIXME blocking call return } destinationFolder := qt.QDir_ToNativeSeparators(folderDialog.SelectedFiles()[0]) folderLineEdit.SetText(destinationFolder) guiConfig.SetDestinationFolder(destinationFolder) }) - folderHBoxLayout := qt.NewQHBoxLayout() - folderHBoxLayout.AddWidget(folderLineEdit, 1, 0) - folderHBoxLayout.AddWidget(folderButton, 0, 0) + folderHBoxLayout := qt.NewQHBoxLayout2() + folderHBoxLayout.AddWidget3(folderLineEdit.QWidget, 1, 0) + folderHBoxLayout.AddWidget3(folderButton.QWidget, 0, 0) playlistCheckBox := qt.NewQCheckBox(nil) playlistCheckBox.SetChecked(guiConfig.PlaylistEnabled) - playlistCheckBox.ConnectToggled(func(checked bool) { + playlistCheckBox.OnToggled(func(checked bool) { guiConfig.SetPlaylistEnabled(checked) }) inputFormLayout := qt.NewQFormLayout(nil) inputFormLayout.SetFieldGrowthPolicy(qt.QFormLayout__AllNonFixedFieldsGrow) - inputFormLayout.AddRow3(tr("Video URL"), urlLineEdit) - inputFormLayout.AddRow4(tr("Destination folder"), folderHBoxLayout) - inputFormLayout.AddRow3(tr("Download playlists"), playlistCheckBox) + inputFormLayout.AddRow3(tr("Video URL"), urlLineEdit.QWidget) + inputFormLayout.AddRow4(tr("Destination folder"), folderHBoxLayout.QLayout) + inputFormLayout.AddRow3(tr("Download playlists"), playlistCheckBox.QWidget) - outputTextEdit := NewPlainTextEdit(nil) + outputTextEdit := qt.NewQPlainTextEdit2() outputTextEdit.SetReadOnly(true) outputTextEdit.SetMinimumHeight(400) outputTextEdit.SetMinimumWidth(1000) outputTextEdit.SetLineWrapMode(qt.QPlainTextEdit__NoWrap) - monospaceFont := qt.NewQFont2("Courier", -1, -1, false) - monospaceFont.SetStyleHint(qt.QFont__Monospace, 0) + monospaceFont := qt.NewQFont2("Courier") + monospaceFont.SetStyleHint(qt.QFont__Monospace) outputTextEdit.SetFont(monospaceFont) output := newOutputBuffer(outputTextEdit) output.addLine(tr("Awaiting user input")) - downloadButton := qt.NewQPushButton2(tr("Download"), nil) - downloadButton.ConnectClicked(func(bool) { + 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")) config.OutputPath = guiConfig.DestinationFolder config.Playlist = guiConfig.PlaylistEnabled - glog.Infof("downloading %s => %s", url, config.OutputPath) + log.Printf("downloading %s => %s", url, config.OutputPath) savedStdout := os.Stdout r, w, _ := os.Pipe() @@ -479,7 +284,7 @@ func main() { if err == io.EOF { break } - glog.Fatal(err) + log.Fatal(err) } // fmt.Fprint(savedStdout, line) } @@ -496,11 +301,11 @@ func main() { } }) - layout := qt.NewQVBoxLayout() - layout.AddLayout(inputFormLayout, 0) - layout.AddWidget(downloadButton, 0, 0) - layout.AddWidget(outputTextEdit, 1, 0) - centralWidget.SetLayout(layout) + layout := qt.NewQVBoxLayout2() + layout.AddLayout2(inputFormLayout.QLayout, 0) + layout.AddWidget3(downloadButton.QWidget, 0, 0) + layout.AddWidget3(outputTextEdit.QWidget, 1, 0) + centralWidget.SetLayout(layout.QLayout) window.Show() @@ -509,7 +314,7 @@ func main() { go func() { for { sig := <-sigs - glog.Error(sig) + log.Print(sig) } }()