]> git.sur5r.net Git - minitube/commitdiff
New upstream version 3.1 upstream/3.1
authorJakob Haufe <sur5r@sur5r.net>
Sun, 16 Jun 2019 20:27:40 +0000 (20:27 +0000)
committerJakob Haufe <sur5r@sur5r.net>
Sun, 16 Jun 2019 20:27:40 +0000 (20:27 +0000)
427 files changed:
.clang-format [deleted file]
.gitignore [deleted file]
README.md
empty.ts
icons/dark/16/audio-volume-high.png [new file with mode: 0644]
icons/dark/16/audio-volume-high@2x.png [new file with mode: 0644]
icons/dark/16/audio-volume-muted.png [new file with mode: 0644]
icons/dark/16/audio-volume-muted@2x.png [new file with mode: 0644]
icons/dark/16/bookmark-new.png [new file with mode: 0644]
icons/dark/16/bookmark-new@2x.png [new file with mode: 0644]
icons/dark/16/bookmark-new_active.png [new file with mode: 0644]
icons/dark/16/bookmark-new_active@2x.png [new file with mode: 0644]
icons/dark/16/bookmark-remove.png [new file with mode: 0644]
icons/dark/16/bookmark-remove@2x.png [new file with mode: 0644]
icons/dark/16/edit-find.png [new file with mode: 0644]
icons/dark/16/edit-find@2x.png [new file with mode: 0644]
icons/dark/16/email.png [new file with mode: 0644]
icons/dark/16/email@2x.png [new file with mode: 0644]
icons/dark/16/facebook.png [new file with mode: 0644]
icons/dark/16/facebook@2x.png [new file with mode: 0644]
icons/dark/16/go-next.png [new file with mode: 0644]
icons/dark/16/go-next@2x.png [new file with mode: 0644]
icons/dark/16/go-previous.png [new file with mode: 0644]
icons/dark/16/go-previous@2x.png [new file with mode: 0644]
icons/dark/16/link.png [new file with mode: 0644]
icons/dark/16/link@2x.png [new file with mode: 0644]
icons/dark/16/mark-watched.png [new file with mode: 0644]
icons/dark/16/mark-watched@2x.png [new file with mode: 0644]
icons/dark/16/media-playback-start.png [new file with mode: 0644]
icons/dark/16/media-playback-start@2x.png [new file with mode: 0644]
icons/dark/16/media-playback-start_checked.png [new file with mode: 0644]
icons/dark/16/media-playback-start_checked@2x.png [new file with mode: 0644]
icons/dark/16/safesearch.png [new file with mode: 0644]
icons/dark/16/safesearch@2x.png [new file with mode: 0644]
icons/dark/16/search-duration.png [new file with mode: 0644]
icons/dark/16/search-duration@2x.png [new file with mode: 0644]
icons/dark/16/search-quality.png [new file with mode: 0644]
icons/dark/16/search-quality@2x.png [new file with mode: 0644]
icons/dark/16/search-sortBy.png [new file with mode: 0644]
icons/dark/16/search-sortBy@2x.png [new file with mode: 0644]
icons/dark/16/search-time.png [new file with mode: 0644]
icons/dark/16/search-time@2x.png [new file with mode: 0644]
icons/dark/16/show-updated.png [new file with mode: 0644]
icons/dark/16/show-updated@2x.png [new file with mode: 0644]
icons/dark/16/sort.png [new file with mode: 0644]
icons/dark/16/sort@2x.png [new file with mode: 0644]
icons/dark/16/twitter.png [new file with mode: 0644]
icons/dark/16/twitter@2x.png [new file with mode: 0644]
icons/dark/16/unwatched.png [new file with mode: 0644]
icons/dark/16/unwatched@2x.png [new file with mode: 0644]
icons/dark/16/video-display.png [new file with mode: 0644]
icons/dark/16/video-display@2x.png [new file with mode: 0644]
icons/dark/16/worldwide.png [new file with mode: 0644]
icons/dark/16/worldwide@2x.png [new file with mode: 0644]
icons/dark/24/edit-find.png [new file with mode: 0644]
icons/dark/24/edit-find@2x.png [new file with mode: 0644]
icons/dark/24/refine-search.png [new file with mode: 0644]
icons/dark/24/refine-search@2x.png [new file with mode: 0644]
icons/dark/32/content-loading.png [new file with mode: 0644]
icons/dark/32/content-loading@2x.png [new file with mode: 0644]
icons/dark/32/document-save.png [new file with mode: 0644]
icons/dark/32/document-save@2x.png [new file with mode: 0644]
icons/dark/32/media-playback-pause.png [new file with mode: 0644]
icons/dark/32/media-playback-pause@2x.png [new file with mode: 0644]
icons/dark/32/media-playback-start.png [new file with mode: 0644]
icons/dark/32/media-playback-start@2x.png [new file with mode: 0644]
icons/dark/32/media-playback-start_checked.png [new file with mode: 0644]
icons/dark/32/media-playback-start_checked@2x.png [new file with mode: 0644]
icons/dark/32/media-playback-stop.png [new file with mode: 0644]
icons/dark/32/media-playback-stop@2x.png [new file with mode: 0644]
icons/dark/32/media-skip-forward.png [new file with mode: 0644]
icons/dark/32/media-skip-forward@2x.png [new file with mode: 0644]
icons/dark/32/open-menu.png [new file with mode: 0644]
icons/dark/32/open-menu@2x.png [new file with mode: 0644]
icons/dark/32/view-fullscreen.png [new file with mode: 0644]
icons/dark/32/view-fullscreen@2x.png [new file with mode: 0644]
icons/dark/32/view-list.png [new file with mode: 0644]
icons/dark/32/view-list@2x.png [new file with mode: 0644]
icons/dark/32/view-restore.png [new file with mode: 0644]
icons/dark/32/view-restore@2x.png [new file with mode: 0644]
icons/dark/88/channels.png [new file with mode: 0644]
icons/dark/88/channels@2x.png [new file with mode: 0644]
icons/dark/88/unwatched.png [new file with mode: 0644]
icons/dark/88/unwatched@2x.png [new file with mode: 0644]
icons/light/16/audio-volume-high.png [new file with mode: 0644]
icons/light/16/audio-volume-high@2x.png [new file with mode: 0644]
icons/light/16/audio-volume-muted.png [new file with mode: 0644]
icons/light/16/audio-volume-muted@2x.png [new file with mode: 0644]
icons/light/16/bookmark-new.png [new file with mode: 0644]
icons/light/16/bookmark-new@2x.png [new file with mode: 0644]
icons/light/16/bookmark-new_active.png [new file with mode: 0644]
icons/light/16/bookmark-new_active@2x.png [new file with mode: 0644]
icons/light/16/bookmark-remove.png [new file with mode: 0644]
icons/light/16/bookmark-remove@2x.png [new file with mode: 0644]
icons/light/16/edit-find.png [new file with mode: 0644]
icons/light/16/edit-find@2x.png [new file with mode: 0644]
icons/light/16/email.png [new file with mode: 0644]
icons/light/16/email@2x.png [new file with mode: 0644]
icons/light/16/facebook.png [new file with mode: 0644]
icons/light/16/facebook@2x.png [new file with mode: 0644]
icons/light/16/go-next.png [new file with mode: 0644]
icons/light/16/go-next@2x.png [new file with mode: 0644]
icons/light/16/go-previous.png [new file with mode: 0644]
icons/light/16/go-previous@2x.png [new file with mode: 0644]
icons/light/16/link.png [new file with mode: 0644]
icons/light/16/link@2x.png [new file with mode: 0644]
icons/light/16/mark-watched.png [new file with mode: 0644]
icons/light/16/mark-watched@2x.png [new file with mode: 0644]
icons/light/16/media-playback-start.png [new file with mode: 0644]
icons/light/16/media-playback-start@2x.png [new file with mode: 0644]
icons/light/16/media-playback-start_checked.png [new file with mode: 0644]
icons/light/16/media-playback-start_checked@2x.png [new file with mode: 0644]
icons/light/16/safesearch.png [new file with mode: 0644]
icons/light/16/safesearch@2x.png [new file with mode: 0644]
icons/light/16/search-duration.png [new file with mode: 0644]
icons/light/16/search-duration@2x.png [new file with mode: 0644]
icons/light/16/search-quality.png [new file with mode: 0644]
icons/light/16/search-quality@2x.png [new file with mode: 0644]
icons/light/16/search-sortBy.png [new file with mode: 0644]
icons/light/16/search-sortBy@2x.png [new file with mode: 0644]
icons/light/16/search-time.png [new file with mode: 0644]
icons/light/16/search-time@2x.png [new file with mode: 0644]
icons/light/16/show-updated.png [new file with mode: 0644]
icons/light/16/show-updated@2x.png [new file with mode: 0644]
icons/light/16/sort.png [new file with mode: 0644]
icons/light/16/sort@2x.png [new file with mode: 0644]
icons/light/16/twitter.png [new file with mode: 0644]
icons/light/16/twitter@2x.png [new file with mode: 0644]
icons/light/16/unwatched.png [new file with mode: 0644]
icons/light/16/unwatched@2x.png [new file with mode: 0644]
icons/light/16/video-display.png [new file with mode: 0644]
icons/light/16/video-display@2x.png [new file with mode: 0644]
icons/light/16/worldwide.png [new file with mode: 0644]
icons/light/16/worldwide@2x.png [new file with mode: 0644]
icons/light/24/edit-find.png [new file with mode: 0644]
icons/light/24/edit-find@2x.png [new file with mode: 0644]
icons/light/24/refine-search.png [new file with mode: 0644]
icons/light/24/refine-search@2x.png [new file with mode: 0644]
icons/light/32/content-loading.png [new file with mode: 0644]
icons/light/32/content-loading@2x.png [new file with mode: 0644]
icons/light/32/document-save.png [new file with mode: 0644]
icons/light/32/document-save@2x.png [new file with mode: 0644]
icons/light/32/media-playback-pause.png [new file with mode: 0644]
icons/light/32/media-playback-pause@2x.png [new file with mode: 0644]
icons/light/32/media-playback-start.png [new file with mode: 0644]
icons/light/32/media-playback-start@2x.png [new file with mode: 0644]
icons/light/32/media-playback-start_checked.png [new file with mode: 0644]
icons/light/32/media-playback-start_checked@2x.png [new file with mode: 0644]
icons/light/32/media-playback-stop.png [new file with mode: 0644]
icons/light/32/media-playback-stop@2x.png [new file with mode: 0644]
icons/light/32/media-skip-forward.png [new file with mode: 0644]
icons/light/32/media-skip-forward@2x.png [new file with mode: 0644]
icons/light/32/open-menu.png [new file with mode: 0644]
icons/light/32/open-menu@2x.png [new file with mode: 0644]
icons/light/32/view-fullscreen.png [new file with mode: 0644]
icons/light/32/view-fullscreen@2x.png [new file with mode: 0644]
icons/light/32/view-list.png [new file with mode: 0644]
icons/light/32/view-list@2x.png [new file with mode: 0644]
icons/light/32/view-restore.png [new file with mode: 0644]
icons/light/32/view-restore@2x.png [new file with mode: 0644]
icons/light/88/channels.png [new file with mode: 0644]
icons/light/88/channels@2x.png [new file with mode: 0644]
icons/light/88/unwatched.png [new file with mode: 0644]
icons/light/88/unwatched@2x.png [new file with mode: 0644]
images/audio-volume-high.png [deleted file]
images/audio-volume-high@2x.png [deleted file]
images/audio-volume-muted.png [deleted file]
images/audio-volume-muted@2x.png [deleted file]
images/bookmark-new.png [deleted file]
images/bookmark-new@2x.png [deleted file]
images/bookmark-new_active.png [deleted file]
images/bookmark-new_active@2x.png [deleted file]
images/bookmark-remove.png [deleted file]
images/bookmark-remove@2x.png [deleted file]
images/channels.png [deleted file]
images/channels@2x.png [deleted file]
images/content-loading.png [deleted file]
images/content-loading@2x.png [deleted file]
images/document-save.png [deleted file]
images/document-save@2x.png [deleted file]
images/edit-clear.png [deleted file]
images/edit-find.png [deleted file]
images/email.png [deleted file]
images/email@2x.png [deleted file]
images/facebook.png [deleted file]
images/facebook@2x.png [deleted file]
images/go-next.png [deleted file]
images/go-next@2x.png [deleted file]
images/go-next_active.png [deleted file]
images/go-next_active@2x.png [deleted file]
images/go-previous.png [deleted file]
images/go-previous@2x.png [deleted file]
images/go-previous_active.png [deleted file]
images/go-previous_active@2x.png [deleted file]
images/go-top.png [deleted file]
images/go-top@2x.png [deleted file]
images/link.png [deleted file]
images/link@2x.png [deleted file]
images/mark-watched.png [deleted file]
images/mark-watched@2x.png [deleted file]
images/media-playback-pause.png [deleted file]
images/media-playback-pause@2x.png [deleted file]
images/media-playback-start.png [deleted file]
images/media-playback-start@2x.png [deleted file]
images/media-playback-stop.png [deleted file]
images/media-playback-stop@2x.png [deleted file]
images/media-skip-forward.png [deleted file]
images/media-skip-forward@2x.png [deleted file]
images/refine-search.png [deleted file]
images/refine-search@2x.png [deleted file]
images/safesearch.png [deleted file]
images/safesearch@2x.png [deleted file]
images/search-duration.png [deleted file]
images/search-duration@2x.png [deleted file]
images/search-quality.png [deleted file]
images/search-quality@2x.png [deleted file]
images/search-sortBy.png [deleted file]
images/search-sortBy@2x.png [deleted file]
images/search-time.png [deleted file]
images/search-time@2x.png [deleted file]
images/show-updated.png [deleted file]
images/show-updated@2x.png [deleted file]
images/sort.png [deleted file]
images/sort@2x.png [deleted file]
images/system-search.png [deleted file]
images/system-search_active.png [deleted file]
images/system-search_selected.png [deleted file]
images/twitter.png [deleted file]
images/twitter@2x.png [deleted file]
images/unwatched.png [deleted file]
images/unwatched@2x.png [deleted file]
images/video-display.png [deleted file]
images/video-display@2x.png [deleted file]
images/view-fullscreen.png [deleted file]
images/view-fullscreen@2x.png [deleted file]
images/view-list.png [deleted file]
images/view-list@2x.png [deleted file]
images/view-more.png [deleted file]
images/view-more@2x.png [deleted file]
images/view-refresh.png [deleted file]
images/view-refresh_active.png [deleted file]
images/view-refresh_selected.png [deleted file]
images/view-restore.png [deleted file]
images/view-restore@2x.png [deleted file]
images/window-close.png [deleted file]
images/window-close_active.png [deleted file]
images/window-close_selected.png [deleted file]
images/worldwide.png [deleted file]
images/worldwide@2x.png [deleted file]
lib/http/.gitignore [new file with mode: 0644]
lib/http/README.md [new file with mode: 0644]
lib/http/http.pri [new file with mode: 0644]
lib/http/http.pro [new file with mode: 0644]
lib/http/src/cachedhttp.cpp [new file with mode: 0644]
lib/http/src/cachedhttp.h [new file with mode: 0644]
lib/http/src/http.cpp [new file with mode: 0644]
lib/http/src/http.h [new file with mode: 0644]
lib/http/src/localcache.cpp [new file with mode: 0644]
lib/http/src/localcache.h [new file with mode: 0644]
lib/http/src/throttledhttp.cpp [new file with mode: 0644]
lib/http/src/throttledhttp.h [new file with mode: 0644]
lib/idle/README.md [new file with mode: 0644]
lib/idle/idle.pri [new file with mode: 0644]
lib/idle/src/idle.h [new file with mode: 0644]
lib/idle/src/idle_linux.cpp [new file with mode: 0644]
lib/idle/src/idle_mac.cpp [new file with mode: 0644]
lib/idle/src/idle_win.cpp [new file with mode: 0644]
lib/media/.gitignore [new file with mode: 0644]
lib/media/README.md [new file with mode: 0644]
lib/media/media.pri [new file with mode: 0644]
lib/media/src/media.h [new file with mode: 0644]
lib/media/src/mpv/mediampv.cpp [new file with mode: 0644]
lib/media/src/mpv/mediampv.h [new file with mode: 0644]
lib/media/src/mpv/mpvwidget.cpp [new file with mode: 0644]
lib/media/src/mpv/mpvwidget.h [new file with mode: 0644]
lib/media/src/qtav/mediaqtav.cpp [new file with mode: 0644]
lib/media/src/qtav/mediaqtav.h [new file with mode: 0644]
locale/ar.ts
locale/ast.ts
locale/be.ts
locale/bg_BG.ts
locale/ca.ts
locale/ca_ES.ts
locale/cs_CZ.ts
locale/da.ts
locale/de_DE.ts
locale/el.ts
locale/en.ts
locale/en_GB.ts [new file with mode: 0644]
locale/es.ts
locale/es_AR.ts
locale/es_ES.ts
locale/es_MX.ts
locale/fi.ts
locale/fi_FI.ts
locale/fr.ts
locale/gl.ts
locale/he_IL.ts
locale/hr.ts
locale/hu.ts
locale/id.ts
locale/it.ts
locale/ja_JP.ts
locale/ko_KR.ts
locale/ky.ts
locale/locale.pri
locale/ms_MY.ts
locale/nb.ts
locale/nl.ts
locale/nn.ts
locale/pl.ts
locale/pl_PL.ts
locale/pt.ts
locale/pt_BR.ts
locale/pt_PT.ts [new file with mode: 0644]
locale/ro.ts
locale/ru.ts
locale/sk.ts
locale/sl.ts
locale/sq.ts
locale/sr.ts
locale/sv_SE.ts
locale/th.ts
locale/tr.ts
locale/uk.ts
locale/uk_UA.ts
locale/vi.ts
locale/zh_CN.ts
locale/zh_TW.ts
minitube.appdata.xml
minitube.pro
resources.qrc
src/aboutview.cpp
src/aboutview.h
src/appwidget.cpp
src/autocomplete.cpp
src/channelitemdelegate.cpp
src/channellistview.cpp
src/channellistview.h
src/channelview.cpp
src/channelview.h
src/clickablelabel.cpp
src/clickablelabel.h
src/constants.cpp
src/datautils.cpp
src/datautils.h
src/exlineedit.cpp [deleted file]
src/exlineedit.h [deleted file]
src/fontutils.cpp
src/gridwidget.cpp
src/gridwidget.h
src/homeview.cpp
src/http/README.md [deleted file]
src/http/http.pri [deleted file]
src/http/src/cachedhttp.cpp [deleted file]
src/http/src/cachedhttp.h [deleted file]
src/http/src/http.cpp [deleted file]
src/http/src/http.h [deleted file]
src/http/src/localcache.cpp [deleted file]
src/http/src/localcache.h [deleted file]
src/http/src/throttledhttp.cpp [deleted file]
src/http/src/throttledhttp.h [deleted file]
src/httputils.cpp
src/iconutils.cpp
src/iconutils.h
src/idle/idle.pri [deleted file]
src/idle/src/idle.h [deleted file]
src/idle/src/idle_linux.cpp [deleted file]
src/idle/src/idle_mac.cpp [deleted file]
src/idle/src/idle_win.cpp [deleted file]
src/jsfunctions.cpp
src/jsfunctions.h
src/loadingwidget.cpp
src/loadingwidget.h
src/main.cpp
src/mainwindow.cpp
src/mainwindow.h
src/mediaview.cpp
src/mediaview.h
src/painterutils.cpp
src/painterutils.h
src/playlistitemdelegate.cpp
src/playlistitemdelegate.h
src/playlistmodel.cpp
src/playlistmodel.h
src/playlistview.cpp
src/refinesearchbutton.cpp
src/refinesearchbutton.h
src/refinesearchwidget.cpp
src/regionsview.cpp
src/searchlineedit.cpp
src/searchlineedit.h
src/searchparams.h
src/searchview.cpp
src/searchview.h
src/searchwidget.h
src/seekslider.cpp
src/seekslider.h
src/segmentedcontrol.cpp
src/segmentedcontrol.h
src/sharetoolbar.cpp
src/sidebarheader.cpp
src/sidebarwidget.cpp
src/sidebarwidget.h
src/snapshotpreview.cpp
src/snapshotpreview.h
src/spacer.h
src/standardfeedsview.cpp
src/toolbarmenu.cpp
src/toolbarmenu.h
src/video.cpp
src/video.h
src/videoarea.cpp [new file with mode: 0644]
src/videoarea.h [new file with mode: 0644]
src/videoareawidget.cpp [deleted file]
src/videoareawidget.h [deleted file]
src/videodefinition.cpp
src/videodefinition.h
src/yt3.cpp
src/yt3.h
src/yt3listparser.cpp
src/ytchannel.cpp
src/ytregions.cpp
src/ytsinglevideosource.cpp
src/ytvideo.cpp
src/ytvideo.h
style.css

diff --git a/.clang-format b/.clang-format
deleted file mode 100644 (file)
index 2409f52..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: LLVM
-IndentWidth: 4
-AccessModifierOffset: -4
-ColumnLimit: 100
-AllowShortIfStatementsOnASingleLine: true
-AllowShortFunctionsOnASingleLine: Inline
-KeepEmptyLinesAtTheStartOfBlocks: false
-ContinuationIndentWidth: 8
-AlignAfterOpenBracket: true
-BinPackParameters: false
-AllowAllParametersOfDeclarationOnNextLine: false
diff --git a/.gitignore b/.gitignore
deleted file mode 100644 (file)
index 48266c8..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-build/
-Makefile*
-minitube.pro.user
-.settings/
-.DS_Store
-.cproject
-.project
-local/
-*.swp
-.tx
-android
-qtc_packaging
-debian
-
-
-*.stash
index d238fe0c7d5998c05f5b8aaff0511f2ebcb28291..1580dc61ffa10bed96c2d14856f8c7ff62599e60 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,44 +1,46 @@
+<p align="center">
+<img src="https://flavio.tordini.org/files/products/minitube.png">
+</p>
+
 # Minitube
 Minitube is a YouTube desktop application. It is written in C++ using the Qt framework. Contributing is welcome, especially in the Linux desktop integration area.
 
-## Translating Minitube to your language
-Translations are done at https://www.transifex.com/projects/p/minitube/
+## Translating to your language
+Translations are done at https://www.transifex.com/flaviotordini/minitube/
 Just register and apply for a language team. Please don't request translation merges on GitHub.
 
 ## Google API Key
 Google is now requiring an API key in order to access YouTube Data web services.
-Create a "Browser Key" at https://console.developers.google.com
+Create a "Browser Key" at https://console.developers.google.com and enable the Youtube Data API.
 
 The key must be specified at compile time as shown below.
 Alternatively Minitube can read an API key from the GOOGLE_API_KEY environment variable.
 
 ## Build instructions
-To compile Minitube you need at least Qt 5.0. The following Qt modules are needed: core, gui, widgets, network, sql (using the Sqlite plugin), declarative, dbus.
+Clone from Github:
+
+    git clone --recursive https://github.com/flaviotordini/minitube.git
+
+You need Qt >= 5.6 and MPV >= 0.29.0. The following Qt modules are needed: core, gui, widgets, network, sql (using the Sqlite plugin), declarative, dbus, x11extras.
 
 To be able to build on a Debian (or derivative) system:
 
-    $ sudo apt-get install build-essential qttools5-dev-tools qt5-qmake  qtdeclarative5-dev libphonon4qt5-dev libqt5sql5-sqlite qt5-default
+    sudo apt install build-essential qt5-default qttools5-dev-tools qt5-qmake qtdeclarative5-dev libqt5sql5-sqlite libqt5x11extras5-dev libmpv-dev
 
 Compiling:
 
-    $ qmake "DEFINES += APP_GOOGLE_API_KEY=YourAPIKeyHere"
-    $ make
-
-Beware of the Qt 4 version of qmake!
+    qmake "DEFINES += APP_GOOGLE_API_KEY=YourAPIKeyHere"
+    make
 
 Running:
 
-       $ build/target/minitube
-       
-Installing on Linux:
+    build/target/minitube
 
-    $ sudo make install
+Installing on Linux:
 
 This is for packagers. End users should not install applications in this way.
 
-## A word about Phonon on Linux
-To be able to actually watch videos you need a working Phonon setup.
-Please don't report bugs about this, ask for help on your distribution support channels.
+    sudo make install
 
 ## Legal Stuff
 Copyright (C) Flavio Tordini
index ec0b1219f340486cb0166f8764271a421faa9da8..ce7cd4a9e8700e245cae80621f14f636467c8adf 100644 (file)
--- a/empty.ts
+++ b/empty.ts
         <source>Translate %1 to your native language using %2</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation type="unfinished"></translation>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
         <translation type="unfinished">
-            <numerusform></numerusform>
+            <numerusform></numerusform><numerusform></numerusform>
         </translation>
     </message>
 </context>
         <source>Show Updated</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation type="unfinished"></translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation type="unfinished"></translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
         <translation type="unfinished">
-            <numerusform></numerusform>
+            <numerusform></numerusform><numerusform></numerusform>
         </translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
         <translation type="unfinished">
-            <numerusform></numerusform>
+            <numerusform></numerusform><numerusform></numerusform>
         </translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished">
-            <numerusform></numerusform>
+            <numerusform></numerusform><numerusform></numerusform>
         </translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished">
-            <numerusform></numerusform>
+            <numerusform></numerusform><numerusform></numerusform>
         </translation>
     </message>
 </context>
     <message numerus="yes">
         <source>%n Download(s)</source>
         <translation type="unfinished">
-            <numerusform></numerusform>
+            <numerusform></numerusform><numerusform></numerusform>
         </translation>
     </message>
 </context>
 </context>
 <context>
     <name>MainWindow</name>
+    <message>
+        <source>&amp;Window</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>&amp;Minimize</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <source>&amp;Stop</source>
         <translation type="unfinished"></translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation type="unfinished"></translation>
         <source>Remaining time: %1</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <source>Volume is muted</source>
         <translation type="unfinished"></translation>
         <source>Update</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <source>Toggle &amp;Menu Bar</source>
-        <translation type="unfinished"></translation>
-    </message>
     <message>
         <source>You can still access the menu bar by pressing the ALT key</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <source>&amp;Window</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <source>&amp;Minimize</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <source>Menu</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>Subscribe to %1</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"></translation>
         <source>Install Update</source>
         <translation type="unfinished"></translation>
     </message>
-    <message>
-        <source>Pick a video</source>
-        <translation type="unfinished"></translation>
-    </message>
 </context>
 <context>
     <name>PasteLineEdit</name>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
+        <source>Pick a video</source>
         <translation type="unfinished"></translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation type="unfinished"></translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation type="unfinished"></translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation type="unfinished"></translation>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <source>Enter</source>
+        <source>to start watching videos.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <source>to start watching videos.</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <source>Watch</source>
+        <source>Enter</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <source>&amp;Back</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"></translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation type="unfinished"></translation>
diff --git a/icons/dark/16/audio-volume-high.png b/icons/dark/16/audio-volume-high.png
new file mode 100644 (file)
index 0000000..26cd8cd
Binary files /dev/null and b/icons/dark/16/audio-volume-high.png differ
diff --git a/icons/dark/16/audio-volume-high@2x.png b/icons/dark/16/audio-volume-high@2x.png
new file mode 100644 (file)
index 0000000..1336f60
Binary files /dev/null and b/icons/dark/16/audio-volume-high@2x.png differ
diff --git a/icons/dark/16/audio-volume-muted.png b/icons/dark/16/audio-volume-muted.png
new file mode 100644 (file)
index 0000000..e15b3e2
Binary files /dev/null and b/icons/dark/16/audio-volume-muted.png differ
diff --git a/icons/dark/16/audio-volume-muted@2x.png b/icons/dark/16/audio-volume-muted@2x.png
new file mode 100644 (file)
index 0000000..e5d2436
Binary files /dev/null and b/icons/dark/16/audio-volume-muted@2x.png differ
diff --git a/icons/dark/16/bookmark-new.png b/icons/dark/16/bookmark-new.png
new file mode 100644 (file)
index 0000000..26faefd
Binary files /dev/null and b/icons/dark/16/bookmark-new.png differ
diff --git a/icons/dark/16/bookmark-new@2x.png b/icons/dark/16/bookmark-new@2x.png
new file mode 100644 (file)
index 0000000..7bc2637
Binary files /dev/null and b/icons/dark/16/bookmark-new@2x.png differ
diff --git a/icons/dark/16/bookmark-new_active.png b/icons/dark/16/bookmark-new_active.png
new file mode 100644 (file)
index 0000000..fa7214b
Binary files /dev/null and b/icons/dark/16/bookmark-new_active.png differ
diff --git a/icons/dark/16/bookmark-new_active@2x.png b/icons/dark/16/bookmark-new_active@2x.png
new file mode 100644 (file)
index 0000000..5283ece
Binary files /dev/null and b/icons/dark/16/bookmark-new_active@2x.png differ
diff --git a/icons/dark/16/bookmark-remove.png b/icons/dark/16/bookmark-remove.png
new file mode 100644 (file)
index 0000000..f98a670
Binary files /dev/null and b/icons/dark/16/bookmark-remove.png differ
diff --git a/icons/dark/16/bookmark-remove@2x.png b/icons/dark/16/bookmark-remove@2x.png
new file mode 100644 (file)
index 0000000..5650d28
Binary files /dev/null and b/icons/dark/16/bookmark-remove@2x.png differ
diff --git a/icons/dark/16/edit-find.png b/icons/dark/16/edit-find.png
new file mode 100644 (file)
index 0000000..84a5f7a
Binary files /dev/null and b/icons/dark/16/edit-find.png differ
diff --git a/icons/dark/16/edit-find@2x.png b/icons/dark/16/edit-find@2x.png
new file mode 100644 (file)
index 0000000..0300a6a
Binary files /dev/null and b/icons/dark/16/edit-find@2x.png differ
diff --git a/icons/dark/16/email.png b/icons/dark/16/email.png
new file mode 100644 (file)
index 0000000..90f9cb2
Binary files /dev/null and b/icons/dark/16/email.png differ
diff --git a/icons/dark/16/email@2x.png b/icons/dark/16/email@2x.png
new file mode 100644 (file)
index 0000000..d7520a4
Binary files /dev/null and b/icons/dark/16/email@2x.png differ
diff --git a/icons/dark/16/facebook.png b/icons/dark/16/facebook.png
new file mode 100644 (file)
index 0000000..6e70bdd
Binary files /dev/null and b/icons/dark/16/facebook.png differ
diff --git a/icons/dark/16/facebook@2x.png b/icons/dark/16/facebook@2x.png
new file mode 100644 (file)
index 0000000..8afb6bf
Binary files /dev/null and b/icons/dark/16/facebook@2x.png differ
diff --git a/icons/dark/16/go-next.png b/icons/dark/16/go-next.png
new file mode 100644 (file)
index 0000000..86786ee
Binary files /dev/null and b/icons/dark/16/go-next.png differ
diff --git a/icons/dark/16/go-next@2x.png b/icons/dark/16/go-next@2x.png
new file mode 100644 (file)
index 0000000..cf3f592
Binary files /dev/null and b/icons/dark/16/go-next@2x.png differ
diff --git a/icons/dark/16/go-previous.png b/icons/dark/16/go-previous.png
new file mode 100644 (file)
index 0000000..f582862
Binary files /dev/null and b/icons/dark/16/go-previous.png differ
diff --git a/icons/dark/16/go-previous@2x.png b/icons/dark/16/go-previous@2x.png
new file mode 100644 (file)
index 0000000..85523c8
Binary files /dev/null and b/icons/dark/16/go-previous@2x.png differ
diff --git a/icons/dark/16/link.png b/icons/dark/16/link.png
new file mode 100644 (file)
index 0000000..a677e34
Binary files /dev/null and b/icons/dark/16/link.png differ
diff --git a/icons/dark/16/link@2x.png b/icons/dark/16/link@2x.png
new file mode 100644 (file)
index 0000000..e32d826
Binary files /dev/null and b/icons/dark/16/link@2x.png differ
diff --git a/icons/dark/16/mark-watched.png b/icons/dark/16/mark-watched.png
new file mode 100644 (file)
index 0000000..216f855
Binary files /dev/null and b/icons/dark/16/mark-watched.png differ
diff --git a/icons/dark/16/mark-watched@2x.png b/icons/dark/16/mark-watched@2x.png
new file mode 100644 (file)
index 0000000..9b38fe2
Binary files /dev/null and b/icons/dark/16/mark-watched@2x.png differ
diff --git a/icons/dark/16/media-playback-start.png b/icons/dark/16/media-playback-start.png
new file mode 100644 (file)
index 0000000..0d1dd2a
Binary files /dev/null and b/icons/dark/16/media-playback-start.png differ
diff --git a/icons/dark/16/media-playback-start@2x.png b/icons/dark/16/media-playback-start@2x.png
new file mode 100644 (file)
index 0000000..5a8ee8e
Binary files /dev/null and b/icons/dark/16/media-playback-start@2x.png differ
diff --git a/icons/dark/16/media-playback-start_checked.png b/icons/dark/16/media-playback-start_checked.png
new file mode 100644 (file)
index 0000000..0d1dd2a
Binary files /dev/null and b/icons/dark/16/media-playback-start_checked.png differ
diff --git a/icons/dark/16/media-playback-start_checked@2x.png b/icons/dark/16/media-playback-start_checked@2x.png
new file mode 100644 (file)
index 0000000..5a8ee8e
Binary files /dev/null and b/icons/dark/16/media-playback-start_checked@2x.png differ
diff --git a/icons/dark/16/safesearch.png b/icons/dark/16/safesearch.png
new file mode 100644 (file)
index 0000000..a359701
Binary files /dev/null and b/icons/dark/16/safesearch.png differ
diff --git a/icons/dark/16/safesearch@2x.png b/icons/dark/16/safesearch@2x.png
new file mode 100644 (file)
index 0000000..0e2257f
Binary files /dev/null and b/icons/dark/16/safesearch@2x.png differ
diff --git a/icons/dark/16/search-duration.png b/icons/dark/16/search-duration.png
new file mode 100644 (file)
index 0000000..9ec10d2
Binary files /dev/null and b/icons/dark/16/search-duration.png differ
diff --git a/icons/dark/16/search-duration@2x.png b/icons/dark/16/search-duration@2x.png
new file mode 100644 (file)
index 0000000..21cb874
Binary files /dev/null and b/icons/dark/16/search-duration@2x.png differ
diff --git a/icons/dark/16/search-quality.png b/icons/dark/16/search-quality.png
new file mode 100644 (file)
index 0000000..a602e45
Binary files /dev/null and b/icons/dark/16/search-quality.png differ
diff --git a/icons/dark/16/search-quality@2x.png b/icons/dark/16/search-quality@2x.png
new file mode 100644 (file)
index 0000000..db43613
Binary files /dev/null and b/icons/dark/16/search-quality@2x.png differ
diff --git a/icons/dark/16/search-sortBy.png b/icons/dark/16/search-sortBy.png
new file mode 100644 (file)
index 0000000..55f6c58
Binary files /dev/null and b/icons/dark/16/search-sortBy.png differ
diff --git a/icons/dark/16/search-sortBy@2x.png b/icons/dark/16/search-sortBy@2x.png
new file mode 100644 (file)
index 0000000..7e6acdd
Binary files /dev/null and b/icons/dark/16/search-sortBy@2x.png differ
diff --git a/icons/dark/16/search-time.png b/icons/dark/16/search-time.png
new file mode 100644 (file)
index 0000000..b1cd0c3
Binary files /dev/null and b/icons/dark/16/search-time.png differ
diff --git a/icons/dark/16/search-time@2x.png b/icons/dark/16/search-time@2x.png
new file mode 100644 (file)
index 0000000..6b3f928
Binary files /dev/null and b/icons/dark/16/search-time@2x.png differ
diff --git a/icons/dark/16/show-updated.png b/icons/dark/16/show-updated.png
new file mode 100644 (file)
index 0000000..f9b36c3
Binary files /dev/null and b/icons/dark/16/show-updated.png differ
diff --git a/icons/dark/16/show-updated@2x.png b/icons/dark/16/show-updated@2x.png
new file mode 100644 (file)
index 0000000..c793b96
Binary files /dev/null and b/icons/dark/16/show-updated@2x.png differ
diff --git a/icons/dark/16/sort.png b/icons/dark/16/sort.png
new file mode 100644 (file)
index 0000000..3b47e71
Binary files /dev/null and b/icons/dark/16/sort.png differ
diff --git a/icons/dark/16/sort@2x.png b/icons/dark/16/sort@2x.png
new file mode 100644 (file)
index 0000000..0152e7b
Binary files /dev/null and b/icons/dark/16/sort@2x.png differ
diff --git a/icons/dark/16/twitter.png b/icons/dark/16/twitter.png
new file mode 100644 (file)
index 0000000..f789e61
Binary files /dev/null and b/icons/dark/16/twitter.png differ
diff --git a/icons/dark/16/twitter@2x.png b/icons/dark/16/twitter@2x.png
new file mode 100644 (file)
index 0000000..8267aae
Binary files /dev/null and b/icons/dark/16/twitter@2x.png differ
diff --git a/icons/dark/16/unwatched.png b/icons/dark/16/unwatched.png
new file mode 100644 (file)
index 0000000..6195ece
Binary files /dev/null and b/icons/dark/16/unwatched.png differ
diff --git a/icons/dark/16/unwatched@2x.png b/icons/dark/16/unwatched@2x.png
new file mode 100644 (file)
index 0000000..7768f24
Binary files /dev/null and b/icons/dark/16/unwatched@2x.png differ
diff --git a/icons/dark/16/video-display.png b/icons/dark/16/video-display.png
new file mode 100644 (file)
index 0000000..ea291f2
Binary files /dev/null and b/icons/dark/16/video-display.png differ
diff --git a/icons/dark/16/video-display@2x.png b/icons/dark/16/video-display@2x.png
new file mode 100644 (file)
index 0000000..2b6dd36
Binary files /dev/null and b/icons/dark/16/video-display@2x.png differ
diff --git a/icons/dark/16/worldwide.png b/icons/dark/16/worldwide.png
new file mode 100644 (file)
index 0000000..cdd59e6
Binary files /dev/null and b/icons/dark/16/worldwide.png differ
diff --git a/icons/dark/16/worldwide@2x.png b/icons/dark/16/worldwide@2x.png
new file mode 100644 (file)
index 0000000..e8b632f
Binary files /dev/null and b/icons/dark/16/worldwide@2x.png differ
diff --git a/icons/dark/24/edit-find.png b/icons/dark/24/edit-find.png
new file mode 100644 (file)
index 0000000..ac08a42
Binary files /dev/null and b/icons/dark/24/edit-find.png differ
diff --git a/icons/dark/24/edit-find@2x.png b/icons/dark/24/edit-find@2x.png
new file mode 100644 (file)
index 0000000..8bb8bfd
Binary files /dev/null and b/icons/dark/24/edit-find@2x.png differ
diff --git a/icons/dark/24/refine-search.png b/icons/dark/24/refine-search.png
new file mode 100644 (file)
index 0000000..ac08a42
Binary files /dev/null and b/icons/dark/24/refine-search.png differ
diff --git a/icons/dark/24/refine-search@2x.png b/icons/dark/24/refine-search@2x.png
new file mode 100644 (file)
index 0000000..8bb8bfd
Binary files /dev/null and b/icons/dark/24/refine-search@2x.png differ
diff --git a/icons/dark/32/content-loading.png b/icons/dark/32/content-loading.png
new file mode 100644 (file)
index 0000000..56d9991
Binary files /dev/null and b/icons/dark/32/content-loading.png differ
diff --git a/icons/dark/32/content-loading@2x.png b/icons/dark/32/content-loading@2x.png
new file mode 100644 (file)
index 0000000..cc80147
Binary files /dev/null and b/icons/dark/32/content-loading@2x.png differ
diff --git a/icons/dark/32/document-save.png b/icons/dark/32/document-save.png
new file mode 100644 (file)
index 0000000..0af10d8
Binary files /dev/null and b/icons/dark/32/document-save.png differ
diff --git a/icons/dark/32/document-save@2x.png b/icons/dark/32/document-save@2x.png
new file mode 100644 (file)
index 0000000..35054ee
Binary files /dev/null and b/icons/dark/32/document-save@2x.png differ
diff --git a/icons/dark/32/media-playback-pause.png b/icons/dark/32/media-playback-pause.png
new file mode 100644 (file)
index 0000000..133fab7
Binary files /dev/null and b/icons/dark/32/media-playback-pause.png differ
diff --git a/icons/dark/32/media-playback-pause@2x.png b/icons/dark/32/media-playback-pause@2x.png
new file mode 100644 (file)
index 0000000..91af18a
Binary files /dev/null and b/icons/dark/32/media-playback-pause@2x.png differ
diff --git a/icons/dark/32/media-playback-start.png b/icons/dark/32/media-playback-start.png
new file mode 100644 (file)
index 0000000..70c3c6e
Binary files /dev/null and b/icons/dark/32/media-playback-start.png differ
diff --git a/icons/dark/32/media-playback-start@2x.png b/icons/dark/32/media-playback-start@2x.png
new file mode 100644 (file)
index 0000000..32f1318
Binary files /dev/null and b/icons/dark/32/media-playback-start@2x.png differ
diff --git a/icons/dark/32/media-playback-start_checked.png b/icons/dark/32/media-playback-start_checked.png
new file mode 100644 (file)
index 0000000..4b7e59e
Binary files /dev/null and b/icons/dark/32/media-playback-start_checked.png differ
diff --git a/icons/dark/32/media-playback-start_checked@2x.png b/icons/dark/32/media-playback-start_checked@2x.png
new file mode 100644 (file)
index 0000000..55f6afb
Binary files /dev/null and b/icons/dark/32/media-playback-start_checked@2x.png differ
diff --git a/icons/dark/32/media-playback-stop.png b/icons/dark/32/media-playback-stop.png
new file mode 100644 (file)
index 0000000..b86d233
Binary files /dev/null and b/icons/dark/32/media-playback-stop.png differ
diff --git a/icons/dark/32/media-playback-stop@2x.png b/icons/dark/32/media-playback-stop@2x.png
new file mode 100644 (file)
index 0000000..28de286
Binary files /dev/null and b/icons/dark/32/media-playback-stop@2x.png differ
diff --git a/icons/dark/32/media-skip-forward.png b/icons/dark/32/media-skip-forward.png
new file mode 100644 (file)
index 0000000..bfb0dc6
Binary files /dev/null and b/icons/dark/32/media-skip-forward.png differ
diff --git a/icons/dark/32/media-skip-forward@2x.png b/icons/dark/32/media-skip-forward@2x.png
new file mode 100644 (file)
index 0000000..982da70
Binary files /dev/null and b/icons/dark/32/media-skip-forward@2x.png differ
diff --git a/icons/dark/32/open-menu.png b/icons/dark/32/open-menu.png
new file mode 100644 (file)
index 0000000..917da50
Binary files /dev/null and b/icons/dark/32/open-menu.png differ
diff --git a/icons/dark/32/open-menu@2x.png b/icons/dark/32/open-menu@2x.png
new file mode 100644 (file)
index 0000000..7a95cba
Binary files /dev/null and b/icons/dark/32/open-menu@2x.png differ
diff --git a/icons/dark/32/view-fullscreen.png b/icons/dark/32/view-fullscreen.png
new file mode 100644 (file)
index 0000000..87864c9
Binary files /dev/null and b/icons/dark/32/view-fullscreen.png differ
diff --git a/icons/dark/32/view-fullscreen@2x.png b/icons/dark/32/view-fullscreen@2x.png
new file mode 100644 (file)
index 0000000..48f63ac
Binary files /dev/null and b/icons/dark/32/view-fullscreen@2x.png differ
diff --git a/icons/dark/32/view-list.png b/icons/dark/32/view-list.png
new file mode 100644 (file)
index 0000000..9508f98
Binary files /dev/null and b/icons/dark/32/view-list.png differ
diff --git a/icons/dark/32/view-list@2x.png b/icons/dark/32/view-list@2x.png
new file mode 100644 (file)
index 0000000..93f8421
Binary files /dev/null and b/icons/dark/32/view-list@2x.png differ
diff --git a/icons/dark/32/view-restore.png b/icons/dark/32/view-restore.png
new file mode 100644 (file)
index 0000000..dcde5ee
Binary files /dev/null and b/icons/dark/32/view-restore.png differ
diff --git a/icons/dark/32/view-restore@2x.png b/icons/dark/32/view-restore@2x.png
new file mode 100644 (file)
index 0000000..3ad5590
Binary files /dev/null and b/icons/dark/32/view-restore@2x.png differ
diff --git a/icons/dark/88/channels.png b/icons/dark/88/channels.png
new file mode 100644 (file)
index 0000000..f472d3a
Binary files /dev/null and b/icons/dark/88/channels.png differ
diff --git a/icons/dark/88/channels@2x.png b/icons/dark/88/channels@2x.png
new file mode 100644 (file)
index 0000000..8172783
Binary files /dev/null and b/icons/dark/88/channels@2x.png differ
diff --git a/icons/dark/88/unwatched.png b/icons/dark/88/unwatched.png
new file mode 100644 (file)
index 0000000..f24b750
Binary files /dev/null and b/icons/dark/88/unwatched.png differ
diff --git a/icons/dark/88/unwatched@2x.png b/icons/dark/88/unwatched@2x.png
new file mode 100644 (file)
index 0000000..917bd2b
Binary files /dev/null and b/icons/dark/88/unwatched@2x.png differ
diff --git a/icons/light/16/audio-volume-high.png b/icons/light/16/audio-volume-high.png
new file mode 100644 (file)
index 0000000..96f746e
Binary files /dev/null and b/icons/light/16/audio-volume-high.png differ
diff --git a/icons/light/16/audio-volume-high@2x.png b/icons/light/16/audio-volume-high@2x.png
new file mode 100644 (file)
index 0000000..3893e4d
Binary files /dev/null and b/icons/light/16/audio-volume-high@2x.png differ
diff --git a/icons/light/16/audio-volume-muted.png b/icons/light/16/audio-volume-muted.png
new file mode 100644 (file)
index 0000000..37be31d
Binary files /dev/null and b/icons/light/16/audio-volume-muted.png differ
diff --git a/icons/light/16/audio-volume-muted@2x.png b/icons/light/16/audio-volume-muted@2x.png
new file mode 100644 (file)
index 0000000..ebe59d2
Binary files /dev/null and b/icons/light/16/audio-volume-muted@2x.png differ
diff --git a/icons/light/16/bookmark-new.png b/icons/light/16/bookmark-new.png
new file mode 100644 (file)
index 0000000..ed1d093
Binary files /dev/null and b/icons/light/16/bookmark-new.png differ
diff --git a/icons/light/16/bookmark-new@2x.png b/icons/light/16/bookmark-new@2x.png
new file mode 100644 (file)
index 0000000..006432b
Binary files /dev/null and b/icons/light/16/bookmark-new@2x.png differ
diff --git a/icons/light/16/bookmark-new_active.png b/icons/light/16/bookmark-new_active.png
new file mode 100644 (file)
index 0000000..cdf415d
Binary files /dev/null and b/icons/light/16/bookmark-new_active.png differ
diff --git a/icons/light/16/bookmark-new_active@2x.png b/icons/light/16/bookmark-new_active@2x.png
new file mode 100644 (file)
index 0000000..9d924a1
Binary files /dev/null and b/icons/light/16/bookmark-new_active@2x.png differ
diff --git a/icons/light/16/bookmark-remove.png b/icons/light/16/bookmark-remove.png
new file mode 100644 (file)
index 0000000..906a7c1
Binary files /dev/null and b/icons/light/16/bookmark-remove.png differ
diff --git a/icons/light/16/bookmark-remove@2x.png b/icons/light/16/bookmark-remove@2x.png
new file mode 100644 (file)
index 0000000..afbf07f
Binary files /dev/null and b/icons/light/16/bookmark-remove@2x.png differ
diff --git a/icons/light/16/edit-find.png b/icons/light/16/edit-find.png
new file mode 100644 (file)
index 0000000..128a933
Binary files /dev/null and b/icons/light/16/edit-find.png differ
diff --git a/icons/light/16/edit-find@2x.png b/icons/light/16/edit-find@2x.png
new file mode 100644 (file)
index 0000000..96e6a39
Binary files /dev/null and b/icons/light/16/edit-find@2x.png differ
diff --git a/icons/light/16/email.png b/icons/light/16/email.png
new file mode 100644 (file)
index 0000000..005123f
Binary files /dev/null and b/icons/light/16/email.png differ
diff --git a/icons/light/16/email@2x.png b/icons/light/16/email@2x.png
new file mode 100644 (file)
index 0000000..5f7d495
Binary files /dev/null and b/icons/light/16/email@2x.png differ
diff --git a/icons/light/16/facebook.png b/icons/light/16/facebook.png
new file mode 100644 (file)
index 0000000..7df7fc6
Binary files /dev/null and b/icons/light/16/facebook.png differ
diff --git a/icons/light/16/facebook@2x.png b/icons/light/16/facebook@2x.png
new file mode 100644 (file)
index 0000000..3d51f50
Binary files /dev/null and b/icons/light/16/facebook@2x.png differ
diff --git a/icons/light/16/go-next.png b/icons/light/16/go-next.png
new file mode 100644 (file)
index 0000000..eb900d8
Binary files /dev/null and b/icons/light/16/go-next.png differ
diff --git a/icons/light/16/go-next@2x.png b/icons/light/16/go-next@2x.png
new file mode 100644 (file)
index 0000000..5d5f123
Binary files /dev/null and b/icons/light/16/go-next@2x.png differ
diff --git a/icons/light/16/go-previous.png b/icons/light/16/go-previous.png
new file mode 100644 (file)
index 0000000..7922ae8
Binary files /dev/null and b/icons/light/16/go-previous.png differ
diff --git a/icons/light/16/go-previous@2x.png b/icons/light/16/go-previous@2x.png
new file mode 100644 (file)
index 0000000..78e528b
Binary files /dev/null and b/icons/light/16/go-previous@2x.png differ
diff --git a/icons/light/16/link.png b/icons/light/16/link.png
new file mode 100644 (file)
index 0000000..239ca25
Binary files /dev/null and b/icons/light/16/link.png differ
diff --git a/icons/light/16/link@2x.png b/icons/light/16/link@2x.png
new file mode 100644 (file)
index 0000000..148c5c3
Binary files /dev/null and b/icons/light/16/link@2x.png differ
diff --git a/icons/light/16/mark-watched.png b/icons/light/16/mark-watched.png
new file mode 100644 (file)
index 0000000..4cb43df
Binary files /dev/null and b/icons/light/16/mark-watched.png differ
diff --git a/icons/light/16/mark-watched@2x.png b/icons/light/16/mark-watched@2x.png
new file mode 100644 (file)
index 0000000..5d232ac
Binary files /dev/null and b/icons/light/16/mark-watched@2x.png differ
diff --git a/icons/light/16/media-playback-start.png b/icons/light/16/media-playback-start.png
new file mode 100644 (file)
index 0000000..0671d08
Binary files /dev/null and b/icons/light/16/media-playback-start.png differ
diff --git a/icons/light/16/media-playback-start@2x.png b/icons/light/16/media-playback-start@2x.png
new file mode 100644 (file)
index 0000000..3877ef1
Binary files /dev/null and b/icons/light/16/media-playback-start@2x.png differ
diff --git a/icons/light/16/media-playback-start_checked.png b/icons/light/16/media-playback-start_checked.png
new file mode 100644 (file)
index 0000000..0671d08
Binary files /dev/null and b/icons/light/16/media-playback-start_checked.png differ
diff --git a/icons/light/16/media-playback-start_checked@2x.png b/icons/light/16/media-playback-start_checked@2x.png
new file mode 100644 (file)
index 0000000..3877ef1
Binary files /dev/null and b/icons/light/16/media-playback-start_checked@2x.png differ
diff --git a/icons/light/16/safesearch.png b/icons/light/16/safesearch.png
new file mode 100644 (file)
index 0000000..8af718d
Binary files /dev/null and b/icons/light/16/safesearch.png differ
diff --git a/icons/light/16/safesearch@2x.png b/icons/light/16/safesearch@2x.png
new file mode 100644 (file)
index 0000000..b77f576
Binary files /dev/null and b/icons/light/16/safesearch@2x.png differ
diff --git a/icons/light/16/search-duration.png b/icons/light/16/search-duration.png
new file mode 100644 (file)
index 0000000..16ae94c
Binary files /dev/null and b/icons/light/16/search-duration.png differ
diff --git a/icons/light/16/search-duration@2x.png b/icons/light/16/search-duration@2x.png
new file mode 100644 (file)
index 0000000..cc38ef6
Binary files /dev/null and b/icons/light/16/search-duration@2x.png differ
diff --git a/icons/light/16/search-quality.png b/icons/light/16/search-quality.png
new file mode 100644 (file)
index 0000000..50cc7b4
Binary files /dev/null and b/icons/light/16/search-quality.png differ
diff --git a/icons/light/16/search-quality@2x.png b/icons/light/16/search-quality@2x.png
new file mode 100644 (file)
index 0000000..d4127f3
Binary files /dev/null and b/icons/light/16/search-quality@2x.png differ
diff --git a/icons/light/16/search-sortBy.png b/icons/light/16/search-sortBy.png
new file mode 100644 (file)
index 0000000..8fb8356
Binary files /dev/null and b/icons/light/16/search-sortBy.png differ
diff --git a/icons/light/16/search-sortBy@2x.png b/icons/light/16/search-sortBy@2x.png
new file mode 100644 (file)
index 0000000..7e42d90
Binary files /dev/null and b/icons/light/16/search-sortBy@2x.png differ
diff --git a/icons/light/16/search-time.png b/icons/light/16/search-time.png
new file mode 100644 (file)
index 0000000..a7aaa62
Binary files /dev/null and b/icons/light/16/search-time.png differ
diff --git a/icons/light/16/search-time@2x.png b/icons/light/16/search-time@2x.png
new file mode 100644 (file)
index 0000000..3ba32ee
Binary files /dev/null and b/icons/light/16/search-time@2x.png differ
diff --git a/icons/light/16/show-updated.png b/icons/light/16/show-updated.png
new file mode 100644 (file)
index 0000000..8b8fb39
Binary files /dev/null and b/icons/light/16/show-updated.png differ
diff --git a/icons/light/16/show-updated@2x.png b/icons/light/16/show-updated@2x.png
new file mode 100644 (file)
index 0000000..c33adae
Binary files /dev/null and b/icons/light/16/show-updated@2x.png differ
diff --git a/icons/light/16/sort.png b/icons/light/16/sort.png
new file mode 100644 (file)
index 0000000..1c22954
Binary files /dev/null and b/icons/light/16/sort.png differ
diff --git a/icons/light/16/sort@2x.png b/icons/light/16/sort@2x.png
new file mode 100644 (file)
index 0000000..8f84953
Binary files /dev/null and b/icons/light/16/sort@2x.png differ
diff --git a/icons/light/16/twitter.png b/icons/light/16/twitter.png
new file mode 100644 (file)
index 0000000..2d597d9
Binary files /dev/null and b/icons/light/16/twitter.png differ
diff --git a/icons/light/16/twitter@2x.png b/icons/light/16/twitter@2x.png
new file mode 100644 (file)
index 0000000..6a74c2a
Binary files /dev/null and b/icons/light/16/twitter@2x.png differ
diff --git a/icons/light/16/unwatched.png b/icons/light/16/unwatched.png
new file mode 100644 (file)
index 0000000..e3fe022
Binary files /dev/null and b/icons/light/16/unwatched.png differ
diff --git a/icons/light/16/unwatched@2x.png b/icons/light/16/unwatched@2x.png
new file mode 100644 (file)
index 0000000..daaea3f
Binary files /dev/null and b/icons/light/16/unwatched@2x.png differ
diff --git a/icons/light/16/video-display.png b/icons/light/16/video-display.png
new file mode 100644 (file)
index 0000000..f5021bd
Binary files /dev/null and b/icons/light/16/video-display.png differ
diff --git a/icons/light/16/video-display@2x.png b/icons/light/16/video-display@2x.png
new file mode 100644 (file)
index 0000000..6daa132
Binary files /dev/null and b/icons/light/16/video-display@2x.png differ
diff --git a/icons/light/16/worldwide.png b/icons/light/16/worldwide.png
new file mode 100644 (file)
index 0000000..97218d0
Binary files /dev/null and b/icons/light/16/worldwide.png differ
diff --git a/icons/light/16/worldwide@2x.png b/icons/light/16/worldwide@2x.png
new file mode 100644 (file)
index 0000000..41d22d8
Binary files /dev/null and b/icons/light/16/worldwide@2x.png differ
diff --git a/icons/light/24/edit-find.png b/icons/light/24/edit-find.png
new file mode 100644 (file)
index 0000000..be603af
Binary files /dev/null and b/icons/light/24/edit-find.png differ
diff --git a/icons/light/24/edit-find@2x.png b/icons/light/24/edit-find@2x.png
new file mode 100644 (file)
index 0000000..bdf4071
Binary files /dev/null and b/icons/light/24/edit-find@2x.png differ
diff --git a/icons/light/24/refine-search.png b/icons/light/24/refine-search.png
new file mode 100644 (file)
index 0000000..be603af
Binary files /dev/null and b/icons/light/24/refine-search.png differ
diff --git a/icons/light/24/refine-search@2x.png b/icons/light/24/refine-search@2x.png
new file mode 100644 (file)
index 0000000..bdf4071
Binary files /dev/null and b/icons/light/24/refine-search@2x.png differ
diff --git a/icons/light/32/content-loading.png b/icons/light/32/content-loading.png
new file mode 100644 (file)
index 0000000..d7a96d9
Binary files /dev/null and b/icons/light/32/content-loading.png differ
diff --git a/icons/light/32/content-loading@2x.png b/icons/light/32/content-loading@2x.png
new file mode 100644 (file)
index 0000000..4b41759
Binary files /dev/null and b/icons/light/32/content-loading@2x.png differ
diff --git a/icons/light/32/document-save.png b/icons/light/32/document-save.png
new file mode 100644 (file)
index 0000000..222b2de
Binary files /dev/null and b/icons/light/32/document-save.png differ
diff --git a/icons/light/32/document-save@2x.png b/icons/light/32/document-save@2x.png
new file mode 100644 (file)
index 0000000..88f62c3
Binary files /dev/null and b/icons/light/32/document-save@2x.png differ
diff --git a/icons/light/32/media-playback-pause.png b/icons/light/32/media-playback-pause.png
new file mode 100644 (file)
index 0000000..5aad9d5
Binary files /dev/null and b/icons/light/32/media-playback-pause.png differ
diff --git a/icons/light/32/media-playback-pause@2x.png b/icons/light/32/media-playback-pause@2x.png
new file mode 100644 (file)
index 0000000..b868bc0
Binary files /dev/null and b/icons/light/32/media-playback-pause@2x.png differ
diff --git a/icons/light/32/media-playback-start.png b/icons/light/32/media-playback-start.png
new file mode 100644 (file)
index 0000000..122b804
Binary files /dev/null and b/icons/light/32/media-playback-start.png differ
diff --git a/icons/light/32/media-playback-start@2x.png b/icons/light/32/media-playback-start@2x.png
new file mode 100644 (file)
index 0000000..88b679a
Binary files /dev/null and b/icons/light/32/media-playback-start@2x.png differ
diff --git a/icons/light/32/media-playback-start_checked.png b/icons/light/32/media-playback-start_checked.png
new file mode 100644 (file)
index 0000000..d799121
Binary files /dev/null and b/icons/light/32/media-playback-start_checked.png differ
diff --git a/icons/light/32/media-playback-start_checked@2x.png b/icons/light/32/media-playback-start_checked@2x.png
new file mode 100644 (file)
index 0000000..6fb34cd
Binary files /dev/null and b/icons/light/32/media-playback-start_checked@2x.png differ
diff --git a/icons/light/32/media-playback-stop.png b/icons/light/32/media-playback-stop.png
new file mode 100644 (file)
index 0000000..421075a
Binary files /dev/null and b/icons/light/32/media-playback-stop.png differ
diff --git a/icons/light/32/media-playback-stop@2x.png b/icons/light/32/media-playback-stop@2x.png
new file mode 100644 (file)
index 0000000..d944741
Binary files /dev/null and b/icons/light/32/media-playback-stop@2x.png differ
diff --git a/icons/light/32/media-skip-forward.png b/icons/light/32/media-skip-forward.png
new file mode 100644 (file)
index 0000000..6283be9
Binary files /dev/null and b/icons/light/32/media-skip-forward.png differ
diff --git a/icons/light/32/media-skip-forward@2x.png b/icons/light/32/media-skip-forward@2x.png
new file mode 100644 (file)
index 0000000..e79b0bf
Binary files /dev/null and b/icons/light/32/media-skip-forward@2x.png differ
diff --git a/icons/light/32/open-menu.png b/icons/light/32/open-menu.png
new file mode 100644 (file)
index 0000000..981d103
Binary files /dev/null and b/icons/light/32/open-menu.png differ
diff --git a/icons/light/32/open-menu@2x.png b/icons/light/32/open-menu@2x.png
new file mode 100644 (file)
index 0000000..6aa8b0d
Binary files /dev/null and b/icons/light/32/open-menu@2x.png differ
diff --git a/icons/light/32/view-fullscreen.png b/icons/light/32/view-fullscreen.png
new file mode 100644 (file)
index 0000000..2725ed2
Binary files /dev/null and b/icons/light/32/view-fullscreen.png differ
diff --git a/icons/light/32/view-fullscreen@2x.png b/icons/light/32/view-fullscreen@2x.png
new file mode 100644 (file)
index 0000000..ce103b6
Binary files /dev/null and b/icons/light/32/view-fullscreen@2x.png differ
diff --git a/icons/light/32/view-list.png b/icons/light/32/view-list.png
new file mode 100644 (file)
index 0000000..0587ecf
Binary files /dev/null and b/icons/light/32/view-list.png differ
diff --git a/icons/light/32/view-list@2x.png b/icons/light/32/view-list@2x.png
new file mode 100644 (file)
index 0000000..8cdc9f2
Binary files /dev/null and b/icons/light/32/view-list@2x.png differ
diff --git a/icons/light/32/view-restore.png b/icons/light/32/view-restore.png
new file mode 100644 (file)
index 0000000..2a9d4aa
Binary files /dev/null and b/icons/light/32/view-restore.png differ
diff --git a/icons/light/32/view-restore@2x.png b/icons/light/32/view-restore@2x.png
new file mode 100644 (file)
index 0000000..c93a128
Binary files /dev/null and b/icons/light/32/view-restore@2x.png differ
diff --git a/icons/light/88/channels.png b/icons/light/88/channels.png
new file mode 100644 (file)
index 0000000..27fbc14
Binary files /dev/null and b/icons/light/88/channels.png differ
diff --git a/icons/light/88/channels@2x.png b/icons/light/88/channels@2x.png
new file mode 100644 (file)
index 0000000..c83867d
Binary files /dev/null and b/icons/light/88/channels@2x.png differ
diff --git a/icons/light/88/unwatched.png b/icons/light/88/unwatched.png
new file mode 100644 (file)
index 0000000..abda7b2
Binary files /dev/null and b/icons/light/88/unwatched.png differ
diff --git a/icons/light/88/unwatched@2x.png b/icons/light/88/unwatched@2x.png
new file mode 100644 (file)
index 0000000..b04db9b
Binary files /dev/null and b/icons/light/88/unwatched@2x.png differ
diff --git a/images/audio-volume-high.png b/images/audio-volume-high.png
deleted file mode 100644 (file)
index dc0141a..0000000
Binary files a/images/audio-volume-high.png and /dev/null differ
diff --git a/images/audio-volume-high@2x.png b/images/audio-volume-high@2x.png
deleted file mode 100644 (file)
index a4bc0de..0000000
Binary files a/images/audio-volume-high@2x.png and /dev/null differ
diff --git a/images/audio-volume-muted.png b/images/audio-volume-muted.png
deleted file mode 100644 (file)
index e2475ac..0000000
Binary files a/images/audio-volume-muted.png and /dev/null differ
diff --git a/images/audio-volume-muted@2x.png b/images/audio-volume-muted@2x.png
deleted file mode 100644 (file)
index 0f8fb55..0000000
Binary files a/images/audio-volume-muted@2x.png and /dev/null differ
diff --git a/images/bookmark-new.png b/images/bookmark-new.png
deleted file mode 100644 (file)
index 796499b..0000000
Binary files a/images/bookmark-new.png and /dev/null differ
diff --git a/images/bookmark-new@2x.png b/images/bookmark-new@2x.png
deleted file mode 100644 (file)
index c7fc497..0000000
Binary files a/images/bookmark-new@2x.png and /dev/null differ
diff --git a/images/bookmark-new_active.png b/images/bookmark-new_active.png
deleted file mode 100644 (file)
index ea26fa5..0000000
Binary files a/images/bookmark-new_active.png and /dev/null differ
diff --git a/images/bookmark-new_active@2x.png b/images/bookmark-new_active@2x.png
deleted file mode 100644 (file)
index 15781af..0000000
Binary files a/images/bookmark-new_active@2x.png and /dev/null differ
diff --git a/images/bookmark-remove.png b/images/bookmark-remove.png
deleted file mode 100644 (file)
index 48d46a5..0000000
Binary files a/images/bookmark-remove.png and /dev/null differ
diff --git a/images/bookmark-remove@2x.png b/images/bookmark-remove@2x.png
deleted file mode 100644 (file)
index 8c8d7e9..0000000
Binary files a/images/bookmark-remove@2x.png and /dev/null differ
diff --git a/images/channels.png b/images/channels.png
deleted file mode 100644 (file)
index 109043d..0000000
Binary files a/images/channels.png and /dev/null differ
diff --git a/images/channels@2x.png b/images/channels@2x.png
deleted file mode 100644 (file)
index abba86b..0000000
Binary files a/images/channels@2x.png and /dev/null differ
diff --git a/images/content-loading.png b/images/content-loading.png
deleted file mode 100644 (file)
index e88c4f0..0000000
Binary files a/images/content-loading.png and /dev/null differ
diff --git a/images/content-loading@2x.png b/images/content-loading@2x.png
deleted file mode 100644 (file)
index d68c67b..0000000
Binary files a/images/content-loading@2x.png and /dev/null differ
diff --git a/images/document-save.png b/images/document-save.png
deleted file mode 100644 (file)
index 4ac54e0..0000000
Binary files a/images/document-save.png and /dev/null differ
diff --git a/images/document-save@2x.png b/images/document-save@2x.png
deleted file mode 100644 (file)
index 360b8f0..0000000
Binary files a/images/document-save@2x.png and /dev/null differ
diff --git a/images/edit-clear.png b/images/edit-clear.png
deleted file mode 100644 (file)
index 42901d8..0000000
Binary files a/images/edit-clear.png and /dev/null differ
diff --git a/images/edit-find.png b/images/edit-find.png
deleted file mode 100644 (file)
index aabed51..0000000
Binary files a/images/edit-find.png and /dev/null differ
diff --git a/images/email.png b/images/email.png
deleted file mode 100644 (file)
index 11e34d7..0000000
Binary files a/images/email.png and /dev/null differ
diff --git a/images/email@2x.png b/images/email@2x.png
deleted file mode 100644 (file)
index ed0f958..0000000
Binary files a/images/email@2x.png and /dev/null differ
diff --git a/images/facebook.png b/images/facebook.png
deleted file mode 100644 (file)
index 016965f..0000000
Binary files a/images/facebook.png and /dev/null differ
diff --git a/images/facebook@2x.png b/images/facebook@2x.png
deleted file mode 100644 (file)
index f3eb5de..0000000
Binary files a/images/facebook@2x.png and /dev/null differ
diff --git a/images/go-next.png b/images/go-next.png
deleted file mode 100644 (file)
index b927522..0000000
Binary files a/images/go-next.png and /dev/null differ
diff --git a/images/go-next@2x.png b/images/go-next@2x.png
deleted file mode 100644 (file)
index 1e617cd..0000000
Binary files a/images/go-next@2x.png and /dev/null differ
diff --git a/images/go-next_active.png b/images/go-next_active.png
deleted file mode 100644 (file)
index 9521341..0000000
Binary files a/images/go-next_active.png and /dev/null differ
diff --git a/images/go-next_active@2x.png b/images/go-next_active@2x.png
deleted file mode 100644 (file)
index 3e3e7dc..0000000
Binary files a/images/go-next_active@2x.png and /dev/null differ
diff --git a/images/go-previous.png b/images/go-previous.png
deleted file mode 100644 (file)
index c62d60f..0000000
Binary files a/images/go-previous.png and /dev/null differ
diff --git a/images/go-previous@2x.png b/images/go-previous@2x.png
deleted file mode 100644 (file)
index fc2664c..0000000
Binary files a/images/go-previous@2x.png and /dev/null differ
diff --git a/images/go-previous_active.png b/images/go-previous_active.png
deleted file mode 100644 (file)
index b410707..0000000
Binary files a/images/go-previous_active.png and /dev/null differ
diff --git a/images/go-previous_active@2x.png b/images/go-previous_active@2x.png
deleted file mode 100644 (file)
index faf42a5..0000000
Binary files a/images/go-previous_active@2x.png and /dev/null differ
diff --git a/images/go-top.png b/images/go-top.png
deleted file mode 100644 (file)
index b2ea402..0000000
Binary files a/images/go-top.png and /dev/null differ
diff --git a/images/go-top@2x.png b/images/go-top@2x.png
deleted file mode 100644 (file)
index c814273..0000000
Binary files a/images/go-top@2x.png and /dev/null differ
diff --git a/images/link.png b/images/link.png
deleted file mode 100644 (file)
index 8c9687a..0000000
Binary files a/images/link.png and /dev/null differ
diff --git a/images/link@2x.png b/images/link@2x.png
deleted file mode 100644 (file)
index bb31be1..0000000
Binary files a/images/link@2x.png and /dev/null differ
diff --git a/images/mark-watched.png b/images/mark-watched.png
deleted file mode 100644 (file)
index 961d149..0000000
Binary files a/images/mark-watched.png and /dev/null differ
diff --git a/images/mark-watched@2x.png b/images/mark-watched@2x.png
deleted file mode 100644 (file)
index 73f8fb4..0000000
Binary files a/images/mark-watched@2x.png and /dev/null differ
diff --git a/images/media-playback-pause.png b/images/media-playback-pause.png
deleted file mode 100644 (file)
index 03a5d7c..0000000
Binary files a/images/media-playback-pause.png and /dev/null differ
diff --git a/images/media-playback-pause@2x.png b/images/media-playback-pause@2x.png
deleted file mode 100644 (file)
index c78ae2d..0000000
Binary files a/images/media-playback-pause@2x.png and /dev/null differ
diff --git a/images/media-playback-start.png b/images/media-playback-start.png
deleted file mode 100644 (file)
index 256686b..0000000
Binary files a/images/media-playback-start.png and /dev/null differ
diff --git a/images/media-playback-start@2x.png b/images/media-playback-start@2x.png
deleted file mode 100644 (file)
index 662fe6a..0000000
Binary files a/images/media-playback-start@2x.png and /dev/null differ
diff --git a/images/media-playback-stop.png b/images/media-playback-stop.png
deleted file mode 100644 (file)
index 6c4e66d..0000000
Binary files a/images/media-playback-stop.png and /dev/null differ
diff --git a/images/media-playback-stop@2x.png b/images/media-playback-stop@2x.png
deleted file mode 100644 (file)
index b3a9971..0000000
Binary files a/images/media-playback-stop@2x.png and /dev/null differ
diff --git a/images/media-skip-forward.png b/images/media-skip-forward.png
deleted file mode 100644 (file)
index e59e645..0000000
Binary files a/images/media-skip-forward.png and /dev/null differ
diff --git a/images/media-skip-forward@2x.png b/images/media-skip-forward@2x.png
deleted file mode 100644 (file)
index 5300dc8..0000000
Binary files a/images/media-skip-forward@2x.png and /dev/null differ
diff --git a/images/refine-search.png b/images/refine-search.png
deleted file mode 100644 (file)
index dd977aa..0000000
Binary files a/images/refine-search.png and /dev/null differ
diff --git a/images/refine-search@2x.png b/images/refine-search@2x.png
deleted file mode 100644 (file)
index 37340a5..0000000
Binary files a/images/refine-search@2x.png and /dev/null differ
diff --git a/images/safesearch.png b/images/safesearch.png
deleted file mode 100644 (file)
index eef8f60..0000000
Binary files a/images/safesearch.png and /dev/null differ
diff --git a/images/safesearch@2x.png b/images/safesearch@2x.png
deleted file mode 100644 (file)
index 9a5fed5..0000000
Binary files a/images/safesearch@2x.png and /dev/null differ
diff --git a/images/search-duration.png b/images/search-duration.png
deleted file mode 100644 (file)
index fb816df..0000000
Binary files a/images/search-duration.png and /dev/null differ
diff --git a/images/search-duration@2x.png b/images/search-duration@2x.png
deleted file mode 100644 (file)
index daef48d..0000000
Binary files a/images/search-duration@2x.png and /dev/null differ
diff --git a/images/search-quality.png b/images/search-quality.png
deleted file mode 100644 (file)
index ae97229..0000000
Binary files a/images/search-quality.png and /dev/null differ
diff --git a/images/search-quality@2x.png b/images/search-quality@2x.png
deleted file mode 100644 (file)
index 3035153..0000000
Binary files a/images/search-quality@2x.png and /dev/null differ
diff --git a/images/search-sortBy.png b/images/search-sortBy.png
deleted file mode 100644 (file)
index 6834651..0000000
Binary files a/images/search-sortBy.png and /dev/null differ
diff --git a/images/search-sortBy@2x.png b/images/search-sortBy@2x.png
deleted file mode 100644 (file)
index 235200c..0000000
Binary files a/images/search-sortBy@2x.png and /dev/null differ
diff --git a/images/search-time.png b/images/search-time.png
deleted file mode 100644 (file)
index e38fb15..0000000
Binary files a/images/search-time.png and /dev/null differ
diff --git a/images/search-time@2x.png b/images/search-time@2x.png
deleted file mode 100644 (file)
index aeaaa21..0000000
Binary files a/images/search-time@2x.png and /dev/null differ
diff --git a/images/show-updated.png b/images/show-updated.png
deleted file mode 100644 (file)
index 9af84f2..0000000
Binary files a/images/show-updated.png and /dev/null differ
diff --git a/images/show-updated@2x.png b/images/show-updated@2x.png
deleted file mode 100644 (file)
index 7f0b807..0000000
Binary files a/images/show-updated@2x.png and /dev/null differ
diff --git a/images/sort.png b/images/sort.png
deleted file mode 100644 (file)
index 95fcb55..0000000
Binary files a/images/sort.png and /dev/null differ
diff --git a/images/sort@2x.png b/images/sort@2x.png
deleted file mode 100644 (file)
index 7475325..0000000
Binary files a/images/sort@2x.png and /dev/null differ
diff --git a/images/system-search.png b/images/system-search.png
deleted file mode 100644 (file)
index 4cf77de..0000000
Binary files a/images/system-search.png and /dev/null differ
diff --git a/images/system-search_active.png b/images/system-search_active.png
deleted file mode 100644 (file)
index ff40df1..0000000
Binary files a/images/system-search_active.png and /dev/null differ
diff --git a/images/system-search_selected.png b/images/system-search_selected.png
deleted file mode 100644 (file)
index 0db8a8f..0000000
Binary files a/images/system-search_selected.png and /dev/null differ
diff --git a/images/twitter.png b/images/twitter.png
deleted file mode 100644 (file)
index 8ccc500..0000000
Binary files a/images/twitter.png and /dev/null differ
diff --git a/images/twitter@2x.png b/images/twitter@2x.png
deleted file mode 100644 (file)
index 2366d6d..0000000
Binary files a/images/twitter@2x.png and /dev/null differ
diff --git a/images/unwatched.png b/images/unwatched.png
deleted file mode 100644 (file)
index 63da996..0000000
Binary files a/images/unwatched.png and /dev/null differ
diff --git a/images/unwatched@2x.png b/images/unwatched@2x.png
deleted file mode 100644 (file)
index 573c25e..0000000
Binary files a/images/unwatched@2x.png and /dev/null differ
diff --git a/images/video-display.png b/images/video-display.png
deleted file mode 100644 (file)
index a32f4a6..0000000
Binary files a/images/video-display.png and /dev/null differ
diff --git a/images/video-display@2x.png b/images/video-display@2x.png
deleted file mode 100644 (file)
index adacbe8..0000000
Binary files a/images/video-display@2x.png and /dev/null differ
diff --git a/images/view-fullscreen.png b/images/view-fullscreen.png
deleted file mode 100644 (file)
index eb7cac5..0000000
Binary files a/images/view-fullscreen.png and /dev/null differ
diff --git a/images/view-fullscreen@2x.png b/images/view-fullscreen@2x.png
deleted file mode 100644 (file)
index 80b93f0..0000000
Binary files a/images/view-fullscreen@2x.png and /dev/null differ
diff --git a/images/view-list.png b/images/view-list.png
deleted file mode 100644 (file)
index 9727d29..0000000
Binary files a/images/view-list.png and /dev/null differ
diff --git a/images/view-list@2x.png b/images/view-list@2x.png
deleted file mode 100644 (file)
index 20ea4b1..0000000
Binary files a/images/view-list@2x.png and /dev/null differ
diff --git a/images/view-more.png b/images/view-more.png
deleted file mode 100644 (file)
index 6143f4b..0000000
Binary files a/images/view-more.png and /dev/null differ
diff --git a/images/view-more@2x.png b/images/view-more@2x.png
deleted file mode 100644 (file)
index f13d0e0..0000000
Binary files a/images/view-more@2x.png and /dev/null differ
diff --git a/images/view-refresh.png b/images/view-refresh.png
deleted file mode 100644 (file)
index c4603d9..0000000
Binary files a/images/view-refresh.png and /dev/null differ
diff --git a/images/view-refresh_active.png b/images/view-refresh_active.png
deleted file mode 100644 (file)
index 2c9a275..0000000
Binary files a/images/view-refresh_active.png and /dev/null differ
diff --git a/images/view-refresh_selected.png b/images/view-refresh_selected.png
deleted file mode 100644 (file)
index 3b83d8b..0000000
Binary files a/images/view-refresh_selected.png and /dev/null differ
diff --git a/images/view-restore.png b/images/view-restore.png
deleted file mode 100644 (file)
index 9f05eb9..0000000
Binary files a/images/view-restore.png and /dev/null differ
diff --git a/images/view-restore@2x.png b/images/view-restore@2x.png
deleted file mode 100644 (file)
index ecee8be..0000000
Binary files a/images/view-restore@2x.png and /dev/null differ
diff --git a/images/window-close.png b/images/window-close.png
deleted file mode 100644 (file)
index 7a23bfd..0000000
Binary files a/images/window-close.png and /dev/null differ
diff --git a/images/window-close_active.png b/images/window-close_active.png
deleted file mode 100644 (file)
index 7553fca..0000000
Binary files a/images/window-close_active.png and /dev/null differ
diff --git a/images/window-close_selected.png b/images/window-close_selected.png
deleted file mode 100644 (file)
index 4158542..0000000
Binary files a/images/window-close_selected.png and /dev/null differ
diff --git a/images/worldwide.png b/images/worldwide.png
deleted file mode 100644 (file)
index d676740..0000000
Binary files a/images/worldwide.png and /dev/null differ
diff --git a/images/worldwide@2x.png b/images/worldwide@2x.png
deleted file mode 100644 (file)
index 2393b38..0000000
Binary files a/images/worldwide@2x.png and /dev/null differ
diff --git a/lib/http/.gitignore b/lib/http/.gitignore
new file mode 100644 (file)
index 0000000..e31cfb2
--- /dev/null
@@ -0,0 +1,2 @@
+
+*.user
diff --git a/lib/http/README.md b/lib/http/README.md
new file mode 100644 (file)
index 0000000..a1370ff
--- /dev/null
@@ -0,0 +1,63 @@
+# A wrapper for the Qt Network Access API
+
+This is just a wrapper around Qt's QNetworkAccessManager and friends. I use it in my Qt apps at http://flavio.tordini.org . It allows me to add missing functionality as needed, e.g.:
+
+- Throttling (as required by many web APIs nowadays)
+- Read timeouts (don't let your requests get stuck forever)
+- Automatic retries
+- User agent and request header defaults
+- Partial requests
+- Redirection support (now supported by Qt >= 5.6)
+
+It has a simpler, higher-level API that I find easier to work with. The design emerged naturally in years of practical use.
+
+A basic example:
+
+```
+QObject *reply = Http::instance().get("https://google.com/");
+connect(reply, SIGNAL(data(QByteArray)), SLOT(onSuccess(QByteArray)));
+connect(reply, SIGNAL(error(QString)), SLOT(onError(QString)));
+
+void MyClass::onSuccess(const QByteArray &bytes) {
+       qDebug() << "Feel the bytes!" << bytes;
+}
+
+void MyClass::onError(const QString &message) {
+       qDebug() << "Something's wrong here" << message;
+}
+```
+
+This is a real-world example of building a Http object suitable to a web service. It throttles requests, uses a custom user agent and caches results:
+
+```
+Http &myHttp() {
+    static Http *http = [] {
+        Http *http = new Http;
+        http->addRequestHeader("User-Agent", userAgent());
+
+        ThrottledHttp *throttledHttp = new ThrottledHttp(*http);
+        throttledHttp->setMilliseconds(1000);
+
+        CachedHttp *cachedHttp = new CachedHttp(*throttledHttp, "mycache");
+        cachedHttp->setMaxSeconds(86400 * 30);
+
+        return cachedHttp;
+    }();
+    return *http;
+}
+```
+
+If the full power (and complexity) of QNetworkReply is needed you can always fallback to it:
+
+```
+HttpRequest req;
+req.url = "https://flavio.tordini.org/";
+QNetworkReply *reply = Http::instance().networkReply(req);
+// Use QNetworkReply as needed...
+```
+
+You can use this library under the MIT license and at your own risk. If you do, you're welcome contributing your changes and fixes.
+
+Cheers,
+
+Flavio
diff --git a/lib/http/http.pri b/lib/http/http.pri
new file mode 100644 (file)
index 0000000..6a210c7
--- /dev/null
@@ -0,0 +1,16 @@
+QT *= network
+
+INCLUDEPATH += $$PWD/src
+DEPENDPATH += $$PWD/src
+
+HEADERS += \
+    $$PWD/src/cachedhttp.h \
+    $$PWD/src/http.h \
+    $$PWD/src/localcache.h \
+    $$PWD/src/throttledhttp.h
+
+SOURCES += \
+    $$PWD/src/cachedhttp.cpp \
+    $$PWD/src/http.cpp \
+    $$PWD/src/localcache.cpp \
+    $$PWD/src/throttledhttp.cpp
diff --git a/lib/http/http.pro b/lib/http/http.pro
new file mode 100644 (file)
index 0000000..7fc2695
--- /dev/null
@@ -0,0 +1,2 @@
+TEMPLATE = lib
+include(http.pri)
diff --git a/lib/http/src/cachedhttp.cpp b/lib/http/src/cachedhttp.cpp
new file mode 100644 (file)
index 0000000..3c6942d
--- /dev/null
@@ -0,0 +1,70 @@
+#include "cachedhttp.h"
+#include "localcache.h"
+
+namespace {
+
+QByteArray requestHash(const HttpRequest &req) {
+    const char sep = '|';
+    QByteArray s = req.url.toEncoded() + sep + req.body + sep + QByteArray::number(req.offset);
+    if (req.operation == QNetworkAccessManager::PostOperation) {
+        s.append(sep);
+        s.append("POST");
+    }
+    return LocalCache::hash(s);
+}
+} // namespace
+
+CachedHttpReply::CachedHttpReply(const QByteArray &body, const HttpRequest &req)
+    : bytes(body), req(req) {
+    QTimer::singleShot(0, this, SLOT(emitSignals()));
+}
+
+QByteArray CachedHttpReply::body() const {
+    return bytes;
+}
+
+void CachedHttpReply::emitSignals() {
+    emit data(body());
+    emit finished(*this);
+    deleteLater();
+}
+
+WrappedHttpReply::WrappedHttpReply(LocalCache *cache, const QByteArray &key, HttpReply *httpReply)
+    : HttpReply(httpReply), cache(cache), key(key), httpReply(httpReply) {
+    connect(httpReply, SIGNAL(data(QByteArray)), SIGNAL(data(QByteArray)));
+    connect(httpReply, SIGNAL(error(QString)), SIGNAL(error(QString)));
+    connect(httpReply, SIGNAL(finished(HttpReply)), SLOT(originFinished(HttpReply)));
+}
+
+void WrappedHttpReply::originFinished(const HttpReply &reply) {
+    if (reply.isSuccessful()) cache->insert(key, reply.body());
+    emit finished(reply);
+}
+
+CachedHttp::CachedHttp(Http &http, const char *name)
+    : http(http), cache(LocalCache::instance(name)), cachePostRequests(false) {}
+
+void CachedHttp::setMaxSeconds(uint seconds) {
+    cache->setMaxSeconds(seconds);
+}
+
+void CachedHttp::setMaxSize(uint maxSize) {
+    cache->setMaxSize(maxSize);
+}
+
+HttpReply *CachedHttp::request(const HttpRequest &req) {
+    bool cacheable = req.operation == QNetworkAccessManager::GetOperation ||
+                     (cachePostRequests && req.operation == QNetworkAccessManager::PostOperation);
+    if (!cacheable) {
+        qDebug() << "Not cacheable" << req.url;
+        return http.request(req);
+    }
+    const QByteArray key = requestHash(req);
+    const QByteArray value = cache->value(key);
+    if (!value.isNull()) {
+        qDebug() << "CachedHttp HIT" << req.url;
+        return new CachedHttpReply(value, req);
+    }
+    qDebug() << "CachedHttp MISS" << req.url.toString();
+    return new WrappedHttpReply(cache, key, http.request(req));
+}
diff --git a/lib/http/src/cachedhttp.h b/lib/http/src/cachedhttp.h
new file mode 100644 (file)
index 0000000..e487a17
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef CACHEDHTTP_H
+#define CACHEDHTTP_H
+
+#include "http.h"
+
+class LocalCache;
+
+class CachedHttp : public Http {
+public:
+    CachedHttp(Http &http = Http::instance(), const char *name = "http");
+    void setMaxSeconds(uint seconds);
+    void setMaxSize(uint maxSize);
+    void setCachePostRequests(bool value) { cachePostRequests = value; }
+    HttpReply *request(const HttpRequest &req);
+
+private:
+    Http &http;
+    LocalCache *cache;
+    bool cachePostRequests;
+};
+
+class CachedHttpReply : public HttpReply {
+    Q_OBJECT
+
+public:
+    CachedHttpReply(const QByteArray &body, const HttpRequest &req);
+    QUrl url() const { return req.url; }
+    int statusCode() const { return 200; }
+    QByteArray body() const;
+
+private slots:
+    void emitSignals();
+
+private:
+    const QByteArray bytes;
+    const HttpRequest req;
+};
+
+class WrappedHttpReply : public HttpReply {
+    Q_OBJECT
+
+public:
+    WrappedHttpReply(LocalCache *cache, const QByteArray &key, HttpReply *httpReply);
+    QUrl url() const { return httpReply->url(); }
+    int statusCode() const { return httpReply->statusCode(); }
+    QByteArray body() const { return httpReply->body(); }
+
+private slots:
+    void originFinished(const HttpReply &reply);
+
+private:
+    LocalCache *cache;
+    QByteArray key;
+    HttpReply *httpReply;
+};
+
+#endif // CACHEDHTTP_H
diff --git a/lib/http/src/http.cpp b/lib/http/src/http.cpp
new file mode 100644 (file)
index 0000000..7f58d39
--- /dev/null
@@ -0,0 +1,305 @@
+#include "http.h"
+
+namespace {
+
+QNetworkAccessManager *createNetworkAccessManager() {
+    QNetworkAccessManager *nam = new QNetworkAccessManager();
+    return nam;
+}
+
+QNetworkAccessManager *networkAccessManager() {
+    static QMap<QThread *, QNetworkAccessManager *> nams;
+    QThread *t = QThread::currentThread();
+    QMap<QThread *, QNetworkAccessManager *>::const_iterator i = nams.constFind(t);
+    if (i != nams.constEnd()) return i.value();
+    QNetworkAccessManager *nam = createNetworkAccessManager();
+    nams.insert(t, nam);
+    return nam;
+}
+
+int defaultReadTimeout = 10000;
+} // namespace
+
+Http::Http() : requestHeaders(getDefaultRequestHeaders()), readTimeout(defaultReadTimeout) {}
+
+void Http::setRequestHeaders(const QMap<QByteArray, QByteArray> &headers) {
+    requestHeaders = headers;
+}
+
+QMap<QByteArray, QByteArray> &Http::getRequestHeaders() {
+    return requestHeaders;
+}
+
+void Http::addRequestHeader(const QByteArray &name, const QByteArray &value) {
+    requestHeaders.insert(name, value);
+}
+
+void Http::setReadTimeout(int timeout) {
+    readTimeout = timeout;
+}
+
+Http &Http::instance() {
+    static Http i;
+    return i;
+}
+
+const QMap<QByteArray, QByteArray> &Http::getDefaultRequestHeaders() {
+    static const QMap<QByteArray, QByteArray> defaultRequestHeaders = [] {
+        QMap<QByteArray, QByteArray> h;
+        h.insert("Accept-Charset", "utf-8");
+        h.insert("Connection", "Keep-Alive");
+        return h;
+    }();
+    return defaultRequestHeaders;
+}
+
+void Http::setDefaultReadTimeout(int timeout) {
+    defaultReadTimeout = timeout;
+}
+
+QNetworkReply *Http::networkReply(const HttpRequest &req) {
+    QNetworkRequest request(req.url);
+
+    QMap<QByteArray, QByteArray> &headers = requestHeaders;
+    if (!req.headers.isEmpty()) headers = req.headers;
+
+    QMap<QByteArray, QByteArray>::const_iterator it;
+    for (it = headers.constBegin(); it != headers.constEnd(); ++it)
+        request.setRawHeader(it.key(), it.value());
+
+    if (req.offset > 0)
+        request.setRawHeader("Range", QString("bytes=%1-").arg(req.offset).toUtf8());
+
+    QNetworkAccessManager *manager = networkAccessManager();
+
+    QNetworkReply *networkReply = nullptr;
+    switch (req.operation) {
+    case QNetworkAccessManager::GetOperation:
+        networkReply = manager->get(request);
+        break;
+
+    case QNetworkAccessManager::HeadOperation:
+        networkReply = manager->head(request);
+        break;
+
+    case QNetworkAccessManager::PostOperation:
+        networkReply = manager->post(request, req.body);
+        break;
+
+    default:
+        qWarning() << "Unknown operation:" << req.operation;
+    }
+
+    return networkReply;
+}
+
+HttpReply *Http::request(const HttpRequest &req) {
+    return new NetworkHttpReply(req, *this);
+}
+
+HttpReply *Http::request(const QUrl &url,
+                         QNetworkAccessManager::Operation operation,
+                         const QByteArray &body,
+                         uint offset) {
+    HttpRequest req;
+    req.url = url;
+    req.operation = operation;
+    req.body = body;
+    req.offset = offset;
+    return request(req);
+}
+
+HttpReply *Http::get(const QUrl &url) {
+    return request(url, QNetworkAccessManager::GetOperation);
+}
+
+HttpReply *Http::head(const QUrl &url) {
+    return request(url, QNetworkAccessManager::HeadOperation);
+}
+
+HttpReply *Http::post(const QUrl &url, const QMap<QString, QString> &params) {
+    QByteArray body;
+    QMapIterator<QString, QString> i(params);
+    while (i.hasNext()) {
+        i.next();
+        body += QUrl::toPercentEncoding(i.key()) + '=' + QUrl::toPercentEncoding(i.value()) + '&';
+    }
+    HttpRequest req;
+    req.url = url;
+    req.operation = QNetworkAccessManager::PostOperation;
+    req.body = body;
+    req.headers = requestHeaders;
+    req.headers.insert("Content-Type", "application/x-www-form-urlencoded");
+    return request(req);
+}
+
+HttpReply *Http::post(const QUrl &url, const QByteArray &body, const QByteArray &contentType) {
+    HttpRequest req;
+    req.url = url;
+    req.operation = QNetworkAccessManager::PostOperation;
+    req.body = body;
+    req.headers = requestHeaders;
+    QByteArray cType = contentType;
+    if (cType.isEmpty()) cType = "application/x-www-form-urlencoded";
+    req.headers.insert("Content-Type", cType);
+    return request(req);
+}
+
+NetworkHttpReply::NetworkHttpReply(const HttpRequest &req, Http &http)
+    : http(http), req(req), retryCount(0) {
+    if (req.url.isEmpty()) {
+        qWarning() << "Empty URL";
+    }
+
+    networkReply = http.networkReply(req);
+    setParent(networkReply);
+    setupReply();
+
+    readTimeoutTimer = new QTimer(this);
+    readTimeoutTimer->setInterval(http.getReadTimeout());
+    readTimeoutTimer->setSingleShot(true);
+    connect(readTimeoutTimer, SIGNAL(timeout()), SLOT(readTimeout()), Qt::UniqueConnection);
+    readTimeoutTimer->start();
+}
+
+void NetworkHttpReply::setupReply() {
+    connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
+            SLOT(replyError(QNetworkReply::NetworkError)), Qt::UniqueConnection);
+    connect(networkReply, SIGNAL(finished()), SLOT(replyFinished()), Qt::UniqueConnection);
+    connect(networkReply, SIGNAL(downloadProgress(qint64, qint64)),
+            SLOT(downloadProgress(qint64, qint64)), Qt::UniqueConnection);
+}
+
+QString NetworkHttpReply::errorMessage() {
+    return url().toString() + QLatin1Char(' ') + QString::number(statusCode()) + QLatin1Char(' ') +
+           reasonPhrase();
+}
+
+void NetworkHttpReply::emitError() {
+    const QString msg = errorMessage();
+#ifndef QT_NO_DEBUG_OUTPUT
+    qDebug() << "Http:" << msg;
+    if (!req.body.isEmpty()) qDebug() << "Http:" << req.body;
+#endif
+    emit error(msg);
+    emitFinished();
+}
+
+void NetworkHttpReply::emitFinished() {
+    readTimeoutTimer->stop();
+
+    // disconnect to avoid replyFinished() from being called
+    networkReply->disconnect();
+
+    emit finished(*this);
+
+    // bye bye my reply
+    // this will also delete this object and HttpReply as the QNetworkReply is their parent
+    networkReply->deleteLater();
+}
+
+void NetworkHttpReply::replyFinished() {
+    QUrl redirection = networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
+    if (redirection.isValid()) {
+        HttpRequest redirectReq;
+        redirectReq.url = redirection;
+        redirectReq.operation = req.operation;
+        redirectReq.body = req.body;
+        redirectReq.offset = req.offset;
+        QNetworkReply *redirectReply = http.networkReply(redirectReq);
+        setParent(redirectReply);
+        networkReply->deleteLater();
+        networkReply = redirectReply;
+        setupReply();
+        readTimeoutTimer->start();
+        return;
+    }
+
+    if (isSuccessful()) {
+        bytes = networkReply->readAll();
+        emit data(bytes);
+
+#ifndef QT_NO_DEBUG_OUTPUT
+        if (!networkReply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool())
+            qDebug() << networkReply->url().toString() << statusCode();
+        else
+            qDebug() << "CACHE" << networkReply->url().toString();
+#endif
+    }
+
+    emitFinished();
+}
+
+void NetworkHttpReply::replyError(QNetworkReply::NetworkError code) {
+    Q_UNUSED(code);
+    const int status = statusCode();
+    if (retryCount <= 3 && status >= 500 && status < 600) {
+        qDebug() << "Retrying" << req.url;
+        networkReply->disconnect();
+        networkReply->deleteLater();
+        QNetworkReply *retryReply = http.networkReply(req);
+        setParent(retryReply);
+        networkReply = retryReply;
+        setupReply();
+        retryCount++;
+        readTimeoutTimer->start();
+    } else {
+        emitError();
+        return;
+    }
+}
+
+void NetworkHttpReply::downloadProgress(qint64 bytesReceived, qint64 /* bytesTotal */) {
+    // qDebug() << "Downloading" << bytesReceived << bytesTotal << networkReply->url();
+    if (bytesReceived > 0 && readTimeoutTimer->isActive()) {
+        readTimeoutTimer->stop();
+        disconnect(networkReply, SIGNAL(downloadProgress(qint64, qint64)), this,
+                   SLOT(downloadProgress(qint64, qint64)));
+    }
+}
+
+void NetworkHttpReply::readTimeout() {
+    if (!networkReply) return;
+    networkReply->disconnect();
+    networkReply->abort();
+    networkReply->deleteLater();
+
+    if (retryCount > 3 && (networkReply->operation() != QNetworkAccessManager::GetOperation &&
+                           networkReply->operation() != QNetworkAccessManager::HeadOperation)) {
+        emitError();
+        emit finished(*this);
+        return;
+    }
+
+    qDebug() << "Timeout" << req.url;
+    QNetworkReply *retryReply = http.networkReply(req);
+    setParent(retryReply);
+    networkReply = retryReply;
+    setupReply();
+    retryCount++;
+    readTimeoutTimer->start();
+}
+
+QUrl NetworkHttpReply::url() const {
+    return networkReply->url();
+}
+
+int NetworkHttpReply::statusCode() const {
+    return networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+}
+
+QString NetworkHttpReply::reasonPhrase() const {
+    return networkReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
+}
+
+const QList<QNetworkReply::RawHeaderPair> NetworkHttpReply::headers() const {
+    return networkReply->rawHeaderPairs();
+}
+
+QByteArray NetworkHttpReply::header(const QByteArray &headerName) const {
+    return networkReply->rawHeader(headerName);
+}
+
+QByteArray NetworkHttpReply::body() const {
+    return bytes;
+}
diff --git a/lib/http/src/http.h b/lib/http/src/http.h
new file mode 100644 (file)
index 0000000..23f3754
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef HTTP_H
+#define HTTP_H
+
+#include <QtNetwork>
+
+class HttpRequest {
+public:
+    HttpRequest() : operation(QNetworkAccessManager::GetOperation), offset(0) {}
+    QUrl url;
+    QNetworkAccessManager::Operation operation;
+    QByteArray body;
+    uint offset;
+    QMap<QByteArray, QByteArray> headers;
+};
+
+class HttpReply : public QObject {
+    Q_OBJECT
+
+public:
+    HttpReply(QObject *parent = nullptr) : QObject(parent) {}
+    virtual QUrl url() const = 0;
+    virtual int statusCode() const = 0;
+    int isSuccessful() const { return statusCode() >= 200 && statusCode() < 300; }
+    virtual QString reasonPhrase() const { return QString(); }
+    virtual const QList<QNetworkReply::RawHeaderPair> headers() const {
+        return QList<QNetworkReply::RawHeaderPair>();
+    }
+    virtual QByteArray header(const QByteArray &headerName) const {
+        Q_UNUSED(headerName);
+        return QByteArray();
+    }
+
+    virtual QByteArray body() const = 0;
+
+signals:
+    void data(const QByteArray &bytes);
+    void error(const QString &message);
+    void finished(const HttpReply &reply);
+};
+
+class Http {
+public:
+    static Http &instance();
+    static const QMap<QByteArray, QByteArray> &getDefaultRequestHeaders();
+    static void setDefaultReadTimeout(int timeout);
+
+    Http();
+
+    void setRequestHeaders(const QMap<QByteArray, QByteArray> &headers);
+    QMap<QByteArray, QByteArray> &getRequestHeaders();
+    void addRequestHeader(const QByteArray &name, const QByteArray &value);
+
+    void setReadTimeout(int timeout);
+    int getReadTimeout() { return readTimeout; }
+
+    QNetworkReply *networkReply(const HttpRequest &req);
+    virtual HttpReply *request(const HttpRequest &req);
+    HttpReply *
+    request(const QUrl &url,
+            QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
+            const QByteArray &body = QByteArray(),
+            uint offset = 0);
+    HttpReply *get(const QUrl &url);
+    HttpReply *head(const QUrl &url);
+    HttpReply *post(const QUrl &url, const QMap<QString, QString> &params);
+    HttpReply *post(const QUrl &url, const QByteArray &body, const QByteArray &contentType);
+
+private:
+    QMap<QByteArray, QByteArray> requestHeaders;
+    int readTimeout;
+};
+
+class NetworkHttpReply : public HttpReply {
+    Q_OBJECT
+
+public:
+    NetworkHttpReply(const HttpRequest &req, Http &http);
+    QUrl url() const;
+    int statusCode() const;
+    QString reasonPhrase() const;
+    const QList<QNetworkReply::RawHeaderPair> headers() const;
+    QByteArray header(const QByteArray &headerName) const;
+    QByteArray body() const;
+
+private slots:
+    void replyFinished();
+    void replyError(QNetworkReply::NetworkError);
+    void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+    void readTimeout();
+
+private:
+    void setupReply();
+    QString errorMessage();
+    void emitError();
+    void emitFinished();
+
+    Http &http;
+    HttpRequest req;
+    QNetworkReply *networkReply;
+    QTimer *readTimeoutTimer;
+    int retryCount;
+    QByteArray bytes;
+};
+
+#endif // HTTP_H
diff --git a/lib/http/src/localcache.cpp b/lib/http/src/localcache.cpp
new file mode 100644 (file)
index 0000000..8bee99e
--- /dev/null
@@ -0,0 +1,171 @@
+#include "localcache.h"
+
+LocalCache *LocalCache::instance(const char *name) {
+    static QMap<QByteArray, LocalCache *> instances;
+    auto i = instances.constFind(QByteArray::fromRawData(name, strlen(name)));
+    if (i != instances.constEnd()) return i.value();
+    LocalCache *instance = new LocalCache(name);
+    instances.insert(instance->getName(), instance);
+    return instance;
+}
+
+LocalCache::LocalCache(const QByteArray &name)
+    : name(name), maxSeconds(86400 * 30), maxSize(1024 * 1024 * 100), size(0), expiring(false),
+      insertCount(0) {
+    directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') +
+                QLatin1String(name) + QLatin1Char('/');
+#ifndef QT_NO_DEBUG_OUTPUT
+    hits = 0;
+    misses = 0;
+#endif
+}
+
+LocalCache::~LocalCache() {
+#ifndef QT_NO_DEBUG_OUTPUT
+    debugStats();
+#endif
+}
+
+QByteArray LocalCache::hash(const QByteArray &s) {
+    QCryptographicHash hash(QCryptographicHash::Sha1);
+    hash.addData(s);
+    const QByteArray h = QByteArray::number(*(qlonglong *)hash.result().constData(), 36);
+    static const char sep('/');
+    QByteArray p;
+    p.reserve(h.length() + 2);
+    p.append(h.at(0));
+    p.append(sep);
+    p.append(h.at(1));
+    p.append(sep);
+    p.append(h.constData() + 2, strlen(h.constData()) - 2); // p.append(h.mid(2));
+    return p;
+}
+
+bool LocalCache::isCached(const QString &path) {
+    bool cached = (QFile::exists(path) &&
+                   (maxSeconds == 0 || QDateTime::currentDateTimeUtc().toTime_t() -
+                                                       QFileInfo(path).created().toTime_t() <
+                                               maxSeconds));
+#ifndef QT_NO_DEBUG_OUTPUT
+    if (!cached) misses++;
+#endif
+    return cached;
+}
+
+QByteArray LocalCache::value(const QByteArray &key) {
+    const QString path = cachePath(key);
+    if (!isCached(path)) return QByteArray();
+
+    QFile file(path);
+    if (!file.open(QIODevice::ReadOnly)) {
+        qWarning() << __PRETTY_FUNCTION__ << file.fileName() << file.errorString();
+#ifndef QT_NO_DEBUG_OUTPUT
+        misses++;
+#endif
+        return QByteArray();
+    }
+#ifndef QT_NO_DEBUG_OUTPUT
+    hits++;
+#endif
+    return file.readAll();
+}
+
+void LocalCache::insert(const QByteArray &key, const QByteArray &value) {
+    const QueueItem item = {key, value};
+    insertQueue.append(item);
+    QTimer::singleShot(0, [this]() {
+        if (insertQueue.isEmpty()) return;
+        for (const auto &item : insertQueue) {
+            const QString path = cachePath(item.key);
+            const QString parentDir = path.left(path.lastIndexOf('/'));
+            if (!QFile::exists(parentDir)) {
+                QDir().mkpath(parentDir);
+            }
+            QFile file(path);
+            if (!file.open(QIODevice::WriteOnly)) {
+                qWarning() << "Cannot create" << path;
+                continue;
+            }
+            file.write(item.value);
+            file.close();
+            if (size > 0) size += item.value.size();
+        }
+        insertQueue.clear();
+
+        // expire cache every n inserts
+        if (maxSize > 0 && ++insertCount % 100 == 0) {
+            if (size == 0 || size > maxSize) size = expire();
+        }
+    });
+}
+
+bool LocalCache::clear() {
+#ifndef QT_NO_DEBUG_OUTPUT
+    hits = 0;
+    misses = 0;
+#endif
+    size = 0;
+    insertCount = 0;
+    return QDir(directory).removeRecursively();
+}
+
+QString LocalCache::cachePath(const QByteArray &key) const {
+    return directory + QLatin1String(key.constData());
+}
+
+qint64 LocalCache::expire() {
+    if (expiring) return size;
+    expiring = true;
+
+    QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot;
+    QDirIterator it(directory, filters, QDirIterator::Subdirectories);
+
+    QMultiMap<QDateTime, QString> cacheItems;
+    qint64 totalSize = 0;
+    while (it.hasNext()) {
+        QString path = it.next();
+        QFileInfo info = it.fileInfo();
+        cacheItems.insert(info.created(), path);
+        totalSize += info.size();
+        qApp->processEvents();
+    }
+
+    int removedFiles = 0;
+    qint64 goal = (maxSize * 9) / 10;
+    auto i = cacheItems.constBegin();
+    while (i != cacheItems.constEnd()) {
+        if (totalSize < goal) break;
+        QString name = i.value();
+        QFile file(name);
+        qint64 size = file.size();
+        file.remove();
+        totalSize -= size;
+        ++removedFiles;
+        ++i;
+        qApp->processEvents();
+    }
+#ifndef QT_NO_DEBUG_OUTPUT
+    debugStats();
+    if (removedFiles > 0) {
+        qDebug() << "Removed:" << removedFiles << "Kept:" << cacheItems.count() - removedFiles
+                 << "New Size:" << totalSize;
+    }
+#endif
+
+    expiring = false;
+
+    return totalSize;
+}
+
+#ifndef QT_NO_DEBUG_OUTPUT
+void LocalCache::debugStats() {
+    int total = hits + misses;
+    if (total > 0) {
+        qDebug() << "Cache:" << name << '\n'
+                 << "Inserts:" << insertCount << '\n'
+                 << "Requests:" << total << '\n'
+                 << "Hits:" << hits << (hits * 100) / total << "%\n"
+                 << "Misses:" << misses << (misses * 100) / total << "%";
+    }
+}
+#endif
diff --git a/lib/http/src/localcache.h b/lib/http/src/localcache.h
new file mode 100644 (file)
index 0000000..ff07109
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef LOCALCACHE_H
+#define LOCALCACHE_H
+
+#include <QtCore>
+
+/**
+ * @brief Not thread-safe
+ */
+class LocalCache {
+public:
+    static LocalCache *instance(const char *name);
+    ~LocalCache();
+    static QByteArray hash(const QByteArray &s);
+
+    const QByteArray &getName() const { return name; }
+
+    void setMaxSeconds(uint value) { maxSeconds = value; }
+    void setMaxSize(uint value) { maxSize = value; }
+
+    QByteArray value(const QByteArray &key);
+    void insert(const QByteArray &key, const QByteArray &value);
+    bool clear();
+
+private:
+    LocalCache(const QByteArray &name);
+    QString cachePath(const QByteArray &key) const;
+    bool isCached(const QString &path);
+    qint64 expire();
+#ifndef QT_NO_DEBUG_OUTPUT
+    void debugStats();
+#endif
+
+    QByteArray name;
+    QString directory;
+    uint maxSeconds;
+    qint64 maxSize;
+    qint64 size;
+    bool expiring;
+    uint insertCount;
+    struct QueueItem {
+        QByteArray key;
+        QByteArray value;
+    };
+    QVector<QueueItem> insertQueue;
+
+#ifndef QT_NO_DEBUG_OUTPUT
+    uint hits;
+    uint misses;
+#endif
+};
+
+#endif // LOCALCACHE_H
diff --git a/lib/http/src/throttledhttp.cpp b/lib/http/src/throttledhttp.cpp
new file mode 100644 (file)
index 0000000..7cfb3fd
--- /dev/null
@@ -0,0 +1,51 @@
+#include "throttledhttp.h"
+
+ThrottledHttp::ThrottledHttp(Http &http) : http(http), milliseconds(1000) {
+    elapsedTimer.start();
+}
+
+HttpReply *ThrottledHttp::request(const HttpRequest &req) {
+    return new ThrottledHttpReply(http, req, milliseconds, elapsedTimer);
+}
+
+ThrottledHttpReply::ThrottledHttpReply(Http &http,
+                                       const HttpRequest &req,
+                                       int milliseconds,
+                                       QElapsedTimer &elapsedTimer)
+    : http(http), req(req), milliseconds(milliseconds), elapsedTimer(elapsedTimer), timer(nullptr) {
+    checkElapsed();
+}
+
+void ThrottledHttpReply::checkElapsed() {
+    /*
+    static QMutex mutex;
+    QMutexLocker locker(&mutex);
+    */
+
+    const qint64 elapsedSinceLastRequest = elapsedTimer.elapsed();
+    if (elapsedSinceLastRequest < milliseconds) {
+        if (!timer) {
+            timer = new QTimer(this);
+            timer->setSingleShot(true);
+            timer->setTimerType(Qt::PreciseTimer);
+            connect(timer, SIGNAL(timeout()), SLOT(checkElapsed()));
+        }
+        qDebug() << "Throttling" << req.url
+                 << QString("%1ms").arg(milliseconds - elapsedSinceLastRequest);
+        timer->setInterval(milliseconds - elapsedSinceLastRequest);
+        timer->start();
+        return;
+    }
+    elapsedTimer.start();
+    doRequest();
+}
+
+void ThrottledHttpReply::doRequest() {
+    QObject *reply = http.request(req);
+    connect(reply, SIGNAL(data(QByteArray)), SIGNAL(data(QByteArray)));
+    connect(reply, SIGNAL(error(QString)), SIGNAL(error(QString)));
+    connect(reply, SIGNAL(finished(HttpReply)), SIGNAL(finished(HttpReply)));
+
+    // this will cause the deletion of this object once the request is finished
+    setParent(reply);
+}
diff --git a/lib/http/src/throttledhttp.h b/lib/http/src/throttledhttp.h
new file mode 100644 (file)
index 0000000..bd78d28
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef THROTTLEDHTTP_H
+#define THROTTLEDHTTP_H
+
+#include "http.h"
+#include <QtCore>
+#include <QtNetwork>
+
+class ThrottledHttp : public Http {
+public:
+    ThrottledHttp(Http &http = Http::instance());
+    void setMilliseconds(int milliseconds) { this->milliseconds = milliseconds; }
+    HttpReply *request(const HttpRequest &req);
+
+private:
+    Http &http;
+    int milliseconds;
+    QElapsedTimer elapsedTimer;
+};
+
+class ThrottledHttpReply : public HttpReply {
+    Q_OBJECT
+
+public:
+    ThrottledHttpReply(Http &http,
+                       const HttpRequest &req,
+                       int milliseconds,
+                       QElapsedTimer &elapsedTimer);
+    QUrl url() const { return req.url; }
+    int statusCode() const { return 200; }
+    QByteArray body() const { return QByteArray(); }
+
+private slots:
+    void checkElapsed();
+
+private:
+    void doRequest();
+    Http &http;
+    HttpRequest req;
+    int milliseconds;
+    QElapsedTimer &elapsedTimer;
+    QTimer *timer;
+};
+
+#endif // THROTTLEDHTTP_H
diff --git a/lib/idle/README.md b/lib/idle/README.md
new file mode 100644 (file)
index 0000000..a10d818
--- /dev/null
@@ -0,0 +1,9 @@
+# Qt Idle library
+
+This simple Qt library manages system and display sleep on Mac, Windows and Linux. Contributions for other platforms are welcome.
+
+You can use this library under the MIT license and at your own risk. If you do, you're welcome contributing your changes and fixes.
+
+Cheers,
+
+Flavio
\ No newline at end of file
diff --git a/lib/idle/idle.pri b/lib/idle/idle.pri
new file mode 100644 (file)
index 0000000..a70e586
--- /dev/null
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD/src
+DEPENDPATH += $$PWD/src
+
+HEADERS += $$PWD/src/idle.h
+
+mac {
+    SOURCES += $$PWD/src/idle_mac.cpp
+} else:win32 {
+    SOURCES += $$PWD/src/idle_win.cpp
+} else {
+    QT *= dbus
+    SOURCES += $$PWD/src/idle_linux.cpp
+}
diff --git a/lib/idle/src/idle.h b/lib/idle/src/idle.h
new file mode 100644 (file)
index 0000000..4f77d0a
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef IDLE_H
+#define IDLE_H
+
+#include <QString>
+
+class Idle {
+public:
+    static bool preventDisplaySleep(const QString &reason);
+    static bool allowDisplaySleep();
+    static QString displayErrorMessage();
+
+    static bool preventSystemSleep(const QString &reason);
+    static bool allowSystemSleep();
+    static QString systemErrorMessage();
+};
+
+#endif // IDLE_H
diff --git a/lib/idle/src/idle_linux.cpp b/lib/idle/src/idle_linux.cpp
new file mode 100644 (file)
index 0000000..1992a66
--- /dev/null
@@ -0,0 +1,68 @@
+#include "idle.h"
+
+#include <QDBusConnectionInterface>
+#include <QDBusInterface>
+#include <QDBusReply>
+#include <QtCore>
+
+namespace {
+
+const QString fdDisplayService = "org.freedesktop.ScreenSaver";
+const QString fdDisplayPath = "/org/freedesktop/ScreenSaver";
+const QString fdDisplayInterface = fdDisplayService;
+
+const QString gnomeSystemService = "org.gnome.SessionManager";
+const QString gnomeSystemPath = "/org/gnome/SessionManager";
+const QString gnomeSystemInterface = gnomeSystemService;
+
+const QString inhibitMethod = "Inhibit";
+const QString uninhibitMethod = "UnInhibit";
+
+quint32 cookie;
+QString errorMessage;
+
+bool handleReply(const QDBusReply<quint32> &reply) {
+    if (reply.isValid()) {
+        cookie = reply.value();
+        errorMessage.clear();
+        return true;
+    }
+    errorMessage = reply.error().message();
+    return false;
+}
+
+} // namespace
+
+bool Idle::preventDisplaySleep(const QString &reason) {
+    QDBusInterface dbus(fdDisplayService, fdDisplayPath, fdDisplayInterface);
+    QDBusReply<quint32> reply =
+            dbus.call(inhibitMethod, QCoreApplication::applicationName(), reason);
+    return handleReply(reply);
+}
+
+bool Idle::allowDisplaySleep() {
+    QDBusInterface dbus(fdDisplayService, fdDisplayPath, fdDisplayInterface);
+    dbus.call(uninhibitMethod, cookie);
+    return true;
+}
+
+QString Idle::displayErrorMessage() {
+    return errorMessage;
+}
+
+bool Idle::preventSystemSleep(const QString &reason) {
+    QDBusInterface dbus(gnomeSystemService, gnomeSystemPath, gnomeSystemInterface);
+    QDBusReply<quint32> reply =
+            dbus.call(inhibitMethod, QCoreApplication::applicationName(), reason);
+    return handleReply(reply);
+}
+
+bool Idle::allowSystemSleep() {
+    QDBusInterface dbus(gnomeSystemService, gnomeSystemPath, gnomeSystemInterface);
+    dbus.call(uninhibitMethod, cookie);
+    return true;
+}
+
+QString Idle::systemErrorMessage() {
+    return errorMessage;
+}
diff --git a/lib/idle/src/idle_mac.cpp b/lib/idle/src/idle_mac.cpp
new file mode 100644 (file)
index 0000000..ff62bff
--- /dev/null
@@ -0,0 +1,47 @@
+#include "idle.h"
+
+#include <IOKit/pwr_mgt/IOPMLib.h>
+
+namespace {
+
+IOPMAssertionID displayAssertionID = 0;
+IOReturn displayRes = 0;
+
+IOPMAssertionID systemAssertionID = 0;
+IOReturn systemRes = 0;
+
+} // namespace
+
+bool Idle::preventDisplaySleep(const QString &reason) {
+    displayRes = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep,
+                                             kIOPMAssertionLevelOn, reason.toCFString(),
+                                             &displayAssertionID);
+    return displayRes == kIOReturnSuccess;
+}
+
+bool Idle::allowDisplaySleep() {
+    displayRes = IOPMAssertionRelease(displayAssertionID);
+    return displayRes == kIOReturnSuccess;
+}
+
+QString Idle::displayErrorMessage() {
+    return QString();
+    // return QString::fromUtf8(IOService::stringFromReturn(displayRes));
+}
+
+bool Idle::preventSystemSleep(const QString &reason) {
+    systemRes = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleSystemSleep,
+                                            kIOPMAssertionLevelOn, reason.toCFString(),
+                                            &systemAssertionID);
+    return systemRes == kIOReturnSuccess;
+}
+
+bool Idle::allowSystemSleep() {
+    systemRes = IOPMAssertionRelease(systemAssertionID);
+    return systemRes == kIOReturnSuccess;
+}
+
+QString Idle::systemErrorMessage() {
+    return QString();
+    // return QString::fromUtf8(IOService::stringFromReturn(systemRes));
+}
diff --git a/lib/idle/src/idle_win.cpp b/lib/idle/src/idle_win.cpp
new file mode 100644 (file)
index 0000000..2f189ef
--- /dev/null
@@ -0,0 +1,35 @@
+#include "idle.h"
+
+#include "windows.h"
+
+namespace {
+EXECUTION_STATE executionState;
+}
+
+bool Idle::preventDisplaySleep(const QString &reason) {
+    executionState = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
+    return true;
+}
+
+bool Idle::allowDisplaySleep() {
+    SetThreadExecutionState(ES_CONTINUOUS | executionState);
+    return true;
+}
+
+QString Idle::displayErrorMessage() {
+    return QString();
+}
+
+bool Idle::preventSystemSleep(const QString &reason) {
+    executionState = SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
+    return true;
+}
+
+bool Idle::allowSystemSleep() {
+    SetThreadExecutionState(ES_CONTINUOUS | executionState);
+    return true;
+}
+
+QString Idle::systemErrorMessage() {
+    return QString();
+}
diff --git a/lib/media/.gitignore b/lib/media/.gitignore
new file mode 100644 (file)
index 0000000..e43b0f9
--- /dev/null
@@ -0,0 +1 @@
+.DS_Store
diff --git a/lib/media/README.md b/lib/media/README.md
new file mode 100644 (file)
index 0000000..4aeec30
--- /dev/null
@@ -0,0 +1,9 @@
+# Qt Media Library Abstraction
+
+This is a simple wrapper around a multimedia playback library.
+
+Define `MEDIA_QTAV` to link to QtAV or `MEDIA_MPV` to link to libmpv (>=0.29.0).
+
+`MEDIA_AUDIOONLY` can be defined if the application does not need video.
+
+You can use this library under the MIT license and at your own risk. If you do, you're welcome contributing your changes and fixes.
diff --git a/lib/media/media.pri b/lib/media/media.pri
new file mode 100644 (file)
index 0000000..f6b64fa
--- /dev/null
@@ -0,0 +1,37 @@
+INCLUDEPATH += $$PWD/src
+DEPENDPATH += $$PWD/src
+
+HEADERS += $$PWD/src/media.h
+
+contains(DEFINES, MEDIA_QTAV) {
+    QT += avwidgets
+    INCLUDEPATH += $$PWD/src/qtav
+    DEPENDPATH += $$PWD/src/qtav
+    HEADERS += $$PWD/src/mediaqtav.h
+    SOURCES += $$PWD/src/mediaqtav.cpp
+}
+
+contains(DEFINES, MEDIA_MPV) {
+    QT *= gui
+
+    LIBS += -lmpv
+mac {
+    # useful for homebrew: brew install mpv
+    # LIBS += -L/usr/local/lib
+    # INCLUDEPATH += /usr/local/include
+}
+
+    INCLUDEPATH += $$PWD/src/mpv
+    DEPENDPATH += $$PWD/src/mpv
+    HEADERS += $$PWD/src/mpv/mediampv.h
+    SOURCES += $$PWD/src/mpv/mediampv.cpp
+
+    !contains(DEFINES, MEDIA_AUDIOONLY) {
+        QT *= widgets
+        unix:!mac {
+            QT *= x11extras
+        }
+        HEADERS += $$PWD/src/mpv/mpvwidget.h
+        SOURCES += $$PWD/src/mpv/mpvwidget.cpp
+    }
+}
diff --git a/lib/media/src/media.h b/lib/media/src/media.h
new file mode 100644 (file)
index 0000000..a14327f
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef MEDIA_H
+#define MEDIA_H
+
+#include <QtCore>
+#ifndef MEDIA_AUDIOONLY
+#include <QtWidgets>
+#endif
+
+class Media : public QObject {
+    Q_OBJECT
+
+public:
+    enum State {
+        StoppedState,
+        LoadingState,
+        BufferingState,
+        PlayingState,
+        PausedState,
+        ErrorState
+    };
+    Q_ENUM(State)
+
+    Media(QObject *parent = nullptr) : QObject(parent) {
+        qRegisterMetaType<Media::State>("Media::State");
+    }
+    virtual void setAudioOnly(bool value) = 0;
+#ifndef MEDIA_AUDIOONLY
+    virtual void setRenderer(const QString &name) = 0;
+    virtual QWidget *videoWidget() = 0;
+    virtual void playSeparateAudioAndVideo(const QString &video, const QString &audio) = 0;
+    virtual void snapshot() = 0;
+#endif
+    virtual void init() = 0;
+
+    virtual Media::State state() const = 0;
+
+    virtual void play(const QString &file) = 0;
+    virtual void play() = 0;
+    virtual void pause() = 0;
+    virtual void stop() = 0;
+    virtual void seek(qint64 ms) = 0;
+    virtual QString file() const = 0;
+
+    virtual void setBufferMilliseconds(qint64 value) = 0;
+    virtual void setUserAgent(const QString &value) = 0;
+
+    virtual void enqueue(const QString &file) = 0;
+    virtual void clearQueue() = 0;
+    virtual bool hasQueue() const = 0;
+
+    virtual qint64 position() const = 0;
+    virtual qint64 duration() const = 0;
+    virtual qint64 remainingTime() const = 0;
+
+    virtual qreal volume() const = 0;
+    virtual void setVolume(qreal value) = 0;
+
+    virtual bool volumeMuted() const = 0;
+    virtual void setVolumeMuted(bool value) = 0;
+
+    virtual QString errorString() const = 0;
+
+signals:
+    void error(const QString &message);
+    void sourceChanged();
+    void bufferStatus(qreal value);
+    void loaded();
+    void started();
+    void stopped();
+    void paused(bool p);
+    void stateChanged(Media::State state);
+    void positionChanged(qint64 ms);
+    void aboutToFinish();
+    void finished();
+    void volumeChanged(qreal value);
+    void volumeMutedChanged(bool value);
+#ifndef MEDIA_AUDIOONLY
+    void snapshotReady(const QImage &image);
+#endif
+};
+
+#endif // MEDIA_H
diff --git a/lib/media/src/mpv/mediampv.cpp b/lib/media/src/mpv/mediampv.cpp
new file mode 100644 (file)
index 0000000..b077530
--- /dev/null
@@ -0,0 +1,410 @@
+#include "mediampv.h"
+
+#include <clocale>
+#include <mpv/qthelper.hpp>
+
+#ifndef MEDIA_AUDIOONLY
+#include "mpvwidget.h"
+#endif
+
+namespace {
+void wakeup(void *ctx) {
+    // This callback is invoked from any mpv thread (but possibly also
+    // recursively from a thread that is calling the mpv API). Just notify
+    // the Qt GUI thread to wake up (so that it can process events with
+    // mpv_wait_event()), and return as quickly as possible.
+    MediaMPV *mediaMPV = (MediaMPV *)ctx;
+    emit mediaMPV->mpvEvents();
+}
+
+} // namespace
+
+MediaMPV::MediaMPV(QObject *parent) : Media(parent), widget(nullptr) {
+    QThread *thread = new QThread(this);
+    thread->start();
+    moveToThread(thread);
+    connect(this, &QObject::destroyed, thread, &QThread::quit);
+
+#ifndef Q_OS_WIN
+    // Qt sets the locale in the QApplication constructor, but libmpv requires
+    // the LC_NUMERIC category to be set to "C", so change it back.
+    std::setlocale(LC_NUMERIC, "C");
+#endif
+
+    mpv = mpv_create();
+    if (!mpv) qFatal("Cannot create MPV instance");
+
+    mpv_set_option_string(mpv, "config", "no");
+    mpv_set_option_string(mpv, "audio-display", "no");
+    mpv_set_option_string(mpv, "gapless-audio", "weak");
+    mpv_set_option_string(mpv, "idle", "yes");
+    mpv_set_option_string(mpv, "input-default-bindings", "no");
+    mpv_set_option_string(mpv, "input-vo-keyboard", "no");
+    mpv_set_option_string(mpv, "input-cursor", "no");
+    mpv_set_option_string(mpv, "input-media-keys", "no");
+    mpv_set_option_string(mpv, "ytdl", "no");
+    mpv_set_option_string(mpv, "fs", "no");
+    mpv_set_option_string(mpv, "osd-level", "0");
+    mpv_set_option_string(mpv, "quiet", "yes");
+    mpv_set_option_string(mpv, "load-scripts", "no");
+    mpv_set_option_string(mpv, "audio-client-name",
+                          QCoreApplication::applicationName().toUtf8().data());
+    mpv_set_option_string(mpv, "hwdec", "auto");
+    mpv_set_option_string(mpv, "vo", "libmpv");
+
+    mpv_set_option_string(mpv, "cache", "no");
+    mpv_set_option_string(mpv, "demuxer-max-bytes", "10485760");
+    mpv_set_option_string(mpv, "demuxer-max-back-bytes", "10485760");
+
+#ifdef MEDIA_MPV_WID
+    widget = new QWidget();
+    widget->setAttribute(Qt::WA_DontCreateNativeAncestors);
+    widget->setAttribute(Qt::WA_NativeWindow);
+    // If you have a HWND, use: int64_t wid = (intptr_t)hwnd;
+    int64_t wid = (intptr_t)widget->winId();
+    mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
+#endif
+
+#ifndef QT_NO_DEBUG_OUTPUT
+    // Request log messages
+    // They are received as MPV_EVENT_LOG_MESSAGE.
+    mpv_request_log_messages(mpv, "info");
+#endif
+
+    // From this point on, the wakeup function will be called. The callback
+    // can come from any thread, so we use the QueuedConnection mechanism to
+    // relay the wakeup in a thread-safe way.
+    connect(this, &MediaMPV::mpvEvents, this, &MediaMPV::onMpvEvents, Qt::QueuedConnection);
+    mpv_set_wakeup_callback(mpv, wakeup, this);
+
+    if (mpv_initialize(mpv) < 0) qFatal("mpv failed to initialize");
+
+    // Let us receive property change events with MPV_EVENT_PROPERTY_CHANGE if
+    // this property changes.
+    mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
+    mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
+    mpv_observe_property(mpv, 0, "volume", MPV_FORMAT_DOUBLE);
+    mpv_observe_property(mpv, 0, "mute", MPV_FORMAT_FLAG);
+}
+
+// This slot is invoked by wakeup() (through the mpvEvents signal).
+void MediaMPV::onMpvEvents() {
+    // Process all events, until the event queue is empty.
+    while (mpv) {
+        mpv_event *event = mpv_wait_event(mpv, 0);
+        if (event->event_id == MPV_EVENT_NONE) break;
+        handleMpvEvent(event);
+    }
+}
+
+void MediaMPV::checkAboutToFinish(qint64 position) {
+    if (!aboutToFinishEmitted && currentState == Media::PlayingState) {
+        const qint64 dur = duration();
+        if (dur > 0 && dur - position < 5000) {
+            aboutToFinishEmitted = true;
+            qDebug() << "aboutToFinish" << position << dur;
+            emit aboutToFinish();
+        }
+    }
+}
+
+void MediaMPV::handleMpvEvent(mpv_event *event) {
+    // qDebug() << event->data;
+    switch (event->event_id) {
+    case MPV_EVENT_START_FILE:
+        clearTrackState();
+        emit sourceChanged();
+        setState(Media::LoadingState);
+        break;
+
+    case MPV_EVENT_SEEK:
+        setState(Media::BufferingState);
+        break;
+
+    case MPV_EVENT_FILE_LOADED:
+        setState(Media::PlayingState);
+        break;
+
+    case MPV_EVENT_PLAYBACK_RESTART:
+    case MPV_EVENT_UNPAUSE:
+        setState(Media::PlayingState);
+        break;
+
+    case MPV_EVENT_END_FILE: {
+        struct mpv_event_end_file *eof_event = (struct mpv_event_end_file *)event->data;
+        if (eof_event->reason == MPV_END_FILE_REASON_EOF ||
+            eof_event->reason == MPV_END_FILE_REASON_ERROR) {
+            qDebug() << "Finished";
+            setState(Media::StoppedState);
+            emit finished();
+        }
+        break;
+    }
+
+    case MPV_EVENT_PAUSE:
+        setState(Media::PausedState);
+        break;
+
+    case MPV_EVENT_PROPERTY_CHANGE: {
+        mpv_event_property *prop = (mpv_event_property *)event->data;
+        // qDebug() << prop->name << prop->data;
+
+        if (strcmp(prop->name, "time-pos") == 0) {
+            if (prop->format == MPV_FORMAT_DOUBLE) {
+                double seconds = *(double *)prop->data;
+                qint64 ms = seconds * 1000.;
+                emit positionChanged(ms);
+                checkAboutToFinish(ms);
+            }
+        }
+
+        else if (strcmp(prop->name, "volume") == 0) {
+            if (prop->format == MPV_FORMAT_DOUBLE) {
+                double vol = *(double *)prop->data;
+                emit volumeChanged(vol / 100.);
+            }
+        }
+
+        else if (strcmp(prop->name, "mute") == 0) {
+            if (prop->format == MPV_FORMAT_FLAG) {
+                int mute = *(int *)prop->data;
+                emit volumeMutedChanged(mute == 1);
+            }
+        }
+
+        break;
+    }
+
+    case MPV_EVENT_LOG_MESSAGE: {
+        struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data;
+        qDebug() << "[" << msg->prefix << "] " << msg->level << ": " << msg->text;
+
+        if (msg->log_level == MPV_LOG_LEVEL_ERROR) {
+            lastErrorString = QString::fromUtf8(msg->text);
+            emit error(lastErrorString);
+        }
+
+        break;
+    }
+
+    case MPV_EVENT_SHUTDOWN: {
+        mpv_terminate_destroy(mpv);
+        mpv = nullptr;
+        break;
+    }
+
+    default:;
+        // Unhandled events
+    }
+}
+
+void MediaMPV::sendCommand(const char *args[]) {
+    // mpv_command_async(mpv, 0, args);
+    mpv_command(mpv, args);
+}
+
+void MediaMPV::setState(Media::State value) {
+    if (value != currentState) {
+        currentState = value;
+        emit stateChanged(currentState);
+    }
+}
+
+void MediaMPV::clearTrackState() {
+    lastErrorString.clear();
+    aboutToFinishEmitted = false;
+}
+
+void MediaMPV::setAudioOnly(bool value) {
+    Q_UNUSED(value);
+}
+
+#ifndef MEDIA_AUDIOONLY
+
+void MediaMPV::setRenderer(const QString &name) {
+    mpv_set_option_string(mpv, "vo", name.toUtf8().data());
+}
+
+QWidget *MediaMPV::videoWidget() {
+    if (!widget) {
+        widget = new MpvWidget(mpv);
+    }
+    return widget;
+}
+
+void MediaMPV::playSeparateAudioAndVideo(const QString &video, const QString &audio) {
+    const QByteArray fileUtf8 = video.toUtf8();
+    const char *args[] = {"loadfile", fileUtf8.constData(), nullptr};
+    sendCommand(args);
+
+    qApp->processEvents();
+
+    const QByteArray audioUtf8 = audio.toUtf8();
+    const char *args2[] = {"audio-add", audioUtf8.constData(), nullptr};
+    sendCommand(args2);
+
+    clearTrackState();
+}
+
+void MediaMPV::snapshot() {
+    if (currentState == State::StoppedState) return;
+
+    const QVariantList args = {"screenshot-raw", "video"};
+    mpv::qt::node_builder nodeBuilder(args);
+    mpv_node node;
+    const int ret = mpv_command_node(mpv, nodeBuilder.node(), &node);
+    if (ret < 0) {
+        emit error("Cannot take snapshot");
+        return;
+    }
+
+    mpv::qt::node_autofree auto_free(&node);
+    if (node.format != MPV_FORMAT_NODE_MAP) {
+        emit error("Cannot take snapshot");
+        return;
+    }
+
+    int width = 0;
+    int height = 0;
+    int stride = 0;
+    mpv_node_list *list = node.u.list;
+    uchar *data = nullptr;
+
+    for (int i = 0; i < list->num; ++i) {
+        const char *key = list->keys[i];
+        if (strcmp(key, "w") == 0) {
+            width = static_cast<int>(list->values[i].u.int64);
+        } else if (strcmp(key, "h") == 0) {
+            height = static_cast<int>(list->values[i].u.int64);
+        } else if (strcmp(key, "stride") == 0) {
+            stride = static_cast<int>(list->values[i].u.int64);
+        } else if (strcmp(key, "data") == 0) {
+            data = static_cast<uchar *>(list->values[i].u.ba->data);
+        }
+    }
+
+    if (data != nullptr) {
+        QImage img = QImage(data, width, height, stride, QImage::Format_RGB32);
+        img.bits();
+        emit snapshotReady(img);
+    }
+}
+
+#endif
+
+void MediaMPV::init() {}
+
+Media::State MediaMPV::state() const {
+    return currentState;
+}
+
+void MediaMPV::play(const QString &file) {
+    const QByteArray fileUtf8 = file.toUtf8();
+    const char *args[] = {"loadfile", fileUtf8.constData(), nullptr};
+    sendCommand(args);
+
+    clearTrackState();
+    if (currentState == Media::PausedState) play();
+}
+
+void MediaMPV::play() {
+    int flag = 0;
+    mpv_set_property_async(mpv, 0, "pause", MPV_FORMAT_FLAG, &flag);
+}
+
+void MediaMPV::pause() {
+    int flag = 1;
+    mpv_set_property_async(mpv, 0, "pause", MPV_FORMAT_FLAG, &flag);
+}
+
+void MediaMPV::stop() {
+    const char *args[] = {"stop", nullptr};
+    sendCommand(args);
+}
+
+void MediaMPV::seek(qint64 ms) {
+    double seconds = ms / 1000.;
+    QByteArray ba = QString::number(seconds).toUtf8();
+    const char *args[] = {"seek", ba.constData(), "absolute", nullptr};
+    sendCommand(args);
+}
+
+QString MediaMPV::file() const {
+    char *path;
+    mpv_get_property(mpv, "path", MPV_FORMAT_STRING, &path);
+    return QString::fromUtf8(path);
+}
+
+void MediaMPV::setBufferMilliseconds(qint64 value) {
+    Q_UNUSED(value);
+    // Not implemented
+}
+
+void MediaMPV::setUserAgent(const QString &value) {
+    mpv_set_option_string(mpv, "user-agent", value.toUtf8());
+}
+
+void MediaMPV::enqueue(const QString &file) {
+    const QByteArray fileUtf8 = file.toUtf8();
+    const char *args[] = {"loadfile", fileUtf8.constData(), "append", nullptr};
+    sendCommand(args);
+}
+
+void MediaMPV::clearQueue() {
+    const char *args[] = {"playlist-clear", nullptr};
+    sendCommand(args);
+}
+
+bool MediaMPV::hasQueue() const {
+    mpv_node node;
+    int r = mpv_get_property(mpv, "playlist", MPV_FORMAT_NODE, &node);
+    if (r < 0) return false;
+    QVariant v = mpv::qt::node_to_variant(&node);
+    mpv_free_node_contents(&node);
+    QVariantList list = v.toList();
+    return list.count() > 1;
+}
+
+qint64 MediaMPV::position() const {
+    double seconds;
+    mpv_get_property(mpv, "time-pos", MPV_FORMAT_DOUBLE, &seconds);
+    return seconds * 1000.;
+}
+
+qint64 MediaMPV::duration() const {
+    double seconds;
+    mpv_get_property(mpv, "duration", MPV_FORMAT_DOUBLE, &seconds);
+    return seconds * 1000.;
+}
+
+qint64 MediaMPV::remainingTime() const {
+    double seconds;
+    mpv_get_property(mpv, "time-remaining", MPV_FORMAT_DOUBLE, &seconds);
+    return seconds * 1000.;
+}
+
+qreal MediaMPV::volume() const {
+    double vol;
+    mpv_get_property(mpv, "volume", MPV_FORMAT_DOUBLE, &vol);
+    return vol / 100.;
+}
+
+void MediaMPV::setVolume(qreal value) {
+    double percent = value * 100.;
+    mpv_set_property_async(mpv, 0, "volume", MPV_FORMAT_DOUBLE, &percent);
+}
+
+bool MediaMPV::volumeMuted() const {
+    int mute;
+    mpv_get_property(mpv, "mute", MPV_FORMAT_FLAG, &mute);
+    return mute == 1;
+}
+
+void MediaMPV::setVolumeMuted(bool value) {
+    int mute = value ? 1 : 0;
+    mpv_set_property(mpv, "mute", MPV_FORMAT_FLAG, &mute);
+}
+
+QString MediaMPV::errorString() const {
+    return lastErrorString;
+}
diff --git a/lib/media/src/mpv/mediampv.h b/lib/media/src/mpv/mediampv.h
new file mode 100644 (file)
index 0000000..1621148
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef MEDIAMPV_H
+#define MEDIAMPV_H
+
+#include <QtCore>
+
+#include "media.h"
+#include <mpv/client.h>
+
+class MediaMPV : public Media {
+    Q_OBJECT
+
+public:
+    MediaMPV(QObject *parent = nullptr);
+
+    void setAudioOnly(bool value);
+#ifndef MEDIA_AUDIOONLY
+    void setRenderer(const QString &name);
+    QWidget *videoWidget();
+    void playSeparateAudioAndVideo(const QString &video, const QString &audio);
+    void snapshot();
+#endif
+    void init();
+    Media::State state() const;
+    void play(const QString &file);
+    void play();
+    void pause();
+    void stop();
+    void seek(qint64 ms);
+    QString file() const;
+    void setBufferMilliseconds(qint64 value);
+    void setUserAgent(const QString &value);
+    void enqueue(const QString &file);
+    void clearQueue();
+    bool hasQueue() const;
+    qint64 position() const;
+    qint64 duration() const;
+    qint64 remainingTime() const;
+    qreal volume() const;
+    void setVolume(qreal value);
+    bool volumeMuted() const;
+    void setVolumeMuted(bool value);
+    QString errorString() const;
+
+private slots:
+    void onMpvEvents();
+    void checkAboutToFinish(qint64 position);
+
+signals:
+    void mpvEvents();
+
+private:
+    void handleMpvEvent(mpv_event *event);
+    void sendCommand(const char *args[]);
+    void setState(Media::State value);
+    void clearTrackState();
+
+    QWidget *widget;
+    mpv_handle *mpv;
+    Media::State currentState = Media::StoppedState;
+    bool aboutToFinishEmitted = false;
+    QString lastErrorString;
+};
+
+#endif // MEDIAMPV_H
diff --git a/lib/media/src/mpv/mpvwidget.cpp b/lib/media/src/mpv/mpvwidget.cpp
new file mode 100644 (file)
index 0000000..335012d
--- /dev/null
@@ -0,0 +1,102 @@
+#include "mpvwidget.h"
+#include <stdexcept>
+
+#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
+#include <QtX11Extras/QX11Info>
+#endif
+
+static void *get_proc_address(void *ctx, const char *name) {
+    Q_UNUSED(ctx);
+    QOpenGLContext *glctx = QOpenGLContext::currentContext();
+    if (!glctx) return nullptr;
+    return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name)));
+}
+
+MpvWidget::MpvWidget(mpv_handle *mpv, QWidget *parent, Qt::WindowFlags f)
+    : QOpenGLWidget(parent, f), mpv(mpv), mpvContext(nullptr) {
+    moveToThread(qApp->thread());
+}
+
+MpvWidget::~MpvWidget() {
+    makeCurrent();
+    if (mpvContext) mpv_render_context_free(mpvContext);
+    mpv_terminate_destroy(mpv);
+}
+
+void MpvWidget::initializeGL() {
+    if (mpvContext) qFatal("Already initialized");
+
+    QWidget *nativeParent = nativeParentWidget();
+    qDebug() << "initializeGL" << nativeParent;
+    if (nativeParent == nullptr) qFatal("No native parent");
+
+    mpv_opengl_init_params gl_init_params{get_proc_address, this, nullptr};
+    mpv_render_param params[]{{MPV_RENDER_PARAM_API_TYPE, (void *)MPV_RENDER_API_TYPE_OPENGL},
+                              {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},
+                              {MPV_RENDER_PARAM_INVALID, nullptr},
+                              {MPV_RENDER_PARAM_INVALID, nullptr}};
+
+#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
+    const QString platformName = QGuiApplication::platformName();
+    if (platformName.contains("xcb")) {
+        params[2].type = MPV_RENDER_PARAM_X11_DISPLAY;
+        params[2].data = (void *)QX11Info::display();
+        qDebug() << platformName << params[2].data;
+    } else if (platformName.contains("wayland")) {
+        qWarning() << "Wayland not supported";
+    }
+#endif
+
+    if (mpv_render_context_create(&mpvContext, mpv, params) < 0)
+        qFatal("failed to initialize mpv GL context");
+    mpv_render_context_set_update_callback(mpvContext, MpvWidget::onUpdate, (void *)this);
+
+    connect(this, &QOpenGLWidget::frameSwapped, this, &MpvWidget::onFrameSwapped);
+}
+
+void MpvWidget::resizeGL(int w, int h) {
+    qreal r = devicePixelRatioF();
+    glWidth = int(w * r);
+    glHeight = int(h * r);
+}
+
+void MpvWidget::paintGL() {
+    mpv_opengl_fbo fbo{static_cast<int>(defaultFramebufferObject()), glWidth, glHeight, 0};
+
+    static bool yes = true;
+    mpv_render_param params[] = {{MPV_RENDER_PARAM_OPENGL_FBO, &fbo},
+                                 {MPV_RENDER_PARAM_FLIP_Y, &yes},
+                                 {MPV_RENDER_PARAM_INVALID, nullptr}};
+    // See render_gl.h on what OpenGL environment mpv expects, and
+    // other API details.
+    mpv_render_context_render(mpvContext, params);
+}
+
+// Make Qt invoke mpv_opengl_cb_draw() to draw a new/updated video frame.
+void MpvWidget::maybeUpdate() {
+    // If the Qt window is not visible, Qt's update() will just skip rendering.
+    // This confuses mpv's opengl-cb API, and may lead to small occasional
+    // freezes due to video rendering timing out.
+    // Handle this by manually redrawing.
+    // Note: Qt doesn't seem to provide a way to query whether update() will
+    //       be skipped, and the following code still fails when e.g. switching
+    //       to a different workspace with a reparenting window manager.
+    if (!updatesEnabled() || isHidden() || window()->isHidden() || window()->isMinimized()) {
+        makeCurrent();
+        paintGL();
+        QOpenGLContext *c = context();
+        c->swapBuffers(c->surface());
+        doneCurrent();
+        mpv_render_context_report_swap(mpvContext);
+    } else {
+        update();
+    }
+}
+
+void MpvWidget::onFrameSwapped() {
+    mpv_render_context_report_swap(mpvContext);
+}
+
+void MpvWidget::onUpdate(void *ctx) {
+    QMetaObject::invokeMethod((MpvWidget *)ctx, "maybeUpdate");
+}
diff --git a/lib/media/src/mpv/mpvwidget.h b/lib/media/src/mpv/mpvwidget.h
new file mode 100644 (file)
index 0000000..c647311
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef PLAYERWINDOW_H
+#define PLAYERWINDOW_H
+
+#include <QtWidgets>
+#include <mpv/client.h>
+#include <mpv/qthelper.hpp>
+#include <mpv/render_gl.h>
+
+class MpvWidget Q_DECL_FINAL : public QOpenGLWidget {
+    Q_OBJECT
+
+public:
+    MpvWidget(mpv_handle *mpv, QWidget *parent = nullptr, Qt::WindowFlags f = nullptr);
+    ~MpvWidget() Q_DECL_OVERRIDE;
+
+    QSize sizeHint() const Q_DECL_OVERRIDE { return QSize(480, 270); }
+
+protected:
+    void initializeGL() Q_DECL_OVERRIDE;
+    void resizeGL(int w, int h) Q_DECL_OVERRIDE;
+    void paintGL() Q_DECL_OVERRIDE;
+
+private slots:
+    void maybeUpdate();
+    void onFrameSwapped();
+
+private:
+    static void onUpdate(void *ctx);
+
+    mpv_handle *mpv;
+    mpv_render_context *mpvContext;
+    int glWidth;
+    int glHeight;
+};
+
+#endif // PLAYERWINDOW_H
diff --git a/lib/media/src/qtav/mediaqtav.cpp b/lib/media/src/qtav/mediaqtav.cpp
new file mode 100644 (file)
index 0000000..c5a095b
--- /dev/null
@@ -0,0 +1,369 @@
+#include "mediaqtav.h"
+
+namespace {
+
+#if defined(Q_OS_ANDROID) || defined(Q_OS_MAC)
+const QtAV::VideoRendererId defaultRenderer = QtAV::VideoRendererId_OpenGLWidget;
+#else
+const QtAV::VideoRendererId defaultRenderer = QtAV::VideoRendererId_GLWidget2;
+#endif
+
+#ifndef MEDIA_AUDIOONLY
+QtAV::VideoRendererId rendererIdFor(const QString &name) {
+    static const struct {
+        const char *name;
+        QtAV::VideoRendererId id;
+    } renderers[] = {{"opengl", QtAV::VideoRendererId_OpenGLWidget},
+                     {"gl", QtAV::VideoRendererId_GLWidget2},
+                     {"d2d", QtAV::VideoRendererId_Direct2D},
+                     {"gdi", QtAV::VideoRendererId_GDI},
+                     {"xv", QtAV::VideoRendererId_XV},
+                     {"x11", QtAV::VideoRendererId_X11},
+                     {"qt", QtAV::VideoRendererId_Widget}};
+
+    for (int i = 0; renderers[i].name; ++i) {
+        if (name == QLatin1String(renderers[i].name)) return renderers[i].id;
+    }
+
+    return defaultRenderer;
+}
+#endif
+
+Media::State stateFor(QtAV::AVPlayer::State state) {
+    static const QMap<QtAV::AVPlayer::State, Media::State> map{
+            {QtAV::AVPlayer::StoppedState, Media::StoppedState},
+            {QtAV::AVPlayer::PlayingState, Media::PlayingState},
+            {QtAV::AVPlayer::PausedState, Media::PausedState}};
+    return map[state];
+}
+
+} // namespace
+
+MediaQtAV::MediaQtAV(QObject *parent)
+    : Media(parent), player1(nullptr), player2(nullptr), currentPlayer(nullptr) {
+#ifndef MEDIA_AUDIOONLY
+    rendererId = defaultRenderer;
+#endif
+}
+
+#ifndef MEDIA_AUDIOONLY
+void MediaQtAV::setRenderer(const QString &name) {
+    rendererId = rendererIdFor(name);
+}
+#endif
+
+void MediaQtAV::setAudioOnly(bool value) {
+    audioOnly = value;
+}
+
+void MediaQtAV::init() {
+#ifndef QT_NO_DEBUG_OUTPUT
+    QtAV::setLogLevel(QtAV::LogAll);
+#endif
+
+#ifndef MEDIA_AUDIOONLY
+    QtAV::Widgets::registerRenderers();
+#endif
+    player1 = createPlayer(audioOnly);
+    setCurrentPlayer(player1);
+}
+
+#ifndef MEDIA_AUDIOONLY
+QWidget *MediaQtAV::videoWidget() {
+    return currentPlayer->renderer()->widget();
+}
+#endif
+
+Media::State MediaQtAV::state() const {
+    if (currentPlayer->mediaStatus() == QtAV::LoadingMedia) return LoadingState;
+    if (currentPlayer->bufferProgress() > 0. && currentPlayer->bufferProgress() < 1.)
+        return BufferingState;
+    return stateFor(currentPlayer->state());
+}
+
+void MediaQtAV::play(const QString &file) {
+    if (currentPlayer->isPlaying()) {
+        smoothSourceChange(file, QString());
+        return;
+    }
+
+#ifndef MEDIA_AUDIOONLY
+    if (!currentPlayer->externalAudio().isEmpty()) currentPlayer->setExternalAudio(QString());
+#endif
+    currentPlayer->play(file);
+    aboutToFinishEmitted = false;
+    lastErrorString.clear();
+    clearQueue();
+}
+
+#ifndef MEDIA_AUDIOONLY
+void MediaQtAV::playSeparateAudioAndVideo(const QString &video, const QString &audio) {
+    if (currentPlayer->isPlaying()) {
+        smoothSourceChange(video, audio);
+        return;
+    }
+    currentPlayer->stop();
+    currentPlayer->setExternalAudio(audio);
+    currentPlayer->play(video);
+    aboutToFinishEmitted = false;
+    lastErrorString.clear();
+    clearQueue();
+}
+
+void MediaQtAV::snapshot() {
+    auto videoCapture = currentPlayer->videoCapture();
+    connect(videoCapture, &QtAV::VideoCapture::imageCaptured, this, &Media::snapshotReady);
+    connect(videoCapture, &QtAV::VideoCapture::failed, this,
+            [this] { emit snapshotReady(QImage()); });
+    videoCapture->capture();
+}
+#endif
+
+void MediaQtAV::play() {
+    if (currentPlayer->isPaused())
+        currentPlayer->togglePause();
+    else
+        currentPlayer->play();
+}
+
+QString MediaQtAV::file() const {
+    return currentPlayer->file();
+}
+
+void MediaQtAV::setBufferMilliseconds(qint64 value) {
+    currentPlayer->setBufferValue(value);
+}
+
+void MediaQtAV::setUserAgent(const QString &value) {
+    qDebug() << "Setting user agent to" << value;
+    auto options = currentPlayer->optionsForFormat();
+    options.insert(QStringLiteral("user_agent"), value);
+    currentPlayer->setOptionsForFormat(options);
+}
+
+void MediaQtAV::enqueue(const QString &file) {
+    queue << file;
+    if (queue.size() == 1) {
+        qDebug() << "Preloading" << file;
+        auto nextPlayer = player1;
+        if (currentPlayer == player1) {
+            if (player2 == nullptr) player2 = createPlayer(audioOnly);
+            nextPlayer = player2;
+        }
+        nextPlayer->setFile(file);
+        nextPlayer->load();
+    }
+}
+
+void MediaQtAV::clearQueue() {
+    queue.clear();
+}
+
+bool MediaQtAV::hasQueue() const {
+    return !queue.isEmpty();
+}
+
+qint64 MediaQtAV::position() const {
+    return currentPlayer->position();
+}
+
+qint64 MediaQtAV::duration() const {
+    return currentPlayer->duration();
+}
+
+qint64 MediaQtAV::remainingTime() const {
+    return currentPlayer->duration() - currentPlayer->position();
+}
+
+qreal MediaQtAV::volume() const {
+    return currentPlayer->audio()->volume();
+}
+
+void MediaQtAV::setVolume(qreal value) {
+    auto audio = currentPlayer->audio();
+    if (!audio->isOpen()) audio->open();
+    audio->setVolume(value);
+}
+
+bool MediaQtAV::volumeMuted() const {
+    return currentPlayer->audio()->isMute();
+}
+
+void MediaQtAV::setVolumeMuted(bool value) {
+    currentPlayer->audio()->setMute(value);
+}
+
+QString MediaQtAV::errorString() const {
+    return lastErrorString;
+}
+
+void MediaQtAV::checkAboutToFinish(qint64 position) {
+    if (!aboutToFinishEmitted && currentPlayer->isPlaying() &&
+        duration() - position < currentPlayer->bufferValue()) {
+        aboutToFinishEmitted = true;
+        emit aboutToFinish();
+    }
+}
+
+void MediaQtAV::onMediaStatusChange(QtAV::MediaStatus status) {
+    qDebug() << QVariant(status).toString();
+
+    switch (status) {
+    case QtAV::LoadingMedia:
+        emit stateChanged(LoadingState);
+        break;
+    case QtAV::BufferedMedia:
+        if (currentPlayer->state() == QtAV::AVPlayer::PlayingState) emit stateChanged(PlayingState);
+        break;
+    case QtAV::BufferingMedia:
+        emit stateChanged(BufferingState);
+        break;
+    case QtAV::EndOfMedia:
+        if (queue.isEmpty())
+            emit finished();
+        else {
+            auto nextPlayer = currentPlayer == player1 ? player2 : player1;
+            if (nextPlayer->isLoaded()) {
+                qDebug() << "Preloaded";
+                setCurrentPlayer(nextPlayer);
+                nextPlayer->play();
+                aboutToFinishEmitted = false;
+                lastErrorString.clear();
+                emit sourceChanged();
+                queue.dequeue();
+            } else {
+                qDebug() << "Not preloaded";
+                currentPlayer->play(queue.dequeue());
+            }
+        }
+        break;
+    case QtAV::InvalidMedia:
+        emit stateChanged(Media::ErrorState);
+        break;
+    default:
+        qDebug() << "Unhandled" << QVariant(status).toString();
+    }
+}
+
+void MediaQtAV::onAVError(const QtAV::AVError &e) {
+    lastErrorString = e.string();
+    qDebug() << lastErrorString;
+    emit error(lastErrorString);
+}
+
+void MediaQtAV::pause() {
+    currentPlayer->pause();
+}
+
+void MediaQtAV::stop() {
+    currentPlayer->stop();
+}
+
+void MediaQtAV::seek(qint64 ms) {
+    currentPlayer->setPosition(ms);
+}
+
+QtAV::AVPlayer *MediaQtAV::createPlayer(bool audioOnly) {
+    QtAV::AVPlayer *p = new QtAV::AVPlayer(this);
+
+#ifndef MEDIA_AUDIOONLY
+    if (!audioOnly) {
+        if (currentPlayer) {
+            p->setRenderer(currentPlayer->renderer());
+            p->setVideoDecoderPriority(currentPlayer->videoDecoderPriority());
+        } else {
+            QtAV::VideoRenderer *renderer = QtAV::VideoRenderer::create(rendererId);
+            if (!renderer || !renderer->isAvailable() || !renderer->widget()) {
+                qFatal("No QtAV video renderer");
+            }
+            p->setRenderer(renderer);
+            p->setVideoDecoderPriority(
+                    {"CUDA", "D3D11", "DXVA", "VAAPI", "VideoToolbox", "FFmpeg"});
+        }
+    }
+#endif
+
+    p->setBufferMode(QtAV::BufferTime);
+
+    if (currentPlayer) {
+        p->setBufferValue(currentPlayer->bufferValue());
+        p->setOptionsForFormat(currentPlayer->optionsForFormat());
+    }
+
+    return p;
+}
+
+void MediaQtAV::connectPlayer(QtAV::AVPlayer *player) {
+    connect(player, &QtAV::AVPlayer::error, this, &MediaQtAV::onAVError);
+
+    connect(player, &QtAV::AVPlayer::sourceChanged, this, &Media::sourceChanged);
+    connect(player, &QtAV::AVPlayer::sourceChanged, this, [this] { aboutToFinishEmitted = false; });
+
+    connect(player, &QtAV::AVPlayer::bufferProgressChanged, this, &Media::bufferStatus);
+    connect(player, &QtAV::AVPlayer::started, this, &Media::started);
+    connect(player, &QtAV::AVPlayer::stopped, this, &Media::stopped);
+    connect(player, &QtAV::AVPlayer::paused, this, &Media::paused);
+
+    connect(player, &QtAV::AVPlayer::positionChanged, this, &Media::positionChanged);
+    connect(player, &QtAV::AVPlayer::positionChanged, this, &MediaQtAV::checkAboutToFinish);
+
+    connect(player, &QtAV::AVPlayer::stateChanged, this, [this](QtAV::AVPlayer::State state) {
+        const State s = stateFor(state);
+        if (s != PlayingState) {
+            emit stateChanged(s);
+        } else if (currentPlayer->mediaStatus() == QtAV::BufferedMedia) {
+            // needed when resuming from pause
+            emit stateChanged(s);
+        }
+    });
+    connect(player, &QtAV::AVPlayer::mediaStatusChanged, this, &MediaQtAV::onMediaStatusChange);
+
+    connect(player->audio(), &QtAV::AudioOutput::volumeChanged, this, &Media::volumeChanged);
+    connect(player->audio(), &QtAV::AudioOutput::muteChanged, this, &Media::volumeMutedChanged);
+}
+
+void MediaQtAV::setCurrentPlayer(QtAV::AVPlayer *player) {
+    if (currentPlayer) {
+        currentPlayer->disconnect(this);
+        player->audio()->setVolume(currentPlayer->audio()->volume());
+        player->audio()->setMute(currentPlayer->audio()->isMute());
+        player->setBufferValue(currentPlayer->bufferValue());
+    }
+    currentPlayer = player;
+    connectPlayer(currentPlayer);
+}
+
+void MediaQtAV::smoothSourceChange(const QString &file, const QString &externalAudio) {
+    qDebug() << "smoothSourceChange";
+    auto nextPlayer = player1;
+    if (currentPlayer == player1) {
+        if (player2 == nullptr) player2 = createPlayer(audioOnly);
+        nextPlayer = player2;
+    }
+    QObject *context = new QObject();
+    connect(nextPlayer, &QtAV::AVPlayer::loaded, context, [this, nextPlayer, context] {
+        qDebug() << "smoothSourceChange preloaded";
+        setCurrentPlayer(nextPlayer);
+
+        aboutToFinishEmitted = false;
+        lastErrorString.clear();
+        clearQueue();
+        emit sourceChanged();
+        context->deleteLater();
+
+        QObject *context2 = new QObject();
+        connect(nextPlayer, &QtAV::AVPlayer::mediaStatusChanged, context2,
+                [this, context2](QtAV::MediaStatus mediaStatus) {
+                    if (mediaStatus == QtAV::BufferedMedia) {
+                        qDebug() << "smoothSourceChange playing";
+                        auto oldPlayer = currentPlayer == player1 ? player2 : player1;
+                        oldPlayer->stop();
+                        context2->deleteLater();
+                    }
+                });
+        currentPlayer->play();
+    });
+    nextPlayer->setExternalAudio(externalAudio);
+    nextPlayer->setFile(file);
+    nextPlayer->load();
+}
diff --git a/lib/media/src/qtav/mediaqtav.h b/lib/media/src/qtav/mediaqtav.h
new file mode 100644 (file)
index 0000000..d343790
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef MEDIAQTAV_H
+#define MEDIAQTAV_H
+
+#include "media.h"
+
+#include <QtAV>
+#ifndef MEDIA_AUDIOONLY
+#include <QtAVWidgets>
+#include <QtWidgets>
+#endif
+
+class MediaQtAV : public Media {
+    Q_OBJECT
+
+public:
+    MediaQtAV(QObject *parent = nullptr);
+#ifndef MEDIA_AUDIOONLY
+    void setRenderer(const QString &name);
+    QWidget *videoWidget();
+    void playSeparateAudioAndVideo(const QString &video, const QString &audio);
+    void snapshot();
+#endif
+    void setAudioOnly(bool value);
+    void init();
+
+    Media::State state() const;
+
+    void play(const QString &file);
+    void play();
+    void pause();
+    void stop();
+    void seek(qint64 ms);
+    QString file() const;
+
+    void setBufferMilliseconds(qint64 value);
+    void setUserAgent(const QString &value);
+
+    void enqueue(const QString &file);
+    void clearQueue();
+    bool hasQueue() const;
+
+    qint64 position() const;
+    qint64 duration() const;
+    qint64 remainingTime() const;
+
+    qreal volume() const;
+    void setVolume(qreal value);
+
+    bool volumeMuted() const;
+    void setVolumeMuted(bool value);
+
+    QString errorString() const;
+
+private slots:
+    void checkAboutToFinish(qint64 position);
+    void onMediaStatusChange(QtAV::MediaStatus status);
+    void onAVError(const QtAV::AVError &e);
+
+private:
+    QtAV::AVPlayer *createPlayer(bool audioOnly);
+    void connectPlayer(QtAV::AVPlayer *player);
+    void setCurrentPlayer(QtAV::AVPlayer *player);
+    void smoothSourceChange(const QString &file, const QString &externalAudio);
+
+    QtAV::AVPlayer *player1;
+    QtAV::AVPlayer *player2;
+    QtAV::AVPlayer *currentPlayer;
+
+    QQueue<QString> queue;
+    bool aboutToFinishEmitted = false;
+    QString lastErrorString;
+
+#ifndef MEDIA_AUDIOONLY
+    QtAV::VideoRendererId rendererId;
+#endif
+    bool audioOnly = false;
+};
+
+#endif // MEDIAQTAV_H
index 79649787a38f9a1295b755781ca1d92ebd8cffe7..fc2a950467655eb4da78bee1932aa5f394b9847f 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>ترجم %1 إلى لغتك الأم بإستعمال %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>صمم الايقونة %1.</translation>
         <source>Show Updated</source>
         <translation>أظهر التّحديثات</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>لا يوجد لديك اشتراكات. استخدم رمز النجمة للإشتراك في القنوات.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>جميع الفيديوهات</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>لا يوجد تحديثات لقوائم اشتراكاتك حاليًا</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>لا يوجد لديك اشتراكات. استخدم رمز النجمة للإشتراك في القنوات.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>مسح</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 مشاهدة</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>هذه ليست سوى النسخة التجريبية من %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>يمكن تحميل الفيديو في أقل من %1  دقيقة بحيث يمكنك اختبار وظيفة التحميل.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>متابعة</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>احصل على النسخة الكاملة</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 حمل في %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;اطفو علي القمة</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;ضبط حجم النافذة</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;أوقف العرض التلقائي بعد الفيديو الحالي </translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>أت&amp;حبّ %1؟ قيّمه!</translation>
         <source>Update</source>
         <translation>تحديث</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>الرابط سيكون صالحا لمدة محدودة.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>هذه ليست سوى النسخة التجريبية من %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>انها تتيح لك تجربة البرنامج.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>احصل على النسخة الكاملة</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>متابعة</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>جاري تحميل %1</translation>
         <source>Subscribe to %1</source>
         <translation>اشترك في %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>تم إلغاء متابعتك ل %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 مشاهدة</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>1% من 2% (3%) — 4%</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>جاري البحث...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>اظهر %1 المزيد</translation>
         <translation>مرحبا بك في &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>أدخل</translation>
+        <source>to start watching videos.</source>
+        <translation>لبدء مشاهدة أشرطة الفيديو</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation> كلمة مفتاح</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>قناة</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>لبدء مشاهدة أشرطة الفيديو</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>شاهد</translation>
+        <source>Enter</source>
+        <translation>أدخل</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;عودة</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>تقدّم إلى %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>لا يمكن الحصول على دفق الفيديو %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>العالم</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>لا يمكن الحصول على دفق الفيديو %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 2a18063ffc27e172b7503105c3ae808209fb15be..bf7f8588231d5d931ab0225fb24a9cd7d1d4eade 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traduz %1 a la to llingua nativa usando %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Iconu diseñáu por %1.</translation>
         <source>Show Updated</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>All Videos</source>
         <translation type="unfinished"/>
         <source>There are no updated subscriptions at this time.</source>
         <translation type="unfinished"/>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Llimpiar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 reproducciones</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esto ye namái la versión demo de %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Namái pues descargar vídeos de duración menor que %1 minutos pa que puedas probar la función de descarga.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Siguir</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Consigui la versión completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descargáu en %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Flotar na parte superior</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Detener tres d&apos;esti videu</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>¿&amp;Préstate %1? ¡Puntúalo!</translation>
         <source>Update</source>
         <translation>Anovar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Esto ye namái la versión de prueba de %1.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esto ye namái la versión demo de %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Déxate probar l&apos;aplicación y ver si te funciona.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Consigui la versión completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Siguir</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Descargando %1</translation>
         <source>Subscribe to %1</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 reproducciones</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Guetando...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Amosar %1 más</translation>
         <translation>Bienveníu a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Escribi</translation>
+        <source>to start watching videos.</source>
+        <translation>pa entamar a ver vídeos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una pallabra clave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>una canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>pa entamar a ver vídeos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>ver</translation>
+        <source>Enter</source>
+        <translation>Escribi</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Atrás</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Dir a %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Nun pue obtenese&apos;l fluxu de videu pa %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Tol mundu</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nun pue obtenese&apos;l fluxu de videu pa %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 64ed283d4f4f9fedd45b4986bf91c09bb317b403..a887a34c7bc0cbfc6e478d035a6ad4c735c5030d 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Перакладайце %1 на родную мову праз %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Дызайн іконкі выканаў %1.</translation>
         <source>Show Updated</source>
         <translation>Паказаць абноўленыя</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Няма жаднай падпіскі. Ужывайце зорачку, каб падпісвацца на каналы.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Усе відэа</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Падпіскі пакуль не абнаўляліся.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Няма жаднай падпіскі. Ужывайце зорачку, каб падпісвацца на каналы.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Ачысціць</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 праглядаў</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Гэта дэма-версія %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Можна пампаваць толькі відэа, каротшыя за %1 хв., што дастаткова для тэсціравання.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Працягнуць</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Атрымаць поўную версію</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 сцягнута ў %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Заставацца па-над усімі вокнамі</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Спыніцца пасля гэтага відэа</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Падабаецца %1? Ацані яго!</translation>
         <source>Update</source>
         <translation>Абнаўленне</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Спасылка будзе заставацца слушнай абмежаваны час.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Гэта дэма-версія %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Яна дазваляе пратэсціраваць праграму і праверыць на адпаведнасць вашым задачам.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Атрымаць поўную версію</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Працягнуць</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Сцягваецца %1</translation>
         <source>Subscribe to %1</source>
         <translation>Падпісацца на %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 праглядаў</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 з %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Пошук...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Паказаць яшчэ %1</translation>
         <translation>Ласкава запрашаем у &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Задайце</translation>
+        <source>to start watching videos.</source>
+        <translation>, каб пачаць глядзець відэа.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ключавое слова</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>канал</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>, каб пачаць глядзець відэа.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Глядзець</translation>
+        <source>Enter</source>
+        <translation>Задайце</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Назад</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Наперад да %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Не ўдалося атрымаць відэапаток для %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Увесь свет</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Не ўдалося атрымаць відэа-паток для %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 7fe65e02ef2a153bcde832f6e2e277a384c7c10f..1a9264f1fbecc5e897bead0bb2149d4123aa9aa8 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Преведи &amp;1 на твоя роден език използвайки &amp;2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Иконите са изработени от %1.</translation>
         <source>Show Updated</source>
         <translation>Покажи обновени</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Нямате абонаменти. Изполвай звезда за абониране към канали</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Всички видео клипове</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Няма обновени абонаменти</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Нямате абонаменти. Изполвай звезда за абониране към канали</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Изчисти</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 гледания</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Това е демо верция на %1</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Може да изтегляте видео клипове по-къси от &amp;1 минута, за да изпробвате функцията за изтегляне.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продължи</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Пълна версия</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 изтеглено за %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Залепи най отгоре</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Спри след този видео клип</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation type="unfinished"/>
         <source>Update</source>
         <translation>Обнови</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Линка ще е валиден само за определено време.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Това е демо верция на %1</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Позволява ви да изпробвате програмата, за да проверите дали работи добре при вас.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Пълна версия</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продължи</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>изтеглане %1</translation>
         <source>Subscribe to %1</source>
         <translation>Абонирай се за %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 гледания</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 от %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Търся...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Покажи %1 повече</translation>
         <translation>Добре дошли в &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Напишете</translation>
+        <source>to start watching videos.</source>
+        <translation>за гледане на видео клипове</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ключова дума</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>канал</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>за гледане на видео клипове</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Гледай</translation>
+        <source>Enter</source>
+        <translation>Напишете</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Назад</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Напред до %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>На световно ниво</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation type="unfinished"/>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 1f8fadec8d9087c194b145650e948b1bbafb8c75..f3d56f3e38672cfa85f0010e41b65263d6c185f6 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traduïu el %1 al vostre idioma natal utilitzant %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>Creat per %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Programari de codi obert</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Icona dissenyada per %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Teniu %n vídeo(s) nou(s)</numerusform><numerusform>Teniu %n vídeos nous</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Mostra actualitzats</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>No teniu subscripcions. Feu servir el símbol de l&apos;estrella per subscríure-us als canals.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Tots els vídeos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>No hi han subscripcions actualitzades.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>No teniu subscripcions. Feu servir el símbol de l&apos;estrella per subscríure-us als canals.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Neteja</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>fa %n hores</numerusform><numerusform>fa %n hores</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>fa %n dies</numerusform><numerusform>fa %n dies</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>fa %n mesos</numerusform><numerusform>fa %n mesos</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualitzacions</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>fa %n setmanes</numerusform><numerusform>fa %n setmanes</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Aquesta només és la versió de demostració del %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Només pot baixar vídeos de menys de %1 minuts per tal que en pugui provar aquesta funció.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continua</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Aconsegueix la versió completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descarregat en %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n baixades</numerusform><numerusform>%n baixades</numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>Manté a &amp;sobre</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajusta la mida de la finestra</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Atura després d&apos;aquest vídeo</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>Mode restringit</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
-        <translation type="unfinished"/>
+        <translation>Amaga vídeos amb contingut no apropiat</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>A&amp;maga la barra de menú</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menú</translation>
     </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <source>Update</source>
         <translation>Actualitza</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Encara podreu accedir a les opcions del menú mitjançant la tecla ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>L&apos;enllaç només serà vàlid durant un temps limitat.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Aquesta només és la versió de demostració del %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Us permet probar l&apos;aplicació i veure si us va bé.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Aconseguiu la versió completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continua</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Baixant %1</translation>
         <source>Subscribe to %1</source>
         <translation>Subscriu-me a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>S&apos;ha canviat a %1</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>De-subscrit de %1 </translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visualitzacions</translation>
+        <source>Pick a video</source>
+        <translation>Trieu un vídeo</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Cercant...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostra %1 Més</translation>
         <translation>Benvinguts al &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Introdueix</translation>
+        <source>to start watching videos.</source>
+        <translation>per comencar a veure vídeos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una paraula</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>per comencar a veure vídeos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Veure</translation>
+        <source>Enter</source>
+        <translation>Introdueix</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Enrere</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>E&amp;ndavant</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avança a %1</translation>
         <translation>Descarregant %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>No es pot obtenir flux de vídeo per %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Global</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>No es pot obtenir flux de vídeo per %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 11f3677a4440077b6fbcb2775bfaf2e3ab3e7ead..300c0f897196e6dbe3a9cda77bfc2146bcd699e4 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traduïu el %1 al vostre idioma natal utilitzant %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Icona dissenyada per %1.</translation>
         <source>Show Updated</source>
         <translation>Mostra actualitzats</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>No teniu subscripcions. Feu servir el símbol de l&apos;estrella per subscríure-us als canals.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Tots els vídeos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>No hi han subscripcions actualitzades.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>No teniu subscripcions. Feu servir el símbol de l&apos;estrella per subscríure-us als canals.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Neteja</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualitzacions</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Aquesta només és la versió de demostració del %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Només pot baixar vídeos de menys de %1 minuts per tal que en pugui provar aquesta funció.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continua</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Aconsegueix la versió completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descarregat en %2</translation>
         <source>&amp;Float on Top</source>
         <translation>Manté a &amp;sobre</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Atura després d&apos;aquest vídeo</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>T&apos;&amp;Agrada %1? Puntua&apos;l!</translation>
         <source>Update</source>
         <translation>Actualitza</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>L&apos;enllaç només serà vàlid durant un temps limitat.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Aquesta només és la versió de demostració del %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Us permet probar l&apos;aplicació i veure si us va bé.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Aconseguiu la versió completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continua</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Baixant %1</translation>
         <source>Subscribe to %1</source>
         <translation>Subscriu-me a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visualitzacions</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Cercant...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostra %1 Més</translation>
         <translation>Benvinguts al &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Introdueix</translation>
+        <source>to start watching videos.</source>
+        <translation>per comencar a veure vídeos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una paraula</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>per comencar a veure vídeos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Veure</translation>
+        <source>Enter</source>
+        <translation>Introdueix</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Enrere</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avança a %1</translation>
         <translation>Descarregant %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>No es pot obtenir flux de vídeo per %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Global</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>No es pot obtenir flux de vídeo per %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 3af0d90ef2d7149c452dad8c3286b46f286448ae..816e0d28db9c2e0df087feeba78ef4b03a010f60 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Přeložte %1 do vašeho mateřského jazyka pomocí %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Autor ikony: %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Zobrazit aktualizace</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nemáte žádné odběry. Použijte hvězdičku k přihlásení odběru kanálů.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Všechna videa</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Nyní nejsou k dispozici žádné aktualizace odběrů.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nemáte žádné odběry. Použijte hvězdičku k přihlásení odběru kanálů.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Odstranit vše</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 zobrazení</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Toto je pouze demoverze %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Umí stahovat pouze videa délky do %1 minut, abyste mohli funkci stahování vyzkoušet</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Pokračovat</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Získat plnou verzi</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 staženo v %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Plovoucí navrchu</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Přizpůsobit velikost okna</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Zastavit po tomto videu</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Skrýt videa, která by mohla obsahovat nevhodný obsah</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Přepnout pruh s &amp;nabídkou</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Nabídka</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Líbí se %1? Ohodnotit!</translation>
         <source>Update</source>
         <translation>Aktualizovat</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Stále ještě můžete přistupovat k pruhu s nabídkou stisknutím klávesy Alt</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Tento odkaz platí jen po omezenou dobu.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Toto je %1 -- demoverze.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Umožňuje vyzkoušet aplikaci, abyste ověřili, jestli pro vás funguje.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Stáhnout plnou verzi</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Pokračovat</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Je stahováno %1</translation>
         <source>Subscribe to %1</source>
         <translation>Přihlásit k %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Odhlášen z odběru %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 zobrazení</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 z %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Hledá se...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Zobrazit dalších %1</translation>
         <translation>Vítejte v &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Vložit</translation>
+        <source>to start watching videos.</source>
+        <translation> pro sledování videí.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>klíčové slovo</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>kanál</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation> pro sledování videí.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Sledovat</translation>
+        <source>Enter</source>
+        <translation>Vložit</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Zpět</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Předat k %1</translation>
         <translation>Stahování %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Nelze získat video stream pro %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Celosvětově</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nelze získat video stream pro %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index e3361891192fda6319c525d00d4df6e4f71f2f66..db5efdc55c2d4c4af14c6c9080b8a9e35f293600 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Oversæt %1 til din modersmål ved hjælp af %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikon designet af %1.</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>Hent</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Show opdateret</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Du har ingen abonnementer. Brug stjernetegnet til at abonnere på kanaler.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Alle videoer</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Der er ingen opdateringer i de abonnerede.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Du har ingen abonnementer. Brug stjernetegnet til at abonnere på kanaler.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Fjern</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visninger</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dette er kun demoversionen af %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Det kan kun hente videoer kortere end %1 minut, så du kan teste downloadfunktionaliteten.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Forsæt</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Hent den fulde version</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 downloaded på %2</translation>
@@ -633,10 +637,6 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Behold øverst</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Juster vinduesstørrelse</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Stop efter denne video</translation>
@@ -667,10 +667,18 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>Begrænset-Måde</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
+        <translation>Gem videoer der muligvis indeholder stødene indhold.</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
         <translation type="unfinished"/>
     </message>
     <message>
@@ -797,6 +805,10 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
         <source>Update</source>
         <translation>Opdatér</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
@@ -812,22 +824,6 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
         <source>The link will be valid only for a limited time.</source>
         <translation>Linket vil kun være gyldigt i en begrænset periode.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dette er kun demoversionen af %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Det giver dig mulighed for at teste programmet og se om det virker for dig.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Hent den fulde version</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Forsæt</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Downloader %1</translation>
@@ -859,6 +855,10 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
         <source>Subscribe to %1</source>
         <translation>Abonner på %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Abonnerer ikke længere på %1</translation>
@@ -903,11 +903,14 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visninger</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 af %2 (%3) — %4</translation>
@@ -947,10 +950,6 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Søger...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Vis %1 mere</translation>
@@ -1072,25 +1071,16 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
         <translation>Velkommen til &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Indtast</translation>
+        <source>to start watching videos.</source>
+        <translation>for at  begynde at se video.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>et nøgleord</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>en kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>for at  begynde at se video.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Afspil</translation>
+        <source>Enter</source>
+        <translation>Indtast</translation>
     </message>
     <message>
         <source>Recent keywords</source>
@@ -1111,6 +1101,10 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
         <source>&amp;Back</source>
         <translation>&amp;Tilbage</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Frem til %1</translation>
@@ -1161,14 +1155,7 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Kan ikke hente videostrøm for %1</translation>
+        <translation>Henter %1...</translation>
     </message>
 </context>
 <context>
@@ -1366,4 +1353,11 @@ Kopiér &amp;URL&apos;en til videostrømmen</translation>
         <translation>Hele verden</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Kan ikke hente videostrøm for %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 6802af7f06b06236fb91539f40127a147de0e635..ec338dbb9a6996c1c2d88a426c7e3dd1999c7d1f 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Übersetzen Sie %1 in Ihre Muttersprache mit %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Icons wurden gestaltet von %1.</translation>
         <source>Show Updated</source>
         <translation>Zeige aktualisierte</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Du hast keine Abonnements. Benutze das Stern-Symbol, um einen Kanal zu abonnieren.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Alle Videos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Zurzeit gibt es nichts Neues bei den Abonnements.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Du hast keine Abonnements. Benutze das Stern-Symbol, um einen Kanal zu abonnieren.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Säubern</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 mal betrachtet</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dies ist nur die Demoversion von %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Sie kann nur Videos herunterladen, die kürzer als %1 Minuten sind, um die Funktion zum Herunterladen zu testen.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Fortfahren</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Die Vollversion kaufen</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 heruntergeladen nach %2</translation>
         <source>&amp;Float on Top</source>
         <translation>Im Vordergrund &amp;bleiben</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Fenstergröße anpassen</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Nach diesem Video &amp;anhalten</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Verstecke Videos die unpassende Inhalte enthalten können</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Gefällt Ihnen %1? Bewertung abgeben!</translation>
         <source>Update</source>
         <translation>Aktualisierung</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Der Link wird nur eine beschränkte Zeit gültig sein.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dies ist nur die Demoversion von %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Sie erlaubt es Ihnen, die Anwendung zu testen und zu schauen, ob sie bei Ihnen läuft.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Die Vollversion kaufen</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Fortfahren</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>%1 herunterladen</translation>
         <source>Subscribe to %1</source>
         <translation>Abonnieren von %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Abonnement von %1 wurde aufgehoben.</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 mal betrachtet</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 von %2 (%3) – %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Suche...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Weitere %1 zeigen</translation>
         <translation>Willkommen bei &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Eingeben</translation>
+        <source>to start watching videos.</source>
+        <translation>um die Wiedergabe zu starten.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ein Suchbegriff</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>ein Kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>um die Wiedergabe zu starten.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Anschauen</translation>
+        <source>Enter</source>
+        <translation>Eingeben</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Zurück</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Weiter zu %1</translation>
         <translation>heruntergeladen %1</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Videostream für %1 konnte nicht geöffnet werden</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Weltweit</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Videostream für %1 konnte nicht geöffnet werden</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 8ecd850515e3c998d6aa0e93bfa5ef6b305c3dbe..330b6c0b792ee07fa17bd8d2d33160c7be1ebd58 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Μεταφράστε το %1 στη γλώσσα σας με χρήση του %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Σχεδιασμός εικονιδίου από %1.</translation>
         <source>Show Updated</source>
         <translation>Εμφάνιση ενημερωμένων</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Δεν έχετε συνδρομές. Χρησιμοποιήστε το αστέρι για να κάνετε συνδρομή σε κανάλια.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Όλα τα βίντεο</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Δεν υπάρχουν ενημερωμένες συδρομές αυτήν την στιγμή.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Δεν έχετε συνδρομές. Χρησιμοποιήστε το αστέρι για να κάνετε συνδρομή σε κανάλια.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Εκκαθάριση</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 προβολές</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Αυτή είναι απλά η δοκιμαστική έκδοση του %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Μπορεί να κάνει λήψη βίντεο μικρότερα από %1 λεπτά ώστε να δοκιμάσετε τη λειτουργία λήψης.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Συνέχεια</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Αποκτήστε την πλήρη έκδοση</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 λήφθηκε σε %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Διατήρηση στην κορυφή</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Προσαρμογή του μεγέθους του παραθύρου</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Διακοπή μετά από αυτό το βίντεο</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Λατρεύετε το %1; Βαθμολογήστε το!</translation>
         <source>Update</source>
         <translation>Ενημέρωση</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Ο σύνδεσμος θα είναι έγκυρος για περιορισμένο χρονικό διάστημα.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Αυτή είναι απλά μια δοκιμαστική έκδοση του %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Σαε επιτρέπει να δοκιμάσετε την εφαρμογή και να δείτε αν σας κάνει.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Αποκτήστε τη πλήρη έκδοση</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Συνέχεια</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Λήψη %1</translation>
         <source>Subscribe to %1</source>
         <translation>Εγγραφή στο %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Διεγράφη από το %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 προβολές</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 από %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Αναζήτηση...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Εμφάνιση %1 ακόμα</translation>
         <translation>Καλωσορίσατε στο &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Εισάγετε</translation>
+        <source>to start watching videos.</source>
+        <translation>για να αρχίσετε να βλέπετε βίντεο.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>μια λέξη-κλειδί</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>ένα κανάλι</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>για να αρχίσετε να βλέπετε βίντεο.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Παρακολουθήστε</translation>
+        <source>Enter</source>
+        <translation>Εισάγετε</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Επιστροφή</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Προώθηση σε %1</translation>
         <translation>Λήψη %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Αδυναμία λήψης της ροής βίντεο για το %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Παγκοσμίως</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Αδυναμία λήψης της ροής βίντεο για το %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 9a66576359a928619f4028c2a352a77e22880a4d..9e662e3197a7489f40e6006dbb4e5d713d898ebe 100644 (file)
@@ -7,7 +7,7 @@
         <location filename="../src/channelaggregator.cpp" line="185"/>
         <source>You have %n new video(s)</source>
         <translation type="unfinished">
-            <numerusform>You have one new video</numerusform>
+            <numerusform>You have a new video</numerusform>
             <numerusform>You have %n new videos</numerusform>
         </translation>
     </message>
@@ -61,7 +61,7 @@
         <location filename="../src/downloadmanager.cpp" line="165"/>
         <source>%n Download(s)</source>
         <translation type="unfinished">
-            <numerusform>One Download</numerusform>
+            <numerusform>1 Download</numerusform>
             <numerusform>%n Downloads</numerusform>
         </translation>
     </message>
diff --git a/locale/en_GB.ts b/locale/en_GB.ts
new file mode 100644 (file)
index 0000000..f029b05
--- /dev/null
@@ -0,0 +1,1362 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="en_GB" version="2.1">
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>There&apos;s life outside the browser!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Version %1</translation>
+    </message>
+    <message>
+        <source>Licensed to: %1</source>
+        <translation>Licensed to: %1</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 is Free Software but its development takes precious time.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</translation>
+    </message>
+    <message>
+        <source>Translate %1 to your native language using %2</source>
+        <translation>Translate %1 to your native language using %2</translation>
+    </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>Powered by %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Open-source software</translation>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Icon designed by %1.</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Close</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>About</translation>
+    </message>
+</context>
+<context>
+    <name>ActivationDialog</name>
+    <message>
+        <source>Enter your License Details</source>
+        <translation>Enter your Licence Details</translation>
+    </message>
+    <message>
+        <source>&amp;Email:</source>
+        <translation>&amp;Email:</translation>
+    </message>
+    <message>
+        <source>&amp;Code:</source>
+        <translation>&amp;Code:</translation>
+    </message>
+</context>
+<context>
+    <name>ActivationView</name>
+    <message>
+        <source>Please license %1</source>
+        <translation>Please license %1</translation>
+    </message>
+    <message>
+        <source>This demo has expired.</source>
+        <translation>This demo has expired.</translation>
+    </message>
+    <message>
+        <source>The full version allows you to watch videos without interruptions.</source>
+        <translation>The full version allows you to watch videos without interruptions.</translation>
+    </message>
+    <message>
+        <source>Without a license, the application will expire in %1 days.</source>
+        <translation>Without a licence, the application will expire in %1 days.</translation>
+    </message>
+    <message>
+        <source>By purchasing the full version, you will also support the hard work I put into creating %1.</source>
+        <translation>By purchasing the full version, you will also support the hard work I put into creating %1.</translation>
+    </message>
+    <message>
+        <source>Use Demo</source>
+        <translation>Use Demo</translation>
+    </message>
+    <message>
+        <source>Enter License</source>
+        <translation>Enter Licence</translation>
+    </message>
+    <message>
+        <source>Buy License</source>
+        <translation>Buy Licence</translation>
+    </message>
+</context>
+<context>
+    <name>AppWidget</name>
+    <message>
+        <source>Download</source>
+        <translation>Download</translation>
+    </message>
+</context>
+<context>
+    <name>ChannelAggregator</name>
+    <message>
+        <source>By %1</source>
+        <translation>By %1</translation>
+    </message>
+    <message numerus="yes">
+        <source>You have %n new video(s)</source>
+        <translation><numerusform>You have a new video</numerusform><numerusform>You have %n new videos</numerusform></translation>
+    </message>
+</context>
+<context>
+    <name>ChannelItemDelegate</name>
+    <message>
+        <source>All Videos</source>
+        <translation>All Videos</translation>
+    </message>
+    <message>
+        <source>Unwatched Videos</source>
+        <translation>Unwatched Videos</translation>
+    </message>
+</context>
+<context>
+    <name>ChannelView</name>
+    <message>
+        <source>Name</source>
+        <translation>Name</translation>
+    </message>
+    <message>
+        <source>Last Updated</source>
+        <translation>Last Updated</translation>
+    </message>
+    <message>
+        <source>Last Added</source>
+        <translation>Last Added</translation>
+    </message>
+    <message>
+        <source>Last Watched</source>
+        <translation>Last Watched</translation>
+    </message>
+    <message>
+        <source>Most Watched</source>
+        <translation>Most Watched</translation>
+    </message>
+    <message>
+        <source>Sort by</source>
+        <translation>Sort by</translation>
+    </message>
+    <message>
+        <source>Mark all as watched</source>
+        <translation>Mark all as watched</translation>
+    </message>
+    <message>
+        <source>Show Updated</source>
+        <translation>Show Updated</translation>
+    </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>You have no subscriptions. Use the star symbol to subscribe to channels.</translation>
+    </message>
+    <message>
+        <source>All Videos</source>
+        <translation>All Videos</translation>
+    </message>
+    <message>
+        <source>Unwatched Videos</source>
+        <translation>Unwatched Videos</translation>
+    </message>
+    <message>
+        <source>Mark as Watched</source>
+        <translation>Mark as Watched</translation>
+    </message>
+    <message>
+        <source>Unsubscribe</source>
+        <translation>Unsubscribe</translation>
+    </message>
+    <message>
+        <source>There are no updated subscriptions at this time.</source>
+        <translation>There are no updated subscriptions at this time.</translation>
+    </message>
+</context>
+<context>
+    <name>DataUtils</name>
+    <message>
+        <source>Just now</source>
+        <translation>Just now</translation>
+    </message>
+    <message numerus="yes">
+        <source>%n hour(s) ago</source>
+        <translation><numerusform>An hour ago</numerusform><numerusform>%n hours ago</numerusform></translation>
+    </message>
+    <message numerus="yes">
+        <source>%n day(s) ago</source>
+        <translation><numerusform>Yesterday</numerusform><numerusform>%n days ago</numerusform></translation>
+    </message>
+    <message numerus="yes">
+        <source>%n month(s) ago</source>
+        <translation><numerusform>A month ago</numerusform><numerusform>%n month(s) ago</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 views</translation>
+    </message>
+    <message numerus="yes">
+        <source>%n week(s) ago</source>
+        <translation><numerusform>A week ago</numerusform><numerusform>%n weeks ago</numerusform></translation>
+    </message>
+</context>
+<context>
+    <name>DownloadItem</name>
+    <message>
+        <source>bytes</source>
+        <translation>bytes</translation>
+    </message>
+    <message>
+        <source>KB</source>
+        <translation>KB</translation>
+    </message>
+    <message>
+        <source>MB</source>
+        <translation>MB</translation>
+    </message>
+    <message>
+        <source>bytes/sec</source>
+        <translation>bytes/sec</translation>
+    </message>
+    <message>
+        <source>KB/sec</source>
+        <translation>KB/sec</translation>
+    </message>
+    <message>
+        <source>MB/sec</source>
+        <translation>MB/sec</translation>
+    </message>
+    <message>
+        <source>seconds</source>
+        <translation>seconds</translation>
+    </message>
+    <message>
+        <source>minutes</source>
+        <translation>minutes</translation>
+    </message>
+    <message>
+        <source>%4 %5 remaining</source>
+        <translation>%4 %5 remaining</translation>
+    </message>
+</context>
+<context>
+    <name>DownloadManager</name>
+    <message>
+        <source>%1 downloaded in %2</source>
+        <translation>%1 downloaded in %2</translation>
+    </message>
+    <message>
+        <source>Download finished</source>
+        <translation>Download finished</translation>
+    </message>
+    <message numerus="yes">
+        <source>%n Download(s)</source>
+        <translation><numerusform>1 Download</numerusform><numerusform>%n Downloads</numerusform></translation>
+    </message>
+</context>
+<context>
+    <name>DownloadSettings</name>
+    <message>
+        <source>Change location...</source>
+        <translation>Change location...</translation>
+    </message>
+    <message>
+        <source>Choose the download location</source>
+        <translation>Choose the download location</translation>
+    </message>
+    <message>
+        <source>Download location changed.</source>
+        <translation>Download location changed.</translation>
+    </message>
+    <message>
+        <source>Current downloads will still go in the previous location.</source>
+        <translation>Current downloads will still go into the previous location.</translation>
+    </message>
+    <message>
+        <source>Downloading to: %1</source>
+        <translation>Downloading to: %1</translation>
+    </message>
+</context>
+<context>
+    <name>DownloadView</name>
+    <message>
+        <source>Downloads</source>
+        <translation>Downloads</translation>
+    </message>
+</context>
+<context>
+    <name>Extra</name>
+    <message>
+        <source>The executable file has been tempered with, maybe by a virus.</source>
+        <translation>The executable file has been tampered with, maybe by a virus.</translation>
+    </message>
+    <message>
+        <source>%1 will not run. Try installing again.</source>
+        <translation>%1 will not run. Try installing again.</translation>
+    </message>
+    <message>
+        <source>Quit</source>
+        <translation>Quit</translation>
+    </message>
+    <message>
+        <source>Reinstall</source>
+        <translation>Reinstall</translation>
+    </message>
+</context>
+<context>
+    <name>GlobalShortcuts</name>
+    <message>
+        <source>Play</source>
+        <translation>Play</translation>
+    </message>
+    <message>
+        <source>Pause</source>
+        <translation>Pause</translation>
+    </message>
+    <message>
+        <source>Play/Pause</source>
+        <translation>Play/Pause</translation>
+    </message>
+    <message>
+        <source>Stop</source>
+        <translation>Stop</translation>
+    </message>
+    <message>
+        <source>Stop playing after current track</source>
+        <translation>Stop playing after current track</translation>
+    </message>
+    <message>
+        <source>Next track</source>
+        <translation>Next track</translation>
+    </message>
+    <message>
+        <source>Previous track</source>
+        <translation>Previous track</translation>
+    </message>
+    <message>
+        <source>Increase volume</source>
+        <translation>Increase volume</translation>
+    </message>
+    <message>
+        <source>Decrease volume</source>
+        <translation>Decrease volume</translation>
+    </message>
+    <message>
+        <source>Mute</source>
+        <translation>Mute</translation>
+    </message>
+    <message>
+        <source>Seek forward</source>
+        <translation>Seek forward</translation>
+    </message>
+    <message>
+        <source>Seek backward</source>
+        <translation>Seek backward</translation>
+    </message>
+</context>
+<context>
+    <name>HomeView</name>
+    <message>
+        <source>Search</source>
+        <translation>Search</translation>
+    </message>
+    <message>
+        <source>Find videos and channels by keyword</source>
+        <translation>Find videos and channels by keyword</translation>
+    </message>
+    <message>
+        <source>Browse</source>
+        <translation>Browse</translation>
+    </message>
+    <message>
+        <source>Browse videos by category</source>
+        <translation>Browse videos by category</translation>
+    </message>
+    <message>
+        <source>Subscriptions</source>
+        <translation>Subscriptions</translation>
+    </message>
+    <message>
+        <source>Channel subscriptions</source>
+        <translation>Channel subscriptions</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Make yourself comfortable</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Window</source>
+        <translation>&amp;Window</translation>
+    </message>
+    <message>
+        <source>&amp;Minimize</source>
+        <translation>&amp;Minimise</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Stop</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Stop playback and go back to the search view</translation>
+    </message>
+    <message>
+        <source>P&amp;revious</source>
+        <translation>&amp;Previous</translation>
+    </message>
+    <message>
+        <source>Go back to the previous track</source>
+        <translation>Go back to the previous track</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>&amp;Skip</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Skip to the next video</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Play</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Resume playback</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Full Screen</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Go full screen</translation>
+    </message>
+    <message>
+        <source>&amp;Compact Mode</source>
+        <translation>&amp;Compact Mode</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Hide the playlist and the toolbar</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube Page</source>
+        <translation>Open the &amp;YouTube Page</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Go to the YouTube video page and pause playback</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;Link</source>
+        <translation>Copy the YouTube &amp;Link</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copy the current video YouTube link to the clipboard</translation>
+    </message>
+    <message>
+        <source>Copy the Video Stream &amp;URL</source>
+        <translation>Copy the Video Stream &amp;URL</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copy the current video stream URL to the clipboard</translation>
+    </message>
+    <message>
+        <source>Find Video &amp;Parts</source>
+        <translation>Find Video &amp;Parts</translation>
+    </message>
+    <message>
+        <source>Find other video parts hopefully in the right order</source>
+        <translation>Find other video parts hopefully in the right order</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Remove</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Remove the selected videos from the playlist</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Move &amp;Up</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Move up the selected videos in the playlist</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Move &amp;Down</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Move down the selected videos in the playlist</translation>
+    </message>
+    <message>
+        <source>&amp;Clear Recent Searches</source>
+        <translation>&amp;Clear Recent Searches</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Clear the search history. Cannot be undone.</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Quit</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Bye</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Website</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 on the Web</translation>
+    </message>
+    <message>
+        <source>Make a &amp;Donation</source>
+        <translation>Make a &amp;Donation</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Please support the continued development of %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;About</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Info about %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Search</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Mute volume</translation>
+    </message>
+    <message>
+        <source>&amp;Manually Start Playing</source>
+        <translation>&amp;Manually Start Playing</translation>
+    </message>
+    <message>
+        <source>Manually start playing videos</source>
+        <translation>Manually start playing videos</translation>
+    </message>
+    <message>
+        <source>&amp;Downloads</source>
+        <translation>&amp;Downloads</translation>
+    </message>
+    <message>
+        <source>Show details about video downloads</source>
+        <translation>Show details about video downloads</translation>
+    </message>
+    <message>
+        <source>&amp;Download</source>
+        <translation>&amp;Download</translation>
+    </message>
+    <message>
+        <source>Download the current video</source>
+        <translation>Download the current video</translation>
+    </message>
+    <message>
+        <source>Take &amp;Snapshot</source>
+        <translation>Take &amp;Snapshot</translation>
+    </message>
+    <message>
+        <source>&amp;Subscribe to Channel</source>
+        <translation>&amp;Subscribe to Channel</translation>
+    </message>
+    <message>
+        <source>Share the current video using %1</source>
+        <translation>Share the current video using %1</translation>
+    </message>
+    <message>
+        <source>&amp;Email</source>
+        <translation>&amp;Email</translation>
+    </message>
+    <message>
+        <source>Email</source>
+        <translation>Email</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Close</translation>
+    </message>
+    <message>
+        <source>&amp;Float on Top</source>
+        <translation>&amp;Float on Top</translation>
+    </message>
+    <message>
+        <source>&amp;Stop After This Video</source>
+        <translation>&amp;Stop After This Video</translation>
+    </message>
+    <message>
+        <source>&amp;Report an Issue...</source>
+        <translation>&amp;Report an Issue...</translation>
+    </message>
+    <message>
+        <source>&amp;Refine Search...</source>
+        <translation>&amp;Refine Search...</translation>
+    </message>
+    <message>
+        <source>More...</source>
+        <translation>More...</translation>
+    </message>
+    <message>
+        <source>&amp;Related Videos</source>
+        <translation>&amp;Related Videos</translation>
+    </message>
+    <message>
+        <source>Watch videos related to the current one</source>
+        <translation>Watch videos related to the current one</translation>
+    </message>
+    <message>
+        <source>Open in &amp;Browser...</source>
+        <translation>Open in &amp;Browser...</translation>
+    </message>
+    <message>
+        <source>Restricted Mode</source>
+        <translation>Restricted Mode</translation>
+    </message>
+    <message>
+        <source>Hide videos that may contain inappropriate content</source>
+        <translation>Hide videos that may contain inappropriate content</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Toggle &amp;Menu Bar</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menu</translation>
+    </message>
+    <message>
+        <source>&amp;Love %1? Rate it!</source>
+        <translation>&amp;Love %1? Rate it!</translation>
+    </message>
+    <message>
+        <source>Buy %1...</source>
+        <translation>Buy %1...</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Application</translation>
+    </message>
+    <message>
+        <source>&amp;Playback</source>
+        <translation>&amp;Playback</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Playlist</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Video</translation>
+    </message>
+    <message>
+        <source>&amp;Share</source>
+        <translation>&amp;Share</translation>
+    </message>
+    <message>
+        <source>&amp;View</source>
+        <translation>&amp;View</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Help</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Press %1 to raise the volume, %2 to lower it</translation>
+    </message>
+    <message>
+        <source>Choose your content location</source>
+        <translation>Choose your content location</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>Opening %1</translation>
+    </message>
+    <message>
+        <source>Do you want to exit %1 with a download in progress?</source>
+        <translation>Do you want to exit %1 with a download in progress?</translation>
+    </message>
+    <message>
+        <source>If you close %1 now, this download will be cancelled.</source>
+        <translation>If you close %1 now, this download will be cancelled.</translation>
+    </message>
+    <message>
+        <source>Close and cancel download</source>
+        <translation>Close and cancel download</translation>
+    </message>
+    <message>
+        <source>Wait for download to finish</source>
+        <translation>Wait for download to finish</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Error: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pause</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pause playback</translation>
+    </message>
+    <message>
+        <source>&amp;Loading...</source>
+        <translation>&amp;Loading...</translation>
+    </message>
+    <message>
+        <source>Leave &amp;Full Screen</source>
+        <translation>Leave &amp;Full Screen</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Remaining time: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume at %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>Volume is muted</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>Volume is unmuted</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Maximum video definition set to %1</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>Your privacy is now safe</translation>
+    </message>
+    <message>
+        <source>Downloads complete</source>
+        <translation>Downloads complete</translation>
+    </message>
+    <message>
+        <source>%1 version %2 is now available.</source>
+        <translation>%1 version %2 is now available.</translation>
+    </message>
+    <message>
+        <source>Remind me later</source>
+        <translation>Remind me later</translation>
+    </message>
+    <message>
+        <source>Update</source>
+        <translation>Update</translation>
+    </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>You can still access the menu bar by pressing the ALT key</translation>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>You can now paste the YouTube link into another application</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>You can now paste the video stream URL into another application</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>The link will be valid for a limited time only.</translation>
+    </message>
+    <message>
+        <source>Downloading %1</source>
+        <translation>Downloading %1</translation>
+    </message>
+    <message>
+        <source>of</source>
+        <comment>Used in video parts, as in '2 of 3'</comment>
+        <translation>of</translation>
+    </message>
+    <message>
+        <source>part</source>
+        <comment>This is for video parts, as in 'Cool video - part 1'</comment>
+        <translation>part</translation>
+    </message>
+    <message>
+        <source>episode</source>
+        <comment>This is for video parts, as in 'Cool series - episode 1'</comment>
+        <translation>episode</translation>
+    </message>
+    <message>
+        <source>Sent from %1</source>
+        <translation>Sent from %1</translation>
+    </message>
+    <message>
+        <source>Unsubscribe from %1</source>
+        <translation>Unsubscribe from %1</translation>
+    </message>
+    <message>
+        <source>Subscribe to %1</source>
+        <translation>Subscribe to %1</translation>
+    </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>Switched to %1</translation>
+    </message>
+    <message>
+        <source>Unsubscribed from %1</source>
+        <translation>Unsubscribed from %1</translation>
+    </message>
+    <message>
+        <source>Subscribed to %1</source>
+        <translation>Subscribed to %1</translation>
+    </message>
+</context>
+<context>
+    <name>MessageWidget</name>
+    <message>
+        <source>A new version of %1 is available!</source>
+        <translation>A new version of %1 is available!</translation>
+    </message>
+    <message>
+        <source>%1 %2 is now available. You have %3.</source>
+        <translation>%1 %2 is now available. You have %3.</translation>
+    </message>
+    <message>
+        <source>Would you like to download it now?</source>
+        <translation>Would you like to download it now?</translation>
+    </message>
+    <message>
+        <source>Skip This Version</source>
+        <translation>Skip This Version</translation>
+    </message>
+    <message>
+        <source>Remind Me Later</source>
+        <translation>Remind Me Later</translation>
+    </message>
+    <message>
+        <source>Install Update</source>
+        <translation>Install Update</translation>
+    </message>
+</context>
+<context>
+    <name>PasteLineEdit</name>
+    <message>
+        <source>Paste</source>
+        <translation>Paste</translation>
+    </message>
+</context>
+<context>
+    <name>PickMessage</name>
+    <message>
+        <source>Pick a video</source>
+        <translation>Pick a video</translation>
+    </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
+    <message>
+        <source>%1 of %2 (%3) — %4</source>
+        <translation>%1 of %2 (%3) — %4</translation>
+    </message>
+    <message>
+        <source>Preparing</source>
+        <translation>Preparing</translation>
+    </message>
+    <message>
+        <source>Failed</source>
+        <translation>Failed</translation>
+    </message>
+    <message>
+        <source>Completed</source>
+        <translation>Completed</translation>
+    </message>
+    <message>
+        <source>Stopped</source>
+        <translation>Stopped</translation>
+    </message>
+    <message>
+        <source>Stop downloading</source>
+        <translation>Stop downloading</translation>
+    </message>
+    <message>
+        <source>Show in %1</source>
+        <translation>Show in %1</translation>
+    </message>
+    <message>
+        <source>Open parent folder</source>
+        <translation>Open parent folder</translation>
+    </message>
+    <message>
+        <source>Restart downloading</source>
+        <translation>Restart downloading</translation>
+    </message>
+</context>
+<context>
+    <name>PlaylistModel</name>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Show %1 More</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>No videos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>No more videos</translation>
+    </message>
+</context>
+<context>
+    <name>RefineSearchWidget</name>
+    <message>
+        <source>Sort by</source>
+        <translation>Sort by</translation>
+    </message>
+    <message>
+        <source>Relevance</source>
+        <translation>Relevance</translation>
+    </message>
+    <message>
+        <source>Date</source>
+        <translation>Date</translation>
+    </message>
+    <message>
+        <source>View Count</source>
+        <translation>View Count</translation>
+    </message>
+    <message>
+        <source>Rating</source>
+        <translation>Rating</translation>
+    </message>
+    <message>
+        <source>Anytime</source>
+        <translation>Any time</translation>
+    </message>
+    <message>
+        <source>Today</source>
+        <translation>Today</translation>
+    </message>
+    <message>
+        <source>7 Days</source>
+        <translation>7 Days</translation>
+    </message>
+    <message>
+        <source>30 Days</source>
+        <translation>30 Days</translation>
+    </message>
+    <message>
+        <source>Duration</source>
+        <translation>Duration</translation>
+    </message>
+    <message>
+        <source>All</source>
+        <translation>All</translation>
+    </message>
+    <message>
+        <source>Short</source>
+        <translation>Short</translation>
+    </message>
+    <message>
+        <source>Medium</source>
+        <translation>Medium</translation>
+    </message>
+    <message>
+        <source>Long</source>
+        <translation>Long</translation>
+    </message>
+    <message>
+        <source>Less than 4 minutes</source>
+        <translation>Less than 4 minutes</translation>
+    </message>
+    <message>
+        <source>Between 4 and 20 minutes</source>
+        <translation>Between 4 and 20 minutes</translation>
+    </message>
+    <message>
+        <source>Longer than 20 minutes</source>
+        <translation>Longer than 20 minutes</translation>
+    </message>
+    <message>
+        <source>Quality</source>
+        <translation>Quality</translation>
+    </message>
+    <message>
+        <source>High Definition</source>
+        <translation>High Definition</translation>
+    </message>
+    <message>
+        <source>720p or higher</source>
+        <translation>720p or higher</translation>
+    </message>
+    <message>
+        <source>Done</source>
+        <translation>Done</translation>
+    </message>
+</context>
+<context>
+    <name>RegionsView</name>
+    <message>
+        <source>Done</source>
+        <translation>Done</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Search</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>to start watching videos.</source>
+        <translation>to start watching videos.</translation>
+    </message>
+    <message>
+        <source>a keyword</source>
+        <translation>a keyword</translation>
+    </message>
+    <message>
+        <source>Enter</source>
+        <translation>Enter</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Recent keywords</translation>
+    </message>
+    <message>
+        <source>Recent channels</source>
+        <translation>Recent channels</translation>
+    </message>
+    <message>
+        <source>Get the full version</source>
+        <translation>Get the full version</translation>
+    </message>
+</context>
+<context>
+    <name>SidebarHeader</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation>&amp;Back</translation>
+    </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>&amp;Forward</translation>
+    </message>
+    <message>
+        <source>Forward to %1</source>
+        <translation>Forward to %1</translation>
+    </message>
+    <message>
+        <source>Back to %1</source>
+        <translation>Back to %1</translation>
+    </message>
+</context>
+<context>
+    <name>SidebarWidget</name>
+    <message>
+        <source>Refine Search</source>
+        <translation>Refine Search</translation>
+    </message>
+    <message>
+        <source>Did you mean: %1</source>
+        <translation>Did you mean: %1?</translation>
+    </message>
+</context>
+<context>
+    <name>SnapshotSettings</name>
+    <message>
+        <source>Change location...</source>
+        <translation>Change location...</translation>
+    </message>
+    <message>
+        <source>Snapshot saved to %1</source>
+        <translation>Snapshot saved to %1</translation>
+    </message>
+    <message>
+        <source>Snapshots location changed.</source>
+        <translation>Snapshot location changed.</translation>
+    </message>
+</context>
+<context>
+    <name>StandardFeedsView</name>
+    <message>
+        <source>Most Popular</source>
+        <translation>Most Popular</translation>
+    </message>
+</context>
+<context>
+    <name>UpdateDialog</name>
+    <message>
+        <source>Downloading update...</source>
+        <translation>Downloading update...</translation>
+    </message>
+    <message>
+        <source>Downloading %1...</source>
+        <translation>Downloading %1...</translation>
+    </message>
+</context>
+<context>
+    <name>YTRegions</name>
+    <message>
+        <source>Algeria</source>
+        <translation>Algeria</translation>
+    </message>
+    <message>
+        <source>Argentina</source>
+        <translation>Argentina</translation>
+    </message>
+    <message>
+        <source>Australia</source>
+        <translation>Australia</translation>
+    </message>
+    <message>
+        <source>Belgium</source>
+        <translation>Belgium</translation>
+    </message>
+    <message>
+        <source>Brazil</source>
+        <translation>Brazil</translation>
+    </message>
+    <message>
+        <source>Canada</source>
+        <translation>Canada</translation>
+    </message>
+    <message>
+        <source>Chile</source>
+        <translation>Chile</translation>
+    </message>
+    <message>
+        <source>Colombia</source>
+        <translation>Colombia</translation>
+    </message>
+    <message>
+        <source>Czech Republic</source>
+        <translation>Czech Republic</translation>
+    </message>
+    <message>
+        <source>Egypt</source>
+        <translation>Egypt</translation>
+    </message>
+    <message>
+        <source>France</source>
+        <translation>France</translation>
+    </message>
+    <message>
+        <source>Germany</source>
+        <translation>Germany</translation>
+    </message>
+    <message>
+        <source>Ghana</source>
+        <translation>Ghana</translation>
+    </message>
+    <message>
+        <source>Greece</source>
+        <translation>Greece</translation>
+    </message>
+    <message>
+        <source>Hong Kong</source>
+        <translation>Hong Kong</translation>
+    </message>
+    <message>
+        <source>Hungary</source>
+        <translation>Hungary</translation>
+    </message>
+    <message>
+        <source>India</source>
+        <translation>India</translation>
+    </message>
+    <message>
+        <source>Indonesia</source>
+        <translation>Indonesia</translation>
+    </message>
+    <message>
+        <source>Ireland</source>
+        <translation>Ireland</translation>
+    </message>
+    <message>
+        <source>Israel</source>
+        <translation>Israel</translation>
+    </message>
+    <message>
+        <source>Italy</source>
+        <translation>Italy</translation>
+    </message>
+    <message>
+        <source>Japan</source>
+        <translation>Japan</translation>
+    </message>
+    <message>
+        <source>Jordan</source>
+        <translation>Jordan</translation>
+    </message>
+    <message>
+        <source>Kenya</source>
+        <translation>Kenya</translation>
+    </message>
+    <message>
+        <source>Malaysia</source>
+        <translation>Malaysia</translation>
+    </message>
+    <message>
+        <source>Mexico</source>
+        <translation>Mexico</translation>
+    </message>
+    <message>
+        <source>Morocco</source>
+        <translation>Morocco</translation>
+    </message>
+    <message>
+        <source>Netherlands</source>
+        <translation>Netherlands</translation>
+    </message>
+    <message>
+        <source>New Zealand</source>
+        <translation>New Zealand</translation>
+    </message>
+    <message>
+        <source>Nigeria</source>
+        <translation>Nigeria</translation>
+    </message>
+    <message>
+        <source>Peru</source>
+        <translation>Peru</translation>
+    </message>
+    <message>
+        <source>Philippines</source>
+        <translation>Philippines</translation>
+    </message>
+    <message>
+        <source>Poland</source>
+        <translation>Poland</translation>
+    </message>
+    <message>
+        <source>Russia</source>
+        <translation>Russia</translation>
+    </message>
+    <message>
+        <source>Saudi Arabia</source>
+        <translation>Saudi Arabia</translation>
+    </message>
+    <message>
+        <source>Singapore</source>
+        <translation>Singapore</translation>
+    </message>
+    <message>
+        <source>South Africa</source>
+        <translation>South Africa</translation>
+    </message>
+    <message>
+        <source>South Korea</source>
+        <translation>South Korea</translation>
+    </message>
+    <message>
+        <source>Spain</source>
+        <translation>Spain</translation>
+    </message>
+    <message>
+        <source>Sweden</source>
+        <translation>Sweden</translation>
+    </message>
+    <message>
+        <source>Taiwan</source>
+        <translation>Taiwan</translation>
+    </message>
+    <message>
+        <source>Tunisia</source>
+        <translation>Tunisia</translation>
+    </message>
+    <message>
+        <source>Turkey</source>
+        <translation>Turkey</translation>
+    </message>
+    <message>
+        <source>Uganda</source>
+        <translation>Uganda</translation>
+    </message>
+    <message>
+        <source>United Arab Emirates</source>
+        <translation>United Arab Emirates</translation>
+    </message>
+    <message>
+        <source>United Kingdom</source>
+        <translation>United Kingdom</translation>
+    </message>
+    <message>
+        <source>Yemen</source>
+        <translation>Yemen</translation>
+    </message>
+    <message>
+        <source>Worldwide</source>
+        <translation>World Wide</translation>
+    </message>
+</context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Cannot retrieve video stream for %1</translation>
+    </message>
+</context>
+</TS>
\ No newline at end of file
index c0d34c44df1e59775b811f8f004fa65d9c0ffecb..001805385fa65c9ed3877258ed6e81c763e02932 100644 (file)
     </message>
     <message>
         <source>Translate %1 to your native language using %2</source>
-        <translation>Traduzca %1 a su idioma natal usando %2</translation>
+        <translation>Traduzca %1 a su idioma usando %2</translation>
+    </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
     </message>
     <message>
         <source>Icon designed by %1.</source>
         <source>Show Updated</source>
         <translation>Mostrar actualizados</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>No se ha suscrito a ningún canal. Use el símbolo de la estrella para suscribirse a los canales.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Todos los vídeos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>No hay suscripciones actualizadas en este momento.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>No se ha suscrito a ningún canal. Use el símbolo de la estrella para suscribirse a los canales.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Vaciar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 reproducciones</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta es solo la versión de prueba de %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Solo puede descargar vídeos de duración menor que %1 minutos para que pueda probar la función de descarga.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtener la versión completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descargados en %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Flotar en la parte superior</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajustar tamaño de la ventana</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Detener tras este vídeo</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Ocultar videos que puedan contener contenido inapropiado</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Apagar &amp;Barra de menú</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menú</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>¿&amp;Le gusta %1? ¡Valórelo!</translation>
         <source>Update</source>
         <translation>Actualizar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Por abrir la barra de menú puede presionar la tecla Alt.</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>El enlace es válido solo por un tiempo limitado.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esto es solo la versión de prueba de %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Le permite probar la aplicación y ver si le funciona.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtener la versión completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Descargando %1</translation>
         <source>Subscribe to %1</source>
         <translation>Suscribirse a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Desuscrito de %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 reproducciones</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Buscando…</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostrar %1 más</translation>
         <translation>Bienvenido a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Escriba</translation>
+        <source>to start watching videos.</source>
+        <translation>para empezar a ver vídeos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una palabra clave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>para empezar a ver vídeos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Ver</translation>
+        <source>Enter</source>
+        <translation>Escriba</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Atrás</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Reenviar a %1</translation>
         <translation>Descargando %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>No se puede obtener el flujo de vídeo para %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Todo el mundo</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>No se puede obtener el stream de vídeo para %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 7945c359e1205ad79735cee4c2bc6ca9e89e5325..5f9e59ce18fd8d3f6c536ed36d9fcf1b697e8253 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traduce %1 a tu idioma natal usando %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Iconos diseñados por %1.</translation>
         <source>Show Updated</source>
         <translation>Show actualizado</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>No tiene suscripciones. Haga click en el ícono de la estrella para suscribirse a un canal</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Todos los videos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>No hay actualizaciones a sus suscripciones en este momento</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>No tiene suscripciones. Haga click en el ícono de la estrella para suscribirse a un canal</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Limpiar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visitas</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta es sólo una versión de demostración de %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Sólo se pueden bajar videos de menos de %1 minutos, para probar la funcionalidad de descarga.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Consigue la versión completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descargado en %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Siempre Visible</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>Ajustar el tamaño de la ventana</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Finalizar Después de este Video</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Ocultar videos que puedan tener contenido inapropiado</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>¿Amas %1? ¡Califícalo!</translation>
         <source>Update</source>
         <translation>Actualizar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>El enlace va a ser válido sólo por un tiempo limitado.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta es sólo la versión de demostración de %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Te permite probar la aplicación y ver si te funciona.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Conseguir la versión completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Descargando %1</translation>
         <source>Subscribe to %1</source>
         <translation>Suscribirse a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Desubscripto de %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visitas</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 of %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Buscando...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostrar %1 más</translation>
         <translation>Bienvenido a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Escribir</translation>
+        <source>to start watching videos.</source>
+        <translation>para empezar a ver videos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una palabra clave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>para empezar a ver videos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Ver</translation>
+        <source>Enter</source>
+        <translation>Escribir</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Atrás</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avanzar a %1</translation>
         <translation>Descargando %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>No puedo obtener el stream de video de %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Todo el mundo</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>No puedo obtener el stream de video de %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index a06f5ca88b41b63ed2426513cfe5df9ff525cb69..5dea2135544e59dad3eca793e0353a8f9243ce58 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traducir %1 a tu idioma utilizando %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Icono diseñado por %1.</translation>
         <source>Show Updated</source>
         <translation>Mostrar actualizados</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>No tienes ninguna subscripción. Usa la estrella para subscribirte a los canales.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Todos los videos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>No existen subscripciones actualizadas en este momento.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>No tienes ninguna subscripción. Usa la estrella para subscribirte a los canales.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Limpiar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 vistas</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta es la versión de prueba de %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Sólo se pueden descargar vídeos inferiores a %1 minutos. Así podrá probar la funcionalidad de descarga.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtener la versión completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descargado en %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Siempre Visible</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajustar el tamaños de la ventana</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Detener después de este vídeo</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Ocultar video que puedan poseer contenido inapropiado</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;¿Te encanta %1? ¡Puntúalo!</translation>
         <source>Update</source>
         <translation>Actualizar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>El enlace será válido sólo por un plazo de tiempo limitado.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta es la versión de prueba de %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Esta versión le permite probar la aplicación y ver si le sirve.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtener la versión completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Descargando %1</translation>
         <source>Subscribe to %1</source>
         <translation>Subscribirse a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Desubscripto de %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 vistas</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Buscando...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostrar %1 más</translation>
         <translation>Bienvenidos a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Introducir</translation>
+        <source>to start watching videos.</source>
+        <translation>para empezar a ver vídeos</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una palabra clave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>para empezar a ver vídeos</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Ver</translation>
+        <source>Enter</source>
+        <translation>Introducir</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Volver</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Hacia adelante %1</translation>
         <translation>Descargando %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>No se puede obtener el flujo de vídeo para %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Mundial</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>No se puede obtener el flujo de vídeo para %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 337e4ac5aff73f4d0733277b62aa6f347377fc5f..80e6566e0b05e0ced5cc05565aa952a76442f72f 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traducir %1 a tu idioma nativo usando %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ícono diseñado por %1</translation>
@@ -54,7 +62,7 @@
     </message>
     <message>
         <source>&amp;Code:</source>
-        <translation>&amp;Códico</translation>
+        <translation>&amp;Código</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Mostrar actualizados</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>No tienes suscripciones. Usa el símbolo de estrella para suscribirte a los canales.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Todos los vídeos</translation>
     </message>
     <message>
         <source>Unsubscribe</source>
-        <translation>Quitar suscripción</translation>
+        <translation>Cancelar suscripción</translation>
     </message>
     <message>
         <source>There are no updated subscriptions at this time.</source>
         <translation>No hay suscripciones actualizadas</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>No tienes suscripciones. Usa el símbolo de estrella para suscribirte a los canales.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Limpiar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 vistas</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta es la versión demostrativa de %1</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Sólo puede descargar videos con duración menor a %1 minutos asi que puedes probar la funcionalidad de descarga</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtener la versión completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>Descargados %1 en %2</translation>
     </message>
     <message>
         <source>&amp;Float on Top</source>
-        <translation>%Mantener arriba</translation>
-    </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajustar tamaño de ventana</translation>
+        <translation>&amp;Mantener arriba</translation>
     </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Ocultar vídeos que pueden tener contenido inapropiado</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Amas %1? Calíficalo!</translation>
         <source>Update</source>
         <translation>Actualizar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>El vínculo será válido sólo por un tiempo limitado</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta es la versión demostrativa de %1</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Le permite probar la aplicación y ver si funciona para ti.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtener la versión completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Descargando %1</translation>
         <source>Subscribe to %1</source>
         <translation>Suscribir a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Desuscribir de %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 vistas</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) - %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Buscando...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostrar %1 más</translation>
         <translation>Bienvenido a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Introduzca</translation>
+        <source>to start watching videos.</source>
+        <translation>para ver vídeos</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una palabra clave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>para ver vídeos</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Ver</translation>
+        <source>Enter</source>
+        <translation>Introduzca</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Retroceder</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avanzar a %1 </translation>
         <translation>Descargando %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>No se puede obtener el vídeo para %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Mundial</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>No se puede obtener el vídeo para %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 2a331538f2d218e875e90661fe42e7f1b14d20b5..37b42f99c946e6406aa180854f8af3b3d5397973 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Käännä %1 äidinkielellesi käyttämällä %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Avoimen lähdekoodin ohjelmisto</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Kuvakkeen suunnitteli %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Sinulle on %n uusi video</numerusform><numerusform>Sinulle on %n uutta videota</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Näytä päivitetyt</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Kaikki videot</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Päivitettyjä tilauksia ei ole tällä hetkellä.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Tyhjennä</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n tunti sitten</numerusform><numerusform>%n tuntia sitten</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n päivä sitten</numerusform><numerusform>%n päivää sitten</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>%n kuukausi sitten</numerusform><numerusform>%n kuukautta sitten</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>Katsottu %1 kertaa</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>%n viikko sitten</numerusform><numerusform>%n viikkoa sitten</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Tämä on vain %1-kokeiluversio.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Voit ladata vain videoita jotka ovat lyhyempiä kuin %1 minuuttia, jotta voit testata latausominaisuutta.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Jatka</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Hanki täysi versio</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 ladattu ajassa %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n lataus</numerusform><numerusform>%n latausta</numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Pysy päällimmäisenä</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Muuta ikkunan kokoa</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Py&amp;säytä toisto tämän videon jälkeen</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>Rajoitettu tila</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
-        <translation type="unfinished"/>
+        <translation>Piilota videot, jotka saattavat sisältää soveltumatonta sisältöä</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Näytä/piilota &amp;valikkopalkki</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Valikko</translation>
     </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <source>Update</source>
         <translation>Päivitä</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Saat valikkopalkin näkyviin painamalla ALT-näppäintä</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Osoite on käytössä vain rajoitetun ajan.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Tämä on vain %1n kokeiluversio.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Voit kokeilla ohjelmaa nähdäksesi, toimiiko se.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Hanki täysi versio</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Jatka</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Ladataan %1ta/tä</translation>
         <source>Subscribe to %1</source>
         <translation>Tilaa kanava %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Kohteen %1 tilaus lopetettu</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>Katsottu %1 kertaa</translation>
+        <source>Pick a video</source>
+        <translation>Valitse video</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 / %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Etsitään...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Näytä %1 lisää</translation>
         <translation>Tervetuloa &lt;a href=&apos;%1&apos;&gt;%2en&lt;/a&gt;</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Syötä</translation>
+        <source>to start watching videos.</source>
+        <translation>aloittaaksesi videoiden katselu.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>hakusana</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>kanava</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>aloittaaksesi videoiden katselu.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Katso</translation>
+        <source>Enter</source>
+        <translation>Syötä</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Takaisin</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>&amp;Eteenpäin</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Eteenpäin kohteeseen %1</translation>
         <translation>Ladataan %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Videostriimiä ei saada kohteelle %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Maailmanlaajuinen</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Videostriimiä ei saada kohteelle %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 9af8f00221556a713726510b75295ebd2174782e..7dd0a9900952e324e107b405cde85f689402f4e1 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Käännä %1 äidinkielellesi käyttämällä %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Kuvakkeen suunnitteli %1.</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>Lataa</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Näytä päivitetyt</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Kaikki videot</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Päivitettyjä tilauksia ei ole tällä hetkellä.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Tyhjennä</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>Katsottu %1 kertaa</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Tämä on vain %1-kokeiluversio.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Voit ladata vain videoita jotka ovat lyhyempiä kuin %1 minuuttia, jotta voit testata latausominaisuutta.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Jatka</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Hanki täysi versio</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 ladattu ajassa %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Pysy päällimmäisenä</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Muuta ikkunan kokoa</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Py&amp;säytä toisto tämän videon jälkeen</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Näytä/piilota &amp;valikkopalkki</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Valikko</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Pidätkö %1sta? Arvostele se!</translation>
         <source>Update</source>
         <translation>Päivitä</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Saat valikkopalkin näkyviin painamalla ALT-näppäintä</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Osoite on käytössä vain rajoitetun ajan.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Tämä on vain %1n kokeiluversio.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Voit kokeilla ohjelmaa nähdäksesi, toimiiko se.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Hanki täysi versio</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Jatka</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Ladataan %1ta/tä</translation>
         <source>Subscribe to %1</source>
         <translation>Tilaa kanava %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Lopeta kohteen %1 tilaaminen</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>Katsottu %1 kertaa</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 / %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Etsitään...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Näytä %1 lisää</translation>
         <translation>Tervetuloa &lt;a href=&apos;%1&apos;&gt;%2en&lt;/a&gt;</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Syötä</translation>
+        <source>to start watching videos.</source>
+        <translation>aloittaaksesi videoiden katselu.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>hakusana</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>kanava</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>aloittaaksesi videoiden katselu.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Katso</translation>
+        <source>Enter</source>
+        <translation>Syötä</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Takaisin</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Eteenpäin kohteeseen %1</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Videostriimiä ei saada kohteelle %1</translation>
+        <translation>Ladataan %1...</translation>
     </message>
 </context>
 <context>
         <translation>Maailmanlaajuinen</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Videostriimiä ei saada kohteelle %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 3392dd081bfc51518c043052307b603cd62d9762..978bb23ba839dc283a2ef2055841b9a665cee240 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traduisez %1 dans votre langue native en utilisant %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Logiciel open-source</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Icône dessinée par %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Vous avez %n nouvelle vidéo</numerusform><numerusform>Vous avez %n nouvelles vidéos</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Afficher les mises à jours</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Vous n&apos;avez pas d&apos;abonnements. Utilisez le symbole en forme d&apos;étoile pour vous abonner à des chaines,</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Toutes les vidéos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Il n&apos;y a pas d&apos;abonnements mis à jour en ce moment.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Vous n&apos;avez pas d&apos;abonnements. Utilisez le symbole en forme d&apos;étoile pour vous abonner à des chaines,</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Nettoyer </translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Il y a une heure</numerusform><numerusform>Il y a %n heures</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Il y a %n jour</numerusform><numerusform>Il y a %n jours</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>Il y a %n mois</numerusform><numerusform>Il y a %n mois</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 vues</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>Il y a %n semaine</numerusform><numerusform>Il y a %n semaines</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Il s&apos;agit seulement de la version de démonstration de %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Vous ne pouvez télécharger que des vidéos plus courtes que %1 minutes de sorte que vous puissiez tester la fonctionnalité de téléchargement.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuer</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtenir la version complète</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 téléchargé sur %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n téléchargement</numerusform><numerusform>%n téléchargements</numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Laisser au dessus</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajuster la taille de la fenètre</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Arrêter après cette vidéo</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Masquer les vidéos qui peuvent contenir un contenu inapproprié</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Bascule la barre de &amp;Menu</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menu</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Aimer %1? Notez-le !</translation>
         <source>Update</source>
         <translation>Mettre à jour</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Vous pouvez continuer d&apos;accéder à la barre de menu en appuyant sur la touche ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Le lien ne sera valide que pour un temps limité.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>C&apos;est juste la version démo de %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Cela vous permet de tester l&apos;application et voir si cela fonctionne pour vous.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obtenir la version complète</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuer</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>%1 Téléchargement</translation>
         <source>Subscribe to %1</source>
         <translation>S&apos;abonner à %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Se désabonner de %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 vues</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Recherche en cours…</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Afficher %1 de plus</translation>
         <translation>Bienvenue sur  &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Entrer</translation>
+        <source>to start watching videos.</source>
+        <translation>pour commencer à regarder des vidéos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>un mot-clé</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>une chaîne</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>pour commencer à regarder des vidéos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Regarder</translation>
+        <source>Enter</source>
+        <translation>Entrer</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Retour</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Continuer à %1</translation>
         <translation>Téléchargement de %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Impossible d&apos;obtenir le flux vidéo de %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Monde entier</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Impossible d&apos;obtenir le flux vidéo de %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index ae4941f78fcda4f5dc85c90f00e65b353b4b57cf..9bd8a11f055fed2187a3f1b51c170897f471fbe9 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traducir %1 ao seu idioma empregando %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>Coa tecnoloxía de %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Software de fontes abertas</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Icona deseñada por %1.</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>Descarga</translation>
     </message>
 </context>
 <context>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Dispón dun novo vídeo</numerusform><numerusform>Dispón de  %n novos vídeos</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Amosar a actualización</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Non ten subscricións. Utilice o símbolo da estrela para subscribirse ás canles.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Todos os vídeos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Neste momento non ten subscricións de actualización.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Non ten subscricións. Utilice o símbolo da estrela para subscribirse ás canles.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Limpar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     <message>
         <source>Just now</source>
-        <translation type="unfinished"/>
+        <translation>Agora mesmo</translation>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Hai %n hora</numerusform><numerusform>Hai %n horas</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Hai %n dia</numerusform><numerusform>Hai %n dias</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>Hai %n mes</numerusform><numerusform>Hai %n meses</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 vistas</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>Hai %n semana</numerusform><numerusform>Hai %n semanas</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Isto é só a versión demo de %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Só se poden descargar vídeos curtos de menos de %1 minutos para que poida probar a utilidade de descargas.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obter a versión completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descargado en %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n descarga</numerusform><numerusform>%n descargas</numerusform></translation>
     </message>
 </context>
 <context>
     <name>Extra</name>
     <message>
         <source>The executable file has been tempered with, maybe by a virus.</source>
-        <translation type="unfinished"/>
+        <translation>O ficheiro executable foi manipulado, quizais por un virus.</translation>
     </message>
     <message>
         <source>%1 will not run. Try installing again.</source>
-        <translation type="unfinished"/>
+        <translation>%1 non se executará. Probe a instalalo de novo.</translation>
     </message>
     <message>
         <source>Quit</source>
-        <translation type="unfinished"/>
+        <translation>Saír</translation>
     </message>
     <message>
         <source>Reinstall</source>
-        <translation type="unfinished"/>
+        <translation>Volver instalar</translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Show details about video downloads</source>
-        <translation>Mostrar os detalles sobre as descargas de vídeo</translation>
+        <translation>Amosar os detalles sobre as descargas de vídeo</translation>
     </message>
     <message>
         <source>&amp;Download</source>
         <source>&amp;Float on Top</source>
         <translation>&amp;Flotante e arriba</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Deter despois deste vídeo</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>Modo restrinxido</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
-        <translation type="unfinished"/>
+        <translation>Agochar os vídeos que poidan conter contido inadecuado</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Alternar a barra do &amp;menú</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menú</translation>
     </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
     </message>
     <message>
         <source>&amp;Loading...</source>
-        <translation type="unfinished"/>
+        <translation>&amp;Cargando...</translation>
     </message>
     <message>
         <source>Leave &amp;Full Screen</source>
         <source>Update</source>
         <translation>Actualizar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Aínda pode acceder á barra de menús premendo a tecla ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>A ligazón ten validez só por un tempo limitado.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Isto é só a versión demo de %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Permítelle probar o aplicativo e comprobar se vai ao seu xeito.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obter a versión completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Descargando %1</translation>
     <message>
         <source>part</source>
         <comment>This is for video parts, as in 'Cool video - part 1'</comment>
-        <translation>peza</translation>
+        <translation>parte</translation>
     </message>
     <message>
         <source>episode</source>
     </message>
     <message>
         <source>Sent from %1</source>
-        <translation>Enviado desde %1</translation>
+        <translation>Enviado dende %1</translation>
     </message>
     <message>
         <source>Unsubscribe from %1</source>
         <source>Subscribe to %1</source>
         <translation>Subscribirse a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>Cambiado a %1</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
-        <translation type="unfinished"/>
+        <translation>Non está subscrito a %1</translation>
     </message>
     <message>
         <source>Subscribed to %1</source>
-        <translation type="unfinished"/>
+        <translation>Subscrito a %1</translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Would you like to download it now?</source>
-        <translation>Queres descargala agora?</translation>
+        <translation>Quere descargala agora?</translation>
     </message>
     <message>
         <source>Skip This Version</source>
-        <translation>Saltar esta versión</translation>
+        <translation>Omitir esta versión</translation>
     </message>
     <message>
         <source>Remind Me Later</source>
-        <translation>Acórdamo máis adiante</translation>
+        <translation>Lembremo máis adiante</translation>
     </message>
     <message>
         <source>Install Update</source>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 vistas</translation>
+        <source>Pick a video</source>
+        <translation>Deleccione un vídeo</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
     </message>
     <message>
         <source>Show in %1</source>
-        <translation>Mostrar en %1</translation>
+        <translation>Amosar en %1</translation>
     </message>
     <message>
         <source>Open parent folder</source>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Buscando...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
-        <translation>Mostrar %1 máis</translation>
+        <translation>Amosar %1 máis</translation>
     </message>
     <message>
         <source>No videos</source>
         <translation>Benvido a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Introduza</translation>
+        <source>to start watching videos.</source>
+        <translation>para comezar a ver vídeos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>unha palabra clave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>unha canle</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>para comezar a ver vídeos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Ver</translation>
+        <source>Enter</source>
+        <translation>Introduza</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Atrás</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>A&amp;diante</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Ir a %1</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Non é posíbel obter o fluxo de vídeo de %1</translation>
+        <translation>Descargando %1...</translation>
     </message>
 </context>
 <context>
         <translation>Todo o mundo</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Non é posíbel obter o fluxo de vídeo de %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 9dfd6114275cd67cac95003e438bbd3b8d6761b8..fffa9668d25453f0a9cd8775c71c0ba96d6a6fb7 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>ניתן לתרגם את %1 לשפת אמך באמצעות %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>מופעל על ידי %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>תכנה בקוד פתוח</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>הסמל עוצב על ידי %1.</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>הורד</translation>
     </message>
 </context>
 <context>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>יש לך סרטון חדש</numerusform><numerusform>יש לך שני סרטונים חדשים</numerusform><numerusform>יש לך %n סרטונים חדשים</numerusform><numerusform>יש לך %n סרטונים חדשים</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>הצג עדכונים</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>אין לך מנויים. השתמש בסמל הכוכב על מנת להיות מנוי לערוצים.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>כל הסרטונים</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>אין עדכונים בערוצים שאתה מנוי עליהם כרגע.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>אין לך מנויים. השתמש בסמל הכוכב על מנת להיות מנוי לערוצים.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>מחיקה</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     <message>
         <source>Just now</source>
-        <translation type="unfinished"/>
+        <translation>זה עתה</translation>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>לפני שעה</numerusform><numerusform>לפני שעתיים</numerusform><numerusform>לפני %n שעות</numerusform><numerusform>לפני %n שעות</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>אתמול</numerusform><numerusform>שלשום</numerusform><numerusform>לפני %n ימים</numerusform><numerusform>לפני %n ימים</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>לפני חודש</numerusform><numerusform>לפני חודשיים</numerusform><numerusform>לפני %n חודשים</numerusform><numerusform>לפני %n חודשים</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>ק׳</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>מ׳</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>ב׳</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 צפיות</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>לפני שבוע</numerusform><numerusform>לפני שבועיים</numerusform><numerusform>לפני %n שבועות</numerusform><numerusform>לפני %n שבועות</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>זוהי רק גרסת ההדגמה של %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>באמצעות גרסה זו ניתן להוריד קטעי וידאו שאורכם אינו עולה על %1 דקות כדי שתהיה באפשרותך לבחור את אפשרות ההורדה.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>המשך</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>קבלת הגרסה המלאה</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 התקבל במהירות של %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>הורדה אחת</numerusform><numerusform>שתי הורדות</numerusform><numerusform>%n הורדות</numerusform><numerusform>%n הורדות</numerusform></translation>
     </message>
 </context>
 <context>
     <name>Extra</name>
     <message>
         <source>The executable file has been tempered with, maybe by a virus.</source>
-        <translation type="unfinished"/>
+        <translation>הקובץ שונה,אולי על ידי וירוס.</translation>
     </message>
     <message>
         <source>%1 will not run. Try installing again.</source>
-        <translation type="unfinished"/>
+        <translation>%1 לא ניתן להפעלה,נסה להתקין מחדש.</translation>
     </message>
     <message>
         <source>Quit</source>
-        <translation type="unfinished"/>
+        <translation>יציאה</translation>
     </message>
     <message>
         <source>Reinstall</source>
-        <translation type="unfinished"/>
+        <translation>התקן מחדש</translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;ציפה מלמעלה</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>ל&amp;עצור לאחר וידאו זה</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>מצב מוגבל</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
-        <translation type="unfinished"/>
+        <translation>הסתר סרטונים שעשויים להכיל תוכן לא הולם</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>החלפת מ&amp;צב סרגל תפריט</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>תפריט</translation>
     </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
     </message>
     <message>
         <source>&amp;Loading...</source>
-        <translation type="unfinished"/>
+        <translation>%טוען...</translation>
     </message>
     <message>
         <source>Leave &amp;Full Screen</source>
         <source>Update</source>
         <translation>עדכון</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>עדיין ניתן לגשת לסרגל התפריט עם לחיצה על ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>הקישור יהיה תקף לזמן מוגבל בלבד.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>זוהי רק גרסת ההדגמה של %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>גרסה זו מאפשרת לך לבחון את היישום ולראות האם הוא מתאים לצרכיך.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>קבלת הגרסה המלאה</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>המשך</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>%1 מתקבל</translation>
         <source>Subscribe to %1</source>
         <translation>מנוי ל %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>הוחלף אל %1</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
-        <translation type="unfinished"/>
+        <translation>בוטלה הרשמה מ%1</translation>
     </message>
     <message>
         <source>Subscribed to %1</source>
-        <translation type="unfinished"/>
+        <translation>בוצע רישום ל%1</translation>
     </message>
 </context>
 <context>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 צפיות</translation>
+        <source>Pick a video</source>
+        <translation>נא לבחור סרטון</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 מתוך %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>בהליכי חיפוש...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>הצגת %1 נוספים</translation>
         <translation>ברוך בואך אל &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>הזנה</translation>
+        <source>to start watching videos.</source>
+        <translation>כדי להתחיל לצפות בסרטונים.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>מילת מפתח</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>ערוץ</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>כדי להתחיל לצפות בסרטונים.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>צפייה</translation>
+        <source>Enter</source>
+        <translation>הזנה</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>הקודם</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>ה&amp;עברה</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>העבר אל %1</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>לא ניתן לקבל את תזרים הווידאו עבור %1</translation>
+        <translation>מוריד %1...</translation>
     </message>
 </context>
 <context>
         <translation>כל העולם</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>לא ניתן לקבל את תזרים הווידאו עבור %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 0ef5015c2f3276126f2836b00b9457d3430bc13b..ce41669bedfecb0a0edeee4ecf299adcafed6ae3 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Prevedite %1 na svoj jezik koristeći %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Dizajn ikone %1.</translation>
         <source>Show Updated</source>
         <translation>Prikaži ažurirano</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Niste pretplaćeni na ni jedan kanal. Za pretplatu kliknite na zvijezdicu.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Svi videozapisi</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Trenutno nema dostupnih ažuriranja pretplata</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Niste pretplaćeni na ni jedan kanal. Za pretplatu kliknite na zvijezdicu.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Obriši</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 pregleda</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ovo je samo probna  verzija %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Može preuzeti samo video kraći od %1 minuta tako da možete testirati mogućnost preuzimanja.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Nastavi</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Preuzmi punu verziju</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 preuzet u %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Budi na vrhu</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Stani nakon ovog videa</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation type="unfinished"/>
         <source>Update</source>
         <translation>Ažuriraj</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Link će biti valjan samo ograničeno vrijeme.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ovo je samo demo verzija %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Omogućava Vam da testirate program i vidite da li Vam odgovara.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Preuzmi punu verziju</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Nastavi</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Preuzimam %1</translation>
         <source>Subscribe to %1</source>
         <translation>Pretplata na %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 pregleda</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 od %2 (%3) - %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Pretražujem...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Prikaži %1 više</translation>
         <translation>Dobrodošli u &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Unesi</translation>
+        <source>to start watching videos.</source>
+        <translation>da počnete gledati video.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ključna riječ</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>da počnete gledati video.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Gledaj</translation>
+        <source>Enter</source>
+        <translation>Unesi</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Nazad</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Naprijed na %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Ne mogu naći video stream za %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Širom svijeta</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Ne mogu naći video stream za %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 46e53e9db227733a529802e2b57ebd08404ae0f6..9d60117634c312f5ecab7e53e263abfac57c9693 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Fordítsa le a %1 programot az anyanyelvére a következővel: %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikon tervezője: %1</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>Letöltés</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Frissítések mutatása</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nincsenek feliratkozások. A csillag szimbólumot kell használni a csatornákra való feliratkozáshoz.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Összes videó</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Nincs frissítés a feliratkozott csatornákon.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nincsenek feliratkozások. A csillag szimbólumot kell használni a csatornákra való feliratkozáshoz.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Törlés</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 néző</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ez csak a demó verziója a %1 programnak.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Csak %1 percnél rövidebb videók tölthetők le vele a letöltési funkciók teszteléséhez.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Folytatás</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Teljes verzió beszerzése</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 letöltve ide: %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Többi ablak fölött</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ablak méretének beállítása</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Videó után leállítás</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>Korlátozott mód</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
+        <translation>Esetleg nem megfelelő tartalmú videók elrejtése</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
         <translation type="unfinished"/>
     </message>
     <message>
         <source>Update</source>
         <translation>Frissítés</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>A hivatkozás csak korlátozott ideig lesz érvényben.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ez csak a demó verziója a %1 programnak.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Kipróbálhatja az alkalmazást, hogy megfelel-e az igényeinek.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Teljes verzió beszerzése</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Folytatás</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Letöltés: %1</translation>
         <source>Subscribe to %1</source>
         <translation>Feliratkozás az alábbi csatornára: %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Leiratkozva %1-ról/ről.</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 néző</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 %2 közül (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Keresés...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>További %1 elem megjelenítése</translation>
         <translation>Üdvözli a &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt; program,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Írjon be</translation>
+        <source>to start watching videos.</source>
+        <translation>a videók megtekintéséhez.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>egy kulcsszót</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>egy csatornát</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>a videók megtekintéséhez.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Megtekintés</translation>
+        <source>Enter</source>
+        <translation>Írjon be</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Vissza</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Tovább a %1-re</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Nem található videó adatfolyam a következőhöz: %1</translation>
+        <translation>Letöltés %1...</translation>
     </message>
 </context>
 <context>
         <translation>Világszerte</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nem található videó adatfolyam a következőhöz: %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 9daf0b843e2eda3cab76262e4e889b77d1b9647f..2672b63339c891d4c28dd869232d3fae1cc1b245 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Terjemahkan %1 ke bahasa aslimu dengan menggunakan %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Desain ikon oleh %1.</translation>
         <source>Show Updated</source>
         <translation>Acara Terbarui</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Anda tidak punya langganan. Gunakan simbol bintang untuk berlangganan ke saluran.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Semua video</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Tidak ada langganan yang diperbarui saat ini</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Anda tidak punya langganan. Gunakan simbol bintang untuk berlangganan ke saluran.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Bersih</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 tampilan</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ini adalah hanya versi demo dari %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Itu bisanya hanya download video yang pendek daripada %1 menit sehingga kamu bisa menguji fungsinya downloadnya.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Teruskan</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Dapatkan versi komplitnya</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 diunduh dalam %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Mengambang ke puncak</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;berhenti setelah video ini</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Love %1? Beri Nilai!</translation>
         <source>Update</source>
         <translation>Pembaruan data</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Link akan berlaku hanya untuk waktu yang terbatas.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>This is just the demo version of %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Ini memungkinkan Anda untuk menguji aplikasi dan melihat apakah ia bekerja untuk Anda.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Get the full version</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continue</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Downloading %1</translation>
         <source>Subscribe to %1</source>
         <translation>Berlangganan ke %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 tampilan</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 dari %2 (%3) --- %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Sedang mencari...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Tampilkan lebih banyak %1</translation>
         <translation>Selamat datang ke &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Masukkan</translation>
+        <source>to start watching videos.</source>
+        <translation>to start watching videos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>sebuah kata kunci</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>a channel</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>to start watching videos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Watch</translation>
+        <source>Enter</source>
+        <translation>Masukkan</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Kembali</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Maju sampai %1</translation>
         <translation>Mengunduh %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Tidak bisa mendapatkan video stream untuk %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Dunia</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Tidak bisa mendapatkan video stream untuk %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 8625647c799fed44985f34df598f8a5a61da08d0..3445da65ef220e314344065727be9e77a5b29ef0 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traduci %1 nella tua lingua usando %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>Utilizza %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Software open-source</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Icona disegnata da %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>C&apos;è un nuovo video</numerusform><numerusform>Ci sono %n nuovi video</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Mostra aggiornati</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Non hai iscrizioni. Usa il simbolo della stella per sottoscrivere i canali.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Tutti i video</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Non ci sono iscrizioni aggiornate in questo momento.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Non hai iscrizioni. Usa il simbolo della stella per sottoscrivere i canali.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Cancella</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Un&apos;ora fa</numerusform><numerusform>%n ore fa</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Ieri</numerusform><numerusform>%n giorni fa</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>Un mese fa</numerusform><numerusform>%n mesi fa</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizzazioni</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>Una settimana fa</numerusform><numerusform>%n settimane fa</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Questa è solo la versione demo di %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Puoi scaricare solo video più corti di %1 minuti, così puoi testare la funzionalità dei download.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continua</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Compra la versione completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 scaricato in %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Un download</numerusform><numerusform>%n download</numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Fluttua in alto</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Adatta le dimensioni della finestra</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Ferma dopo questo video</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Nascondi contenuti inappropriati</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Mostra/Nascondi &amp;Menu</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menu</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>Ti piace %1?</translation>
         <source>Update</source>
         <translation>Aggiorna</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Puoi accedere di nuovo al menu premendo ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Il link rimarrà valido per un periodo di tempo limitato.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Questa è solo la versione demo di %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Ti permette di testare l&apos;applicazione e verificare che funzioni sul tuo computer.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Compra la versione completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continua</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Scarica in: %1</translation>
         <source>Subscribe to %1</source>
         <translation>Iscriviti a %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>Passato a %1</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Iscrizione a %1 annullata</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visualizzazioni</translation>
+        <source>Pick a video</source>
+        <translation>Scegli un video</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 di %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Ricerca...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostra altri %1</translation>
         <translation>Benvenuto su &lt;a href=&quot;%1&quot;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Scrivi</translation>
+        <source>to start watching videos.</source>
+        <translation>per iniziare a guardare i video.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>una parola chiave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canale</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>per iniziare a guardare i video.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Guarda</translation>
+        <source>Enter</source>
+        <translation>Scrivi</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Indietro</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>&amp;Avanti</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avanza a %1</translation>
         <translation>Download di %1</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Impossibile ottenere il flusso video per %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Tutto il mondo</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Impossibile ottenere il flusso video per %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 40a6fc7a41a5dd90d413a46ddd2a6daa49cf9d2f..ed2e914126a617ee6d22a632218cbb8f482bedb5 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>%2を使って、%1をあなたの言語に翻訳してください</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>アイコンは%1さんのデザインです。</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>ダウンロード</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>動画の更新を確認</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>購読済みのものがありません。チャンネルを購読するには星マークをクリックします。</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>全ての動画</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>購読したチャンネルに更新はありません。</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>購読済みのものがありません。チャンネルを購読するには星マークをクリックします。</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>クリア</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1回 閲覧</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>これは%1 の試用版です。</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>動画を%1分程度でダウンロードできる機能を試すことができます。</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>続ける</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>製品版を入手する</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1を%2にダウンロード中</translation>
         <source>&amp;Float on Top</source>
         <translation>最前面に表示(&amp;F)</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;ウィンドウの大きさを調節</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>再生終了後に停止(&amp;S)</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>%1を評価(&amp;L)</translation>
         <source>Update</source>
         <translation>更新</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>リンクは制限時間内のみ有効です。</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>これは%1 の試用版です。</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>アプリケーションのテストや動作確認にご利用いただけます。</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>製品版を入手する</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>続ける</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>%1をダウンロード中</translation>
         <source>Subscribe to %1</source>
         <translation>%1を購読する</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>%1の購読を解除しました</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1回 閲覧</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1は%2(%3)%4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>検索中...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>さらに%1件表示</translation>
         <translation>ようこそ&lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;へ</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>キーワードを入力して</translation>
+        <source>to start watching videos.</source>
+        <translation>を検索する。</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>動画</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>チャンネル</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>を検索する。</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>検索</translation>
+        <source>Enter</source>
+        <translation>キーワードを入力して</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>戻る(&amp;B)</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>%1 に進む</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>%1の動画を取得できませんでした</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>全世界</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>%1の動画を取得できませんでした</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 87ec5f94dbbeb67c2a9e0b5fcdb790119eaa765a..1240d49a73cd7277533e6e3acc611efe67f1b666 100644 (file)
     </message>
     <message>
         <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
-        <translation>%2ì\9d\98 ê³\84ì\86\8dë\90\9c ê°\9cë°\9cì\9d\84 ì\9c\84í\95´  &lt;a href=&apos;%1&apos;&gt;기ë¶\80&lt;/a&gt;룰 í\95´ì£¼ì\9a\94...</translation>
+        <translation>%2ì\9d\98 ê³\84ì\86\8dë\90\9c ê°\9cë°\9cì\9d\84 ì\9c\84í\95´  &lt;a href=&apos;%1&apos;&gt;기ë¶\80&lt;/a&gt;룰 í\95´ì£¼ì\84¸ì\9a\94.</translation>
     </message>
     <message>
         <source>Translate %1 to your native language using %2</source>
         <translation>%2을(를) 사용해서 %1를 사용자의 언어로 번역 하세요.</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>아이콘 디자인: %1</translation>
@@ -65,7 +73,7 @@
     </message>
     <message>
         <source>This demo has expired.</source>
-        <translation>데모 만료!</translation>
+        <translation>데모버전이 만료되었습니다!</translation>
     </message>
     <message>
         <source>The full version allows you to watch videos without interruptions.</source>
@@ -77,7 +85,7 @@
     </message>
     <message>
         <source>By purchasing the full version, you will also support the hard work I put into creating %1.</source>
-        <translation>구입하면, 제가 %1를 만드는데 드는 노력을 지원 합니다.</translation>
+        <translation>구입하면, 개발자가 %1를 만드는데 드는 소중한 노력을 지원 합니다.</translation>
     </message>
     <message>
         <source>Use Demo</source>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>다운로드</translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Unwatched Videos</source>
-        <translation>ì\95\88 ë³¸ 비디오</translation>
+        <translation>ì\8b\9cì²­í\95\98ì§\80 ì\95\8aì\9d\80 비디오</translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Mark all as watched</source>
-        <translation>모두 본 걸로 표시</translation>
+        <translation>모두 시청함으로 표시</translation>
     </message>
     <message>
         <source>Show Updated</source>
         <translation>업데이트 표시</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>구독 항목이 없습니다. 채널을 구독 하려면 별 아이콘을 사용 하세요.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>모든 비디오</translation>
     </message>
     <message>
         <source>Unwatched Videos</source>
-        <translation>ì\95\88 ë³¸ 비디오</translation>
+        <translation>ì\8b\9cì²­í\95\98ì§\80 ì\95\8aì\9d\80 비디오</translation>
     </message>
     <message>
         <source>Mark as Watched</source>
-        <translation>본 ë¹\84ë\94\94ì\98¤ë¡\9c ë§\88í\81¬</translation>
+        <translation>모ë\91\90 ì\8b\9cì²­í\95¨ì\9c¼ë¡\9c í\91\9cì\8b\9c</translation>
     </message>
     <message>
         <source>Unsubscribe</source>
         <source>There are no updated subscriptions at this time.</source>
         <translation>현재 업데이트된 구독이 없습니다.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>구독 항목이 없습니다. 채널을 구독 하려면 별 아이콘을 사용 하세요.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>지우기</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     <message>
         <source>Just now</source>
-        <translation type="unfinished"/>
+        <translation>지금</translation>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 조회수</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>%1의 데모 버전 입니다.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>사용자가 다운로드 기능을 테스트 할수 있도록 %1보다 짧은 비디오만 다운로드 됩니다.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>계속</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>풀 버전 구입</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 downloaded in %2</translation>
     <name>Extra</name>
     <message>
         <source>The executable file has been tempered with, maybe by a virus.</source>
-        <translation type="unfinished"/>
+        <translation>실행 파일이 바이러스 또는 다른 외부 원인에 의해 손상되었습니다.</translation>
     </message>
     <message>
         <source>%1 will not run. Try installing again.</source>
-        <translation type="unfinished"/>
+        <translation>%1을 실행할 수 없습니다. 다시 설치하시기 바랍니다.</translation>
     </message>
     <message>
         <source>Quit</source>
-        <translation type="unfinished"/>
+        <translation>종료</translation>
     </message>
     <message>
         <source>Reinstall</source>
-        <translation type="unfinished"/>
+        <translation>재설치</translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Mute</source>
-        <translation>무음</translation>
+        <translation>음소거</translation>
     </message>
     <message>
         <source>Seek forward</source>
-        <translation>앞으로찾기</translation>
+        <translation>되감기</translation>
     </message>
     <message>
         <source>Seek backward</source>
-        <translation>ë\92¤ë¡\9cì°¾기</translation>
+        <translation>빨리ê°\90기</translation>
     </message>
 </context>
 <context>
     <name>MainWindow</name>
     <message>
         <source>&amp;Window</source>
-        <translation type="unfinished"/>
+        <translation>창 (&amp;W)</translation>
     </message>
     <message>
         <source>&amp;Minimize</source>
-        <translation type="unfinished"/>
+        <translation>최소화 (&amp;M)</translation>
     </message>
     <message>
         <source>&amp;Stop</source>
     </message>
     <message>
         <source>Stop playback and go back to the search view</source>
-        <translation>재생을 멈추고 검색 보기로 이동</translation>
+        <translation>재생을 멈추고 검색 로 이동</translation>
     </message>
     <message>
         <source>P&amp;revious</source>
     </message>
     <message>
         <source>Go back to the previous track</source>
-        <translation>이전트랙으로 이동</translation>
+        <translation>이전 트랙으로 이동</translation>
     </message>
     <message>
         <source>S&amp;kip</source>
     </message>
     <message>
         <source>Move &amp;Down</source>
-        <translation>아래로이동(&amp;D)</translation>
+        <translation>아래로 이동(&amp;D)</translation>
     </message>
     <message>
         <source>Move down the selected videos in the playlist</source>
     </message>
     <message>
         <source>Bye</source>
-        <translation>안녕...</translation>
+        <translation>안녕히 가세요.</translation>
     </message>
     <message>
         <source>&amp;Website</source>
         <source>&amp;Float on Top</source>
         <translation>위에 떠있기(&amp;F)</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>이 비디오 재생 후 정지(&amp;S)</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>제한된 보기</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
+        <translation>부적절한 내용을 포함한 동영상 숨기기</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
         <translation type="unfinished"/>
     </message>
     <message>
     </message>
     <message>
         <source>&amp;Loading...</source>
-        <translation type="unfinished"/>
+        <translation>로딩중 (&amp;L)</translation>
     </message>
     <message>
         <source>Leave &amp;Full Screen</source>
     </message>
     <message>
         <source>Volume is muted</source>
-        <translation>볼륨 소거됨</translation>
+        <translation>볼륨 ì\9d\8cì\86\8cê±°ë\90¨</translation>
     </message>
     <message>
         <source>Volume is unmuted</source>
-        <translation>볼륨 소거 해제됨</translation>
+        <translation>볼륨 ì\9d\91ì\86\8cê±° í\95´ì \9cë\90¨</translation>
     </message>
     <message>
         <source>Maximum video definition set to %1</source>
         <source>Update</source>
         <translation>업데이트</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>해당 링크는 제한된 시간 동안만 유효 합니다.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>%1의 데모 버전 입니다.%1의 데모 버전 입니다.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>이 버전으로 프로그램이 사용자 필요에 맞는지 테스트 할수 있습니다.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>풀 버전 구입</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>계속</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>%1 다운로드</translation>
     <message>
         <source>of</source>
         <comment>Used in video parts, as in '2 of 3'</comment>
-        <translation>/</translation>
+        <translation>of</translation>
     </message>
     <message>
         <source>part</source>
         <translation>%1 구독</translation>
     </message>
     <message>
-        <source>Unsubscribed from %1</source>
+        <source>Switched to %1</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Unsubscribed from %1</source>
+        <translation>%1 구독 해지됨</translation>
+    </message>
     <message>
         <source>Subscribed to %1</source>
-        <translation type="unfinished"/>
+        <translation>%1 구독됨</translation>
     </message>
 </context>
 <context>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 views</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 of %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>검색중...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>%1 추가 보기</translation>
         <translation>&lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;에 오신걸 환영 합니다!</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>언터</translation>
+        <source>to start watching videos.</source>
+        <translation>비디오 보기 시작</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>키워드</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>채널</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>비디오 보기 시작</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>감상</translation>
+        <source>Enter</source>
+        <translation>언터</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>뒤로(&amp;B)</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>%1(으)로 이동</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>%1의 비디오 가져올수 없음</translation>
+        <translation>%1 다운로드중</translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Worldwide</source>
-        <translation>전세계적</translation>
+        <translation>전세계</translation>
+    </message>
+</context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>%1의 비디오 가져올수 없음</translation>
     </message>
 </context>
 </TS>
\ No newline at end of file
index e379eef3bc0dad6242223a87c3d081bd3462877d..be95ecc6b2428ff8d3c291682282da0d9a521998 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>%2 аркылуу %1&apos;ду өз эне тилиңизге которуңуз</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Иконканын автору %1.</translation>
         <source>Show Updated</source>
         <translation>Жаңыланганын көрсөтүү</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Сизде жазылуулар жок. Каналдарга жазылуу үчүн жылдызча символун колдонуңуз.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Бардык видеолор</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Учурда жаңыланган жазылуулар жок.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Сизде жазылуулар жок. Каналдарга жазылуу үчүн жылдызча символун колдонуңуз.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Тазалоо</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 көрүү</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Бул жөн эле %1&apos;дун демо-версиясы.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Жүктөө функционалдуулугун текшерүү үчүн, бул %1 минутадан кыскараак видеолорду гана жүктөп бере алат.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Улантуу</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Толук жоромолун алуу</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%2 жерине %1 файлы жүктөлдү</translation>
         <source>&amp;Float on Top</source>
         <translation>Үстүнөн &amp;калкытуу</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Бул видеодон кийин &amp;токтотуу</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation type="unfinished"/>
         <source>Update</source>
         <translation>Жаңылоо</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Чакан убакытка чейин гана шилтеме анык болот.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Бул жөн эле %1&apos;дун демо-версиясы.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Бул тиркемени сынап көргөнгө мүмкүндүк берет.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Толук версиясын алуу</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Улантуу</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>%1 жүктөп алынууда</translation>
         <source>Subscribe to %1</source>
         <translation>%1 каналына жазылуу</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 көрүү</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 / %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Изделүүдө...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Дагы %1 видеону көрсөтүү</translation>
         <translation>&lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;&apos;га кош келиңиз,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Видеолорду</translation>
+        <source>to start watching videos.</source>
+        <translation>менен табып көрүү.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ачкыч сөз</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>канал</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>менен табып көрүү.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Көрүү</translation>
+        <source>Enter</source>
+        <translation>Видеолорду</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Артка</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>%1 алга</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>%1 үчүн видео агымын алуу мүмкүн эмес</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Бүткүл дүйнө</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>%1 үчүн видео агымын алуу мүмкүн эмес</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index edf92820463eadfe5781e2a2e4889f325785d929..a5f367ce384f8f3a54def046038f31d45ec29f2f 100644 (file)
@@ -5,7 +5,7 @@ DEPENDPATH += $$PWD
 VPATH += $$PWD
 
 # ls -1 *.ts | tr '\n' ' '
-TRANSLATIONS += ar.ts ast.ts be.ts bg_BG.ts ca.ts ca_ES.ts cs_CZ.ts da.ts de_DE.ts el.ts en.ts es.ts es_AR.ts es_ES.ts es_MX.ts fi.ts fi_FI.ts fr.ts gl.ts he_IL.ts hr.ts hu.ts id.ts it.ts ja_JP.ts ko_KR.ts ky.ts ms_MY.ts nb.ts nl.ts nn.ts pl.ts pl_PL.ts pt.ts pt_BR.ts ro.ts ru.ts sk.ts sl.ts sq.ts sr.ts sv_SE.ts th.ts tr.ts uk.ts uk_UA.ts vi.ts zh_CN.ts zh_TW.ts 
+TRANSLATIONS += ar.ts ast.ts be.ts bg_BG.ts ca.ts ca_ES.ts cs_CZ.ts da.ts de_DE.ts el.ts en.ts en_GB.ts es.ts es_AR.ts es_ES.ts es_MX.ts fi.ts fi_FI.ts fr.ts gl.ts he_IL.ts hr.ts hu.ts id.ts it.ts ja_JP.ts ko_KR.ts ky.ts ms_MY.ts nb.ts nl.ts nn.ts pl.ts pl_PL.ts pt.ts pt_BR.ts pt_PT.ts ro.ts ru.ts sk.ts sl.ts sq.ts sr.ts sv_SE.ts th.ts tr.ts uk.ts uk_UA.ts vi.ts zh_CN.ts zh_TW.ts 
 isEmpty(QMAKE_LRELEASE) {
     win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\lrelease.exe
     else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
index 0aa3a61f4fb0bdd93bbf674e60521d79191418d1..9d6756bf00fd659b68e52cc1e23c5c4efd119d54 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Terjemah %1 kepada bahasa ibunda anda menggunakan %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>Diperkasakan oleh %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Perisian sumber-terbuka</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikon direka oleh %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>Anda mempunyai %n video baharu</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Papar Dikemaskini</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Anda tidak mempunyai langganan. Gunakan simbol bintang untuk melanggan saluran.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Semua Video</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Tiada langganan dikemaskini buat masa ini.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Anda tidak mempunyai langganan. Gunakan simbol bintang untuk melanggan saluran.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Kosongkan</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>%n jam yang lalu</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>%n hari yang lalu</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>%n bulan yang lalu</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>ditonton %1 kali</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>%n minggu yang lalu</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ini hanyalah versi demo %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Ia hanya boleh muat turun video kurang daripada %1 minit supaya anda boleh menguji kefungsian muat turunnya.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Teruskan</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Dapatkan versi penuh</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 dimuat turun dalam %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>%n Muat Turun</numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>Te&amp;rapung Diatas</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Laras Saiz Tetingkap</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Henti Selepas Video Ini</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Sembunyi video yang mengandungi kandungan tidak senonoh</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Togol Palang &amp;Menu</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menu</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Suka %1? Beri penarafan!</translation>
         <source>Update</source>
         <translation>Kemaskini</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Anda masih boleh mencapai palang menu dengan menekan kekunci ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Pautan hanya sah untuk masa yang terhad.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ini hanyalah versi demo %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Ia membolehkan anda uji aplikasi dan lihat jika ia berfungsi untuk anda.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Dapatkan versi penuh</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Teruskan</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Memuat turun %1</translation>
         <source>Subscribe to %1</source>
         <translation>Langgan ke %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>Beralih ke %1</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Nyahlanggan daripada %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>ditonton %1 kali</translation>
+        <source>Pick a video</source>
+        <translation>Pilih satu video</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 daripada %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Menggelintar...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Papar %1 Lagi</translation>
         <translation>Selamat datang ke &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Masukkan</translation>
+        <source>to start watching videos.</source>
+        <translation>untuk menonton video.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>kata kunci</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>saluran</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>untuk menonton video.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Tonton</translation>
+        <source>Enter</source>
+        <translation>Masukkan</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Undur</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>&amp;Maju</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Maju ke %1</translation>
         <translation>Memuat turun %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Tidak dapat strim video untuk %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Seluruh Dunia</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Tidak dapat strim video untuk %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 8e6ae678133dbab68b5577bec4149d2a9be37df0..c0ebc3072f61d93212b879f05ec9097b1f70c1c7 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Oversett %1 til ditt morsmål ved hjelp av %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikonet er designet av %1.</translation>
         <source>Show Updated</source>
         <translation>Program Oppdatert</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Du har ingen abonnement. Bruk stjernesymbolet for å abonnemere på kanaler.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Alle Videoer</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Det er ingen oppdaterte abonnement for øyeblikket</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Du har ingen abonnement. Bruk stjernesymbolet for å abonnemere på kanaler.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Nullstill</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visninger</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dette er kun demo-versjonen av %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Den kan kun laste ned videoer på under %1 minutter, for at du skal kunne prøve ut nedlastingsfunksjonen.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Fortsett</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Kjøp fullversjonen</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 nedlastet på %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Vis over andre</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Tilpass Vindusstørrelse</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Stopp etter denne videoen</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Skjul videoer som kan inneholde upassende innhold</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Liker du %1? Ranger den!</translation>
         <source>Update</source>
         <translation>Oppdater</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Denne linken vil kun være gyldig i en begrenset tid.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dette er kun demoversjonen av %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Dette gir deg muligheten til å prøve ut applikasjonen og se om du den er noe for deg.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Kjøp fullversjonen</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Fortsett</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Nedlasting %1</translation>
         <source>Subscribe to %1</source>
         <translation>Abonnér på %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Du er utmeldt fra %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visninger</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 av %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Søker...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Vis %1 Mer</translation>
         <translation>Velkommen til &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Skriv</translation>
+        <source>to start watching videos.</source>
+        <translation>for å begynne avspilling av video</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ett nøkkelord</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>en kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>for å begynne avspilling av video</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Se</translation>
+        <source>Enter</source>
+        <translation>Skriv</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Tilbake</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Fremover til %1</translation>
         <translation>Laster ned %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Kan ikke hente mediastrøm for %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Over hele verden</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Kan ikke hente mediastrøm for %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index f4666af15cd1454f52f3737720691c5613992b30..3f2cbeb0f646e5450127f26e7d11381cf7fee2a0 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Vertaal %1 naar uw moedertaal met behulp van %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Pictogram ontworpen door %1.</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>Downloaden</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Toon bijgewerkte</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>U heeft geen abonnementen. Gebruik het ster-symbool om te abonneren op kanalen.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Alle video&apos;s</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Er zijn op dit moment geen bijgewerkte abonnementen.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>U heeft geen abonnementen. Gebruik het ster-symbool om te abonneren op kanalen.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Wis</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 bekeken</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dit is slechts de demoversie van %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Het kan alleen maar videos downloaden korter dan %1 minuten zodat u de downloadfunctionaliteit kunt testen.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Ga door</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Verkrijg de volledige versie</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 gedownload in %2</translation>
     <name>Extra</name>
     <message>
         <source>The executable file has been tempered with, maybe by a virus.</source>
-        <translation type="unfinished"/>
+        <translation>Het uitvoerbare bestand is aangepast, misschien door een virus.</translation>
     </message>
     <message>
         <source>%1 will not run. Try installing again.</source>
-        <translation type="unfinished"/>
+        <translation>%1 kan niet starten. Probeer opnieuw te installeren.</translation>
     </message>
     <message>
         <source>Quit</source>
         <source>&amp;Float on Top</source>
         <translation>&amp;Zweef erboven</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Stop na deze video</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>&amp;Menubalk weergeven/verbergen</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menu</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>Vindt u %1 te &amp;gek? Waardeer het!</translation>
         <source>Update</source>
         <translation>Werk bij</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>U kunt de menubalk nog steeds benaderen door op ALT te drukken</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>De link zal maar een beperkte tijd geldig zijn.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dit is slechts de demoversie van %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Het biedt de mogelijkheid de applicatie te testen en te beoordelen.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Verkrijg de volledige versie</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Ga door</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Bezig met downloaden van %1</translation>
         <source>Subscribe to %1</source>
         <translation>Abonneer op %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 bekeken</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 van %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Bezig met zoeken...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Toon %1 meer</translation>
         <translation>Welkom bij &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Typ</translation>
+        <source>to start watching videos.</source>
+        <translation>om te beginnen met het bekijken van video&apos;s.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>een zoekwoord</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>een kanaal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>om te beginnen met het bekijken van video&apos;s.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Bekijk</translation>
+        <source>Enter</source>
+        <translation>Typ</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Terug</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Spoel vooruit naar %1</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Kan de videostream niet verkrijgen voor %1</translation>
+        <translation>Downloaden van %1...</translation>
     </message>
 </context>
 <context>
         <translation>Wereldwijd</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Kan de videostream niet verkrijgen voor %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 94c5c64ce1e24c4b56b87ad19bf11ded41e065df..40af51a304d109f05c51aee2abc1a4101a313fa1 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Omset %1 til morsmålet ditt med %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikonet er utforma av %1.</translation>
         <source>Show Updated</source>
         <translation>Vis oppdaterte</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Du har ingen tingingar. Bruk stjernesymbolet for å tinga kanalar.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Alle videoar</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Ingen oppdaterte tingingar enno.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Du har ingen tingingar. Bruk stjernesymbolet for å tinga kanalar.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Nullstill</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visingar</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dette er berre demoutgåva av %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Han kan berre lasta ned videoar på under %1 minutt, for at du skal kunna prøva ut nedlastingsfunksjonen.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Hald fram</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Kjøp fullversjonen</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 lasta ned på %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Vis over andre</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Stopp etter denne videoen</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation type="unfinished"/>
         <source>Update</source>
         <translation>Oppdater</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Denne lenkja vil berre vera gyldig i ei avgrensa tid.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Dette er berre demoutgåva av %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Dette lèt prøva ut programmet og sjå om det er noko for deg.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Kjøp fullversjonen</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Hald fram</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Lastar ned %1</translation>
         <source>Subscribe to %1</source>
         <translation>Ting %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visingar</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 av %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Søkjer …</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Vis %1 til</translation>
         <translation>Velkomen til &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Skriv</translation>
+        <source>to start watching videos.</source>
+        <translation>for å å sjå videoar.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>eit nøkkelord</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>ein kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>for å å sjå videoar.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Snurr film</translation>
+        <source>Enter</source>
+        <translation>Skriv</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Tilbake</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Fram til %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Kan ikkje henta videostraumen til %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Heile verda</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Kan ikkje henta videostraumen til %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 6b1aac803ca8e93f411a33299b27b96f80b0d24c..760f2d1454f1e20cedcfb1508c92c861d76ce230 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Przetłumacz %1 na swój język używając %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikony zaprojektowane przez %1.</translation>
         <source>Show Updated</source>
         <translation>Pokaż nowości</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nie masz żadnych subskrypcji. Użyj symbolu gwiazdy do subskrybowania kanałów. </translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Wszystkie filmy </translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Obecnie nie ma żadnych aktualizacji subskrypcji.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nie masz żadnych subskrypcji. Użyj symbolu gwiazdy do subskrybowania kanałów. </translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Wyczyść</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>Wyświetleń: %1</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>To jest jedynie wersja demonstracyjna %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Funkcja testowa - można pobierać filmy krótsze niż %1 minut.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Kontynuuj</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Pobierz pełną wersję</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 pobrane w %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Zawsze na wierzchu </translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>Dostosuj wielkość okn&amp;a</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Zatrzymaj odtwarzanie, po obejrzeniu tego filmu  </translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Ukryj filmy mogące zawierać nieodpowiednie treści</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Przełącz pasek &amp;menu</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Kochasz %1? Oceń to!</translation>
         <source>Update</source>
         <translation>Aktualizuj</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Wciąz masz dostęp do paska menu poprzez przyciśnięcie klawisza ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Link będzie ważny tylko przez ograniczony czas.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>To jest jedynie wersja demonstracyjna %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Pozwala ci to na testowanie i sprawdzenie działania aplikacji.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Uzyskaj pełną wersję</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Kontynuuj</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Pobieranie %1</translation>
         <source>Subscribe to %1</source>
         <translation>Subskrybuj %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Zakończono subskrypcję %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>Wyświetleń: %1</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 z %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Wyszukiwanie ...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Pokaż kolejne %1</translation>
         <translation>Witaj w &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Zatwierdź</translation>
+        <source>to start watching videos.</source>
+        <translation>aby rozpocząć oglądanie</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>słowo kluczowe</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>kanał</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>aby rozpocząć oglądanie</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Oglądaj</translation>
+        <source>Enter</source>
+        <translation>Zatwierdź</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Wstecz</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Przewiń do %1</translation>
         <translation>Pobieranie %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Nie można uzyskać dostępu do %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Ogólnoświatowy</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nie można uzyskać dostępu do %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index f541c976b9c9b63dc4580341e359f18a0e453aeb..826dfb7ecfc8fbfb1b04e71a136e07a8bd568162 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Przetłumacz %1 na swój język używając %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikony zaprojektowane przez %1.</translation>
         <source>Show Updated</source>
         <translation>Pokaż zaktualizowane</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nie masz żadnej subskrypcji. Użyj symbolu gwiazdki, aby subskrybować kanały.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Wszystkie filmy</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Brak zaktualizowanych subskrypcji. </translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nie masz żadnej subskrypcji. Użyj symbolu gwiazdki, aby subskrybować kanały.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Wyczyść</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1widziany</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>To jest tylko wersja demo %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Może pobierać jedynie filmy krótsze niż %1 minut, dla przetestowania funkcji pobierania.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Dalej</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Pobierz pełną wersję</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 ściągnięte w %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Ustaw na wierzchu</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Dopasuj rozmiar okna</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Zatrzymaj  po tym filmie</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Ukryj filmy mogące zawierać nieodpowiednie treści</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Przełącz pasek &amp;menu</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>Uwie&amp;lbiasz %1? Oceń to!</translation>
         <source>Update</source>
         <translation>Zaktualizuj</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Dostęp do paska menu można uzyskać naciskając klawisz ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Link będzie ważny tylko przez ograniczony czas.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>To jest tylko wersja demo %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Pozwala przetestować aplikację, i zobaczyć czy Ci odpowiada.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Zdobądź pełną wersję</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Dalej</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Pobieranie %1</translation>
         <source>Subscribe to %1</source>
         <translation>Subskrybuj %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Zaprzestano subskrybować %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1widziany</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 z %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Szukanie...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Pokaż kolejne %1</translation>
         <translation>Witaj w &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Zatwierdź</translation>
+        <source>to start watching videos.</source>
+        <translation>aby rozpocząć oglądanie</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>słowo kluczowe</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>kanał</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>aby rozpocząć oglądanie</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Oglądaj</translation>
+        <source>Enter</source>
+        <translation>Zatwierdź</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Wstecz</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Idź do %1</translation>
         <translation>Pobieranie %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Strumieniowanie %1 nie powiodło się</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Ogólnoświatowy</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nie można uzyskać dostępu do %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 8cd19c12938937c20923262f6fee9e442a993b74..eb05ba19dc962c3222fd9154631179b267e7956b 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Ajude a traduzir o %1 através do %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ícone criado por %1.</translation>
         <source>Show Updated</source>
         <translation>Mostrar atualizados</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Ainda não possui subscrições. Utilize a estrela para subscrever os canais.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Todos os vídeos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Não existem atualizações de subscrições.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Ainda não possui subscrições. Utilize a estrela para subscrever os canais.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Limpar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizações</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta é uma versão de demonstração do %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Apenas pode transferir vídeos mais curtos que %1 minuto(s) de forma a testar a funcionalidade de transferência.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obter a versão completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 transferência em %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Flutuante na frente</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajuste o tamanho da janela</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Parar após es&amp;te vídeo</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Gosta? %1? Avalie!</translation>
         <source>Update</source>
         <translation>Atualizar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>A ligação será válida por tempo limitado.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta é uma versão de demonstração do %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Permite-lhe testar ea aplicação e verificar se é do seu agrado.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obter a versão completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Transferência: %1</translation>
         <source>Subscribe to %1</source>
         <translation>Subscrever %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Não subscrito de %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visualizações</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Procura...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostrar mais %1</translation>
         <translation>Bem-vindo ao &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Introduza</translation>
+        <source>to start watching videos.</source>
+        <translation>para começar a visualizar os vídeos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>uma palavra-chave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>um canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>para começar a visualizar os vídeos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Ver</translation>
+        <source>Enter</source>
+        <translation>Introduza</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Recuar</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avançar para %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Não é possível obter a emissão de %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Global</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Não é possível obter a emissão de %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 589af55839f4d803002b194f36b2462ea6709b85..2330982e25048296676e645e8658a2a4f65cbd18 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Traduza %1 para seu idioma nativo usando %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>Produzido por %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Software open-source</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ícone desenhado por %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>Você tem %n vídeo(s) novo(s)</numerusform><numerusform>Você tem %n vídeo(s) novo(s)</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Mostrar Atualização</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Você não tem assinaturas. Use o símbolo da estrela para assinar canais.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Todos Os Vídeos</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Não há assinaturas atualizadas neste momento.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Você não tem assinaturas. Use o símbolo da estrela para assinar canais.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Limpar</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n hora(s) atrás</numerusform><numerusform>%n hora(s) atrás</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n dia(s) atrás</numerusform><numerusform>%n dia(s) atrás</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>%n mes(es) atrás</numerusform><numerusform>%n mes(es) atrás</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizações</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>%n semana(s) atrás</numerusform><numerusform>%n semana(s) atrás</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta é apenas a versão demonstração de %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Só pode fazer download de vídeos menores que %1 minutos para que você possa testar a funcionalidade de download.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obter a versão completa</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 baixados em %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n Downloads</numerusform><numerusform>%n Download(s)</numerusform></translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Reinstall</source>
-        <translation>Reinstale</translation>
+        <translation>Reinstalar</translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Sempre Acima</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajuste o tamanho da janela</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Parar Após Este Vídeo</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
-        <translation>Ocultar vídeos com conteúdo inapropriado</translation>
+        <translation>Ocultar vídeos que podem conter conteúdo inapropriado</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>Mostrar Barra De &amp;Menu</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menu</translation>
     </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <source>Update</source>
         <translation>Atualizar</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>Você ainda pode acessar a barra de menu pressionando a tecla ALT</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>O link só será válido por um tempo limitado.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Esta é apenas a versão demonstração de %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Ele permite que você teste o aplicativo e veja se ele funciona para você.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obter a versão completa</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuar</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Baixando %1</translation>
         <source>Subscribe to %1</source>
         <translation>Assinar %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>Mudar para %1</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
-        <translation>Desinscrito de %1</translation>
+        <translation>Não subscrito de %1</translation>
     </message>
     <message>
         <source>Subscribed to %1</source>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visualizações</translation>
+        <source>Pick a video</source>
+        <translation>Escolher um vídeo</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 de %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Pesquisando...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Mostrar Mais %1</translation>
         <translation>Bem-vindo ao &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Digite</translation>
+        <source>to start watching videos.</source>
+        <translation>para começar a assistir vídeos.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>uma palavra-chave</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>um canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>para começar a assistir vídeos.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Assistir</translation>
+        <source>Enter</source>
+        <translation>Digite</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Voltar</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>&amp;Avançar</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avançar para %1</translation>
         <translation>Baixando %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Não foi possível obter stream de vídeo de %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Mundial</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Não foi possível obter stream de vídeo de %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
diff --git a/locale/pt_PT.ts b/locale/pt_PT.ts
new file mode 100644 (file)
index 0000000..6caf996
--- /dev/null
@@ -0,0 +1,1362 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="pt_PT" version="2.1">
+<context>
+    <name>AboutView</name>
+    <message>
+        <source>There&apos;s life outside the browser!</source>
+        <translation>Existe vida para além do browser!</translation>
+    </message>
+    <message>
+        <source>Version %1</source>
+        <translation>Versão %1</translation>
+    </message>
+    <message>
+        <source>Licensed to: %1</source>
+        <translation>Licenciado a:</translation>
+    </message>
+    <message>
+        <source>%1 is Free Software but its development takes precious time.</source>
+        <translation>%1 é Software Gratuito mas o seu desenvolvimento leva tempo precioso.</translation>
+    </message>
+    <message>
+        <source>Please &lt;a href=&apos;%1&apos;&gt;donate&lt;/a&gt; to support the continued development of %2.</source>
+        <translation>Por favor &lt;a href=&apos;%1&apos;&gt;doe&lt;/a&gt; para suportar o desenvolvimento continuado do %2.</translation>
+    </message>
+    <message>
+        <source>Translate %1 to your native language using %2</source>
+        <translation>Traduza %1 para a sua língua utilizando %2</translation>
+    </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Icon designed by %1.</source>
+        <translation>Ícone desenhado por %1.</translation>
+    </message>
+    <message>
+        <source>Released under the &lt;a href=&apos;%1&apos;&gt;GNU General Public License&lt;/a&gt;</source>
+        <translation>Lançado nos termos da &lt;a href=&apos;&quot;%1&apos;&gt;GNU General Public License&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Fechar</translation>
+    </message>
+    <message>
+        <source>About</source>
+        <translation>Sobre</translation>
+    </message>
+</context>
+<context>
+    <name>ActivationDialog</name>
+    <message>
+        <source>Enter your License Details</source>
+        <translation>Introduza os detalhes da sua Licença</translation>
+    </message>
+    <message>
+        <source>&amp;Email:</source>
+        <translation>&amp;Email:</translation>
+    </message>
+    <message>
+        <source>&amp;Code:</source>
+        <translation>&amp;Código:</translation>
+    </message>
+</context>
+<context>
+    <name>ActivationView</name>
+    <message>
+        <source>Please license %1</source>
+        <translation>Por favor licencie %1</translation>
+    </message>
+    <message>
+        <source>This demo has expired.</source>
+        <translation>Esta demonstração expirou.</translation>
+    </message>
+    <message>
+        <source>The full version allows you to watch videos without interruptions.</source>
+        <translation>A versão completa permite-lhe ver vídeos sem interrupções. </translation>
+    </message>
+    <message>
+        <source>Without a license, the application will expire in %1 days.</source>
+        <translation>Sem uma licença, a aplicação irá expirar em %1 dias.</translation>
+    </message>
+    <message>
+        <source>By purchasing the full version, you will also support the hard work I put into creating %1.</source>
+        <translation>Ao comprar a versão completa, também irá apoiar o trabalho árduo que pus  %1.</translation>
+    </message>
+    <message>
+        <source>Use Demo</source>
+        <translation>Usar Demo</translation>
+    </message>
+    <message>
+        <source>Enter License</source>
+        <translation>Introduzir Licença</translation>
+    </message>
+    <message>
+        <source>Buy License</source>
+        <translation>Comprar Licença</translation>
+    </message>
+</context>
+<context>
+    <name>AppWidget</name>
+    <message>
+        <source>Download</source>
+        <translation>Descarregar</translation>
+    </message>
+</context>
+<context>
+    <name>ChannelAggregator</name>
+    <message>
+        <source>By %1</source>
+        <translation>Por %1</translation>
+    </message>
+    <message numerus="yes">
+        <source>You have %n new video(s)</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+</context>
+<context>
+    <name>ChannelItemDelegate</name>
+    <message>
+        <source>All Videos</source>
+        <translation>Todos os Vídeos</translation>
+    </message>
+    <message>
+        <source>Unwatched Videos</source>
+        <translation>Vídeos não visualizados</translation>
+    </message>
+</context>
+<context>
+    <name>ChannelView</name>
+    <message>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <source>Last Updated</source>
+        <translation>Actualizado ultimamente</translation>
+    </message>
+    <message>
+        <source>Last Added</source>
+        <translation>Adicionado ultimamente</translation>
+    </message>
+    <message>
+        <source>Last Watched</source>
+        <translation>Visualizado ultimamente</translation>
+    </message>
+    <message>
+        <source>Most Watched</source>
+        <translation>Mais visualizados</translation>
+    </message>
+    <message>
+        <source>Sort by</source>
+        <translation>Ordenar por</translation>
+    </message>
+    <message>
+        <source>Mark all as watched</source>
+        <translation>Marcar todos com vistos</translation>
+    </message>
+    <message>
+        <source>Show Updated</source>
+        <translation>Mostrar actualizados</translation>
+    </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Você não tem subscrições. Use o símbolo estrela para subscrever canais.</translation>
+    </message>
+    <message>
+        <source>All Videos</source>
+        <translation>Todos os Vídeos</translation>
+    </message>
+    <message>
+        <source>Unwatched Videos</source>
+        <translation>Vídeos não visualizados</translation>
+    </message>
+    <message>
+        <source>Mark as Watched</source>
+        <translation>Marcar como visto</translation>
+    </message>
+    <message>
+        <source>Unsubscribe</source>
+        <translation>Anular subscrição</translation>
+    </message>
+    <message>
+        <source>There are no updated subscriptions at this time.</source>
+        <translation>Não há subscrições actualizadas de momento.</translation>
+    </message>
+</context>
+<context>
+    <name>DataUtils</name>
+    <message>
+        <source>Just now</source>
+        <translation>Agora mesmo</translation>
+    </message>
+    <message numerus="yes">
+        <source>%n hour(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+    <message numerus="yes">
+        <source>%n day(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+    <message numerus="yes">
+        <source>%n month(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visualizações</translation>
+    </message>
+    <message numerus="yes">
+        <source>%n week(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+</context>
+<context>
+    <name>DownloadItem</name>
+    <message>
+        <source>bytes</source>
+        <translation>bytes</translation>
+    </message>
+    <message>
+        <source>KB</source>
+        <translation>KB</translation>
+    </message>
+    <message>
+        <source>MB</source>
+        <translation>MB</translation>
+    </message>
+    <message>
+        <source>bytes/sec</source>
+        <translation>bytes/seg</translation>
+    </message>
+    <message>
+        <source>KB/sec</source>
+        <translation>KB/seg</translation>
+    </message>
+    <message>
+        <source>MB/sec</source>
+        <translation>MB/seg</translation>
+    </message>
+    <message>
+        <source>seconds</source>
+        <translation>segundos</translation>
+    </message>
+    <message>
+        <source>minutes</source>
+        <translation>minutos</translation>
+    </message>
+    <message>
+        <source>%4 %5 remaining</source>
+        <translation>%4 %5 restante</translation>
+    </message>
+</context>
+<context>
+    <name>DownloadManager</name>
+    <message>
+        <source>%1 downloaded in %2</source>
+        <translation>%1 transferido em %2</translation>
+    </message>
+    <message>
+        <source>Download finished</source>
+        <translation>Transferência concluída</translation>
+    </message>
+    <message numerus="yes">
+        <source>%n Download(s)</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+</context>
+<context>
+    <name>DownloadSettings</name>
+    <message>
+        <source>Change location...</source>
+        <translation>Mudar local...</translation>
+    </message>
+    <message>
+        <source>Choose the download location</source>
+        <translation>Escolha o local de downloads</translation>
+    </message>
+    <message>
+        <source>Download location changed.</source>
+        <translation>Local de downloads mudado</translation>
+    </message>
+    <message>
+        <source>Current downloads will still go in the previous location.</source>
+        <translation>Transferências actuais ainda irão para o local prévio</translation>
+    </message>
+    <message>
+        <source>Downloading to: %1</source>
+        <translation>A transferir para: %1</translation>
+    </message>
+</context>
+<context>
+    <name>DownloadView</name>
+    <message>
+        <source>Downloads</source>
+        <translation>Transferências</translation>
+    </message>
+</context>
+<context>
+    <name>Extra</name>
+    <message>
+        <source>The executable file has been tempered with, maybe by a virus.</source>
+        <translation>O ficheiro executável foi alterado ilegalmente, provavelmente por um vírus.</translation>
+    </message>
+    <message>
+        <source>%1 will not run. Try installing again.</source>
+        <translation>%1 não irá correr. Tente instalar outra vez.</translation>
+    </message>
+    <message>
+        <source>Quit</source>
+        <translation>Sair</translation>
+    </message>
+    <message>
+        <source>Reinstall</source>
+        <translation>Reinstalar</translation>
+    </message>
+</context>
+<context>
+    <name>GlobalShortcuts</name>
+    <message>
+        <source>Play</source>
+        <translation>Reproduzir</translation>
+    </message>
+    <message>
+        <source>Pause</source>
+        <translation>Pausa</translation>
+    </message>
+    <message>
+        <source>Play/Pause</source>
+        <translation>Reproduzir/Pausa</translation>
+    </message>
+    <message>
+        <source>Stop</source>
+        <translation>Parar</translation>
+    </message>
+    <message>
+        <source>Stop playing after current track</source>
+        <translation>Parar reprodução após faixa actual</translation>
+    </message>
+    <message>
+        <source>Next track</source>
+        <translation>Próxima faixa</translation>
+    </message>
+    <message>
+        <source>Previous track</source>
+        <translation>Faixa anterior</translation>
+    </message>
+    <message>
+        <source>Increase volume</source>
+        <translation>Aumentar volume</translation>
+    </message>
+    <message>
+        <source>Decrease volume</source>
+        <translation>Baixar volume</translation>
+    </message>
+    <message>
+        <source>Mute</source>
+        <translation>Desactivar som</translation>
+    </message>
+    <message>
+        <source>Seek forward</source>
+        <translation>Avançar</translation>
+    </message>
+    <message>
+        <source>Seek backward</source>
+        <translation>Retroceder</translation>
+    </message>
+</context>
+<context>
+    <name>HomeView</name>
+    <message>
+        <source>Search</source>
+        <translation>Pesquisar</translation>
+    </message>
+    <message>
+        <source>Find videos and channels by keyword</source>
+        <translation>Encontrar vídeos e canais por palavra-chave</translation>
+    </message>
+    <message>
+        <source>Browse</source>
+        <translation>Navegar</translation>
+    </message>
+    <message>
+        <source>Browse videos by category</source>
+        <translation>Navegar vídeos por categoria</translation>
+    </message>
+    <message>
+        <source>Subscriptions</source>
+        <translation>Subscrições</translation>
+    </message>
+    <message>
+        <source>Channel subscriptions</source>
+        <translation>Subscrições de canais</translation>
+    </message>
+    <message>
+        <source>Make yourself comfortable</source>
+        <translation>Sinta-se confortável</translation>
+    </message>
+</context>
+<context>
+    <name>LoadingWidget</name>
+    <message>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>&amp;Window</source>
+        <translation>&amp;Janela</translation>
+    </message>
+    <message>
+        <source>&amp;Minimize</source>
+        <translation>&amp;Minimizar</translation>
+    </message>
+    <message>
+        <source>&amp;Stop</source>
+        <translation>&amp;Parar</translation>
+    </message>
+    <message>
+        <source>Stop playback and go back to the search view</source>
+        <translation>Parar reprodução e voltar para à vista de pesquisa</translation>
+    </message>
+    <message>
+        <source>P&amp;revious</source>
+        <translation>A&amp;nterior</translation>
+    </message>
+    <message>
+        <source>Go back to the previous track</source>
+        <translation>Retroceder para a faixa anterior</translation>
+    </message>
+    <message>
+        <source>S&amp;kip</source>
+        <translation>S&amp;altar</translation>
+    </message>
+    <message>
+        <source>Skip to the next video</source>
+        <translation>Saltar para o próximo vídeo</translation>
+    </message>
+    <message>
+        <source>&amp;Play</source>
+        <translation>&amp;Reproduzir</translation>
+    </message>
+    <message>
+        <source>Resume playback</source>
+        <translation>Retomar reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Full Screen</source>
+        <translation>&amp;Ecrã Inteiro</translation>
+    </message>
+    <message>
+        <source>Go full screen</source>
+        <translation>Ir para ecrã inteiro</translation>
+    </message>
+    <message>
+        <source>&amp;Compact Mode</source>
+        <translation>&amp;Modo compacto</translation>
+    </message>
+    <message>
+        <source>Hide the playlist and the toolbar</source>
+        <translation>Esconder a lista de reprodução e a barra de ferramentas</translation>
+    </message>
+    <message>
+        <source>Open the &amp;YouTube Page</source>
+        <translation>Abrir a &amp;Página do YouTube</translation>
+    </message>
+    <message>
+        <source>Go to the YouTube video page and pause playback</source>
+        <translation>Ir para a página de vídeos do YouTube e pausar reprodução</translation>
+    </message>
+    <message>
+        <source>Copy the YouTube &amp;Link</source>
+        <translation>Copiar a &amp;ligação do YouTube</translation>
+    </message>
+    <message>
+        <source>Copy the current video YouTube link to the clipboard</source>
+        <translation>Copiar o URL do video actual do YouTube para a área de transferência</translation>
+    </message>
+    <message>
+        <source>Copy the Video Stream &amp;URL</source>
+        <translation>Copiar o &amp;URL da Stream do Vídeo</translation>
+    </message>
+    <message>
+        <source>Copy the current video stream URL to the clipboard</source>
+        <translation>Copiar o URL da emissão de vídeo actual para a área de transferência</translation>
+    </message>
+    <message>
+        <source>Find Video &amp;Parts</source>
+        <translation>Encontrar &amp;Partes do Vídeo</translation>
+    </message>
+    <message>
+        <source>Find other video parts hopefully in the right order</source>
+        <translation>Procurar outras partes do video, com esperança de estarem na ordenação correcta</translation>
+    </message>
+    <message>
+        <source>&amp;Remove</source>
+        <translation>&amp;Remover</translation>
+    </message>
+    <message>
+        <source>Remove the selected videos from the playlist</source>
+        <translation>Remover os vídeos selecionados da lista de reprodução</translation>
+    </message>
+    <message>
+        <source>Move &amp;Up</source>
+        <translation>Mover &amp;Para cima</translation>
+    </message>
+    <message>
+        <source>Move up the selected videos in the playlist</source>
+        <translation>Mover para cima os vídeos selecionados na lista de reprodução</translation>
+    </message>
+    <message>
+        <source>Move &amp;Down</source>
+        <translation>Mover &amp;Para baixo</translation>
+    </message>
+    <message>
+        <source>Move down the selected videos in the playlist</source>
+        <translation>Mover para baixo os vídeos selecionados na lista de reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Clear Recent Searches</source>
+        <translation>&amp;Limpar as Pesquisas Recentes</translation>
+    </message>
+    <message>
+        <source>Clear the search history. Cannot be undone.</source>
+        <translation>Limpar o histórico de pesquisa. Não poderá ser desfeito.</translation>
+    </message>
+    <message>
+        <source>&amp;Quit</source>
+        <translation>&amp;Sair</translation>
+    </message>
+    <message>
+        <source>Bye</source>
+        <translation>Adeus</translation>
+    </message>
+    <message>
+        <source>&amp;Website</source>
+        <translation>&amp;Website</translation>
+    </message>
+    <message>
+        <source>%1 on the Web</source>
+        <translation>%1 na Web</translation>
+    </message>
+    <message>
+        <source>Make a &amp;Donation</source>
+        <translation>Faça um &amp;Donativo</translation>
+    </message>
+    <message>
+        <source>Please support the continued development of %1</source>
+        <translation>Por favor apoie o desenvolvimento contínuo do %1</translation>
+    </message>
+    <message>
+        <source>&amp;About</source>
+        <translation>&amp;Sobre</translation>
+    </message>
+    <message>
+        <source>Info about %1</source>
+        <translation>Info sobre %1</translation>
+    </message>
+    <message>
+        <source>Search</source>
+        <translation>Procurar</translation>
+    </message>
+    <message>
+        <source>Mute volume</source>
+        <translation>Desactivar som</translation>
+    </message>
+    <message>
+        <source>&amp;Manually Start Playing</source>
+        <translation>&amp;Começar a Reproduzir Manualmente</translation>
+    </message>
+    <message>
+        <source>Manually start playing videos</source>
+        <translation>Manualmente começar a reproduzir vídeos</translation>
+    </message>
+    <message>
+        <source>&amp;Downloads</source>
+        <translation>&amp;Transferências</translation>
+    </message>
+    <message>
+        <source>Show details about video downloads</source>
+        <translation>Mostrar detalhes sobre transferências de vídeos</translation>
+    </message>
+    <message>
+        <source>&amp;Download</source>
+        <translation>&amp;Download</translation>
+    </message>
+    <message>
+        <source>Download the current video</source>
+        <translation>Transferir o vídeo actual</translation>
+    </message>
+    <message>
+        <source>Take &amp;Snapshot</source>
+        <translation>&amp;Captura de ecrã</translation>
+    </message>
+    <message>
+        <source>&amp;Subscribe to Channel</source>
+        <translation>&amp;Subscrever ao Canal</translation>
+    </message>
+    <message>
+        <source>Share the current video using %1</source>
+        <translation>Partilhar o video actual usando %1</translation>
+    </message>
+    <message>
+        <source>&amp;Email</source>
+        <translation>&amp;Email</translation>
+    </message>
+    <message>
+        <source>Email</source>
+        <translation>Email</translation>
+    </message>
+    <message>
+        <source>&amp;Close</source>
+        <translation>&amp;Fechar</translation>
+    </message>
+    <message>
+        <source>&amp;Float on Top</source>
+        <translation>&amp;Flutuar no Topo</translation>
+    </message>
+    <message>
+        <source>&amp;Stop After This Video</source>
+        <translation>&amp;Parar Após Este Vídeo</translation>
+    </message>
+    <message>
+        <source>&amp;Report an Issue...</source>
+        <translation>&amp;Comunicar um Problema...</translation>
+    </message>
+    <message>
+        <source>&amp;Refine Search...</source>
+        <translation>&amp;Redefinir Pesquisa...</translation>
+    </message>
+    <message>
+        <source>More...</source>
+        <translation>Mais...</translation>
+    </message>
+    <message>
+        <source>&amp;Related Videos</source>
+        <translation>&amp;Vídeos Relacionados</translation>
+    </message>
+    <message>
+        <source>Watch videos related to the current one</source>
+        <translation>Ver vídeos relacionados com o actual</translation>
+    </message>
+    <message>
+        <source>Open in &amp;Browser...</source>
+        <translation>Abrir no &amp;Browser</translation>
+    </message>
+    <message>
+        <source>Restricted Mode</source>
+        <translation>Modo Restrito</translation>
+    </message>
+    <message>
+        <source>Hide videos that may contain inappropriate content</source>
+        <translation>Ocultar vídeos que possam conter conteúdo inapropriado</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>&amp;Love %1? Rate it!</source>
+        <translation>&amp;Gosta do %1? Classifique-o!</translation>
+    </message>
+    <message>
+        <source>Buy %1...</source>
+        <translation>Comprar %1...</translation>
+    </message>
+    <message>
+        <source>&amp;Application</source>
+        <translation>&amp;Aplicação</translation>
+    </message>
+    <message>
+        <source>&amp;Playback</source>
+        <translation>&amp;Reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Playlist</source>
+        <translation>&amp;Lista de reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Video</source>
+        <translation>&amp;Vídeo</translation>
+    </message>
+    <message>
+        <source>&amp;Share</source>
+        <translation>&amp;Partilhar</translation>
+    </message>
+    <message>
+        <source>&amp;View</source>
+        <translation>&amp;Visualizar</translation>
+    </message>
+    <message>
+        <source>&amp;Help</source>
+        <translation>&amp;Ajuda</translation>
+    </message>
+    <message>
+        <source>Press %1 to raise the volume, %2 to lower it</source>
+        <translation>Pressione %1 para aumentar o volume, %2 para baixá-lo</translation>
+    </message>
+    <message>
+        <source>Choose your content location</source>
+        <translation>Escolha o local do seu conteúdo</translation>
+    </message>
+    <message>
+        <source>Opening %1</source>
+        <translation>A abrir %1</translation>
+    </message>
+    <message>
+        <source>Do you want to exit %1 with a download in progress?</source>
+        <translation>Deseja sair %1 com uma transferência activa?</translation>
+    </message>
+    <message>
+        <source>If you close %1 now, this download will be cancelled.</source>
+        <translation>Se fechar %1 agora, a transferência será cancelada.</translation>
+    </message>
+    <message>
+        <source>Close and cancel download</source>
+        <translation>Fechar e cancelar transferência</translation>
+    </message>
+    <message>
+        <source>Wait for download to finish</source>
+        <translation>Aguarde para que a transferência termine</translation>
+    </message>
+    <message>
+        <source>Error: %1</source>
+        <translation>Erro: %1</translation>
+    </message>
+    <message>
+        <source>&amp;Pause</source>
+        <translation>&amp;Pausar</translation>
+    </message>
+    <message>
+        <source>Pause playback</source>
+        <translation>Pausar reprodução</translation>
+    </message>
+    <message>
+        <source>&amp;Loading...</source>
+        <translation>&amp;A carregar...</translation>
+    </message>
+    <message>
+        <source>Leave &amp;Full Screen</source>
+        <translation>Sair do &amp;Ecrã Completo</translation>
+    </message>
+    <message>
+        <source>Remaining time: %1</source>
+        <translation>Tempo restante: %1</translation>
+    </message>
+    <message>
+        <source>Volume at %1%</source>
+        <translation>Volume a %1%</translation>
+    </message>
+    <message>
+        <source>Volume is muted</source>
+        <translation>O volume está silenciado</translation>
+    </message>
+    <message>
+        <source>Volume is unmuted</source>
+        <translation>O volume não está silenciado</translation>
+    </message>
+    <message>
+        <source>Maximum video definition set to %1</source>
+        <translation>Definição de vídeo máxima definida para %1</translation>
+    </message>
+    <message>
+        <source>Your privacy is now safe</source>
+        <translation>A sua privacidade está agora segura</translation>
+    </message>
+    <message>
+        <source>Downloads complete</source>
+        <translation>Transferência completa</translation>
+    </message>
+    <message>
+        <source>%1 version %2 is now available.</source>
+        <translation>%1 versão %2 está agora disponível.</translation>
+    </message>
+    <message>
+        <source>Remind me later</source>
+        <translation>Relembrar-me mais tarde</translation>
+    </message>
+    <message>
+        <source>Update</source>
+        <translation>Actualizar</translation>
+    </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
+</context>
+<context>
+    <name>MediaView</name>
+    <message>
+        <source>You can now paste the YouTube link into another application</source>
+        <translation>Pode agora colar a ligação do Youtube para outra aplicação</translation>
+    </message>
+    <message>
+        <source>You can now paste the video stream URL into another application</source>
+        <translation>Agora você pode colar o URL do vídeo noutra aplicação</translation>
+    </message>
+    <message>
+        <source>The link will be valid only for a limited time.</source>
+        <translation>A ligação será válida apenas por um tempo limitado.</translation>
+    </message>
+    <message>
+        <source>Downloading %1</source>
+        <translation>A transferir %1</translation>
+    </message>
+    <message>
+        <source>of</source>
+        <comment>Used in video parts, as in '2 of 3'</comment>
+        <translation>de</translation>
+    </message>
+    <message>
+        <source>part</source>
+        <comment>This is for video parts, as in 'Cool video - part 1'</comment>
+        <translation>parte</translation>
+    </message>
+    <message>
+        <source>episode</source>
+        <comment>This is for video parts, as in 'Cool series - episode 1'</comment>
+        <translation>episódio</translation>
+    </message>
+    <message>
+        <source>Sent from %1</source>
+        <translation>Enviado de %1</translation>
+    </message>
+    <message>
+        <source>Unsubscribe from %1</source>
+        <translation>Anular subscrição de %1</translation>
+    </message>
+    <message>
+        <source>Subscribe to %1</source>
+        <translation>Subscrever %1</translation>
+    </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Unsubscribed from %1</source>
+        <translation>Anular subscrição de %1</translation>
+    </message>
+    <message>
+        <source>Subscribed to %1</source>
+        <translation>Subscrito a %1</translation>
+    </message>
+</context>
+<context>
+    <name>MessageWidget</name>
+    <message>
+        <source>A new version of %1 is available!</source>
+        <translation>Uma nova versão do %1 está disponível!</translation>
+    </message>
+    <message>
+        <source>%1 %2 is now available. You have %3.</source>
+        <translation>%1 %2 está agora disponível. Você tem %3.</translation>
+    </message>
+    <message>
+        <source>Would you like to download it now?</source>
+        <translation>Gostaria de descarregar agora?</translation>
+    </message>
+    <message>
+        <source>Skip This Version</source>
+        <translation>Saltar Esta Versão</translation>
+    </message>
+    <message>
+        <source>Remind Me Later</source>
+        <translation>Relembrar-me Mais Tarde</translation>
+    </message>
+    <message>
+        <source>Install Update</source>
+        <translation>Instalar Actualização</translation>
+    </message>
+</context>
+<context>
+    <name>PasteLineEdit</name>
+    <message>
+        <source>Paste</source>
+        <translation>Colar</translation>
+    </message>
+</context>
+<context>
+    <name>PickMessage</name>
+    <message>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
+    </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
+    <message>
+        <source>%1 of %2 (%3) — %4</source>
+        <translation>%1 de %2 (%3) — %4</translation>
+    </message>
+    <message>
+        <source>Preparing</source>
+        <translation>A preparar</translation>
+    </message>
+    <message>
+        <source>Failed</source>
+        <translation>Falhou</translation>
+    </message>
+    <message>
+        <source>Completed</source>
+        <translation>Completado</translation>
+    </message>
+    <message>
+        <source>Stopped</source>
+        <translation>Parado</translation>
+    </message>
+    <message>
+        <source>Stop downloading</source>
+        <translation>Parar de transferir</translation>
+    </message>
+    <message>
+        <source>Show in %1</source>
+        <translation>Mostrar em %1</translation>
+    </message>
+    <message>
+        <source>Open parent folder</source>
+        <translation>Abrir a pasta-mãe</translation>
+    </message>
+    <message>
+        <source>Restart downloading</source>
+        <translation>Recomeçar a transferir</translation>
+    </message>
+</context>
+<context>
+    <name>PlaylistModel</name>
+    <message>
+        <source>Show %1 More</source>
+        <translation>Mostrar Mais %1</translation>
+    </message>
+    <message>
+        <source>No videos</source>
+        <translation>Sem vídeos</translation>
+    </message>
+    <message>
+        <source>No more videos</source>
+        <translation>Sem mais vídeos</translation>
+    </message>
+</context>
+<context>
+    <name>RefineSearchWidget</name>
+    <message>
+        <source>Sort by</source>
+        <translation>Ordenar por</translation>
+    </message>
+    <message>
+        <source>Relevance</source>
+        <translation>Relevância</translation>
+    </message>
+    <message>
+        <source>Date</source>
+        <translation>Data</translation>
+    </message>
+    <message>
+        <source>View Count</source>
+        <translation>Contador de visualizações</translation>
+    </message>
+    <message>
+        <source>Rating</source>
+        <translation>Classificação</translation>
+    </message>
+    <message>
+        <source>Anytime</source>
+        <translation>Qualquer altura</translation>
+    </message>
+    <message>
+        <source>Today</source>
+        <translation>Hoje</translation>
+    </message>
+    <message>
+        <source>7 Days</source>
+        <translation>7 Dias</translation>
+    </message>
+    <message>
+        <source>30 Days</source>
+        <translation>30 Dias</translation>
+    </message>
+    <message>
+        <source>Duration</source>
+        <translation>Duração</translation>
+    </message>
+    <message>
+        <source>All</source>
+        <translation>Todos</translation>
+    </message>
+    <message>
+        <source>Short</source>
+        <translation>Curto</translation>
+    </message>
+    <message>
+        <source>Medium</source>
+        <translation>Médio</translation>
+    </message>
+    <message>
+        <source>Long</source>
+        <translation>Longo</translation>
+    </message>
+    <message>
+        <source>Less than 4 minutes</source>
+        <translation>Menos de 4 minutos</translation>
+    </message>
+    <message>
+        <source>Between 4 and 20 minutes</source>
+        <translation>Entre 4 a 20 minutos</translation>
+    </message>
+    <message>
+        <source>Longer than 20 minutes</source>
+        <translation>Mais de 20 minutos</translation>
+    </message>
+    <message>
+        <source>Quality</source>
+        <translation>Qualidade</translation>
+    </message>
+    <message>
+        <source>High Definition</source>
+        <translation>Alta-Definição</translation>
+    </message>
+    <message>
+        <source>720p or higher</source>
+        <translation>720p ou mais alto</translation>
+    </message>
+    <message>
+        <source>Done</source>
+        <translation>Feito</translation>
+    </message>
+</context>
+<context>
+    <name>RegionsView</name>
+    <message>
+        <source>Done</source>
+        <translation>Feito</translation>
+    </message>
+</context>
+<context>
+    <name>SearchLineEdit</name>
+    <message>
+        <source>Search</source>
+        <translation>Pesquisar</translation>
+    </message>
+</context>
+<context>
+    <name>SearchView</name>
+    <message>
+        <source>Welcome to &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</source>
+        <translation>Bem-vindo ao &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
+    </message>
+    <message>
+        <source>to start watching videos.</source>
+        <translation>para começar a ver vídeos.</translation>
+    </message>
+    <message>
+        <source>a keyword</source>
+        <translation>uma palava-chave</translation>
+    </message>
+    <message>
+        <source>Enter</source>
+        <translation>Insira</translation>
+    </message>
+    <message>
+        <source>Recent keywords</source>
+        <translation>Palavras-chave recentes</translation>
+    </message>
+    <message>
+        <source>Recent channels</source>
+        <translation>Canais recentes</translation>
+    </message>
+    <message>
+        <source>Get the full version</source>
+        <translation>Obter a versão completa</translation>
+    </message>
+</context>
+<context>
+    <name>SidebarHeader</name>
+    <message>
+        <source>&amp;Back</source>
+        <translation>&amp;Atrás</translation>
+    </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Forward to %1</source>
+        <translation>Avançar até %1</translation>
+    </message>
+    <message>
+        <source>Back to %1</source>
+        <translation>Retroceder até %1</translation>
+    </message>
+</context>
+<context>
+    <name>SidebarWidget</name>
+    <message>
+        <source>Refine Search</source>
+        <translation>Redefinir Pesquisa</translation>
+    </message>
+    <message>
+        <source>Did you mean: %1</source>
+        <translation>Queria dizer: %1</translation>
+    </message>
+</context>
+<context>
+    <name>SnapshotSettings</name>
+    <message>
+        <source>Change location...</source>
+        <translation>Mudar local...</translation>
+    </message>
+    <message>
+        <source>Snapshot saved to %1</source>
+        <translation>Captura de ecrã guardada em %1</translation>
+    </message>
+    <message>
+        <source>Snapshots location changed.</source>
+        <translation>Local de capturas de ecrã mudado.</translation>
+    </message>
+</context>
+<context>
+    <name>StandardFeedsView</name>
+    <message>
+        <source>Most Popular</source>
+        <translation>Mais Popular</translation>
+    </message>
+</context>
+<context>
+    <name>UpdateDialog</name>
+    <message>
+        <source>Downloading update...</source>
+        <translation>A transferir actualização...</translation>
+    </message>
+    <message>
+        <source>Downloading %1...</source>
+        <translation>A transferir %1...</translation>
+    </message>
+</context>
+<context>
+    <name>YTRegions</name>
+    <message>
+        <source>Algeria</source>
+        <translation>Argélia</translation>
+    </message>
+    <message>
+        <source>Argentina</source>
+        <translation>Argentina</translation>
+    </message>
+    <message>
+        <source>Australia</source>
+        <translation>Austrália</translation>
+    </message>
+    <message>
+        <source>Belgium</source>
+        <translation>Bélgica</translation>
+    </message>
+    <message>
+        <source>Brazil</source>
+        <translation>Brasil</translation>
+    </message>
+    <message>
+        <source>Canada</source>
+        <translation>Canadá</translation>
+    </message>
+    <message>
+        <source>Chile</source>
+        <translation>Chile</translation>
+    </message>
+    <message>
+        <source>Colombia</source>
+        <translation>Colômbia</translation>
+    </message>
+    <message>
+        <source>Czech Republic</source>
+        <translation>República Checa</translation>
+    </message>
+    <message>
+        <source>Egypt</source>
+        <translation>Egipto</translation>
+    </message>
+    <message>
+        <source>France</source>
+        <translation>França</translation>
+    </message>
+    <message>
+        <source>Germany</source>
+        <translation>Alemanha</translation>
+    </message>
+    <message>
+        <source>Ghana</source>
+        <translation>Gana</translation>
+    </message>
+    <message>
+        <source>Greece</source>
+        <translation>Grécia</translation>
+    </message>
+    <message>
+        <source>Hong Kong</source>
+        <translation>Hong Kong</translation>
+    </message>
+    <message>
+        <source>Hungary</source>
+        <translation>Hungria</translation>
+    </message>
+    <message>
+        <source>India</source>
+        <translation>Índia</translation>
+    </message>
+    <message>
+        <source>Indonesia</source>
+        <translation>Indonésia</translation>
+    </message>
+    <message>
+        <source>Ireland</source>
+        <translation>Irlanda</translation>
+    </message>
+    <message>
+        <source>Israel</source>
+        <translation>Israel</translation>
+    </message>
+    <message>
+        <source>Italy</source>
+        <translation>Itália</translation>
+    </message>
+    <message>
+        <source>Japan</source>
+        <translation>Japão</translation>
+    </message>
+    <message>
+        <source>Jordan</source>
+        <translation>Jordânia</translation>
+    </message>
+    <message>
+        <source>Kenya</source>
+        <translation>Quénia</translation>
+    </message>
+    <message>
+        <source>Malaysia</source>
+        <translation>Malásia</translation>
+    </message>
+    <message>
+        <source>Mexico</source>
+        <translation>México</translation>
+    </message>
+    <message>
+        <source>Morocco</source>
+        <translation>Marrocos</translation>
+    </message>
+    <message>
+        <source>Netherlands</source>
+        <translation>Holanda</translation>
+    </message>
+    <message>
+        <source>New Zealand</source>
+        <translation>Nova Zelândia</translation>
+    </message>
+    <message>
+        <source>Nigeria</source>
+        <translation>Nigéria</translation>
+    </message>
+    <message>
+        <source>Peru</source>
+        <translation>Perú</translation>
+    </message>
+    <message>
+        <source>Philippines</source>
+        <translation>Filipinas</translation>
+    </message>
+    <message>
+        <source>Poland</source>
+        <translation>Polónia</translation>
+    </message>
+    <message>
+        <source>Russia</source>
+        <translation>Rússia </translation>
+    </message>
+    <message>
+        <source>Saudi Arabia</source>
+        <translation>Arábia Saudita</translation>
+    </message>
+    <message>
+        <source>Singapore</source>
+        <translation>Singapura</translation>
+    </message>
+    <message>
+        <source>South Africa</source>
+        <translation>África do Sul</translation>
+    </message>
+    <message>
+        <source>South Korea</source>
+        <translation>Coreia do Sul</translation>
+    </message>
+    <message>
+        <source>Spain</source>
+        <translation>Espanha</translation>
+    </message>
+    <message>
+        <source>Sweden</source>
+        <translation>Suécia</translation>
+    </message>
+    <message>
+        <source>Taiwan</source>
+        <translation>Ilha Formosa</translation>
+    </message>
+    <message>
+        <source>Tunisia</source>
+        <translation>Tunísia</translation>
+    </message>
+    <message>
+        <source>Turkey</source>
+        <translation>Turquia</translation>
+    </message>
+    <message>
+        <source>Uganda</source>
+        <translation>Uganda</translation>
+    </message>
+    <message>
+        <source>United Arab Emirates</source>
+        <translation>Emirados Árabes Unidos</translation>
+    </message>
+    <message>
+        <source>United Kingdom</source>
+        <translation>Reino Unido</translation>
+    </message>
+    <message>
+        <source>Yemen</source>
+        <translation>Iémen</translation>
+    </message>
+    <message>
+        <source>Worldwide</source>
+        <translation>Mundial</translation>
+    </message>
+</context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Não é possível obter a stream do video para %1</translation>
+    </message>
+</context>
+</TS>
\ No newline at end of file
index fd0155c00fa241daa6e193d5e6c66d9abd1f9dbf..6f62bb5e0cc1a55de45a020e6ed3f8663cf88723 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Tradu %1 în limba proprie folosind %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Iconița a fost concepută de %1.</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>Descarcă</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Arata actualizeazările</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nu esti abonat la nimic. Ca sa te abonezi la canale, foloseste simbolul stea.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Toate videoclipurile</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Nici unul din canalele la care esti abonat nu are actualizari momentan.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nu esti abonat la nimic. Ca sa te abonezi la canale, foloseste simbolul stea.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Șterge</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 vizionări</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Aceasta este doar o versiune demo a %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Poate să descarce doar videoclipurile mai mici de %1 minute astfel încât să puteți testa funcționalitatea de descărcare.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuă</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>ObțineIa versiunea integrală</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 descărcat în %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Detașează</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Ajustați dimensiunea ferestrei</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Oprește După Acest Videoclip</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>Mod restricționat</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
+        <translation>Ascundeți videoclipuri care pot conține conținut neadecvat</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
         <translation type="unfinished"/>
     </message>
     <message>
         <source>Update</source>
         <translation>Actualizează</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Adresa va fi validă doar pentru o perioadă limitată de timp.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Aceasta este doar o versiune demo a %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Vă permite să testați aplicația și să vedeți dacă funcționează.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Obține versiunea integrală</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Continuă</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Descărcare %1</translation>
         <source>Subscribe to %1</source>
         <translation>Aboneaza-te de la %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Dezabonează-te de la %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 vizionări</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 din %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Căutare...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Afișează încă %1</translation>
         <translation>Bine ați venit la &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Introduceți</translation>
+        <source>to start watching videos.</source>
+        <translation>pentru a începe să vizionați videoclipuri.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>un cuvânt cheie</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>un canal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>pentru a începe să vizionați videoclipuri.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Urmărește</translation>
+        <source>Enter</source>
+        <translation>Introduceți</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Î&amp;napoi</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Avanseaza la %1</translation>
     </message>
     <message>
         <source>Snapshots location changed.</source>
-        <translation type="unfinished"/>
+        <translation>Locația instantaneelor a fost modificată.</translation>
     </message>
 </context>
 <context>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Nu poate fi accesat fluxul video pentru %1</translation>
+        <translation>Se descarcă %1</translation>
     </message>
 </context>
 <context>
         <translation>Global</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nu poate fi accesat fluxul video pentru %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index a810188e6c80de052613b103c2017fe628ef48e8..179bbb5811820e5ac9956afbe766742f8c8a1b90 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Перевести %1 на ваш родной язык с помощью %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Автор значка %1.</translation>
         <source>Show Updated</source>
         <translation>Показать обновленные</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>У вас нет подписок. Используйте символ звездочки чтобы подписаться на каналы.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Все видео</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>В настоящее время нет обновлений подписок.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>У вас нет подписок. Используйте символ звездочки чтобы подписаться на каналы.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Очистить</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 просмотров</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Это всего лишь демо версия %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Можно загружать только видео не длиннее %1 минут, для проверки функциональности загрузчика. </translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продолжить</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Получить полную версию</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 загружен в %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Поверх всех окон</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Подстраивать размер окна</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Ост&amp;ановить после этого видео</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Скрыть видео, содержащие недопустимый контент </translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Нравится %1? Оцени!</translation>
         <source>Update</source>
         <translation>Обновление</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Адрес будет существовать ограниченное время.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Данная программа является демо-версией %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Она позволяет вам оценить приложение.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Купить полную версию</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продолжить</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Загружаю %1</translation>
         <source>Subscribe to %1</source>
         <translation>Подписаться на %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Отписаны от %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 просмотров</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 из %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Идет поиск...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Показать ещё %1</translation>
         <translation>Добро пожаловать в &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Введите</translation>
+        <source>to start watching videos.</source>
+        <translation>чтобы начать просмотр.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>запрос</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>канал</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>чтобы начать просмотр.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Смотреть</translation>
+        <source>Enter</source>
+        <translation>Введите</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Н&amp;азад</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Вперед к %1</translation>
         <translation>Загрузка %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Не удалось получить видео поток для %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Во всем мире</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Не удалось получить видео поток для %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index d393d1b25226b9bbeab6a70a99162c931dd410f3..61d4fd2f25eef0e62552c36c49b56d159cae5e83 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Prelož %1 do svojho materinského jazyka cez %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikonu nadizajnoval %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Zobraziť aktualizované</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nemáš žiadne subskripcie. Použi symbol hviezdy pre odoberanie kanálov.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Všetky videá</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Nie sú k dispozícii žiadne aktualizované subskripcie.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nemáš žiadne subskripcie. Použi symbol hviezdy pre odoberanie kanálov.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Vyčisiť</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 prezretí</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ide o demoverziu %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Umožní ti stiahnuť iba videý kratšie ako %1 minút, takže aspoň môžeš otestovať túto funkcionalitu.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Pokračuj</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Získať plnú verziu</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 stiahnuté za %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Vždy na vrchu</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Upraviť veľkosť okna</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Zastav po tomto videu</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>%Páči sa vám %1? ohodnoťte ho!</translation>
         <source>Update</source>
         <translation>Aktualizácia</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Odkaz bude platný len obmedzenú dobu.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ide o demoverziu %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Umožní ti aplikáciu vyskúšať a pohodlne otestovať.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Získať plnú verziu</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Pokračuj</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Sťahujem %1.</translation>
         <source>Subscribe to %1</source>
         <translation>Odoberať z %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Bol zrušený odber kanála %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 prezretí</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 z %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Hľadám...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Zobraz %1 viac</translation>
         <translation>Vitaj v aplikácii &lt;a href=&apos;%1&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Vlož</translation>
+        <source>to start watching videos.</source>
+        <translation>pre spustenie sledovania.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>kľúčové slovo</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>názov kanálu</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>pre spustenie sledovania.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Pozerať</translation>
+        <source>Enter</source>
+        <translation>Vlož</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Späť</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Vpred k %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Nedostupný video stream pre %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Celosvetovo</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nedostupný video stream pre %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index bfa3343260d29c3f4c398258404f8ea0c99ae6fa..3f5ea5989a397f8e9653346a6a5885028b689e0b 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Prevedite %1 v vaš jezik z uporabo programa %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikone je izrisal %1.</translation>
     <name>AppWidget</name>
     <message>
         <source>Download</source>
-        <translation type="unfinished"/>
+        <translation>Prenesi</translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Pokaži posodobitve</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nimate naročnin. Uporabite zvezdo da se naročite na kanale.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Vsi videoposnetki</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Za zdaj ni nobenih novih posnetkov, na katere ste prijavljeni</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nimate naročnin. Uporabite zvezdo da se naročite na kanale.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Počisti</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 predvajanj</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>To je samo preizkusna različica programa %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Prenese lahko samo posnetke krajše od %1 minut, da lahko preverite delovanje funkcije prenosa.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Nadaljuj</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Pridobi popolno različico</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 preneseno v %2</translation>
         <source>&amp;Float on Top</source>
         <translation>Lebdeče na vrhu</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Prilagodi velikost okna</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>U&amp;stavi za tem posnetkom</translation>
     </message>
     <message>
         <source>Restricted Mode</source>
-        <translation type="unfinished"/>
+        <translation>Omejeni način</translation>
     </message>
     <message>
         <source>Hide videos that may contain inappropriate content</source>
+        <translation>Skriti videoposnetki lahko vsebujejo neprimerno vsebino.</translation>
+    </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
         <translation type="unfinished"/>
     </message>
     <message>
         <source>Update</source>
         <translation>Posodobitev</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Povezava bo delovala le za omejen čas.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>To je samo demo različica programa %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Dovoli vam testiranje aplikacije in preverjanje delovanja,</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Pridobi celotno različico</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Nadaljuj</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Prenašanje %1</translation>
         <source>Subscribe to %1</source>
         <translation>Naroči se na %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Odjavljeni od %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 predvajanj</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 od %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Iskanje ...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Pokaži %1 več</translation>
         <translation>Pozdravljeni v &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Vnesite</translation>
+        <source>to start watching videos.</source>
+        <translation>in začnite gledati posnetke.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ključno besedo</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>in začnite gledati posnetke.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Glejte</translation>
+        <source>Enter</source>
+        <translation>Vnesite</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Na&amp;zaj</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Naprej na %1</translation>
     </message>
     <message>
         <source>Downloading %1...</source>
-        <translation type="unfinished"/>
-    </message>
-</context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Za %1 ni mogoče pridobiti video pretoka</translation>
+        <translation>Prenašanje %1 ...</translation>
     </message>
 </context>
 <context>
         <translation>Svetovno</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Za %1 ni mogoče pridobiti video pretoka</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 02f56b3c693f039044460b196d6acd94787bbc5d..bebe5187707606142db4c58c8b2140f57fd39815 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Perkthe %1 ne gjuhen e tuaj ame duke perdorur %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Dizajnimi i ikones u be nga %1</translation>
         <source>Show Updated</source>
         <translation>Shfaq arrnimin</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Nuk keni abonime . Perdor simbolin yll për tu abonuar te kanalet</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>T&apos;gjitha videot</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Nuk ka abonime të freskuar për momentin</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Nuk keni abonime . Perdor simbolin yll për tu abonuar te kanalet</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Paster</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 e shikimeve</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ky eshte vetem version per demonstrim i %1</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Mund te shkarkoj vetem video me te shkurta se %1 minut ne menyr qe te testoni funksionimin e shkarkuesit. </translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Vazhdon</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Merrni versionin e plote</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 shkarkuar në %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Nxjerr ne Krye</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Ndalo pas kesaj video</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation type="unfinished"/>
         <source>Update</source>
         <translation>Arrnim</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Linku do te jet i vlefshem per nje kohe te kufizuar</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ky eshte version vetem per demonstrim i %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Ju lejon qe te provoni programin dhe te shifni se a funksionon per ju .</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Merrni versionin e plote</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Vazhdim</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Duke shkarkuar %1</translation>
         <source>Subscribe to %1</source>
         <translation>Abonohu në %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 e shikimeve</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 e %2 (%3)--%4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Duke kerkuar</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Shfaq %1 më shumë</translation>
         <translation>Mire se erdhet ne &lt;h href=&quot;%1&quot;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Hyr</translation>
+        <source>to start watching videos.</source>
+        <translation>Per te filluar shikimin e videove.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>Nje fjal kyqe</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>Nje kanal </translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>Per te filluar shikimin e videove.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Shiko</translation>
+        <source>Enter</source>
+        <translation>Hyr</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Prapa</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Përpara në %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Nuk mund te merr rrjedhen e videos per %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Mbar Bota</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Nuk mund te merr rrjedhen e videos per %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 8663b60bde56f25029ad866422be6a91ec149261..15e77deccafdb17a51fa2eaeb4c8c10df67e03f6 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Преведите %1 на ваш матерњи језик помоћу %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Иконицу дизајниро %1.</translation>
         <source>Show Updated</source>
         <translation>Прикажи ажурирано</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Немате претплате. Користи звезду да се претплатиш.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Сви видеи</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Нема ажурираних субскрипција тренутно</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Немате претплате. Користи звезду да се претплатиш.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Очисти</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 прегледа</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ово је тек демо верзија  %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Може преузети само снимке краће од %1 минута тако да можете испробати употребљивост преузимања.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Настави</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Преузмите пуну верзију</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 преузето у %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Флутај на врху</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Прилагоди величину прозора</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Зау&amp;стави након овог видеа</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Обожавате %1? Оцените!</translation>
         <source>Update</source>
         <translation>Ажурирај</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Веза је исправна само одређено време.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Ово је тек демо верзија %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Омогућава вам да испробате програм и увидите да ли вам одговара.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Преузмите пуну верзију</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Настави</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>преузимам %1</translation>
         <source>Subscribe to %1</source>
         <translation>Претплати се на %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Ун-претплаћен од %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 прегледа</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 од %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Тражим...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Прикажи још %1</translation>
         <translation>Добродошли у &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>унесите</translation>
+        <source>to start watching videos.</source>
+        <translation>да би започели преглед видео снимака.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>кључну реч</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>канал</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>да би започели преглед видео снимака.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Гледај</translation>
+        <source>Enter</source>
+        <translation>унесите</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Назад</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Напред до %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>не могу да добавим видео ток за %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Широм света</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>не могу да добавим видео ток за %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index b581eb3506d4503e9ffe94c78406629759df46ad..88d809e90b0e2ea6ebb41fc1e3cdf28a07a1c75e 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Översätt %1 till ditt modersmål med %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Ikon designad av %1</translation>
         <source>Show Updated</source>
         <translation>Visa Uppdaterat</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Du har inga prenumerationer. Använd stjärnsymbolen för att prenumerera på kanaler.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Alla Videor</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Det finns inga uppdaterade prenumerationer just nu.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Du har inga prenumerationer. Använd stjärnsymbolen för att prenumerera på kanaler.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Rensa</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 visningar</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Detta är bara en demoversion av %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Den kan bara ladda ner filmer kortare än %1 minuter så att du kan testa ladda-ned funktionen.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Fortsätt</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Skaffa den fullständiga versionen</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 nedladdad i %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Flyt ovanpå</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Justera Fönster Storleken</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Stoppa efter denna video</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Älskar du %1? Betygsätt den!</translation>
         <source>Update</source>
         <translation>Uppdatera</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Länken kommer att gälla endast under en begränsad tid.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Detta är bara en demoversion av %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Den tillåter dig att testa programmet och se om det fungerar för dig.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Skaffa den fullständiga versionen</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Fortsätt</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Hämtar %1</translation>
         <source>Subscribe to %1</source>
         <translation>Prenumerera på %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Prenumeration avbruten från %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 visningar</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 av %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Söker...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Visa %1 Till</translation>
         <translation>Välkommen till &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Ange</translation>
+        <source>to start watching videos.</source>
+        <translation>för att börja titta på video.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>ett sökord</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>en kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>för att börja titta på video.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Titta</translation>
+        <source>Enter</source>
+        <translation>Ange</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Tillbaka</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Framåt till %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Kan inte få videoström för %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Från Hela Världen</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Kan inte få videoström för %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 99814e2ca805ddfb2d770a9c0bd1c0d80bc83f70..0992c983d5234b63913f4217c917c26d08598266 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>แปล %1 สุ่ภาษาของคุณโดยใช้ %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>ออกแบบไอคอนโดย %1</translation>
         <source>Show Updated</source>
         <translation>แสดงอัพเดต</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>คุณไม่มีการสมัครติดตาม ใช้สัญลักษณ์ดาวเพื่อสมัครติดตามช่อง</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>วิดีโอทั้งหมด</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>ไม่มีการสมัครติดตามที่อัพเดต ณ เวลานี้</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>คุณไม่มีการสมัครติดตาม ใช้สัญลักษณ์ดาวเพื่อสมัครติดตามช่อง</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>ล้าง</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>ดู %1 ครั้ง </translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>นี้เป็นแค่เวอร์ชั่นทดลองใช้ของ %1</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>สามารถดาวน์โหลดวิดึโอส้ันๆกว่า %1 นาที เพื่อทดสอบฟังชั่นดาวน์โหลด</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>ต่อไป</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>รับเวอร์ชั่นเต็ม</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 ถูกดาวน์โหลดใน %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;วางอยู่บนสุด</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;หยุดหลังจากวิดีโอนี้</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;ชอบ %1 มั้ย? ให้คะแนนมันหน่อย!</translation>
         <source>Update</source>
         <translation>อัพเดต</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>ลิงค์จะใช้ได้ในระยะเวลาที่จำกัดเท่านั้น</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>นี้เป็นแค่เวอร์ชั่นทดลองใช้ %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>มันช่วยให้คุณทดสอบโปรแกรมและดูว่ามันใช้ได้ผลกับคุณหรือเปล่า</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>ทำให้เป็นเวอร์ชั่นเต็ม</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>ต่อไป</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>กำลังดาวน์โหลด %1</translation>
         <source>Subscribe to %1</source>
         <translation>สมัครติดตามสู่ %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>ดู %1 ครั้ง </translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 ของ %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>กำลังค้นหา...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>แสดง %1 เพิ่มเติม</translation>
         <translation>ยินดีต้อนรับสู่ &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>ตกลง</translation>
+        <source>to start watching videos.</source>
+        <translation>เพื่อเริ่มรับชมวิดีโอ</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>คำสำคัญ</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>ช่อง</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>เพื่อเริ่มรับชมวิดีโอ</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>รับชม</translation>
+        <source>Enter</source>
+        <translation>ตกลง</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;กลับ</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>เดินหน้าสู่ %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>ไม่สามารถรับกระแสวิดีโอของ %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>ทั่วโลก</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>ไม่สามารถรับกระแสวิดีโอของ %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index d2f44624df0759ffa4804dcaa8a0f95bd47d99e9..73a4a73aa6ee3f7a030895d613ef76e893006580 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>%1&apos;i, %2 kullanarak kendi dilinize çevirin</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>% 1 tarafından desteklenmektedir</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>Açık kaynaklı yazılım</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Simge %1 tarafından tasarlandı.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>%n yeni videonuz var</numerusform><numerusform>%n yeni videonuz var</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Güncellenenleri Göster</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Hiç aboneliğiniz yok. Kanallara abone olmak için yıldız simgesini kullanın.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Tüm Videolar</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Şu anda güncellenen abonelik yok.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Hiç aboneliğiniz yok. Kanallara abone olmak için yıldız simgesini kullanın.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Temizle</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>&amp;n saat önce</numerusform><numerusform>&amp;n saat önce</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>&amp;n gün önce</numerusform><numerusform>&amp;n gün önce</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>&amp;n ay önce</numerusform><numerusform>&amp;n ay önce</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 görüntülenme</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>&amp;n hafta önce</numerusform><numerusform>&amp;n hafta önce</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Bu sadece %&apos;in demo sürümüdür.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Bu sadece %1 dakikadan kısa videoları indirebilir, indirme özelliğini böylece test edebilirsiniz.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Devam</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Tam sürüme geç</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%2 de %1 indirildi</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation><numerusform>&amp;n İndirme</numerusform><numerusform>&amp;n İndirme</numerusform></translation>
     </message>
 </context>
 <context>
     <name>MainWindow</name>
     <message>
         <source>&amp;Window</source>
-        <translation>Pencere</translation>
+        <translation>&amp;Pencere</translation>
     </message>
     <message>
         <source>&amp;Minimize</source>
-        <translation>Küçült</translation>
+        <translation>&amp;Küçült</translation>
     </message>
     <message>
         <source>&amp;Stop</source>
         <source>&amp;Float on Top</source>
         <translation>Üstte Sabitle</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Pencere Boyutunu Ayarla</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Bu Videodan &amp;Sonra Durdur</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Uygunsuz içerik içerebilecek videoları gizle</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>&amp;Menü Çubuğunu Aç/Kapat</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>Menü</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>%1 &amp;seviyor musunuz? Değerlendirin!</translation>
         <source>Update</source>
         <translation>Güncelle</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>ALT tuşuna basarak hala menü çubuğuna erişebilirsiniz</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Bağlantı kısıtlı bir süre için geçerli olacak.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Bu sadece %1&apos;in demo sürümüdür.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Bu, uygulamayı test etmenizi ve çalışıp çalışmadığını görmenizi sağlar.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Tam sürüme geç</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Devam</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>İndiriliyor %1</translation>
         <source>Subscribe to %1</source>
         <translation>%1 Abone Ol</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>% 1 olarak değiştirildi</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>%1 aboneliğinden çık</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 görüntülenme</translation>
+        <source>Pick a video</source>
+        <translation>Bir video seç</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 of %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Aranıyor...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>%1 Tane Daha Göster</translation>
         <translation>&lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;&apos;a Hoşgeldiniz</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Giriş yapın</translation>
+        <source>to start watching videos.</source>
+        <translation>ve videoları izlemeye başlayın.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>bir anahtar kelime</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>bir kanal</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>ve videoları izlemeye başlayın.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>İzle</translation>
+        <source>Enter</source>
+        <translation>Giriş yapın</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Geri</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>&amp;İleri</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>%1 Yönlendir</translation>
         <translation>%1 indiriliyor...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>%1 için video akışı alınamıyor.</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Dünya Çapında</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>%1 için video akışı alınamıyor.</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 10dae88b8cecde58cf48f4a79ae0ea4af6ce1eb7..fccc3709cf349b21aaaa1b5a9a4afd6539473003 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Перекласти %1 Вашою рідною мовою за допомогою %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Розробник піктоґрам %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Показати оновлені</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Усі видива</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Наразі оновлень підписок немає.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Очистити</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 переглядів</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Це демонстраційна версія %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Із метою тестування, Ви можете завантажити видиво тривалістю до %1 хв. </translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продовжити</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Отримати повну версію</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 завантажений до %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Згори всіх вікон</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Змінити розмір вікна</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Зу&amp;пинити після цього видиво</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>Приховати відео, які можуть містити небажаний контент</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Подобається %1? Оцініть !</translation>
         <source>Update</source>
         <translation>Оновлення</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Посилання буде дійсне лише протягом обмеженого часу.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>&gt;Це демонстраційна версія %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Ви маєте змогу протестувати проґраму та перевірити працездатність.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Отримати повну версію</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продовжити</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Завантаження %1</translation>
         <source>Subscribe to %1</source>
         <translation>Підписуватися на %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Відписані від %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 переглядів</translation>
+        <source>Pick a video</source>
+        <translation>Вибрати відео</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 з %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Пошук...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Наступні %1 </translation>
         <translation>Ласкаво просимо до &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Уведіть</translation>
+        <source>to start watching videos.</source>
+        <translation>аби розпочати перегляд.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>запит</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>канал</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>аби розпочати перегляд.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Дивитися</translation>
+        <source>Enter</source>
+        <translation>Уведіть</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Н&amp;азад</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>&amp;Вперед</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Уперед до %1</translation>
         <translation>Завантаження %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Не вдалося отримати видивопотік для %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Світовий</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Не вдалося отримати видиво потік для %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 50a6fb7031fd8f12b74eeead0508824bc1b4efc4..9c41c384a50325b137b2e37a537674577e84775a 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Перекласти %1 Вашою рідною мовою за допомогою %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Розробник піктоґрам %1.</translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>Показати оновлені</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Усі видива</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Наразі оновлень підписок немає.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Очистити</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 переглядів</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Це демонстраційна версія %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Із метою тестування, Ви можете завантажити видиво тривалістю до %1 хв. </translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продовжити</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Отримати повну версію</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 завантажений до %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
+        <translation type="unfinished"><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform><numerusform></numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>&amp;Згори всіх вікон</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>&amp;Змінити розмір вікна</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>Зу&amp;пинити після цього видиво</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Подобається %1? Оцініть !</translation>
         <source>Update</source>
         <translation>Оновлення</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Посилання буде дійсне лише протягом обмеженого часу.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>&gt;Це демонстраційна версія %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Ви маєте змогу протестувати проґраму та перевірити працездатність.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Отримати повну версію</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Продовжити</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Завантаження %1</translation>
         <source>Subscribe to %1</source>
         <translation>Підписуватися на %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>Відписані від %1</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 переглядів</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 з %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Пошук...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Наступні %1 </translation>
         <translation>Ласкаво просимо до &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Уведіть</translation>
+        <source>to start watching videos.</source>
+        <translation>аби розпочати перегляд.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>запит</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>канал</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>аби розпочати перегляд.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Дивитися</translation>
+        <source>Enter</source>
+        <translation>Уведіть</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>Н&amp;азад</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Уперед до %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Не вдалося отримати видивопотік для %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Світовий</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Не вдалося отримати видивопотік для %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 5517eaf6d4baad9dce032221518a886a17357543..dce52fb1eeba256d3196672a9cb7c83c1d99c133 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>Chuyển ngữ %1 sang ngôn ngữ của bạn bằng cách sử dụng %2</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>Biểu tượng được thiết kế bởi %1.</translation>
         <source>Show Updated</source>
         <translation>Hiển thị Cập Nhật</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>Bạn không có phần đăng ký theo dõi nào. Sử dụng biểu tượng ngôi sao để đăng ký theo dõi một kênh nào đó.</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>Tất cả các video</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>Hiện không có phần theo dõi đăng ký nào được cập nhật vào thời điểm này.</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>Bạn không có phần đăng ký theo dõi nào. Sử dụng biểu tượng ngôi sao để đăng ký theo dõi một kênh nào đó.</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>Dọn dẹp phần nội dung</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 lượt xem</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Đây là phiên bản dùng thử của %1.</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>Phần này có thể được tải về các video ngắn hơn %1 phút để bạn có thể thử nghiệm tính năng tải về.</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Tiếp tục</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Nhận phiên bản đầy đủ</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 đã tải xuống trong %2</translation>
         <source>&amp;Float on Top</source>
         <translation>&amp;Nổi lên trên cùng</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation type="unfinished"/>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>&amp;Dừng phát sau video này</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation type="unfinished"/>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>&amp;Yêu thích %1? Hãy đánh giá!</translation>
         <source>Update</source>
         <translation>Cập nhật</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>Liên kết này chỉ hợp lệ trong một khoảng thời gian hạn định.</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>Đây chỉ là phần dùng thử của phiên bản %1.</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>Cho phép bạn thử nghiệm ứng dụng và xét độ hiệu quả của ứng dụng đối với bạn.</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>Nhận phiên bản đầy đủ</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>Tiếp tục</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>Đang tải về %1</translation>
         <source>Subscribe to %1</source>
         <translation>Đăng ký theo dõi đối với %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation type="unfinished"/>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 lượt xem</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 của %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>Đang tìm kiếm...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>Hiển thị %1 thêm</translation>
         <translation>Chào mừng đến với &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>Nhập vào</translation>
+        <source>to start watching videos.</source>
+        <translation>để bắt đầu xem các đoạn video.</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>một từ khóa</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>một kênh</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>để bắt đầu xem các đoạn video.</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>Xem</translation>
+        <source>Enter</source>
+        <translation>Nhập vào</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>&amp;Quay lại</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>Chuyển tới %1</translation>
         <translation type="unfinished"/>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>Không thể tiếp nhận luồng video từ %1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>Toàn thế giới</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>Không thể tiếp nhận luồng video từ %1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 36d4e3e56271282e822b5553a1d300af4e2d78b1..27a7517eaf1043cb44d15396fbe5c3c961fa08bd 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>使用 %2 将 %1 翻译为您的母语</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>图标设计:%1。</translation>
         <source>Show Updated</source>
         <translation>显示更新</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>你没有任何订阅,使用星形符号订阅频道</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>所有视频</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>当前没有订阅更新</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>你没有任何订阅,使用星形符号订阅频道</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>清除</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
+        <source>%n month(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>%1 人次观看</translation>
+    </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
+        <source>%n week(s) ago</source>
         <translation type="unfinished"><numerusform></numerusform></translation>
     </message>
 </context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>这只是 %1 的演示版。</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>本版本只能下载 %1 分钟以下的视频,仅用于测试下载功能。</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>继续</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>获取完整版</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 已下载(%2)</translation>
         <source>&amp;Float on Top</source>
         <translation>窗口置顶(&amp;F)</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>调整窗口大小(&amp;A)</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>该视频后停止播放(&amp;S)</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>隐藏可能含有不恰当内容的视频</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation type="unfinished"/>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>喜欢 %1? 为其评分!</translation>
         <source>Update</source>
         <translation>更新</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation type="unfinished"/>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>此链接仅能保持短时间的有效性。</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>这仅是 %1 的演示版。</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>本版本允许您测试,以确认本应用是否适合您。</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>获取完整版</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>继续</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>正在下载 %1</translation>
         <source>Subscribe to %1</source>
         <translation>订阅 %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>从 %1 退订</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>%1 人次观看</translation>
+        <source>Pick a video</source>
+        <translation type="unfinished"/>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 之 %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>搜索中……</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>再多显示 %1</translation>
         <translation>欢迎使用&lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;!</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>输入</translation>
+        <source>to start watching videos.</source>
+        <translation>开始观看视频。</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>关键字</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>频道名称</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>开始观看视频。</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>观看</translation>
+        <source>Enter</source>
+        <translation>输入</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>后退(_B)</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation type="unfinished"/>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>前进至 %1</translation>
         <translation>正在下载 %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>无法获得视频流。可能原因:%1</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>全球</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>无法获得视频流。可能原因:%1</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index 4e9424e59cc98fa4028d71476300f4ac337bc881..854e248d824cbac02fc2df2cb2067e1388392131 100644 (file)
         <source>Translate %1 to your native language using %2</source>
         <translation>使用 %2 翻譯 %1 介面成為您的本國語言</translation>
     </message>
+    <message>
+        <source>Powered by %1</source>
+        <translation>威力本源 %1</translation>
+    </message>
+    <message>
+        <source>Open-source software</source>
+        <translation>開放原始碼軟體</translation>
+    </message>
     <message>
         <source>Icon designed by %1.</source>
         <translation>圖示由 %1 所設計。 </translation>
     </message>
     <message numerus="yes">
         <source>You have %n new video(s)</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>您有 %n 個新影片</numerusform></translation>
     </message>
 </context>
 <context>
         <source>Show Updated</source>
         <translation>顯示更新</translation>
     </message>
+    <message>
+        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
+        <translation>您暫時沒有任何訂閱。使用星星符號訂閱頻道。</translation>
+    </message>
     <message>
         <source>All Videos</source>
         <translation>全部影片</translation>
         <source>There are no updated subscriptions at this time.</source>
         <translation>目前沒有更新的訂閱。</translation>
     </message>
-    <message>
-        <source>You have no subscriptions. Use the star symbol to subscribe to channels.</source>
-        <translation>您暫時沒有任何訂閱。使用星星符號訂閱頻道。</translation>
-    </message>
-</context>
-<context>
-    <name>ClearButton</name>
-    <message>
-        <source>Clear</source>
-        <translation>清除</translation>
-    </message>
 </context>
 <context>
     <name>DataUtils</name>
     </message>
     <message numerus="yes">
         <source>%n hour(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>%n 小時前</numerusform></translation>
     </message>
     <message numerus="yes">
         <source>%n day(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>%n 天前</numerusform></translation>
     </message>
     <message numerus="yes">
-        <source>%n weeks(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <source>%n month(s) ago</source>
+        <translation><numerusform>%n 個月前</numerusform></translation>
+    </message>
+    <message>
+        <source>K</source>
+        <comment>K as in Kilo, i.e. thousands</comment>
+        <translation>K</translation>
+    </message>
+    <message>
+        <source>M</source>
+        <comment>M stands for Millions</comment>
+        <translation>M</translation>
+    </message>
+    <message>
+        <source>B</source>
+        <comment>B stands for Billions</comment>
+        <translation>B</translation>
+    </message>
+    <message>
+        <source>%1 views</source>
+        <translation>瀏覽次數:%1 次</translation>
     </message>
     <message numerus="yes">
-        <source>%n month(s) ago</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <source>%n week(s) ago</source>
+        <translation><numerusform>%n 週前</numerusform></translation>
     </message>
 </context>
 <context>
 </context>
 <context>
     <name>DownloadManager</name>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>這僅僅是展示版的  %1。</translation>
-    </message>
-    <message>
-        <source>It can only download videos shorter than %1 minutes so you can test the download functionality.</source>
-        <translation>它只能下載影片少於 %1 分鐘,使您可以測試下載功能。</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>繼續</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>取得完整版</translation>
-    </message>
     <message>
         <source>%1 downloaded in %2</source>
         <translation>%1 下載在 %2</translation>
     </message>
     <message numerus="yes">
         <source>%n Download(s)</source>
-        <translation type="unfinished"><numerusform></numerusform></translation>
+        <translation><numerusform>%n 次下載</numerusform></translation>
     </message>
 </context>
 <context>
         <source>&amp;Float on Top</source>
         <translation>浮在上面(&amp;F)</translation>
     </message>
-    <message>
-        <source>&amp;Adjust Window Size</source>
-        <translation>調整視窗大小 (&amp;A)</translation>
-    </message>
     <message>
         <source>&amp;Stop After This Video</source>
         <translation>在這個影片播完之後停止(&amp;S)</translation>
         <source>Hide videos that may contain inappropriate content</source>
         <translation>隱藏可能包含不適當內容的影片</translation>
     </message>
+    <message>
+        <source>Toggle &amp;Menu Bar</source>
+        <translation>切換選單列(&amp;M)</translation>
+    </message>
+    <message>
+        <source>Menu</source>
+        <translation>選單</translation>
+    </message>
     <message>
         <source>&amp;Love %1? Rate it!</source>
         <translation>喜歡 %1 ?為它評分!(&amp;L)</translation>
         <source>Update</source>
         <translation>更新</translation>
     </message>
+    <message>
+        <source>You can still access the menu bar by pressing the ALT key</source>
+        <translation>您還是可以透過按下 ALT 鍵存取選單列</translation>
+    </message>
 </context>
 <context>
     <name>MediaView</name>
         <source>The link will be valid only for a limited time.</source>
         <translation>這個連結將只在有限的時間內有效。</translation>
     </message>
-    <message>
-        <source>This is just the demo version of %1.</source>
-        <translation>這僅僅是展示版的  %1。</translation>
-    </message>
-    <message>
-        <source>It allows you to test the application and see if it works for you.</source>
-        <translation>它可以讓您測試應用程式,看它是否適合您。</translation>
-    </message>
-    <message>
-        <source>Get the full version</source>
-        <translation>取得完整版</translation>
-    </message>
-    <message>
-        <source>Continue</source>
-        <translation>繼續</translation>
-    </message>
     <message>
         <source>Downloading %1</source>
         <translation>正在下載 %1</translation>
         <source>Subscribe to %1</source>
         <translation>訂閱 %1</translation>
     </message>
+    <message>
+        <source>Switched to %1</source>
+        <translation>切換至 %1</translation>
+    </message>
     <message>
         <source>Unsubscribed from %1</source>
         <translation>從 %1 取消訂閱</translation>
     </message>
 </context>
 <context>
-    <name>PlaylistItemDelegate</name>
+    <name>PickMessage</name>
     <message>
-        <source>%1 views</source>
-        <translation>瀏覽次數:%1 次</translation>
+        <source>Pick a video</source>
+        <translation>挑選影片</translation>
     </message>
+</context>
+<context>
+    <name>PlaylistItemDelegate</name>
     <message>
         <source>%1 of %2 (%3) — %4</source>
         <translation>%1 / %2 (%3) — %4</translation>
 </context>
 <context>
     <name>PlaylistModel</name>
-    <message>
-        <source>Searching...</source>
-        <translation>搜尋中...</translation>
-    </message>
     <message>
         <source>Show %1 More</source>
         <translation>顯示再多 %1個影片</translation>
         <translation>歡迎使用 &lt;a href=&apos;%1&apos;&gt;%2&lt;/a&gt;,</translation>
     </message>
     <message>
-        <source>Enter</source>
-        <extracomment>&quot;Enter&quot;, as in &quot;type&quot;. The whole phrase says: &quot;Enter a keyword to start watching videos&quot;</extracomment>
-        <translation>輸入</translation>
+        <source>to start watching videos.</source>
+        <translation>以開始觀看影片。</translation>
     </message>
     <message>
         <source>a keyword</source>
         <translation>一個關鍵字</translation>
     </message>
     <message>
-        <source>a channel</source>
-        <translation>一個頻道</translation>
-    </message>
-    <message>
-        <source>to start watching videos.</source>
-        <translation>以開始觀看影片。</translation>
-    </message>
-    <message>
-        <source>Watch</source>
-        <translation>觀看</translation>
+        <source>Enter</source>
+        <translation>輸入</translation>
     </message>
     <message>
         <source>Recent keywords</source>
         <source>&amp;Back</source>
         <translation>後退(&amp;B)</translation>
     </message>
+    <message>
+        <source>&amp;Forward</source>
+        <translation>前進(&amp;F)</translation>
+    </message>
     <message>
         <source>Forward to %1</source>
         <translation>向前到 %1</translation>
         <translation>正在下載 %1...</translation>
     </message>
 </context>
-<context>
-    <name>Video</name>
-    <message>
-        <source>Cannot get video stream for %1</source>
-        <translation>無法為 %1 獲得影片串流</translation>
-    </message>
-</context>
 <context>
     <name>YTRegions</name>
     <message>
         <translation>全世界</translation>
     </message>
 </context>
+<context>
+    <name>YTVideo</name>
+    <message>
+        <source>Cannot get video stream for %1</source>
+        <translation>無法為 %1 取得影片串流</translation>
+    </message>
+</context>
 </TS>
\ No newline at end of file
index c6547b6084fc1f11d1a8301841084971df21179a..9789fa1866da608bf435e414c1a075604a10d855 100644 (file)
@@ -1,19 +1,20 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<application>
-  <id type="desktop">minitube.desktop</id>
+<component>
+  <name>Minitube</name>
+  <id>org.tordini.flavio.minitube</id>
   <metadata_license>CC0-1.0</metadata_license>
-  <license>GPL-3.0+</license>
+  <project_license>GPL-3.0+</project_license>
   <summary>YouTube app</summary>
   <description>
     <p>
-      Minitube is a YouTube desktop application.
+      Minitube is a YouTube desktop application. It is written in C++ using the Qt framework.
     </p>
   </description>
   <url type="homepage">http://flavio.tordini.org/minitube</url>
   <screenshots>
-    <screenshot type="default">http://flavio.tordini.org/files/minitube/minitube-04.jpg</screenshot>
-    <screenshot>http://flavio.tordini.org/files/minitube/minitube-03.jpg</screenshot>
-    <screenshot>http://flavio.tordini.org/files/minitube/minitube-02.jpg</screenshot>
-    <screenshot>http://flavio.tordini.org/files/minitube/minitube-01.jpg</screenshot>
+    <screenshot type="default"><image>http://flavio.tordini.org/files/minitube/minitube-04.jpg</image></screenshot>
+    <screenshot><image>http://flavio.tordini.org/files/minitube/minitube-03.jpg</image></screenshot>
+    <screenshot><image>http://flavio.tordini.org/files/minitube/minitube-02.jpg</image></screenshot>
+    <screenshot><image>http://flavio.tordini.org/files/minitube/minitube-01.jpg</image></screenshot>
   </screenshots>
-</application>
+</component>
index 453cf15f4b30093ec6ee02eb922a44ee165ec174..27c5a01681bb096f021d2315314cd4e0e9552f42 100644 (file)
@@ -1,7 +1,7 @@
-CONFIG += release c++11
-CONFIG -= rtti exceptions
+CONFIG += c++14 exceptions_off rtti_off optimize_full
+
 TEMPLATE = app
-VERSION = 2.9
+VERSION = 3.1
 DEFINES += APP_VERSION="$$VERSION"
 
 APP_NAME = Minitube
@@ -10,8 +10,6 @@ DEFINES += APP_NAME="$$APP_NAME"
 APP_UNIX_NAME = minitube
 DEFINES += APP_UNIX_NAME="$$APP_UNIX_NAME"
 
-DEFINES += APP_PHONON
-DEFINES += APP_PHONON_SEEK
 DEFINES += APP_SNAPSHOT
 
 message(Building $${APP_NAME} $${VERSION})
@@ -29,12 +27,15 @@ TARGET = $${APP_UNIX_NAME}
 
 QT += widgets network sql qml
 
+include(lib/http/http.pri)
+include(lib/idle/idle.pri)
+
+DEFINES += MEDIA_MPV
+include(lib/media/media.pri)
+
 include(src/qtsingleapplication/qtsingleapplication.pri)
-include(src/http/http.pri)
-include(src/idle/idle.pri)
 
 HEADERS += src/video.h \
-    src/searchlineedit.h \
     src/spacer.h \
     src/constants.h \
     src/playlistitemdelegate.h \
@@ -43,7 +44,6 @@ HEADERS += src/video.h \
     src/searchparams.h \
     src/minisplitter.h \
     src/loadingwidget.h \
-    src/videoareawidget.h \
     src/autocomplete.h \
     src/videodefinition.h \
     src/fontutils.h \
@@ -101,16 +101,16 @@ HEADERS += src/video.h \
     src/yt3.h \
     src/paginatedvideosource.h \
     src/searchwidget.h \
-    src/exlineedit.h \
     src/channellistview.h \
     src/httputils.h \
     src/appwidget.h \
     src/clickablelabel.h \
     src/ytvideo.h \
     src/toolbarmenu.h \
-    src/sharetoolbar.h
+    src/sharetoolbar.h \
+    src/videoarea.h \
+    src/searchlineedit.h
 SOURCES += src/main.cpp \
-    src/searchlineedit.cpp \
     src/spacer.cpp \
     src/video.cpp \
     src/videomimedata.cpp \
@@ -118,7 +118,6 @@ SOURCES += src/main.cpp \
     src/searchparams.cpp \
     src/minisplitter.cpp \
     src/loadingwidget.cpp \
-    src/videoareawidget.cpp \
     src/autocomplete.cpp \
     src/videodefinition.cpp \
     src/constants.cpp \
@@ -175,15 +174,19 @@ SOURCES += src/main.cpp \
     src/ytchannel.cpp \
     src/yt3.cpp \
     src/paginatedvideosource.cpp \
-    src/exlineedit.cpp \
     src/channellistview.cpp \
     src/httputils.cpp \
     src/appwidget.cpp \
     src/clickablelabel.cpp \
     src/ytvideo.cpp \
     src/toolbarmenu.cpp \
-    src/sharetoolbar.cpp
+    src/sharetoolbar.cpp \
+    src/videoarea.cpp \
+    src/searchlineedit.cpp
+
 RESOURCES += resources.qrc
+RESOURCES += $$files(icons/*.png, true)
+
 DESTDIR = build/target/
 OBJECTS_DIR = build/obj/
 MOC_DIR = build/moc/
@@ -192,26 +195,30 @@ RCC_DIR = build/rcc/
 # Tell Qt Linguist that we use UTF-8 strings in our sources
 CODECFORTR = UTF-8
 CODECFORSRC = UTF-8
+
 include(locale/locale.pri)
 
 # deploy
 DISTFILES += CHANGES COPYING
 unix:!mac {
     DEFINES += APP_LINUX
-    LIBS += -lphonon4qt5
-    INCLUDEPATH += /usr/include/phonon4qt5
     QT += dbus
     HEADERS += src/gnomeglobalshortcutbackend.h
     SOURCES += src/gnomeglobalshortcutbackend.cpp
+
     isEmpty(PREFIX):PREFIX = /usr
+
     BINDIR = $$PREFIX/bin
     INSTALLS += target
     target.path = $$BINDIR
+
     DATADIR = $$PREFIX/share
     PKGDATADIR = $$DATADIR/minitube
     DEFINES += DATADIR=\\\"$$DATADIR\\\" \
         PKGDATADIR=\\\"$$PKGDATADIR\\\"
+
     INSTALLS += translations \
+        sounds \
         desktop \
         appdata \
         iconsvg \
@@ -225,6 +232,8 @@ unix:!mac {
         icon512
     translations.path = $$PKGDATADIR
     translations.files += $$DESTDIR/locale
+    sounds.path = $$PKGDATADIR
+    sounds.files += sounds/
     desktop.path = $$DATADIR/applications
     desktop.files += minitube.desktop
     appdata.path = $$DATADIR/appdata
@@ -248,4 +257,5 @@ unix:!mac {
     icon512.path = $$DATADIR/icons/hicolor/512x512/apps
     icon512.files += data/512x512/minitube.png
 }
+
 mac|win32|contains(DEFINES, APP_UBUNTU):include(local/local.pri)
index 76556e843d82b1fa7abab4e0e583562e0c88e3c3..9933d32159a1412fa4a66f35af708eace8c5f22b 100644 (file)
@@ -1,11 +1,6 @@
 <RCC>
     <qresource prefix="/">
         <file>images/app.png</file>
-        <file>images/refine-search.png</file>
-        <file>images/search-time.png</file>
-        <file>images/search-sortBy.png</file>
-        <file>images/search-quality.png</file>
-        <file>images/search-duration.png</file>
         <file>flags/dz.png</file>
         <file>flags/ar.png</file>
         <file>flags/au.png</file>
         <file>flags/gb.png</file>
         <file>flags/ye.png</file>
         <file>style.css</file>
-        <file>images/worldwide.png</file>
-        <file>images/show-updated.png</file>
-        <file>images/sort.png</file>
-        <file>images/mark-watched.png</file>
-        <file>images/channels.png</file>
-        <file>images/unwatched.png</file>
-        <file>sounds/snapshot.wav</file>
         <file>images/app@2x.png</file>
-        <file>images/sort@2x.png</file>
-        <file>images/unwatched@2x.png</file>
-        <file>images/channels@2x.png</file>
-        <file>images/mark-watched@2x.png</file>
-        <file>images/show-updated@2x.png</file>
-        <file>images/worldwide@2x.png</file>
-        <file>images/refine-search@2x.png</file>
-        <file>images/search-duration@2x.png</file>
-        <file>images/search-quality@2x.png</file>
-        <file>images/search-time@2x.png</file>
-        <file>images/search-sortBy@2x.png</file>
-        <file>images/audio-volume-high.png</file>
-        <file>images/audio-volume-high@2x.png</file>
-        <file>images/audio-volume-muted.png</file>
-        <file>images/audio-volume-muted@2x.png</file>
-        <file>images/bookmark-new.png</file>
-        <file>images/bookmark-new@2x.png</file>
-        <file>images/bookmark-new_active.png</file>
-        <file>images/bookmark-new_active@2x.png</file>
-        <file>images/bookmark-remove.png</file>
-        <file>images/bookmark-remove@2x.png</file>
-        <file>images/content-loading.png</file>
-        <file>images/content-loading@2x.png</file>
-        <file>images/document-save.png</file>
-        <file>images/document-save@2x.png</file>
-        <file>images/edit-clear.png</file>
-        <file>images/edit-find.png</file>
-        <file>images/go-next.png</file>
-        <file>images/go-next@2x.png</file>
-        <file>images/go-next_active.png</file>
-        <file>images/go-next_active@2x.png</file>
-        <file>images/go-previous.png</file>
-        <file>images/go-previous@2x.png</file>
-        <file>images/go-previous_active.png</file>
-        <file>images/go-previous_active@2x.png</file>
-        <file>images/go-top.png</file>
-        <file>images/go-top@2x.png</file>
-        <file>images/media-playback-pause.png</file>
-        <file>images/media-playback-pause@2x.png</file>
-        <file>images/media-playback-start.png</file>
-        <file>images/media-playback-start@2x.png</file>
-        <file>images/media-playback-stop.png</file>
-        <file>images/media-playback-stop@2x.png</file>
-        <file>images/media-skip-forward.png</file>
-        <file>images/media-skip-forward@2x.png</file>
-        <file>images/system-search.png</file>
-        <file>images/system-search_active.png</file>
-        <file>images/system-search_selected.png</file>
-        <file>images/video-display.png</file>
-        <file>images/video-display@2x.png</file>
-        <file>images/view-fullscreen.png</file>
-        <file>images/view-fullscreen@2x.png</file>
-        <file>images/view-list.png</file>
-        <file>images/view-list@2x.png</file>
-        <file>images/view-refresh.png</file>
-        <file>images/view-refresh_active.png</file>
-        <file>images/view-refresh_selected.png</file>
-        <file>images/view-restore.png</file>
-        <file>images/view-restore@2x.png</file>
-        <file>images/window-close.png</file>
-        <file>images/window-close_active.png</file>
-        <file>images/window-close_selected.png</file>
         <file>images/64x64/app.png</file>
         <file>images/64x64/app@2x.png</file>
-        <file>images/safesearch.png</file>
-        <file>images/safesearch@2x.png</file>
-        <file>images/view-more.png</file>
-        <file>images/view-more@2x.png</file>
-        <file>images/email.png</file>
-        <file>images/email@2x.png</file>
-        <file>images/facebook.png</file>
-        <file>images/facebook@2x.png</file>
-        <file>images/link.png</file>
-        <file>images/link@2x.png</file>
-        <file>images/twitter.png</file>
-        <file>images/twitter@2x.png</file>
     </qresource>
 </RCC>
index 1fe723a4215df4afc037859e7707f66370826e98..2c5b456ae356a3ea2f5bcf75c165a139bc57d014 100644 (file)
@@ -27,23 +27,22 @@ $END_LICENSE */
 #include "activation.h"
 #endif
 #ifdef APP_MAC
-#include "macutils.h"
 #include "mac_startup.h"
+#include "macutils.h"
 #endif
-#include "fontutils.h"
-#include "iconutils.h"
 #include "appwidget.h"
 #include "clickablelabel.h"
+#include "fontutils.h"
+#include "iconutils.h"
 #include "mainwindow.h"
 
 AboutView::AboutView(QWidget *parent) : View(parent) {
-
     const int padding = 30;
     const char *buildYear = __DATE__ + 7;
 
-    // speedup painting since we'll paint the whole background
-    // by ourselves anyway in paintEvent()
-    setAttribute(Qt::WA_OpaquePaintEvent);
+    setBackgroundRole(QPalette::Base);
+    setForegroundRole(QPalette::Text);
+    setAutoFillBackground(true);
 
     QBoxLayout *verticalLayout = new QVBoxLayout(this);
     verticalLayout->setMargin(0);
@@ -55,8 +54,13 @@ AboutView::AboutView(QWidget *parent) : View(parent) {
     aboutlayout->setMargin(padding);
     aboutlayout->setSpacing(padding);
 
-    logo = new ClickableLabel();
-    logo->setPixmap(IconUtils::pixmap(":/images/app.png"));
+    ClickableLabel *logo = new ClickableLabel();
+    auto setLogoPixmap = [logo] {
+        logo->setPixmap(IconUtils::pixmap(":/images/app.png", logo->devicePixelRatioF()));
+    };
+    setLogoPixmap();
+    connect(window()->windowHandle(), &QWindow::screenChanged, this, setLogoPixmap);
+
     connect(logo, &ClickableLabel::clicked, MainWindow::instance(), &MainWindow::visitSite);
     aboutlayout->addWidget(logo, 0, Qt::AlignTop);
 
@@ -65,39 +69,76 @@ AboutView::AboutView(QWidget *parent) : View(parent) {
     layout->setSpacing(padding);
     aboutlayout->addLayout(layout);
 
-    QString css = "a { color: palette(text); text-decoration: none; font-weight: bold } h1 { font-weight: 300 }";
+    QColor lightTextColor = palette().text().color();
+#ifdef APP_MAC
+    lightTextColor.setAlphaF(.75);
+#endif
+#ifdef APP_MAC
+    QColor linkColor = mac::accentColor();
+#else
+    QColor linkColor = palette().highlight().color();
+#endif
 
-    QString info = "<html><style>" + css + "</style><body>"
-            "<h1>" + QString(Constants::NAME) + "</h1>"
-            "<p>" + tr("There's life outside the browser!") + "</p>"
-            "<p>" + tr("Version %1").arg(Constants::VERSION) + "</p>"
-            + QString("<p><a href=\"%1/\">%1</a></p>").arg(Constants::WEBSITE);
+    QString info = "<html><style>"
+                   "body { color: " +
+                   lightTextColor.name(QColor::HexArgb) +
+                   "; } "
+                   "h1 { color: palette(text); font-weight: 100; } "
+                   "a { color: " +
+                   linkColor.name(QColor::HexArgb) +
+                   "; text-decoration: none; font-weight: normal; }"
+                   "</style><body>";
+
+    info += "<h1>" + QString(Constants::NAME) +
+            "</h1>"
+            "<p>" +
+            tr("There's life outside the browser!") +
+            "</p>"
+            "<p>" +
+            tr("Version %1").arg(Constants::VERSION) + "</p>" +
+            QString("<p><a href=\"%1/\">%1</a></p>").arg(Constants::WEBSITE);
 
 #ifdef APP_ACTIVATION
     QString email = Activation::instance().getEmail();
-    if (!email.isEmpty())
-        info += "<p>" + tr("Licensed to: %1").arg("<b>" + email + "</b>");
+    if (!email.isEmpty()) info += "<p>" + tr("Licensed to: %1").arg("<b>" + email + "</b>");
 #endif
 
 #ifndef APP_EXTRA
-    info += "<p>" +  tr("%1 is Free Software but its development takes precious time.").arg(Constants::NAME) + "<br/>"
-            + tr("Please <a href='%1'>donate</a> to support the continued development of %2.")
-            .arg(QString(Constants::WEBSITE).append("#donate"), Constants::NAME) + "</p>";
+    info += "<p>" +
+            tr("%1 is Free Software but its development takes precious time.")
+                    .arg(Constants::NAME) +
+            "<br/>" +
+            tr("Please <a href='%1'>donate</a> to support the continued development of %2.")
+                    .arg(QString(Constants::WEBSITE).append("#donate"), Constants::NAME) +
+            "</p>";
 #endif
 
-    info += "<p>" + tr("Translate %1 to your native language using %2").arg(Constants::NAME)
-            .arg("<a href='http://www.transifex.net/projects/p/" + QString(Constants::UNIX_NAME) + "/'>Transifex</a>")
-            + "</p>"
+    info += "<p>" +
+            tr("Translate %1 to your native language using %2")
+                    .arg(Constants::NAME)
+                    .arg("<a href='http://www.transifex.net/projects/p/" +
+                         QString(Constants::UNIX_NAME) + "/'>Transifex</a>") +
+            "</p>";
+
+    info += "<p>" +
+            tr("Powered by %1")
+                    .arg("<a href='https://" + QLatin1String(Constants::ORG_DOMAIN) +
+                         "/opensource'>" + tr("Open-source software") + "</a>") +
+            "</p>";
 
-            "<p>"
-            + tr("Icon designed by %1.").arg("<a href='http://www.kolorguild.co.za/'>David Nel</a>")
-            "</p>"
+    info += "<p>" +
+            tr("Icon designed by %1.").arg("<a href='http://www.kolorguild.co.za/'>David Nel</a>") +
+            "</p>"
 
-        #ifndef APP_EXTRA
-            "<p>" + tr("Released under the <a href='%1'>GNU General Public License</a>")
-            .arg("http://www.gnu.org/licenses/gpl.html") + "</p>"
-        #endif
-            "<p>&copy; " + buildYear + " " + Constants::ORG_NAME + "</p>"
+#ifndef APP_EXTRA
+            "<p>" +
+            tr("Released under the <a href='%1'>GNU General Public License</a>")
+                    .arg("http://www.gnu.org/licenses/gpl.html") +
+            "</p>"
+#endif
+            "<p>&copy; " +
+            buildYear + " " + Constants::ORG_NAME +
+            "</p>"
             "</body></html>";
 
     QLabel *infoLabel = new QLabel(info, this);
@@ -112,7 +153,7 @@ AboutView::AboutView(QWidget *parent) : View(parent) {
 
     closeButton->setDefault(true);
     closeButton->setFocus();
-    connect(closeButton, SIGNAL(clicked()), parent, SLOT(goBack()));
+    connect(closeButton, SIGNAL(clicked()), MainWindow::instance(), SLOT(goBack()));
     buttonLayout->addWidget(closeButton);
 
     layout->addLayout(buttonLayout);
@@ -122,22 +163,4 @@ AboutView::AboutView(QWidget *parent) : View(parent) {
 
 void AboutView::appear() {
     closeButton->setFocus();
-    connect(window()->windowHandle(), SIGNAL(screenChanged(QScreen*)), SLOT(screenChanged()), Qt::UniqueConnection);
-}
-
-void AboutView::paintEvent(QPaintEvent *event) {
-    QWidget::paintEvent(event);
-    QBrush brush;
-    if (window()->isActiveWindow()) {
-        brush = Qt::white;
-    } else {
-        brush = palette().window();
-    }
-    QPainter painter(this);
-    painter.fillRect(0, 0, width(), height(), brush);
-    painter.end();
-}
-
-void AboutView::screenChanged() {
-    logo->setPixmap(IconUtils::pixmap(":/images/app.png"));
 }
index 1f2a0fedbf35ab5998d78622ea7397880d976528..4d06db003b44b64241ea93758a815e26cd607cfd 100644 (file)
@@ -26,8 +26,6 @@ $END_LICENSE */
 #include "constants.h"
 #include "view.h"
 
-class ClickableLabel;
-
 class AboutView : public View {
     Q_OBJECT
 
@@ -39,14 +37,7 @@ public:
         return s;
     }
 
-protected:
-    void paintEvent(QPaintEvent *e);
-
-private slots:
-    void screenChanged();
-
 private:
-    ClickableLabel *logo;
     QPushButton *closeButton;
 };
 #endif
index 43b3b58adee5c22e4f35c2516fbd85c99fa825b9..295388cc9c75afed077a1e5463acad100582871a 100644 (file)
@@ -41,7 +41,7 @@ void AppsWidget::paintEvent(QPaintEvent *e) {
 
 AppWidget::AppWidget(const QString &name, const QString &code, QWidget *parent) : QWidget(parent), icon(0), name(name), downloadButton(0) {
     const QString unixName = code.left(code.lastIndexOf('.'));
-    const QString baseUrl = QLatin1String("http://") + Constants::ORG_DOMAIN;
+    const QString baseUrl = QLatin1String("https://") + Constants::ORG_DOMAIN;
     const QString filesUrl = baseUrl + QLatin1String("/files/");
     url = filesUrl + unixName + QLatin1String("/") + code;
     webPage = baseUrl + QLatin1String("/") +  unixName;
index 50f4e72d7c29326a115fbdcba5c67286e187129d..2c8e5225ee2cbc670cf08b62c97a3ede1132793c 100644 (file)
@@ -33,22 +33,25 @@ $END_LICENSE */
 
 #ifndef QT_NO_DEBUG_OUTPUT
 /// Gives human-readable event type information.
-QDebug operator<<(QDebug str, const QEvent * ev) {
+QDebug operator<<(QDebug str, const QEvent *ev) {
     static int eventEnumIndex = QEvent::staticMetaObject.indexOfEnumerator("Type");
     str << "QEvent";
     if (ev) {
         QString name = QEvent::staticMetaObject.enumerator(eventEnumIndex).valueToKey(ev->type());
-        if (!name.isEmpty()) str << name; else str << ev->type();
+        if (!name.isEmpty())
+            str << name;
+        else
+            str << ev->type();
     } else {
-        str << (void*)ev;
+        str << (void *)ev;
     }
     return str.maybeSpace();
 }
 #endif
 
-AutoComplete::AutoComplete(SearchWidget *buddy, QLineEdit *lineEdit):
-    QObject(lineEdit), buddy(buddy), lineEdit(lineEdit), enabled(true), suggester(0), itemHovering(false) {
-
+AutoComplete::AutoComplete(SearchWidget *buddy, QLineEdit *lineEdit)
+    : QObject(lineEdit), buddy(buddy), lineEdit(lineEdit), enabled(true), suggester(0),
+      itemHovering(false) {
     popup = new QListWidget();
     popup->setWindowFlags(Qt::Popup);
     popup->setFocusProxy(buddy->toWidget());
@@ -62,10 +65,10 @@ AutoComplete::AutoComplete(SearchWidget *buddy, QLineEdit *lineEdit):
     popup->setWindowOpacity(.9);
     popup->setProperty("suggest", true);
 
-    connect(popup, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(acceptSuggestion()));
-    connect(popup, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
-            SLOT(currentItemChanged(QListWidgetItem*)));
-    connect(popup, SIGNAL(itemEntered(QListWidgetItem*)), SLOT(itemEntered(QListWidgetItem *)));
+    connect(popup, SIGNAL(itemClicked(QListWidgetItem *)), SLOT(acceptSuggestion()));
+    connect(popup, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
+            SLOT(currentItemChanged(QListWidgetItem *)));
+    connect(popup, SIGNAL(itemEntered(QListWidgetItem *)), SLOT(itemEntered(QListWidgetItem *)));
 
     timer = new QTimer(this);
     timer->setSingleShot(true);
@@ -75,7 +78,6 @@ AutoComplete::AutoComplete(SearchWidget *buddy, QLineEdit *lineEdit):
 }
 
 bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) {
-
     if (obj != popup) {
         switch (ev->type()) {
         case QEvent::Move:
@@ -104,7 +106,7 @@ bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) {
 
     if (ev->type() == QEvent::KeyPress) {
         bool consumed = false;
-        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(ev);
+        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
         // qWarning() << keyEvent->text();
         switch (keyEvent->key()) {
         case Qt::Key_Enter:
@@ -165,10 +167,9 @@ void AutoComplete::showSuggestions(const QVector<Suggestion *> &suggestions) {
         QListWidgetItem *item = new QListWidgetItem(popup);
         Suggestion *s = suggestions[i];
         item->setText(s->value);
-        if (!s->type.isEmpty())
-            item->setIcon(QIcon(":/images/" + s->type + ".png"));
+        if (!s->type.isEmpty()) item->setIcon(QIcon(":/images/" + s->type + ".png"));
     }
-    popup->setCurrentItem(0);
+    popup->setCurrentItem(nullptr);
     int h = popup->frameWidth() * 2;
     for (int i = 0; i < suggestions.count(); ++i)
         h += popup->sizeHintForRow(i);
@@ -187,13 +188,14 @@ void AutoComplete::showSuggestions(const QVector<Suggestion *> &suggestions) {
 void AutoComplete::acceptSuggestion() {
     int index = popup->currentIndex().row();
     if (index >= 0 && index < suggestions.size()) {
-        Suggestionsuggestion = suggestions.at(index);
+        Suggestion *suggestion = suggestions.at(index);
         buddy->setText(suggestion->value);
         emit suggestionAccepted(suggestion);
         emit suggestionAccepted(suggestion->value);
         originalText.clear();
         hideSuggestions();
-    } else qWarning() << "No suggestion for index" << index;
+    } else
+        qWarning() << "No suggestion for index" << index;
 }
 
 void AutoComplete::preventSuggest() {
@@ -206,16 +208,17 @@ void AutoComplete::enableSuggest() {
     enabled = true;
 }
 
-void AutoComplete::setSuggester(Suggestersuggester) {
+void AutoComplete::setSuggester(Suggester *suggester) {
     if (this->suggester) this->suggester->disconnect();
     this->suggester = suggester;
-    connect(suggester, SIGNAL(ready(QVector<Suggestion*>)), SLOT(suggestionsReady(QVector<Suggestion*>)));
+    connect(suggester, SIGNAL(ready(QVector<Suggestion *>)),
+            SLOT(suggestionsReady(QVector<Suggestion *>)));
 }
 
 void AutoComplete::suggest() {
     if (!enabled) return;
 
-    popup->setCurrentItem(0);
+    popup->setCurrentItem(nullptr);
     popup->clearSelection();
 
     originalText = buddy->text();
index 664efc460072e2d8c6530ed0ee19b2f95f7c4d58..76a501275f4d919ff8af96c29f8c57638472b951 100644 (file)
@@ -19,30 +19,28 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "channelitemdelegate.h"
+#include "channelaggregator.h"
 #include "channelmodel.h"
-#include "ytchannel.h"
 #include "fontutils.h"
-#include "channelaggregator.h"
-#include "painterutils.h"
 #include "iconutils.h"
+#include "painterutils.h"
+#include "ytchannel.h"
 
 static const int ITEM_WIDTH = 150;
 static const int ITEM_HEIGHT = 150;
 static const int THUMB_WIDTH = 88;
 static const int THUMB_HEIGHT = 88;
 
-ChannelItemDelegate::ChannelItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {
+ChannelItemDelegate::ChannelItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
 
-}
-
-QSize ChannelItemDelegate::sizeHint(const QStyleOptionViewItem& /*option*/,
-                                     const QModelIndex& /*index*/ ) const {
+QSize ChannelItemDelegate::sizeHint(const QStyleOptionViewItem & /*option*/,
+                                    const QModelIndex & /*index*/) const {
     return QSize(ITEM_WIDTH, ITEM_HEIGHT);
 }
 
-void ChannelItemDelegate::paint( QPainter* painter,
-                                  const QStyleOptionViewItem& option,
-                                  const QModelIndex& index ) const {
+void ChannelItemDelegate::paint(QPainter *painter,
+                                const QStyleOptionViewItem &option,
+                                const QModelIndex &index) const {
     const int itemType = index.data(ChannelModel::ItemTypeRole).toInt();
     if (itemType == ChannelModel::ItemChannel)
         paintChannel(painter, option, index);
@@ -54,37 +52,37 @@ void ChannelItemDelegate::paint( QPainter* painter,
         QStyledItemDelegate::paint(painter, option, index);
 }
 
-void ChannelItemDelegate::paintAggregate(QPainterpainter,
-                                          const QStyleOptionViewItem& option,
-                                          const QModelIndex& index) const {
+void ChannelItemDelegate::paintAggregate(QPainter *painter,
+                                         const QStyleOptionViewItem &option,
+                                         const QModelIndex &index) const {
     Q_UNUSED(index);
     painter->save();
-
     painter->translate(option.rect.topLeft());
     const QRect line(0, 0, option.rect.width(), option.rect.height());
 
-    static const QPixmap thumbnail = IconUtils::pixmap(":/images/channels.png");
+    static QPixmap thumbnail = IconUtils::icon("channels").pixmap(88, 88);
+    connect(qApp, &QGuiApplication::paletteChanged, this,
+            [] { thumbnail = IconUtils::icon("channels").pixmap(88, 88); });
 
     QString name = tr("All Videos");
-
     drawItem(painter, line, thumbnail, name);
-
     painter->restore();
 }
 
-void ChannelItemDelegate::paintUnwatched(QPainterpainter,
-                                          const QStyleOptionViewItem& option,
-                                          const QModelIndex& index) const {
+void ChannelItemDelegate::paintUnwatched(QPainter *painter,
+                                         const QStyleOptionViewItem &option,
+                                         const QModelIndex &index) const {
     Q_UNUSED(index);
     painter->save();
 
     painter->translate(option.rect.topLeft());
     const QRect line(0, 0, option.rect.width(), option.rect.height());
 
-    static const QPixmap thumbnail = IconUtils::pixmap(":/images/unwatched.png");
+    static QPixmap thumbnail = IconUtils::icon("unwatched").pixmap(88, 88);
+    connect(qApp, &QGuiApplication::paletteChanged, this,
+            [] { thumbnail = IconUtils::icon("unwatched").pixmap(88, 88); });
 
     QString name = tr("Unwatched Videos");
-
     drawItem(painter, line, thumbnail, name);
 
     int notifyCount = ChannelAggregator::instance()->getUnwatchedCount();
@@ -94,9 +92,9 @@ void ChannelItemDelegate::paintUnwatched(QPainter* painter,
     painter->restore();
 }
 
-void ChannelItemDelegate::paintChannel(QPainterpainter,
-                                        const QStyleOptionViewItem& option,
-                                        const QModelIndex& index) const {
+void ChannelItemDelegate::paintChannel(QPainter *painter,
+                                       const QStyleOptionViewItem &option,
+                                       const QModelIndex &index) const {
     YTChannel *channel = index.data(ChannelModel::DataObjectRole).value<YTChannelPointer>().data();
     if (!channel) return;
 
@@ -120,16 +118,15 @@ void ChannelItemDelegate::paintChannel(QPainter* painter,
     drawItem(painter, line, thumbnail, name);
 
     int notifyCount = channel->getNotifyCount();
-    if (notifyCount > 0)
-        paintBadge(painter, line, QString::number(notifyCount));
+    if (notifyCount > 0) paintBadge(painter, line, QString::number(notifyCount));
 
     painter->restore();
 }
 
 void ChannelItemDelegate::drawItem(QPainter *painter,
-                                    const QRect &line,
-                                    const QPixmap &thumbnail,
-                                    const QString &name) const {
+                                   const QRect &line,
+                                   const QPixmap &thumbnail,
+                                   const QString &name) const {
     painter->drawPixmap((line.width() - THUMB_WIDTH) / 2, 8, thumbnail);
 
     QRect nameBox = line;
@@ -138,12 +135,10 @@ void ChannelItemDelegate::drawItem(QPainter *painter,
     bool tooBig = false;
 
     QRect textBox = painter->boundingRect(nameBox,
-                                          Qt::AlignTop | Qt::AlignHCenter | Qt::TextWordWrap,
-                                          name);
+                                          Qt::AlignTop | Qt::AlignHCenter | Qt::TextWordWrap, name);
     if (textBox.height() > nameBox.height() || textBox.width() > nameBox.width()) {
         painter->setFont(FontUtils::small());
-        textBox = painter->boundingRect(nameBox,
-                                        Qt::AlignTop | Qt::AlignHCenter | Qt::TextWordWrap,
+        textBox = painter->boundingRect(nameBox, Qt::AlignTop | Qt::AlignHCenter | Qt::TextWordWrap,
                                         name);
         if (textBox.height() > nameBox.height()) {
             painter->setClipRect(nameBox);
@@ -157,12 +152,12 @@ void ChannelItemDelegate::drawItem(QPainter *painter,
 }
 
 void ChannelItemDelegate::paintBadge(QPainter *painter,
-                                              const QRect &line,
-                                              const QString &text) const {
+                                     const QRect &line,
+                                     const QString &text) const {
     const int topLeft = (line.width() + THUMB_WIDTH) / 2;
     painter->save();
     painter->translate(topLeft, 0);
     painter->setClipping(false);
-    PainterUtils::paintBadge(painter, text, true);
+    PainterUtils::paintBadge(painter, text, true, QColor(230, 36, 41), true);
     painter->restore();
 }
index f791f71c7e0ffb217c65155615f6bd7dbf0441c3..7fa5fa9c5f1d48cbfa44c6275b4ec592b414d6eb 100644 (file)
@@ -22,7 +22,6 @@ $END_LICENSE */
 #include "painterutils.h"
 
 ChannelListView::ChannelListView() {
-
     setSelectionMode(QAbstractItemView::NoSelection);
 
     // layout
@@ -38,20 +37,10 @@ ChannelListView::ChannelListView() {
     setFrameShape(QFrame::NoFrame);
     setAttribute(Qt::WA_MacShowFocusRect, false);
 
-    QPalette p = palette();
-    /*
-    p.setColor(QPalette::Base, p.window().color());
-    p.setColor(QPalette::Text, p.windowText().color());
-    */
-    p.setColor(QPalette::Disabled, QPalette::Base, p.base().color());
-    p.setColor(QPalette::Disabled, QPalette::Text, p.text().color());
-    setPalette(p);
-
     verticalScrollBar()->setPageStep(3);
     verticalScrollBar()->setSingleStep(1);
 
     setMouseTracking(true);
-
 }
 
 void ChannelListView::mousePressEvent(QMouseEvent *event) {
@@ -64,8 +53,10 @@ void ChannelListView::mousePressEvent(QMouseEvent *event) {
 void ChannelListView::mouseMoveEvent(QMouseEvent *event) {
     QWidget::mouseMoveEvent(event);
     const QModelIndex index = indexAt(event->pos());
-    if (index.isValid()) setCursor(Qt::PointingHandCursor);
-    else unsetCursor();
+    if (index.isValid())
+        setCursor(Qt::PointingHandCursor);
+    else
+        unsetCursor();
 }
 
 void ChannelListView::paintEvent(QPaintEvent *event) {
index ebb4d8beb191e53b1261dc3bb77ff597a3d1d818..dcbc8bfe01e2cba48b89db87b9c419b58229559a 100644 (file)
@@ -24,7 +24,6 @@ $END_LICENSE */
 #include <QtWidgets>
 
 class ChannelListView : public QListView {
-
     Q_OBJECT
 
 public:
@@ -42,7 +41,6 @@ protected:
 
 private:
     QString errorMessage;
-
 };
 
 #endif // CHANNELLISTVIEW_H
index d6dcd46556a34cffa93d44c3eeb486e08aecb56d..eb6ccf902f876040ae6f8324e2bf57e2a1582814 100644 (file)
@@ -19,30 +19,27 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "channelview.h"
-#include "ytchannel.h"
-#include "ytsearch.h"
-#include "searchparams.h"
-#include "channelmodel.h"
+#include "aggregatevideosource.h"
+#include "channelaggregator.h"
 #include "channelitemdelegate.h"
+#include "channelmodel.h"
 #include "database.h"
-#include "channelaggregator.h"
-#include "aggregatevideosource.h"
-#include "mainwindow.h"
 #include "iconutils.h"
+#include "mainwindow.h"
+#include "searchparams.h"
+#include "ytchannel.h"
+#include "ytsearch.h"
 #ifdef APP_EXTRA
 #include "extra.h"
 #endif
 #include "channellistview.h"
 
 namespace {
-static const QString sortByKey = "subscriptionsSortBy";
-static const QString showUpdatedKey = "subscriptionsShowUpdated";
-}
-
-ChannelView::ChannelView(QWidget *parent) : View(parent),
-    showUpdated(false),
-    sortBy(SortByName) {
+const QString sortByKey = "subscriptionsSortBy";
+const QString showUpdatedKey = "subscriptionsShowUpdated";
+} // namespace
 
+ChannelView::ChannelView(QWidget *parent) : View(parent), showUpdated(false), sortBy(SortByName) {
     QBoxLayout *layout = new QVBoxLayout(this);
     layout->setMargin(0);
     layout->setSpacing(0);
@@ -53,7 +50,8 @@ ChannelView::ChannelView(QWidget *parent) : View(parent),
     channelsModel = new ChannelModel(this);
     listView->setModel(channelsModel);
 
-    connect(listView, SIGNAL(clicked(const QModelIndex &)), SLOT(itemActivated(const QModelIndex &)));
+    connect(listView, SIGNAL(clicked(const QModelIndex &)),
+            SLOT(itemActivated(const QModelIndex &)));
     connect(listView, SIGNAL(contextMenu(QPoint)), SLOT(showContextMenu(QPoint)));
     connect(listView, SIGNAL(viewportEntered()), channelsModel, SLOT(clearHover()));
 
@@ -61,8 +59,8 @@ ChannelView::ChannelView(QWidget *parent) : View(parent),
 
     setupActions();
 
-    connect(ChannelAggregator::instance(), SIGNAL(channelChanged(YTChannel*)),
-            channelsModel, SLOT(updateChannel(YTChannel*)));
+    connect(ChannelAggregator::instance(), SIGNAL(channelChanged(YTChannel *)), channelsModel,
+            SLOT(updateChannel(YTChannel *)));
     connect(ChannelAggregator::instance(), SIGNAL(unwatchedCountChanged(int)),
             SLOT(unwatchedCountChanged(int)));
 
@@ -114,7 +112,7 @@ void ChannelView::setupActions() {
 
     QToolButton *sortButton = new QToolButton(this);
     sortButton->setText(tr("Sort by"));
-    sortButton->setIcon(IconUtils::icon("sort"));
+    IconUtils::setIcon(sortButton, "sort");
     sortButton->setIconSize(QSize(16, 16));
     sortButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
     sortButton->setPopupMode(QToolButton::InstantPopup);
@@ -124,16 +122,16 @@ void ChannelView::setupActions() {
     widgetAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
     statusActions << widgetAction;
 
-    markAsWatchedAction = new QAction(
-                IconUtils::icon("mark-watched"), tr("Mark all as watched"), this);
+    markAsWatchedAction = new QAction(tr("Mark all as watched"), this);
+    IconUtils::setIcon(markAsWatchedAction, "mark-watched");
     markAsWatchedAction->setEnabled(false);
     markAsWatchedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W));
     connect(markAsWatchedAction, SIGNAL(triggered()), SLOT(markAllAsWatched()));
     statusActions << markAsWatchedAction;
 
     showUpdated = settings.value(showUpdatedKey, false).toBool();
-    QAction *showUpdatedAction = new QAction(
-                IconUtils::icon("show-updated"), tr("Show Updated"), this);
+    QAction *showUpdatedAction = new QAction(tr("Show Updated"), this);
+    IconUtils::setIcon(showUpdatedAction, "show-updated");
     showUpdatedAction->setCheckable(true);
     showUpdatedAction->setChecked(showUpdated);
     showUpdatedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U));
@@ -142,26 +140,24 @@ void ChannelView::setupActions() {
 
     for (QAction *action : statusActions) {
         window()->addAction(action);
-        IconUtils::setupAction(action);
+        MainWindow::instance()->setupAction(action);
     }
 }
 
 QString ChannelView::noSubscriptionsMessage() {
     return tr("You have no subscriptions. "
-                    "Use the star symbol to subscribe to channels.");
+              "Use the star symbol to subscribe to channels.");
 }
 
 void ChannelView::appear() {
     updateQuery();
-    for (QAction* action : statusActions)
-        MainWindow::instance()->showActionInStatusBar(action, true);
+    MainWindow::instance()->showActionsInStatusBar(statusActions, true);
     setFocus();
     ChannelAggregator::instance()->start();
 }
 
 void ChannelView::disappear() {
-    for (QAction* action : statusActions)
-        MainWindow::instance()->showActionInStatusBar(action, false);
+    MainWindow::instance()->showActionsInStatusBar(statusActions, false);
 }
 
 void ChannelView::itemActivated(const QModelIndex &index) {
@@ -200,22 +196,23 @@ void ChannelView::showContextMenu(const QPoint &point) {
     QMenu menu;
 
     if (channel->getNotifyCount() > 0) {
-        QAction *markAsWatchedAction = menu.addAction(tr("Mark as Watched"), channel, SLOT(updateWatched()));
-        connect(markAsWatchedAction, SIGNAL(triggered()),
-                ChannelAggregator::instance(), SLOT(updateUnwatchedCount()));
+        QAction *markAsWatchedAction =
+                menu.addAction(tr("Mark as Watched"), channel, SLOT(updateWatched()));
+        connect(markAsWatchedAction, SIGNAL(triggered()), ChannelAggregator::instance(),
+                SLOT(updateUnwatchedCount()));
         menu.addSeparator();
     }
 
     /*
     // TODO
-    QAction *notificationsAction = menu.addAction(tr("Receive Notifications"), user, SLOT(unsubscribe()));
-    notificationsAction->setCheckable(true);
+    QAction *notificationsAction = menu.addAction(tr("Receive Notifications"), user,
+    SLOT(unsubscribe())); notificationsAction->setCheckable(true);
     notificationsAction->setChecked(true);
     */
 
     QAction *unsubscribeAction = menu.addAction(tr("Unsubscribe"), channel, SLOT(unsubscribe()));
-    connect(unsubscribeAction, SIGNAL(triggered()),
-            ChannelAggregator::instance(), SLOT(updateUnwatchedCount()));
+    connect(unsubscribeAction, SIGNAL(triggered()), ChannelAggregator::instance(),
+            SLOT(updateUnwatchedCount()));
 
     menu.exec(mapToGlobal(point));
 }
@@ -237,8 +234,7 @@ void ChannelView::updateQuery(bool transition) {
     }
 
     QString sql = "select user_id from subscriptions";
-    if (showUpdated)
-        sql += " where notify_count>0";
+    if (showUpdated) sql += " where notify_count>0";
 
     switch (sortBy) {
     case SortByUpdated:
@@ -259,8 +255,7 @@ void ChannelView::updateQuery(bool transition) {
     }
 
 #ifdef APP_EXTRA
-    if (transition)
-        Extra::fadeInWidget(this, this);
+    if (transition) Extra::fadeInWidget(this, this);
 #endif
 
     channelsModel->setQuery(sql, Database::instance().getConnection());
index 35c709b69da2bb10e141b55849a4ff71d0212247..6285fc3274e1ead023d1bd0b3bfec251c8633b5f 100644 (file)
@@ -30,12 +30,11 @@ class ChannelModel;
 class ChannelListView;
 
 class ChannelView : public View {
-
     Q_OBJECT
 
 public:
-    ChannelView(QWidget *parent = 0);
-    
+    ChannelView(QWidget *parent = nullptr);
+
 signals:
     void activated(VideoSource *videoSource);
 
@@ -72,11 +71,10 @@ private:
 
     ChannelListView *listView;
     ChannelModel *channelsModel;
-    QVector<QAction*> statusActions;
+    QVector<QAction *> statusActions;
     bool showUpdated;
     SortBy sortBy;
     QAction *markAsWatchedAction;
-
 };
 
 #endif // CHANNELSVIEW_H
index d1f69147f340e447d37fa885afb9608006c5dac3..bc07301e3f0e29cb7024f481b864ca025bdaa695 100644 (file)
@@ -4,6 +4,20 @@ ClickableLabel::ClickableLabel(QWidget *parent) : QLabel(parent) {
     setCursor(Qt::PointingHandCursor);
 }
 
+ClickableLabel::ClickableLabel(const QString &text, QWidget *parent) : QLabel(text, parent) {
+    setCursor(Qt::PointingHandCursor);
+}
+
 void ClickableLabel::mouseReleaseEvent(QMouseEvent *e) {
-    if (rect().contains(e->pos())) emit clicked();
+    if (e->button() == Qt::LeftButton && rect().contains(e->pos())) emit clicked();
+}
+
+void ClickableLabel::leaveEvent(QEvent *e) {
+    emit hovered(false);
+    QLabel::leaveEvent(e);
+}
+
+void ClickableLabel::enterEvent(QEvent *e) {
+    emit hovered(true);
+    QLabel::enterEvent(e);
 }
index 747c861a558f016d63aadca6607876dbd4476430..9a24a5c69d193f5572edaf0ba83a2a88b8a292fe 100644 (file)
@@ -4,18 +4,20 @@
 #include <QtWidgets>
 
 class ClickableLabel : public QLabel {
-
     Q_OBJECT
 
 public:
-    explicit ClickableLabel(QWidget *parent = 0);
+    explicit ClickableLabel(QWidget *parent = nullptr);
+    explicit ClickableLabel(const QString &text, QWidget *parent = nullptr);
 
 signals:
     void clicked();
+    void hovered(bool value);
 
 protected:
     void mouseReleaseEvent(QMouseEvent *e);
-
+    void enterEvent(QEvent *e);
+    void leaveEvent(QEvent *e);
 };
 
 #endif // CLICKABLELABEL_H
index 0a542922c372f902f094b5713e62628b7028f537..a9bfb0b395ec6fbdfb5d345aa4b83987e2be138e 100644 (file)
@@ -28,5 +28,5 @@ const char *Constants::NAME = STRINGIFY(APP_NAME);
 const char *Constants::UNIX_NAME = STRINGIFY(APP_UNIX_NAME);
 const char *Constants::ORG_NAME = "Flavio Tordini";
 const char *Constants::ORG_DOMAIN = "flavio.tordini.org";
-const char *Constants::WEBSITE = "http://flavio.tordini.org/minitube";
+const char *Constants::WEBSITE = "https://flavio.tordini.org/minitube";
 const char *Constants::EMAIL = "flavio.tordini@gmail.com";
index d35fc132bd1b07720ac4d32b7e178337304869a6..310188024312db0055d1e6ade33ef1aee53254af 100644 (file)
@@ -72,7 +72,7 @@ QString DataUtils::formatDateTime(const QDateTime &dt) {
         s = QCoreApplication::translate("DataUtils", "%n day(s) ago", Q_NULLPTR, n);
     } else if (seconds < (f = 60 * 60 * 24 * 30)) {
         int n = seconds / (60 * 60 * 24 * 7);
-        s = QCoreApplication::translate("DataUtils", "%n weeks(s) ago", Q_NULLPTR, n);
+        s = QCoreApplication::translate("DataUtils", "%n week(s) ago", Q_NULLPTR, n);
     } else if (seconds < (f = 60 * 60 * 24 * 365)) {
         int n = seconds / (60 * 60 * 24 * 30);
         s = QCoreApplication::translate("DataUtils", "%n month(s) ago", Q_NULLPTR, n);
@@ -93,3 +93,27 @@ QString DataUtils::formatDuration(uint secs) {
     if (hours == 0) return res.sprintf("%d:%02d", minutes, seconds);
     return res.sprintf("%d:%02d:%02d", hours, minutes, seconds);
 }
+
+QString DataUtils::formatCount(int c) {
+    QString s;
+    int f = 1;
+    if (c < 1) {
+        return s;
+    } else if (c < (f *= 1000)) {
+        s = QString::number(c);
+    } else if (c < (f *= 1000)) {
+        int n = c / 1000;
+        s = QString::number(n) +
+            QCoreApplication::translate("DataUtils", "K", "K as in Kilo, i.e. thousands");
+    } else if (c < (f *= 1000)) {
+        int n = c / (1000 * 1000);
+        s = QString::number(n) +
+            QCoreApplication::translate("DataUtils", "M", "M stands for Millions");
+    } else {
+        int n = c / (1000 * 1000 * 1000);
+        s = QString::number(n) +
+            QCoreApplication::translate("DataUtils", "B", "B stands for Billions");
+    }
+
+    return QCoreApplication::translate("DataUtils", "%1 views").arg(s);
+}
index ad6d203461bce514b59feef57c0bc586c86399df..f41adf1c3c90f75777bbbf38adbbf28485c084c1 100644 (file)
@@ -4,7 +4,6 @@
 #include <QtCore>
 
 class DataUtils {
-
 public:
     static QString stringToFilename(const QString &s);
     static QString regioneCode(const QLocale &locale);
@@ -12,10 +11,10 @@ public:
     static uint parseIsoPeriod(const QString &isoPeriod);
     static QString formatDateTime(const QDateTime &dt);
     static QString formatDuration(uint secs);
+    static QString formatCount(int c);
 
 private:
-    DataUtils() { }
-
+    DataUtils() {}
 };
 
 #endif // DATAUTILS_H
diff --git a/src/exlineedit.cpp b/src/exlineedit.cpp
deleted file mode 100644 (file)
index 1984eb6..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-#include "exlineedit.h"
-#include "iconutils.h"
-
-ClearButton::ClearButton(QWidget *parent) : QAbstractButton(parent), hovered(false), mousePressed(false) {
-    setCursor(Qt::ArrowCursor);
-    setToolTip(tr("Clear"));
-    setVisible(false);
-    setFocusPolicy(Qt::NoFocus);
-}
-
-void ClearButton::paintEvent(QPaintEvent *e) {
-    Q_UNUSED(e);
-    QPainter painter(this);
-    const int h = height();
-    int iconSize = 16;
-    if (h > 30) iconSize = 22;
-    QIcon::Mode iconMode = QIcon::Normal;
-    if (mousePressed) iconMode = QIcon::Active;
-    QPixmap p = IconUtils::icon("edit-clear").pixmap(iconSize, iconSize, iconMode);
-    int x = (width() - p.width()) / 2;
-    int y = (h - p.height()) / 2;
-    painter.drawPixmap(x, y, p);
-}
-
-void ClearButton::textChanged(const QString &text) {
-    setVisible(!text.isEmpty());
-}
-
-void ClearButton::enterEvent(QEvent *e) {
-    hovered = true;
-    QAbstractButton::enterEvent(e);
-}
-
-void ClearButton::leaveEvent(QEvent *e) {
-    hovered = false;
-    QAbstractButton::leaveEvent(e);
-}
-
-void ClearButton::mousePressEvent(QMouseEvent *e) {
-    mousePressed = true;
-    QAbstractButton::mousePressEvent(e);
-}
-
-void ClearButton::mouseReleaseEvent(QMouseEvent *e) {
-    mousePressed = false;
-    QAbstractButton::mouseReleaseEvent(e);
-}
-
-ExLineEdit::ExLineEdit(QWidget *parent)
-    : QWidget(parent)
-    , m_leftWidget(0)
-    , m_lineEdit(new QLineEdit(this))
-    , m_clearButton(new ClearButton(this)) {
-    setFocusPolicy(m_lineEdit->focusPolicy());
-    setAttribute(Qt::WA_InputMethodEnabled);
-    setSizePolicy(m_lineEdit->sizePolicy());
-    setBackgroundRole(m_lineEdit->backgroundRole());
-    setMouseTracking(true);
-    setAcceptDrops(true);
-    setAttribute(Qt::WA_MacShowFocusRect, true);
-    QPalette p = m_lineEdit->palette();
-    setPalette(p);
-
-    // line edit
-    m_lineEdit->setFrame(false);
-    m_lineEdit->setFocusProxy(this);
-    m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
-    m_lineEdit->setStyleSheet("background:transparent");
-    QPalette clearPalette = m_lineEdit->palette();
-    clearPalette.setBrush(QPalette::Base, QBrush(Qt::transparent));
-    m_lineEdit->setPalette(clearPalette);
-
-    // clearButton
-    connect(m_clearButton, SIGNAL(clicked()), m_lineEdit, SLOT(clear()));
-    connect(m_lineEdit, SIGNAL(textChanged(const QString&)), m_clearButton, SLOT(textChanged(const QString&)));
-}
-
-void ExLineEdit::setFont(const QFont &font) {
-    m_lineEdit->setFont(font);
-    updateGeometries();
-}
-
-void ExLineEdit::setLeftWidget(QWidget *widget) {
-    m_leftWidget = widget;
-}
-
-QWidget *ExLineEdit::leftWidget() const {
-    return m_leftWidget;
-}
-
-void ExLineEdit::clear() {
-    m_lineEdit->clear();
-}
-
-QString ExLineEdit::text() {
-    return m_lineEdit->text();
-}
-
-void ExLineEdit::resizeEvent(QResizeEvent *e) {
-    Q_ASSERT(m_leftWidget);
-    updateGeometries();
-    QWidget::resizeEvent(e);
-}
-
-void ExLineEdit::updateGeometries() {
-    QStyleOptionFrame panel;
-    initStyleOption(&panel);
-    QRect rect = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
-
-    int padding = 3;
-    // int height = rect.height() + padding*2;
-    int width = rect.width();
-
-    // int m_leftWidgetHeight = m_leftWidget->height();
-    m_leftWidget->setGeometry(rect.x() + 2,          0,
-                              m_leftWidget->width(), m_leftWidget->height());
-
-    int clearButtonWidth = this->height();
-    m_lineEdit->setGeometry(m_leftWidget->x() + m_leftWidget->width(),        padding,
-                            width - clearButtonWidth - m_leftWidget->width(), this->height() - padding*2);
-
-    m_clearButton->setGeometry(this->width() - clearButtonWidth, 0,
-                               clearButtonWidth, this->height());
-}
-
-void ExLineEdit::initStyleOption(QStyleOptionFrame *option) const {
-    option->initFrom(this);
-    option->rect = contentsRect();
-    option->lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this);
-    option->midLineWidth = 0;
-    option->state |= QStyle::State_Sunken;
-    if (m_lineEdit->isReadOnly())
-        option->state |= QStyle::State_ReadOnly;
-#ifdef QT_KEYPAD_NAVIGATION
-    if (hasEditFocus())
-        option->state |= QStyle::State_HasEditFocus;
-#endif
-    option->features = QStyleOptionFrame::None;
-}
-
-QSize ExLineEdit::sizeHint() const {
-    m_lineEdit->setFrame(true);
-    QSize size = m_lineEdit->sizeHint();
-    m_lineEdit->setFrame(false);
-    size = size + QSize(3, 3);
-    return size;
-}
-
-void ExLineEdit::focusInEvent(QFocusEvent *e) {
-    m_lineEdit->event(e);
-    QWidget::focusInEvent(e);
-}
-
-void ExLineEdit::focusOutEvent(QFocusEvent *e) {
-    m_lineEdit->event(e);
-
-    if (m_lineEdit->completer()) {
-        connect(m_lineEdit->completer(), SIGNAL(activated(QString)),
-                         m_lineEdit, SLOT(setText(QString)));
-        connect(m_lineEdit->completer(), SIGNAL(highlighted(QString)),
-                         m_lineEdit, SLOT(_q_completionHighlighted(QString)));
-    }
-    QWidget::focusOutEvent(e);
-}
-
-void ExLineEdit::keyPressEvent(QKeyEvent *e) {
-    if (e->key() == Qt::Key_Escape && !m_lineEdit->text().isEmpty()) {
-        m_lineEdit->clear();
-    }
-    m_lineEdit->event(e);
-    QWidget::keyPressEvent(e);
-}
-
-bool ExLineEdit::event(QEvent *e) {
-    if (e->type() == QEvent::ShortcutOverride || e->type() == QEvent::InputMethod)
-        m_lineEdit->event(e);
-    return QWidget::event(e);
-}
-
-void ExLineEdit::paintEvent(QPaintEvent *e) {
-    Q_UNUSED(e);
-    QPainter p(this);
-    QStyleOptionFrame panel;
-    initStyleOption(&panel);
-    style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &p, this);
-}
diff --git a/src/exlineedit.h b/src/exlineedit.h
deleted file mode 100644 (file)
index 94b3f46..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef EXLINEEDIT_H
-#define EXLINEEDIT_H
-
-#include <QtWidgets>
-
-class ClearButton : public QAbstractButton {
-
-    Q_OBJECT
-
-public:
-    ClearButton(QWidget *parent = 0);
-
-public slots:
-    void textChanged(const QString &text);
-
-protected:
-    void paintEvent(QPaintEvent *e);
-    void enterEvent(QEvent *e);
-    void leaveEvent(QEvent *e);
-    void mousePressEvent(QMouseEvent *e);
-    void mouseReleaseEvent(QMouseEvent *e);
-
-private:
-    bool hovered;
-    bool mousePressed;
-};
-
-class ExLineEdit : public QWidget {
-
-    Q_OBJECT
-
-public:
-    ExLineEdit(QWidget *parent = 0);
-    QLineEdit *lineEdit() const { return m_lineEdit; }
-    void setLeftWidget(QWidget *widget);
-    QWidget *leftWidget() const;
-    void clear();
-    QString text();
-    QSize sizeHint() const;
-    void updateGeometries();
-    void setFont(const QFont &font);
-
-protected:
-    void focusInEvent(QFocusEvent *e);
-    void focusOutEvent(QFocusEvent *e);
-    void keyPressEvent(QKeyEvent *e);
-    void paintEvent(QPaintEvent *e);
-    void resizeEvent(QResizeEvent *e);
-    bool event(QEvent *e);
-    void initStyleOption(QStyleOptionFrame *option) const;
-
-    QWidget *m_leftWidget;
-    QLineEdit *m_lineEdit;
-    ClearButton *m_clearButton;
-};
-
-#endif // EXLINEEDIT_H
-
index c9bf5c28d4457c8b0aa08a261e8ba20e09ee5cb3..505e8d91536792728f213ca3e960b47473b8099f 100644 (file)
@@ -35,12 +35,11 @@ QFont createFont(bool isBold, double sizeScale) {
 QFont createFontWithMinSize(bool isBold, double sizeScale) {
     const int minPixels = 11;
     QFont font = createFont(isBold, sizeScale);
-    if (font.pixelSize() < minPixels)
-        font.setPixelSize(minPixels);
+    if (font.pixelSize() < minPixels) font.setPixelSize(minPixels);
     return font;
 }
 
-}
+} // namespace
 
 const QFont &FontUtils::small() {
     static const QFont font = createFontWithMinSize(false, .9);
@@ -53,12 +52,12 @@ const QFont &FontUtils::smallBold() {
 }
 
 const QFont &FontUtils::medium() {
-    static const QFont font = createFont(false, 1.1);
+    static const QFont font = createFont(false, 1.15);
     return font;
 }
 
 const QFont &FontUtils::mediumBold() {
-    static const QFont font = createFont(true, 1.1);
+    static const QFont font = createFont(true, 1.15);
     return font;
 }
 
index 3e445aae9dcfbb41fb0b4ff01532799a752f04c0..032fdbaf1e8fd13092aa58956ea45818ebe4a6ec 100644 (file)
@@ -20,10 +20,7 @@ $END_LICENSE */
 
 #include "gridwidget.h"
 
-GridWidget::GridWidget(QWidget *parent) :
-    QWidget(parent),
-    hovered(false),
-    pressed(false) {
+GridWidget::GridWidget(QWidget *parent) : QWidget(parent), hovered(false), pressed(false) {
     setCursor(Qt::PointingHandCursor);
     setFocusPolicy(Qt::StrongFocus);
 }
@@ -60,6 +57,5 @@ void GridWidget::enterEvent(QEvent *event) {
 }
 
 void GridWidget::keyReleaseEvent(QKeyEvent *event) {
-    if (event->key() == Qt::Key_Return)
-        emit activated();
+    if (event->key() == Qt::Key_Return) emit activated();
 }
index 28462fb911560f2229595b703c7c453c974d57f2..c23a78568d4c6a8926f729198e38a60ab78f6a6a 100644 (file)
@@ -24,15 +24,14 @@ $END_LICENSE */
 #include <QtWidgets>
 
 class GridWidget : public QWidget {
-
     Q_OBJECT
 
 public:
-    GridWidget(QWidget *parent = 0);
-    
+    GridWidget(QWidget *parent = nullptr);
+
 signals:
     void activated();
-    
+
 protected:
     void mouseMoveEvent(QMouseEvent *event);
     void mousePressEvent(QMouseEvent *event);
@@ -40,7 +39,7 @@ protected:
     void enterEvent(QEvent *event);
     void leaveEvent(QEvent *event);
     void keyReleaseEvent(QKeyEvent *event);
-    
+
     bool hovered;
     bool pressed;
 };
index a8f061b7f40e4dd575e500e5b5a9f167d8f688f8..8ee32410f5641ed7428acca3cfabe3a403d5f29c 100644 (file)
@@ -19,23 +19,21 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "homeview.h"
-#include "segmentedcontrol.h"
-#include "searchview.h"
-#include "standardfeedsview.h"
+#include "channelaggregator.h"
 #include "channelview.h"
+#include "iconutils.h"
 #include "mainwindow.h"
 #include "mediaview.h"
+#include "searchview.h"
+#include "segmentedcontrol.h"
+#include "standardfeedsview.h"
 #include "ytstandardfeed.h"
-#include "iconutils.h"
-#include "channelaggregator.h"
 #ifdef APP_MAC
 #include "macutils.h"
 #endif
 
-HomeView::HomeView(QWidget *parent) : View(parent),
-    standardFeedsView(0),
-    channelsView(0) {
-
+HomeView::HomeView(QWidget *parent)
+    : View(parent), searchView(nullptr), standardFeedsView(nullptr), channelsView(nullptr) {
     QBoxLayout *layout = new QVBoxLayout(this);
     layout->setMargin(0);
     layout->setSpacing(0);
@@ -45,11 +43,6 @@ HomeView::HomeView(QWidget *parent) : View(parent),
 
     stackedWidget = new QStackedWidget();
     layout->addWidget(stackedWidget);
-
-    searchView = new SearchView(this);
-    connect(searchView, SIGNAL(search(SearchParams*)),
-            MainWindow::instance(), SLOT(showMedia(SearchParams*)));
-    stackedWidget->addWidget(searchView);
 }
 
 void HomeView::setupBar() {
@@ -76,31 +69,29 @@ void HomeView::setupBar() {
     connect(ChannelAggregator::instance(), SIGNAL(unwatchedCountChanged(int)),
             SLOT(unwatchedCountChanged(int)));
 
-    const auto a = bar->actions();
-    for (QActionaction : a) {
+    const auto &a = bar->actions();
+    for (QAction *action : a) {
         addAction(action);
-        IconUtils::setupAction(action);
+        MainWindow::instance()->setupAction(action);
     }
 }
 
 void HomeView::showWidget(QWidget *widget) {
-    QWidget* currentWidget = stackedWidget->currentWidget();
-    if (currentWidget == widget) return;
-    QMetaObject::invokeMethod(currentWidget, "disappear");
-    currentWidget->setEnabled(false);
+    QWidget *currentWidget = stackedWidget->currentWidget();
+    if (currentWidget && currentWidget != widget) {
+        QMetaObject::invokeMethod(currentWidget, "disappear");
+        currentWidget->setEnabled(false);
+    }
     stackedWidget->setCurrentWidget(widget);
     widget->setEnabled(true);
-    QMetaObject::invokeMethod(widget, "appear");
-    QTimer::singleShot(0, widget, SLOT(setFocus()));
-
-#ifdef APP_MAC
-    // Workaround cursor bug on macOS
-    window()->unsetCursor();
-#endif
+    QMetaObject::invokeMethod(widget, "appear", Qt::QueuedConnection);
 }
 
 void HomeView::appear() {
-    QMetaObject::invokeMethod(stackedWidget->currentWidget(), "appear", Qt::QueuedConnection);
+    if (stackedWidget->count() == 0)
+        showSearch();
+    else
+        QMetaObject::invokeMethod(stackedWidget->currentWidget(), "appear", Qt::QueuedConnection);
 }
 
 void HomeView::disappear() {
@@ -108,6 +99,12 @@ void HomeView::disappear() {
 }
 
 void HomeView::showSearch() {
+    if (!searchView) {
+        searchView = new SearchView(this);
+        connect(searchView, SIGNAL(search(SearchParams *)), MainWindow::instance(),
+                SLOT(showMedia(SearchParams *)));
+        stackedWidget->addWidget(searchView);
+    }
     showWidget(searchView);
     bar->setCheckedAction(0);
 }
@@ -115,9 +112,8 @@ void HomeView::showSearch() {
 void HomeView::showStandardFeeds() {
     if (!standardFeedsView) {
         standardFeedsView = new StandardFeedsView();
-        connect(standardFeedsView, SIGNAL(activated(VideoSource*)),
-                MainWindow::instance(),
-                SLOT(showMedia(VideoSource*)));
+        connect(standardFeedsView, SIGNAL(activated(VideoSource *)), MainWindow::instance(),
+                SLOT(showMedia(VideoSource *)));
         stackedWidget->addWidget(standardFeedsView);
     }
     showWidget(standardFeedsView);
@@ -127,9 +123,8 @@ void HomeView::showStandardFeeds() {
 void HomeView::showChannels() {
     if (!channelsView) {
         channelsView = new ChannelView();
-        connect(channelsView, SIGNAL(activated(VideoSource*)),
-                MainWindow::instance(),
-                SLOT(showMedia(VideoSource*)));
+        connect(channelsView, SIGNAL(activated(VideoSource *)), MainWindow::instance(),
+                SLOT(showMedia(VideoSource *)));
         stackedWidget->addWidget(channelsView);
     }
     showWidget(channelsView);
diff --git a/src/http/README.md b/src/http/README.md
deleted file mode 100644 (file)
index a1370ff..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-# A wrapper for the Qt Network Access API
-
-This is just a wrapper around Qt's QNetworkAccessManager and friends. I use it in my Qt apps at http://flavio.tordini.org . It allows me to add missing functionality as needed, e.g.:
-
-- Throttling (as required by many web APIs nowadays)
-- Read timeouts (don't let your requests get stuck forever)
-- Automatic retries
-- User agent and request header defaults
-- Partial requests
-- Redirection support (now supported by Qt >= 5.6)
-
-It has a simpler, higher-level API that I find easier to work with. The design emerged naturally in years of practical use.
-
-A basic example:
-
-```
-QObject *reply = Http::instance().get("https://google.com/");
-connect(reply, SIGNAL(data(QByteArray)), SLOT(onSuccess(QByteArray)));
-connect(reply, SIGNAL(error(QString)), SLOT(onError(QString)));
-
-void MyClass::onSuccess(const QByteArray &bytes) {
-       qDebug() << "Feel the bytes!" << bytes;
-}
-
-void MyClass::onError(const QString &message) {
-       qDebug() << "Something's wrong here" << message;
-}
-```
-
-This is a real-world example of building a Http object suitable to a web service. It throttles requests, uses a custom user agent and caches results:
-
-```
-Http &myHttp() {
-    static Http *http = [] {
-        Http *http = new Http;
-        http->addRequestHeader("User-Agent", userAgent());
-
-        ThrottledHttp *throttledHttp = new ThrottledHttp(*http);
-        throttledHttp->setMilliseconds(1000);
-
-        CachedHttp *cachedHttp = new CachedHttp(*throttledHttp, "mycache");
-        cachedHttp->setMaxSeconds(86400 * 30);
-
-        return cachedHttp;
-    }();
-    return *http;
-}
-```
-
-If the full power (and complexity) of QNetworkReply is needed you can always fallback to it:
-
-```
-HttpRequest req;
-req.url = "https://flavio.tordini.org/";
-QNetworkReply *reply = Http::instance().networkReply(req);
-// Use QNetworkReply as needed...
-```
-
-You can use this library under the MIT license and at your own risk. If you do, you're welcome contributing your changes and fixes.
-
-Cheers,
-
-Flavio
diff --git a/src/http/http.pri b/src/http/http.pri
deleted file mode 100644 (file)
index 6a210c7..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-QT *= network
-
-INCLUDEPATH += $$PWD/src
-DEPENDPATH += $$PWD/src
-
-HEADERS += \
-    $$PWD/src/cachedhttp.h \
-    $$PWD/src/http.h \
-    $$PWD/src/localcache.h \
-    $$PWD/src/throttledhttp.h
-
-SOURCES += \
-    $$PWD/src/cachedhttp.cpp \
-    $$PWD/src/http.cpp \
-    $$PWD/src/localcache.cpp \
-    $$PWD/src/throttledhttp.cpp
diff --git a/src/http/src/cachedhttp.cpp b/src/http/src/cachedhttp.cpp
deleted file mode 100644 (file)
index b49f5b8..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "cachedhttp.h"
-#include "localcache.h"
-
-namespace {
-
-QByteArray requestHash(const HttpRequest &req) {
-    const char sep = '|';
-    QByteArray s = req.url.toEncoded() + sep + req.body + sep + QByteArray::number(req.offset);
-    if (req.operation == QNetworkAccessManager::PostOperation) {
-        s.append(sep);
-        s.append("POST");
-    }
-    return LocalCache::hash(s);
-}
-}
-
-CachedHttpReply::CachedHttpReply(const QByteArray &body, const HttpRequest &req)
-    : bytes(body), req(req) {
-    QTimer::singleShot(0, this, SLOT(emitSignals()));
-}
-
-QByteArray CachedHttpReply::body() const {
-    return bytes;
-}
-
-void CachedHttpReply::emitSignals() {
-    emit data(body());
-    emit finished(*this);
-    deleteLater();
-}
-
-WrappedHttpReply::WrappedHttpReply(LocalCache *cache, const QByteArray &key, QObject *httpReply)
-    : QObject(httpReply), cache(cache), key(key), httpReply(httpReply) {
-    connect(httpReply, SIGNAL(data(QByteArray)), SIGNAL(data(QByteArray)));
-    connect(httpReply, SIGNAL(error(QString)), SIGNAL(error(QString)));
-    connect(httpReply, SIGNAL(finished(HttpReply)), SLOT(originFinished(HttpReply)));
-}
-
-void WrappedHttpReply::originFinished(const HttpReply &reply) {
-    if (reply.isSuccessful()) cache->insert(key, reply.body());
-    emit finished(reply);
-}
-
-CachedHttp::CachedHttp(Http &http, const char *name)
-    : http(http), cache(LocalCache::instance(name)), cachePostRequests(false) {}
-
-void CachedHttp::setMaxSeconds(uint seconds) {
-    cache->setMaxSeconds(seconds);
-}
-
-void CachedHttp::setMaxSize(uint maxSize) {
-    cache->setMaxSize(maxSize);
-}
-
-QObject *CachedHttp::request(const HttpRequest &req) {
-    bool cacheable = req.operation == QNetworkAccessManager::GetOperation ||
-                     (cachePostRequests && req.operation == QNetworkAccessManager::PostOperation);
-    if (!cacheable) {
-        qDebug() << "Not cacheable" << req.url;
-        return http.request(req);
-    }
-    const QByteArray key = requestHash(req);
-    const QByteArray value = cache->value(key);
-    if (!value.isNull()) {
-        qDebug() << "CachedHttp HIT" << req.url;
-        return new CachedHttpReply(value, req);
-    }
-    qDebug() << "CachedHttp MISS" << req.url.toString();
-    return new WrappedHttpReply(cache, key, http.request(req));
-}
diff --git a/src/http/src/cachedhttp.h b/src/http/src/cachedhttp.h
deleted file mode 100644 (file)
index 8758f24..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef CACHEDHTTP_H
-#define CACHEDHTTP_H
-
-#include "http.h"
-
-class LocalCache;
-
-class CachedHttp : public Http {
-public:
-    CachedHttp(Http &http = Http::instance(), const char *name = "http");
-    void setMaxSeconds(uint seconds);
-    void setMaxSize(uint maxSize);
-    void setCachePostRequests(bool value) { cachePostRequests = value; }
-    QObject *request(const HttpRequest &req);
-
-private:
-    Http &http;
-    LocalCache *cache;
-    bool cachePostRequests;
-};
-
-class CachedHttpReply : public HttpReply {
-    Q_OBJECT
-
-public:
-    CachedHttpReply(const QByteArray &body, const HttpRequest &req);
-    QUrl url() const { return req.url; }
-    int statusCode() const { return 200; }
-    QByteArray body() const;
-
-private slots:
-    void emitSignals();
-
-private:
-    const QByteArray bytes;
-    const HttpRequest &req;
-};
-
-class WrappedHttpReply : public QObject {
-    Q_OBJECT
-
-public:
-    WrappedHttpReply(LocalCache *cache, const QByteArray &key, QObject *httpReply);
-
-signals:
-    void data(const QByteArray &bytes);
-    void error(const QString &message);
-    void finished(const HttpReply &reply);
-
-private slots:
-    void originFinished(const HttpReply &reply);
-
-private:
-    LocalCache *cache;
-    QByteArray key;
-    QObject *httpReply;
-};
-
-#endif // CACHEDHTTP_H
diff --git a/src/http/src/http.cpp b/src/http/src/http.cpp
deleted file mode 100644 (file)
index 2326e66..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-#include "http.h"
-
-namespace {
-
-QNetworkAccessManager *createNetworkAccessManager() {
-    QNetworkAccessManager *nam = new QNetworkAccessManager();
-    return nam;
-}
-
-QNetworkAccessManager *networkAccessManager() {
-    static QMap<QThread *, QNetworkAccessManager *> nams;
-    QThread *t = QThread::currentThread();
-    QMap<QThread *, QNetworkAccessManager *>::const_iterator i = nams.constFind(t);
-    if (i != nams.constEnd()) return i.value();
-    QNetworkAccessManager *nam = createNetworkAccessManager();
-    nams.insert(t, nam);
-    return nam;
-}
-
-static int defaultReadTimeout = 10000;
-}
-
-Http::Http() : requestHeaders(getDefaultRequestHeaders()), readTimeout(defaultReadTimeout) {}
-
-void Http::setRequestHeaders(const QMap<QByteArray, QByteArray> &headers) {
-    requestHeaders = headers;
-}
-
-QMap<QByteArray, QByteArray> &Http::getRequestHeaders() {
-    return requestHeaders;
-}
-
-void Http::addRequestHeader(const QByteArray &name, const QByteArray &value) {
-    requestHeaders.insert(name, value);
-}
-
-void Http::setReadTimeout(int timeout) {
-    readTimeout = timeout;
-}
-
-Http &Http::instance() {
-    static Http *i = new Http();
-    return *i;
-}
-
-const QMap<QByteArray, QByteArray> &Http::getDefaultRequestHeaders() {
-    static const QMap<QByteArray, QByteArray> defaultRequestHeaders = [] {
-        QMap<QByteArray, QByteArray> h;
-        h.insert("Accept-Charset", "utf-8");
-        h.insert("Connection", "Keep-Alive");
-        return h;
-    }();
-    return defaultRequestHeaders;
-}
-
-void Http::setDefaultReadTimeout(int timeout) {
-    defaultReadTimeout = timeout;
-}
-
-QNetworkReply *Http::networkReply(const HttpRequest &req) {
-    QNetworkRequest request(req.url);
-
-    QMap<QByteArray, QByteArray> &headers = requestHeaders;
-    if (!req.headers.isEmpty()) headers = req.headers;
-
-    QMap<QByteArray, QByteArray>::const_iterator it;
-    for (it = headers.constBegin(); it != headers.constEnd(); ++it)
-        request.setRawHeader(it.key(), it.value());
-
-    if (req.offset > 0)
-        request.setRawHeader("Range", QString("bytes=%1-").arg(req.offset).toUtf8());
-
-    QNetworkAccessManager *manager = networkAccessManager();
-
-    QNetworkReply *networkReply = 0;
-    switch (req.operation) {
-    case QNetworkAccessManager::GetOperation:
-        networkReply = manager->get(request);
-        break;
-
-    case QNetworkAccessManager::HeadOperation:
-        networkReply = manager->head(request);
-        break;
-
-    case QNetworkAccessManager::PostOperation:
-        networkReply = manager->post(request, req.body);
-        break;
-
-    default:
-        qWarning() << "Unknown operation:" << req.operation;
-    }
-
-    return networkReply;
-}
-
-QObject *Http::request(const HttpRequest &req) {
-    return new NetworkHttpReply(req, *this);
-}
-
-QObject *Http::request(const QUrl &url,
-                       QNetworkAccessManager::Operation operation,
-                       const QByteArray &body,
-                       uint offset) {
-    HttpRequest req;
-    req.url = url;
-    req.operation = operation;
-    req.body = body;
-    req.offset = offset;
-    return request(req);
-}
-
-QObject *Http::get(const QUrl &url) {
-    return request(url, QNetworkAccessManager::GetOperation);
-}
-
-QObject *Http::head(const QUrl &url) {
-    return request(url, QNetworkAccessManager::HeadOperation);
-}
-
-QObject *Http::post(const QUrl &url, const QMap<QString, QString> &params) {
-    QByteArray body;
-    QMapIterator<QString, QString> i(params);
-    while (i.hasNext()) {
-        i.next();
-        body += QUrl::toPercentEncoding(i.key()) + '=' + QUrl::toPercentEncoding(i.value()) + '&';
-    }
-    HttpRequest req;
-    req.url = url;
-    req.operation = QNetworkAccessManager::PostOperation;
-    req.body = body;
-    req.headers = requestHeaders;
-    req.headers.insert("Content-Type", "application/x-www-form-urlencoded");
-    return request(req);
-}
-
-QObject *Http::post(const QUrl &url, const QByteArray &body, const QByteArray &contentType) {
-    HttpRequest req;
-    req.url = url;
-    req.operation = QNetworkAccessManager::PostOperation;
-    req.body = body;
-    req.headers = requestHeaders;
-    QByteArray cType = contentType;
-    if (cType.isEmpty()) cType = "application/x-www-form-urlencoded";
-    req.headers.insert("Content-Type", cType);
-    return request(req);
-}
-
-NetworkHttpReply::NetworkHttpReply(const HttpRequest &req, Http &http)
-    : http(http), req(req), retryCount(0) {
-    if (req.url.isEmpty()) {
-        qWarning() << "Empty URL";
-    }
-
-    networkReply = http.networkReply(req);
-    setParent(networkReply);
-    setupReply();
-
-    readTimeoutTimer = new QTimer(this);
-    readTimeoutTimer->setInterval(http.getReadTimeout());
-    readTimeoutTimer->setSingleShot(true);
-    connect(readTimeoutTimer, SIGNAL(timeout()), SLOT(readTimeout()), Qt::UniqueConnection);
-    readTimeoutTimer->start();
-}
-
-void NetworkHttpReply::setupReply() {
-    connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
-            SLOT(replyError(QNetworkReply::NetworkError)), Qt::UniqueConnection);
-    connect(networkReply, SIGNAL(finished()), SLOT(replyFinished()), Qt::UniqueConnection);
-    connect(networkReply, SIGNAL(downloadProgress(qint64, qint64)),
-            SLOT(downloadProgress(qint64, qint64)), Qt::UniqueConnection);
-}
-
-QString NetworkHttpReply::errorMessage() {
-    return url().toString() + QLatin1Char(' ') + QString::number(statusCode()) + QLatin1Char(' ') +
-           reasonPhrase();
-}
-
-void NetworkHttpReply::emitError() {
-    const QString msg = errorMessage();
-#ifndef QT_NO_DEBUG_OUTPUT
-    qDebug() << "Http:" << msg;
-    if (!req.body.isEmpty()) qDebug() << "Http:" << req.body;
-#endif
-    emit error(msg);
-    emitFinished();
-}
-
-void NetworkHttpReply::emitFinished() {
-    readTimeoutTimer->stop();
-
-    // disconnect to avoid replyFinished() from being called
-    networkReply->disconnect();
-
-    emit finished(*this);
-
-    // bye bye my reply
-    // this will also delete this object and HttpReply as the QNetworkReply is their parent
-    networkReply->deleteLater();
-}
-
-void NetworkHttpReply::replyFinished() {
-    QUrl redirection = networkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
-    if (redirection.isValid()) {
-        HttpRequest redirectReq;
-        redirectReq.url = redirection;
-        redirectReq.operation = req.operation;
-        redirectReq.body = req.body;
-        redirectReq.offset = req.offset;
-        QNetworkReply *redirectReply = http.networkReply(redirectReq);
-        setParent(redirectReply);
-        networkReply->deleteLater();
-        networkReply = redirectReply;
-        setupReply();
-        readTimeoutTimer->start();
-        return;
-    }
-
-    if (isSuccessful()) {
-        bytes = networkReply->readAll();
-        emit data(bytes);
-
-#ifndef QT_NO_DEBUG_OUTPUT
-        if (!networkReply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool())
-            qDebug() << networkReply->url().toString() << statusCode();
-        else
-            qDebug() << "CACHE" << networkReply->url().toString();
-#endif
-    }
-
-    emitFinished();
-}
-
-void NetworkHttpReply::replyError(QNetworkReply::NetworkError code) {
-    Q_UNUSED(code);
-    const int status = statusCode();
-    if (retryCount <= 3 && status >= 500 && status < 600) {
-        qDebug() << "Retrying" << req.url;
-        networkReply->disconnect();
-        networkReply->deleteLater();
-        QNetworkReply *retryReply = http.networkReply(req);
-        setParent(retryReply);
-        networkReply = retryReply;
-        setupReply();
-        retryCount++;
-        readTimeoutTimer->start();
-    } else {
-        emitError();
-        return;
-    }
-}
-
-void NetworkHttpReply::downloadProgress(qint64 bytesReceived, qint64 /* bytesTotal */) {
-    // qDebug() << "Downloading" << bytesReceived << bytesTotal << networkReply->url();
-    if (bytesReceived > 0 && readTimeoutTimer->isActive()) {
-        readTimeoutTimer->stop();
-        disconnect(networkReply, SIGNAL(downloadProgress(qint64, qint64)), this,
-                   SLOT(downloadProgress(qint64, qint64)));
-    }
-}
-
-void NetworkHttpReply::readTimeout() {
-    if (!networkReply) return;
-    networkReply->disconnect();
-    networkReply->abort();
-    networkReply->deleteLater();
-
-    if (retryCount > 3 && (networkReply->operation() != QNetworkAccessManager::GetOperation &&
-                           networkReply->operation() != QNetworkAccessManager::HeadOperation)) {
-        emitError();
-        emit finished(*this);
-        return;
-    }
-
-    qDebug() << "Timeout" << req.url;
-    QNetworkReply *retryReply = http.networkReply(req);
-    setParent(retryReply);
-    networkReply = retryReply;
-    setupReply();
-    retryCount++;
-    readTimeoutTimer->start();
-}
-
-QUrl NetworkHttpReply::url() const {
-    return networkReply->url();
-}
-
-int NetworkHttpReply::statusCode() const {
-    return networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-}
-
-QString NetworkHttpReply::reasonPhrase() const {
-    return networkReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
-}
-
-const QList<QNetworkReply::RawHeaderPair> NetworkHttpReply::headers() const {
-    return networkReply->rawHeaderPairs();
-}
-
-QByteArray NetworkHttpReply::header(const QByteArray &headerName) const {
-    return networkReply->rawHeader(headerName);
-}
-
-QByteArray NetworkHttpReply::body() const {
-    return bytes;
-}
diff --git a/src/http/src/http.h b/src/http/src/http.h
deleted file mode 100644 (file)
index 02c9695..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#ifndef HTTP_H
-#define HTTP_H
-
-#include <QtNetwork>
-
-class HttpRequest {
-public:
-    HttpRequest() : operation(QNetworkAccessManager::GetOperation), offset(0) {}
-    QUrl url;
-    QNetworkAccessManager::Operation operation;
-    QByteArray body;
-    uint offset;
-    QMap<QByteArray, QByteArray> headers;
-};
-
-class Http {
-public:
-    static Http &instance();
-    static const QMap<QByteArray, QByteArray> &getDefaultRequestHeaders();
-    static void setDefaultReadTimeout(int timeout);
-
-    Http();
-
-    void setRequestHeaders(const QMap<QByteArray, QByteArray> &headers);
-    QMap<QByteArray, QByteArray> &getRequestHeaders();
-    void addRequestHeader(const QByteArray &name, const QByteArray &value);
-
-    void setReadTimeout(int timeout);
-    int getReadTimeout() { return readTimeout; }
-
-    QNetworkReply *networkReply(const HttpRequest &req);
-    virtual QObject *request(const HttpRequest &req);
-    QObject *request(const QUrl &url,
-            QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
-            const QByteArray &body = QByteArray(),
-            uint offset = 0);
-    QObject *get(const QUrl &url);
-    QObject *head(const QUrl &url);
-    QObject *post(const QUrl &url, const QMap<QString, QString> &params);
-    QObject *post(const QUrl &url, const QByteArray &body, const QByteArray &contentType);
-
-private:
-    QMap<QByteArray, QByteArray> requestHeaders;
-    int readTimeout;
-};
-
-class HttpReply : public QObject {
-    Q_OBJECT
-
-public:
-    HttpReply(QObject *parent = 0) : QObject(parent) {}
-    virtual QUrl url() const = 0;
-    virtual int statusCode() const = 0;
-    int isSuccessful() const { return statusCode() >= 200 && statusCode() < 300; }
-    virtual QString reasonPhrase() const { return QString(); }
-    virtual const QList<QNetworkReply::RawHeaderPair> headers() const {
-        return QList<QNetworkReply::RawHeaderPair>();
-    }
-    virtual QByteArray header(const QByteArray &headerName) const {
-        Q_UNUSED(headerName);
-        return QByteArray();
-    }
-
-    virtual QByteArray body() const = 0;
-
-signals:
-    void data(const QByteArray &bytes);
-    void error(const QString &message);
-    void finished(const HttpReply &reply);
-};
-
-class NetworkHttpReply : public HttpReply {
-    Q_OBJECT
-
-public:
-    NetworkHttpReply(const HttpRequest &req, Http &http);
-    QUrl url() const;
-    int statusCode() const;
-    QString reasonPhrase() const;
-    const QList<QNetworkReply::RawHeaderPair> headers() const;
-    QByteArray header(const QByteArray &headerName) const;
-    QByteArray body() const;
-
-private slots:
-    void replyFinished();
-    void replyError(QNetworkReply::NetworkError);
-    void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
-    void readTimeout();
-
-private:
-    void setupReply();
-    QString errorMessage();
-    void emitError();
-    void emitFinished();
-
-    Http &http;
-    HttpRequest req;
-    QNetworkReply *networkReply;
-    QTimer *readTimeoutTimer;
-    int retryCount;
-    QByteArray bytes;
-};
-
-#endif // HTTP_H
diff --git a/src/http/src/localcache.cpp b/src/http/src/localcache.cpp
deleted file mode 100644 (file)
index 1d38262..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-#include "localcache.h"
-
-LocalCache *LocalCache::instance(const char *name) {
-    static QMap<QByteArray, LocalCache *> instances;
-    auto i = instances.constFind(QByteArray::fromRawData(name, strlen(name)));
-    if (i != instances.constEnd()) return i.value();
-    LocalCache *instance = new LocalCache(name);
-    instances.insert(instance->getName(), instance);
-    return instance;
-}
-
-LocalCache::LocalCache(const QByteArray &name)
-    : name(name), maxSeconds(86400 * 30), maxSize(1024 * 1024 * 100), size(0), expiring(false),
-      insertCount(0) {
-    directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') +
-                QLatin1String(name) + QLatin1Char('/');
-#ifndef QT_NO_DEBUG_OUTPUT
-    hits = 0;
-    misses = 0;
-#endif
-}
-
-LocalCache::~LocalCache() {
-#ifndef QT_NO_DEBUG_OUTPUT
-    debugStats();
-#endif
-}
-
-QByteArray LocalCache::hash(const QByteArray &s) {
-    QCryptographicHash hash(QCryptographicHash::Sha1);
-    hash.addData(s);
-    const QByteArray h = QByteArray::number(*(qlonglong *)hash.result().constData(), 36);
-    static const char sep('/');
-    QByteArray p;
-    p.reserve(h.length() + 2);
-    p.append(h.at(0));
-    p.append(sep);
-    p.append(h.at(1));
-    p.append(sep);
-    p.append(h.constData() + 2, strlen(h.constData()) - 2); // p.append(h.mid(2));
-    return p;
-}
-
-bool LocalCache::isCached(const QString &path) {
-    bool cached = (QFile::exists(path) &&
-                   (maxSeconds == 0 ||
-                    QDateTime::currentDateTime().toTime_t() - QFileInfo(path).created().toTime_t() <
-                            maxSeconds));
-#ifndef QT_NO_DEBUG_OUTPUT
-    if (!cached) misses++;
-#endif
-    return cached;
-}
-
-QByteArray LocalCache::value(const QByteArray &key) {
-    const QString path = cachePath(key);
-    if (!isCached(path)) return QByteArray();
-
-    QFile file(path);
-    if (!file.open(QIODevice::ReadOnly)) {
-        qWarning() << __PRETTY_FUNCTION__ << file.fileName() << file.errorString();
-#ifndef QT_NO_DEBUG_OUTPUT
-        misses++;
-#endif
-        return QByteArray();
-    }
-#ifndef QT_NO_DEBUG_OUTPUT
-    hits++;
-#endif
-    return file.readAll();
-}
-
-void LocalCache::insert(const QByteArray &key, const QByteArray &value) {
-    const QueueItem item = {key, value};
-    insertQueue.append(item);
-    QTimer::singleShot(0, [this]() {
-        if (insertQueue.isEmpty()) return;
-        for (const auto &item : insertQueue) {
-            const QString path = cachePath(item.key);
-            const QString parentDir = path.left(path.lastIndexOf('/'));
-            if (!QFile::exists(parentDir)) {
-                QDir().mkpath(parentDir);
-            }
-            QFile file(path);
-            if (!file.open(QIODevice::WriteOnly)) {
-                qWarning() << "Cannot create" << path;
-                continue;
-            }
-            file.write(item.value);
-            file.close();
-            if (size > 0) size += item.value.size();
-        }
-        insertQueue.clear();
-
-        // expire cache every n inserts
-        if (maxSize > 0 && ++insertCount % 100 == 0) {
-            if (size == 0 || size > maxSize) size = expire();
-        }
-    });
-}
-
-bool LocalCache::clear() {
-#ifndef QT_NO_DEBUG_OUTPUT
-    hits = 0;
-    misses = 0;
-#endif
-    size = 0;
-    insertCount = 0;
-    return QDir(directory).removeRecursively();
-}
-
-QString LocalCache::cachePath(const QByteArray &key) const {
-    return directory + QLatin1String(key.constData());
-}
-
-qint64 LocalCache::expire() {
-    if (expiring) return size;
-    expiring = true;
-
-    QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot;
-    QDirIterator it(directory, filters, QDirIterator::Subdirectories);
-
-    QMultiMap<QDateTime, QString> cacheItems;
-    qint64 totalSize = 0;
-    while (it.hasNext()) {
-        QString path = it.next();
-        QFileInfo info = it.fileInfo();
-        cacheItems.insert(info.created(), path);
-        totalSize += info.size();
-        qApp->processEvents();
-    }
-
-    int removedFiles = 0;
-    qint64 goal = (maxSize * 9) / 10;
-    auto i = cacheItems.constBegin();
-    while (i != cacheItems.constEnd()) {
-        if (totalSize < goal) break;
-        QString name = i.value();
-        QFile file(name);
-        qint64 size = file.size();
-        file.remove();
-        totalSize -= size;
-        ++removedFiles;
-        ++i;
-        qApp->processEvents();
-    }
-#ifndef QT_NO_DEBUG_OUTPUT
-    debugStats();
-    if (removedFiles > 0) {
-        qDebug() << "Removed:" << removedFiles << "Kept:" << cacheItems.count() - removedFiles
-                 << "New Size:" << totalSize;
-    }
-#endif
-
-    expiring = false;
-
-    return totalSize;
-}
-
-#ifndef QT_NO_DEBUG_OUTPUT
-void LocalCache::debugStats() {
-    int total = hits + misses;
-    if (total > 0) {
-        qDebug() << "Cache:" << name << '\n'
-                 << "Inserts:" << insertCount << '\n'
-                 << "Requests:" << total << '\n'
-                 << "Hits:" << hits << (hits * 100) / total << "%\n"
-                 << "Misses:" << misses << (misses * 100) / total << "%";
-    }
-}
-#endif
diff --git a/src/http/src/localcache.h b/src/http/src/localcache.h
deleted file mode 100644 (file)
index ff07109..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef LOCALCACHE_H
-#define LOCALCACHE_H
-
-#include <QtCore>
-
-/**
- * @brief Not thread-safe
- */
-class LocalCache {
-public:
-    static LocalCache *instance(const char *name);
-    ~LocalCache();
-    static QByteArray hash(const QByteArray &s);
-
-    const QByteArray &getName() const { return name; }
-
-    void setMaxSeconds(uint value) { maxSeconds = value; }
-    void setMaxSize(uint value) { maxSize = value; }
-
-    QByteArray value(const QByteArray &key);
-    void insert(const QByteArray &key, const QByteArray &value);
-    bool clear();
-
-private:
-    LocalCache(const QByteArray &name);
-    QString cachePath(const QByteArray &key) const;
-    bool isCached(const QString &path);
-    qint64 expire();
-#ifndef QT_NO_DEBUG_OUTPUT
-    void debugStats();
-#endif
-
-    QByteArray name;
-    QString directory;
-    uint maxSeconds;
-    qint64 maxSize;
-    qint64 size;
-    bool expiring;
-    uint insertCount;
-    struct QueueItem {
-        QByteArray key;
-        QByteArray value;
-    };
-    QVector<QueueItem> insertQueue;
-
-#ifndef QT_NO_DEBUG_OUTPUT
-    uint hits;
-    uint misses;
-#endif
-};
-
-#endif // LOCALCACHE_H
diff --git a/src/http/src/throttledhttp.cpp b/src/http/src/throttledhttp.cpp
deleted file mode 100644 (file)
index 1438a3a..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#include "throttledhttp.h"
-
-namespace {
-
-QElapsedTimer initElapsedTimer() {
-    QElapsedTimer timer;
-    timer.start();
-    return timer;
-}
-}
-
-ThrottledHttp::ThrottledHttp(Http &http) : http(http), elapsedTimer(initElapsedTimer()) {}
-
-QObject *ThrottledHttp::request(const HttpRequest &req) {
-    return new ThrottledHttpReply(http, req, milliseconds, elapsedTimer);
-}
-
-ThrottledHttpReply::ThrottledHttpReply(Http &http,
-                                       const HttpRequest &req,
-                                       int milliseconds,
-                                       QElapsedTimer &elapsedTimer)
-    : http(http), req(req), milliseconds(milliseconds), elapsedTimer(elapsedTimer), timer(0) {
-    checkElapsed();
-}
-
-void ThrottledHttpReply::checkElapsed() {
-    /*
-    static QMutex mutex;
-    QMutexLocker locker(&mutex);
-    */
-
-    const qint64 elapsedSinceLastRequest = elapsedTimer.elapsed();
-    if (elapsedSinceLastRequest < milliseconds) {
-        if (!timer) {
-            timer = new QTimer(this);
-            timer->setSingleShot(true);
-            timer->setTimerType(Qt::PreciseTimer);
-            connect(timer, SIGNAL(timeout()), SLOT(checkElapsed()));
-        }
-        qDebug() << "Throttling" << req.url
-                 << QString("%1ms").arg(milliseconds - elapsedSinceLastRequest);
-        timer->setInterval(milliseconds - elapsedSinceLastRequest);
-        timer->start();
-        return;
-    }
-    elapsedTimer.start();
-    doRequest();
-}
-
-void ThrottledHttpReply::doRequest() {
-    QObject *reply = http.request(req);
-    connect(reply, SIGNAL(data(QByteArray)), SIGNAL(data(QByteArray)));
-    connect(reply, SIGNAL(error(QString)), SIGNAL(error(QString)));
-    connect(reply, SIGNAL(finished(HttpReply)), SIGNAL(finished(HttpReply)));
-
-    // this will cause the deletion of this object once the request is finished
-    setParent(reply);
-}
diff --git a/src/http/src/throttledhttp.h b/src/http/src/throttledhttp.h
deleted file mode 100644 (file)
index 4173b37..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef THROTTLEDHTTP_H
-#define THROTTLEDHTTP_H
-
-#include "http.h"
-#include <QtCore>
-#include <QtNetwork>
-
-class ThrottledHttp : public Http {
-public:
-    ThrottledHttp(Http &http = Http::instance());
-    void setMilliseconds(int milliseconds) { this->milliseconds = milliseconds; }
-    QObject *request(const HttpRequest &req);
-
-private:
-    Http &http;
-    int milliseconds;
-    QElapsedTimer elapsedTimer;
-};
-
-class ThrottledHttpReply : public HttpReply {
-    Q_OBJECT
-
-public:
-    ThrottledHttpReply(Http &http,
-                       const HttpRequest &req,
-                       int milliseconds,
-                       QElapsedTimer &elapsedTimer);
-    QUrl url() const { return req.url; }
-    int statusCode() const { return 200; }
-    QByteArray body() const { return QByteArray(); }
-
-private slots:
-    void checkElapsed();
-
-private:
-    void doRequest();
-    Http &http;
-    HttpRequest req;
-    int milliseconds;
-    QElapsedTimer &elapsedTimer;
-    QTimer *timer;
-};
-
-#endif // THROTTLEDHTTP_H
index fe007cbb00df0c48ad7e65109bf49fe8c83fe9f6..d522f6e3423e9c2fad7e3fcbad6571ea88ac1a94 100644 (file)
@@ -1,9 +1,9 @@
 #include "httputils.h"
+#include "cachedhttp.h"
 #include "constants.h"
 #include "http.h"
-#include "throttledhttp.h"
-#include "cachedhttp.h"
 #include "localcache.h"
+#include "throttledhttp.h"
 
 Http &HttpUtils::notCached() {
     static Http *h = [] {
@@ -47,14 +47,15 @@ void HttpUtils::clearCaches() {
 
 const QByteArray &HttpUtils::userAgent() {
     static const QByteArray ua = [] {
-        return QString(QLatin1String(Constants::NAME)
-                       + QLatin1Char('/') + QLatin1String(Constants::VERSION)
-                       + QLatin1String(" ( ") + Constants::WEBSITE + QLatin1String(" )")).toUtf8();
+        return QString(QLatin1String(Constants::NAME) + QLatin1Char('/') +
+                       QLatin1String(Constants::VERSION) + QLatin1String(" ( ") +
+                       Constants::WEBSITE + QLatin1String(" )"))
+                .toUtf8();
     }();
     return ua;
 }
 
 const QByteArray &HttpUtils::stealthUserAgent() {
-    static const QByteArray ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36";
+    static const QByteArray ua = "curl/7.37.0";
     return ua;
 }
index b09d400b2e4e024079ec776900d2f3f88586d578..6b536453ee37d1b926f012511c3e311290aec12d 100644 (file)
@@ -19,44 +19,61 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "iconutils.h"
-#include <QAction>
 #include "mainwindow.h"
+#include <QAction>
+
+namespace {
+void addIconFile(QIcon &icon,
+                 const QString &filename,
+                 int size,
+                 QIcon::Mode mode = QIcon::Normal,
+                 QIcon::State state = QIcon::Off) {
+    if (QFile::exists(filename)) {
+        icon.addFile(filename, QSize(size, size), mode, state);
+    }
+}
+} // namespace
 
 QIcon IconUtils::fromTheme(const QString &name) {
-    const QLatin1String symbolic("-symbolic");
+    static const QLatin1String symbolic("-symbolic");
     if (name.endsWith(symbolic)) return QIcon::fromTheme(name);
     QIcon icon = QIcon::fromTheme(name + symbolic);
     if (icon.isNull()) return QIcon::fromTheme(name);
     return icon;
 }
 
-QIcon IconUtils::fromResources(const QString &name) {
-    QLatin1String path(":/images/");
-    QLatin1String ext(".png");
-    const QString pathAndName = path + name;
-    QIcon icon = QIcon(pathAndName + ext);
-    if (!icon.isNull()) {
-        QLatin1String active("_active");
-        QLatin1String selected("_selected");
-        QLatin1String disabled("_disabled");
-        QLatin1String checked("_checked");
-        QLatin1String twoX("@2x");
-
-        icon.addPixmap(QPixmap(pathAndName + active + ext), QIcon::Active);
-        icon.addPixmap(QPixmap(pathAndName + selected + ext), QIcon::Selected);
-        icon.addPixmap(QPixmap(pathAndName + disabled + ext), QIcon::Disabled);
-        icon.addPixmap(QPixmap(pathAndName + checked + ext), QIcon::Normal, QIcon::On);
-
-        const QString twoXAndExt = twoX + ext;
-        icon.addPixmap(QPixmap(pathAndName + active + twoXAndExt), QIcon::Active);
-        icon.addPixmap(QPixmap(pathAndName + selected + twoXAndExt), QIcon::Selected);
-        icon.addPixmap(QPixmap(pathAndName + disabled + twoXAndExt), QIcon::Disabled);
-        icon.addPixmap(QPixmap(pathAndName + checked + twoXAndExt), QIcon::Normal, QIcon::On);
+QIcon IconUtils::fromResources(const char *name) {
+    static const QLatin1String active("_active");
+    static const QLatin1String selected("_selected");
+    static const QLatin1String disabled("_disabled");
+    static const QLatin1String checked("_checked");
+    static const QLatin1String ext(".png");
+
+    QString path(":/icons/");
+
+    if (MainWindow::instance()->palette().window().color().value() > 128)
+        path += QLatin1String("light/");
+    else
+        path += QLatin1String("dark/");
+
+    QIcon icon;
+
+    // WARN keep these sizes updated with what we really use
+    for (int size : {16, 24, 32, 88}) {
+        const QString pathAndName = path + QString::number(size) + '/' + name;
+        QString iconFilename = pathAndName + ext;
+        if (QFile::exists(iconFilename)) {
+            addIconFile(icon, iconFilename, size);
+            addIconFile(icon, pathAndName + active + ext, size, QIcon::Active);
+            addIconFile(icon, pathAndName + selected + ext, size, QIcon::Selected);
+            addIconFile(icon, pathAndName + disabled + ext, size, QIcon::Disabled);
+            addIconFile(icon, pathAndName + checked + ext, size, QIcon::Normal, QIcon::On);
+        }
     }
     return icon;
 }
 
-QIcon IconUtils::icon(const QString &name) {
+QIcon IconUtils::icon(const char *name) {
 #ifdef APP_LINUX
     QIcon icon = fromTheme(name);
     if (icon.isNull()) icon = fromResources(name);
@@ -66,29 +83,42 @@ QIcon IconUtils::icon(const QString &name) {
 #endif
 }
 
-QIcon IconUtils::icon(const QStringList &names) {
+QIcon IconUtils::icon(const QVector<const char *> &names) {
     QIcon icon;
-    for (const QString &name : names) {
+    for (auto name : names) {
         icon = IconUtils::icon(name);
         if (!icon.availableSizes().isEmpty()) break;
     }
     return icon;
 }
 
-QIcon IconUtils::tintedIcon(const QString &name, const QColor &color, QList<QSize> sizes) {
+QPixmap IconUtils::iconPixmap(const char *name,
+                              int size,
+                              const QColor &background,
+                              const qreal pixelRatio) {
+    QString path(":/icons/");
+    if (background.value() > 128)
+        path += "light/";
+    else
+        path += "dark/";
+    path += QString::number(size) + '/' + name + QLatin1String(".png");
+    return IconUtils::pixmap(path, pixelRatio);
+}
+
+QIcon IconUtils::tintedIcon(const char *name, const QColor &color, const QVector<QSize> &sizes) {
     QIcon i = IconUtils::icon(name);
     QIcon t;
-    if (sizes.isEmpty()) sizes = i.availableSizes();
+    // if (sizes.isEmpty()) sizes = i.availableSizes();
     for (const QSize &size : sizes) {
         QPixmap pixmap = i.pixmap(size);
-        QImage tintedImage = tinted(pixmap.toImage(), color);
-        t.addPixmap(QPixmap::fromImage(tintedImage));
+        tint(pixmap, color);
+        t.addPixmap(pixmap);
     }
     return t;
 }
 
-QIcon IconUtils::tintedIcon(const QString &name, const QColor &color, const QSize &size) {
-    return IconUtils::tintedIcon(name, color, QList<QSize>() << size);
+QIcon IconUtils::tintedIcon(const char *name, const QColor &color, const QSize &size) {
+    return IconUtils::tintedIcon(name, color, QVector<QSize>() << size);
 }
 
 QImage IconUtils::grayscaled(const QImage &image) {
@@ -102,8 +132,7 @@ QImage IconUtils::grayscaled(const QImage &image) {
     return img;
 }
 
-QImage IconUtils::tinted(const QImage &image, const QColor &color,
-                         QPainter::CompositionMode mode) {
+QImage IconUtils::tinted(const QImage &image, const QColor &color, QPainter::CompositionMode mode) {
     QImage img(image.size(), QImage::Format_ARGB32_Premultiplied);
     QPainter painter(&img);
     painter.drawImage(0, 0, grayscaled(image));
@@ -114,26 +143,18 @@ QImage IconUtils::tinted(const QImage &image, const QColor &color,
     return img;
 }
 
-void IconUtils::setupAction(QAction *action) {
-    // never autorepeat.
-    // unexperienced users tend to keep keys pressed for a "long" time
-    action->setAutoRepeat(false);
-
-    // show keyboard shortcuts in the status bar
-    if (!action->shortcut().isEmpty())
-        action->setStatusTip(action->statusTip() +
-                             QLatin1String(" (") +
-                             action->shortcut().toString(QKeySequence::NativeText) +
-                             QLatin1String(")"));
+void IconUtils::tint(QPixmap &pixmap, const QColor &color, QPainter::CompositionMode mode) {
+    QPainter painter(&pixmap);
+    painter.setCompositionMode(mode);
+    painter.fillRect(pixmap.rect(), color);
 }
 
-QPixmap IconUtils::pixmap(const QString &name) {
+QPixmap IconUtils::pixmap(const QString &filename, const qreal pixelRatio) {
     // Check if a "@2x" file exists
-    const qreal pixelRatio = IconUtils::pixelRatio();
     if (pixelRatio > 1.0) {
-        int dotIndex = name.lastIndexOf(QLatin1Char('.'));
+        int dotIndex = filename.lastIndexOf(QLatin1Char('.'));
         if (dotIndex != -1) {
-            QString at2xfileName = name;
+            QString at2xfileName = filename;
             at2xfileName.insert(dotIndex, QLatin1String("@2x"));
             if (QFile::exists(at2xfileName)) {
                 QPixmap pixmap(at2xfileName);
@@ -142,13 +163,5 @@ QPixmap IconUtils::pixmap(const QString &name) {
             }
         }
     }
-    return QPixmap(name);
-}
-
-qreal IconUtils::pixelRatio() {
-#if QT_VERSION >= 0x050600
-    return MainWindow::instance()->devicePixelRatioF();
-#else
-    return MainWindow::instance()->devicePixelRatio();
-#endif
+    return QPixmap(filename);
 }
index 2fbe0b2340c891273343020563d34e6be8c59007..52d915c5ecf46339818bc22513a0343ae1fe723f 100644 (file)
@@ -24,26 +24,39 @@ $END_LICENSE */
 #include <QtGui>
 
 class IconUtils {
-
 public:
     static QIcon fromTheme(const QString &name);
-    static QIcon fromResources(const QString &name);
-    static QIcon icon(const QString &name);
-    static QIcon icon(const QStringList &names);
-    static QIcon tintedIcon(const QString &name, const QColor &color,
-                            QList<QSize> sizes = QList<QSize>());
-    static QIcon tintedIcon(const QString &name, const QColor &color, const QSize &size);
-    static void setupAction(QAction *action);
+    static QIcon fromResources(const char *name);
+
+    template <class T> static void setIcon(T *obj, const char *name) {
+        QIcon i = icon(name);
+        obj->setIcon(i);
+        obj->connect(qApp, &QGuiApplication::paletteChanged, obj, [obj, name] {
+            QIcon i = icon(name);
+            obj->setIcon(i);
+        });
+    }
+    static QIcon icon(const char *name);
+    static QIcon icon(const QVector<const char *> &names);
+
+    static QPixmap
+    iconPixmap(const char *name, int size, const QColor &background, const qreal pixelRatio);
+
+    static QIcon tintedIcon(const char *name, const QColor &color, const QVector<QSize> &sizes);
+    static QIcon tintedIcon(const char *name, const QColor &color, const QSize &size);
 
     // HiDPI stuff
-    static QPixmap pixmap(const QString &name);
-    static qreal maxSupportedPixelRatio() { return 2.0; }
-    static qreal pixelRatio();
+    static QPixmap pixmap(const QString &filename, const qreal pixelRatio);
+
+    static void tint(QPixmap &pixmap,
+                     const QColor &color,
+                     QPainter::CompositionMode mode = QPainter::CompositionMode_SourceIn);
 
 private:
-    IconUtils() { }
+    IconUtils() {}
     static QImage grayscaled(const QImage &image);
-    static QImage tinted(const QImage &image, const QColor &color,
+    static QImage tinted(const QImage &image,
+                         const QColor &color,
                          QPainter::CompositionMode mode = QPainter::CompositionMode_Screen);
 };
 
diff --git a/src/idle/idle.pri b/src/idle/idle.pri
deleted file mode 100644 (file)
index a70e586..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-INCLUDEPATH += $$PWD/src
-DEPENDPATH += $$PWD/src
-
-HEADERS += $$PWD/src/idle.h
-
-mac {
-    SOURCES += $$PWD/src/idle_mac.cpp
-} else:win32 {
-    SOURCES += $$PWD/src/idle_win.cpp
-} else {
-    QT *= dbus
-    SOURCES += $$PWD/src/idle_linux.cpp
-}
diff --git a/src/idle/src/idle.h b/src/idle/src/idle.h
deleted file mode 100644 (file)
index 6a004af..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef IDLE_H
-#define IDLE_H
-
-#include <QString>
-
-class Idle {
-
-public:
-    static bool preventDisplaySleep(const QString &reason);
-    static bool allowDisplaySleep();
-    static QString displayErrorMessage();
-
-    static bool preventSystemSleep(const QString &reason);
-    static bool allowSystemSleep();
-    static QString systemErrorMessage();
-
-};
-
-#endif // IDLE_H
diff --git a/src/idle/src/idle_linux.cpp b/src/idle/src/idle_linux.cpp
deleted file mode 100644 (file)
index 80c9e8c..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#include "idle.h"
-
-#include <QtCore>
-#include <QDBusInterface>
-#include <QDBusReply>
-#include <QDBusConnectionInterface>
-
-namespace {
-
-const char *fdDisplayService = "org.freedesktop.ScreenSaver";
-const char *fdDisplayPath = "/org/freedesktop/ScreenSaver";
-const char *fdDisplayInterface = fdDisplayService;
-
-const char *gnomeSystemService = "org.gnome.SessionManager";
-const char *gnomeSystemPath = "/org/gnome/SessionManager";
-const char *gnomeSystemInterface = gnomeSystemService;
-
-quint32 cookie;
-QString errorMessage;
-
-bool handleReply(const QDBusReply<quint32> &reply) {
-    if (reply.isValid()) {
-        cookie = reply.value();
-        qDebug() << "Success!" << cookie;
-        errorMessage.clear();
-        return true;
-    }
-    errorMessage = reply.error().message();
-    return false;
-}
-
-}
-
-bool Idle::preventDisplaySleep(const QString &reason) {
-    QDBusInterface dbus(fdDisplayService, fdDisplayPath, fdDisplayInterface);
-    QDBusReply<quint32> reply = dbus.call("Inhibit", QCoreApplication::applicationName(), reason);
-    return handleReply(reply);
-}
-
-bool Idle::allowDisplaySleep() {
-    QDBusInterface dbus(fdDisplayService, fdDisplayPath, fdDisplayInterface);
-    dbus.call("UnInhibit", cookie);
-    return true;
-}
-
-QString Idle::displayErrorMessage() {
-    return errorMessage;
-}
-
-bool Idle::preventSystemSleep(const QString &reason) {
-    QDBusInterface dbus(gnomeSystemService, gnomeSystemPath, gnomeSystemInterface);
-    QDBusReply<quint32> reply = dbus.call("Inhibit", QCoreApplication::applicationName(), reason);
-    return handleReply(reply);
-}
-
-bool Idle::allowSystemSleep() {
-    QDBusInterface dbus(gnomeSystemService, gnomeSystemPath, gnomeSystemInterface);
-    dbus.call("UnInhibit", cookie);
-    return true;
-}
-
-QString Idle::systemErrorMessage() {
-    return errorMessage;
-}
-
diff --git a/src/idle/src/idle_mac.cpp b/src/idle/src/idle_mac.cpp
deleted file mode 100644 (file)
index 315cdef..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "idle.h"
-
-#include <IOKit/pwr_mgt/IOPMLib.h>
-
-namespace {
-
-IOPMAssertionID displayAssertionID = 0;
-IOReturn displayRes = 0;
-
-IOPMAssertionID systemAssertionID = 0;
-IOReturn systemRes = 0;
-
-}
-
-bool Idle::preventDisplaySleep(const QString &reason) {
-    displayRes = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep,
-                                        kIOPMAssertionLevelOn, reason.toCFString(), &displayAssertionID);
-    return displayRes == kIOReturnSuccess;
-}
-
-bool Idle::allowDisplaySleep() {
-    displayRes = IOPMAssertionRelease(displayAssertionID);
-    return displayRes == kIOReturnSuccess;
-}
-
-QString Idle::displayErrorMessage() {
-    return QString();
-    // return QString::fromUtf8(IOService::stringFromReturn(displayRes));
-}
-
-bool Idle::preventSystemSleep(const QString &reason) {
-    systemRes = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleSystemSleep,
-                                        kIOPMAssertionLevelOn, reason.toCFString(), &systemAssertionID);
-    return systemRes == kIOReturnSuccess;
-}
-
-bool Idle::allowSystemSleep() {
-    systemRes = IOPMAssertionRelease(systemAssertionID);
-    return systemRes == kIOReturnSuccess;
-}
-
-QString Idle::systemErrorMessage() {
-    return QString();
-    // return QString::fromUtf8(IOService::stringFromReturn(systemRes));
-}
diff --git a/src/idle/src/idle_win.cpp b/src/idle/src/idle_win.cpp
deleted file mode 100644 (file)
index 2f189ef..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "idle.h"
-
-#include "windows.h"
-
-namespace {
-EXECUTION_STATE executionState;
-}
-
-bool Idle::preventDisplaySleep(const QString &reason) {
-    executionState = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
-    return true;
-}
-
-bool Idle::allowDisplaySleep() {
-    SetThreadExecutionState(ES_CONTINUOUS | executionState);
-    return true;
-}
-
-QString Idle::displayErrorMessage() {
-    return QString();
-}
-
-bool Idle::preventSystemSleep(const QString &reason) {
-    executionState = SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
-    return true;
-}
-
-bool Idle::allowSystemSleep() {
-    SetThreadExecutionState(ES_CONTINUOUS | executionState);
-    return true;
-}
-
-QString Idle::systemErrorMessage() {
-    return QString();
-}
index e63f14efbc25b7338f0bc4294f25bb915a7b3756..d0cef41401143ac19408962c208f3fe4c44460a4 100644 (file)
@@ -19,17 +19,18 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "jsfunctions.h"
-#include "http.h"
 #include "constants.h"
+#include "http.h"
 
 #include <QJSValueIterator>
 
-JsFunctionsJsFunctions::instance() {
+JsFunctions *JsFunctions::instance() {
     static JsFunctions *i = new JsFunctions(QLatin1String(Constants::WEBSITE) + "-ws/functions.js");
     return i;
 }
 
-JsFunctions::JsFunctions(const QString &url, QObject *parent) : QObject(parent), url(url), engine(0) {
+JsFunctions::JsFunctions(const QString &url, QObject *parent)
+    : QObject(parent), url(url), engine(nullptr) {
     QFile file(jsPath());
     if (file.exists()) {
         if (file.open(QIODevice::ReadOnly | QIODevice::Text))
@@ -37,7 +38,8 @@ JsFunctions::JsFunctions(const QString &url, QObject *parent) : QObject(parent),
         else
             qWarning() << "Cannot open" << file.errorString() << file.fileName();
         QFileInfo info(file);
-        bool stale = info.size() == 0 || info.lastModified().toTime_t() < QDateTime::currentDateTime().toTime_t() - 1800;
+        bool stale = info.size() == 0 || info.lastModified().toTime_t() <
+                                                 QDateTime::currentDateTime().toTime_t() - 1800;
         if (stale) loadJs();
     } else {
         QFile resFile(QLatin1String(":/") + jsFilename());
@@ -73,7 +75,7 @@ void JsFunctions::loadJs() {
     QUrlQuery q;
     q.addQueryItem("v", Constants::VERSION);
     url.setQuery(q);
-    QObjectreply = Http::instance().get(url);
+    QObject *reply = Http::instance().get(url);
     connect(reply, SIGNAL(data(QByteArray)), SLOT(gotJs(QByteArray)));
     connect(reply, SIGNAL(error(QString)), SLOT(errorJs(QString)));
 }
@@ -101,10 +103,8 @@ void JsFunctions::errorJs(const QString &message) {
 QJSValue JsFunctions::evaluate(const QString &js) {
     if (!engine) return QString();
     QJSValue value = engine->evaluate(js);
-    if (value.isUndefined())
-        qWarning() << "Undefined result for" << js;
-    if (value.isError())
-        qWarning() << "Error in" << js << value.toString();
+    if (value.isUndefined()) qWarning() << "Undefined result for" << js;
+    if (value.isError()) qWarning() << "Error in" << js << value.toString();
 
     return value;
 }
@@ -163,6 +163,10 @@ QString JsFunctions::signatureFunctionNameRE() {
     return string("signatureFunctionNameRE()");
 }
 
+QStringList JsFunctions::signatureFunctionNameREs() {
+    return stringArray("signatureFunctionNameREs()");
+}
+
 QStringList JsFunctions::apiKeys() {
     return stringArray("apiKeys()");
 }
index 3cb0d457454d84e3812a1f880dd9dcfd5341d3f1..4bc369a98732cf0926017e3f4261ef570e5c8cf5 100644 (file)
@@ -21,18 +21,17 @@ $END_LICENSE */
 #ifndef JSFUNCTIONS_H
 #define JSFUNCTIONS_H
 
-#include <QtCore>
-#include <QtNetwork>
 #include <QJSEngine>
 #include <QJSValue>
+#include <QtCore>
+#include <QtNetwork>
 
 class JsFunctions : public QObject {
-
     Q_OBJECT
 
 public:
-    static JsFunctionsinstance();
-    JsFunctions(const QString &url, QObject *parent = 0);
+    static JsFunctions *instance();
+    JsFunctions(const QString &url, QObject *parent = nullptr);
     QJSValue evaluate(const QString &js);
     QString string(const QString &js);
     QStringList stringArray(const QString &js);
@@ -48,6 +47,7 @@ public:
     QString ageGateRE();
     QString jsPlayerRE();
     QString signatureFunctionNameRE();
+    QStringList signatureFunctionNameREs();
     QStringList apiKeys();
 
 signals:
index 13e7cb6c882e68c6f175e772ef4fa67a54f127bc..66ec79d2a1eafcb9f4a48ac1d559a8a696677ec6 100644 (file)
@@ -75,7 +75,7 @@ void LoadingWidget::setVideo(Video *video) {
     titleLabel->setText(title);
     titleLabel->setVisible(window()->height() > 100);
 
-    const int maxDescLength = 400;
+    const int maxDescLength = 500;
 
     QString videoDesc = video->getDescription();
     if (videoDesc.length() > maxDescLength) {
@@ -106,8 +106,9 @@ void LoadingWidget::setError(const QString &message) {
     progressBar->setValue(0);
 }
 
-void LoadingWidget::bufferStatus(int percent) {
-    if (startTime.elapsed() > 2000 && percent > progressBar->value())
+void LoadingWidget::bufferStatus(qreal value) {
+    int percent = value * 100.;
+    if (startTime.elapsed() > 1000 && percent > progressBar->value())
         progressBar->setValue(percent);
 }
 
@@ -122,8 +123,9 @@ void LoadingWidget::adjustFontSize() {
     layout()->setMargin(spacing);
     titleLabel->setFont(f);
 
-    f.setPixelSize(f.pixelSize() / 2);
-    descriptionLabel->setFont(f);
+    QFont descFont = descriptionLabel->font();
+    descFont.setPixelSize(f.pixelSize() / 2);
+    descriptionLabel->setFont(descFont);
 }
 
 void LoadingWidget::clear() {
index ca19c06490abe743badf572c52ffe081ca2041d4..ad4c60ac0084d24f5fbe9ac004fe316295840f4f 100644 (file)
@@ -26,7 +26,6 @@ $END_LICENSE */
 #include "video.h"
 
 class LoadingWidget : public QWidget {
-
     Q_OBJECT
 
 public:
@@ -39,7 +38,7 @@ protected:
     void resizeEvent(QResizeEvent *e);
 
 public slots:
-    void bufferStatus(int);
+    void bufferStatus(qreal value);
 
 private:
     void adjustFontSize();
@@ -48,7 +47,6 @@ private:
     QLabel *descriptionLabel;
     QProgressBar *progressBar;
     QTime startTime;
-
 };
 
 #endif // LOADINGWIDGET_H
index 3a0873f03a94e6eb7c2695a961db47e9b8e56cd2..717179ec295418d8be8b2889d2cf8cbb159399b2 100644 (file)
@@ -18,14 +18,14 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 
 $END_LICENSE */
 
-#include <QtWidgets>
 #include <QtNetwork>
+#include <QtWidgets>
 
-#include <qtsingleapplication.h>
 #include "constants.h"
+#include "iconutils.h"
 #include "mainwindow.h"
 #include "searchparams.h"
-#include "iconutils.h"
+#include <qtsingleapplication.h>
 #ifdef APP_EXTRA
 #include "extra.h"
 #endif
@@ -33,19 +33,20 @@ $END_LICENSE */
 #include "mac_startup.h"
 #endif
 
-void showWindow(QtSingleApplication &app, const QString &dataDir) {
+void showWindow(QtSingleApplication &app, const QString &pkgDataDir) {
     MainWindow *mainWin = new MainWindow();
 
 #ifndef APP_MAC
     QIcon appIcon;
-    if (QDir(dataDir).exists()) {
+    if (!pkgDataDir.isEmpty()) {
         appIcon = IconUtils::icon(Constants::UNIX_NAME);
     } else {
         QString dataDir = qApp->applicationDirPath() + "/data";
-        const int iconSizes [] = { 16, 22, 32, 48, 64, 128, 256, 512 };
+        const int iconSizes[] = {16, 22, 32, 48, 64, 128, 256, 512};
         for (int i = 0; i < 8; i++) {
             QString size = QString::number(iconSizes[i]);
-            QString png = dataDir + "/" + size + "x" + size + "/" + Constants::UNIX_NAME + ".png";
+            QString png = dataDir + '/' + size + 'x' + size + '/' + Constants::UNIX_NAME +
+                          QLatin1String(".png");
             appIcon.addFile(png, QSize(iconSizes[i], iconSizes[i]));
         }
     }
@@ -53,8 +54,8 @@ void showWindow(QtSingleApplication &app, const QString &dataDir) {
     mainWin->setWindowIcon(appIcon);
 #endif
 
-    mainWin->connect(&app, SIGNAL(messageReceived(const QString &)),
-                    mainWin, SLOT(messageReceived(const QString &)));
+    mainWin->connect(&app, SIGNAL(messageReceived(const QString &)), mainWin,
+                     SLOT(messageReceived(const QString &)));
     app.setActivationWindow(mainWin, true);
 
     mainWin->show();
@@ -68,18 +69,30 @@ int main(int argc, char **argv) {
     // Seed random number generator
     qsrand(QDateTime::currentDateTime().toTime_t());
 
+#ifdef MEDIA_MPV
+    QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+#ifdef APP_MAC
+    format.setMajorVersion(4);
+    format.setMinorVersion(1);
+#endif
+    format.setProfile(QSurfaceFormat::CoreProfile);
+    QSurfaceFormat::setDefaultFormat(format);
+#endif
+
 #ifdef Q_OS_MAC
     mac::MacMain();
 #endif
 
     QtSingleApplication app(argc, argv);
-    QString message = app.arguments().size() > 1 ? app.arguments().at(1) : QString();
-    if (message == QLatin1String("--help")) {
-        MainWindow::printHelp();
-        return 0;
+    QString message;
+    if (app.arguments().size() > 1) {
+        message = app.arguments().at(1);
+        if (message == QLatin1String("--help")) {
+            MainWindow::printHelp();
+            return 0;
+        }
     }
-    if (app.sendMessage(message))
-        return 0;
+    if (app.sendMessage(message)) return 0;
 
     app.setApplicationName(Constants::NAME);
     app.setOrganizationName(Constants::ORG_NAME);
@@ -96,6 +109,7 @@ int main(int argc, char **argv) {
     cssFile.open(QFile::ReadOnly);
     QString styleSheet = QLatin1String(cssFile.readAll());
     app.setStyleSheet(styleSheet);
+    cssFile.close();
 #endif
 
     // qt translations
@@ -104,28 +118,28 @@ int main(int argc, char **argv) {
                       QLibraryInfo::location(QLibraryInfo::TranslationsPath));
     app.installTranslator(&qtTranslator);
 
-    // app translations
+    QString pkgDataDir;
 #ifdef PKGDATADIR
-    QString dataDir = QLatin1String(PKGDATADIR);
-#else
-    QString dataDir = "";
+    pkgDataDir = QLatin1String(PKGDATADIR);
 #endif
+
+    // app translations
 #ifdef APP_MAC
     QString localeDir = qApp->applicationDirPath() + QLatin1String("/../Resources/locale");
 #else
     QString localeDir = qApp->applicationDirPath() + QLatin1String("/locale");
 #endif
     if (!QDir(localeDir).exists()) {
-        localeDir = dataDir + QLatin1String("/locale");
+        localeDir = pkgDataDir + QLatin1String("/locale");
     }
-    // qDebug() << "Using locale dir" << localeDir << locale;
+    qDebug() << "Using locale dir" << localeDir << QLocale::system();
     QTranslator translator;
     translator.load(QLocale::system(), QString(), QString(), localeDir);
     app.installTranslator(&translator);
 
     QNetworkProxyFactory::setUseSystemConfiguration(true);
 
-    showWindow(app, dataDir);
+    showWindow(app, pkgDataDir);
 
     return app.exec();
 }
index 3fcf2fe8684ef4ec2f056d4144a9a14a63a46343..4a3a480ef200d8594bcd428c9e254f5360d20c24 100644 (file)
@@ -60,6 +60,7 @@ $END_LICENSE */
 #endif
 #include <iostream>
 #ifdef APP_EXTRA
+#include "compositefader.h"
 #include "extra.h"
 #include "updatedialog.h"
 #endif
@@ -74,12 +75,19 @@ $END_LICENSE */
 #include "seekslider.h"
 #include "sidebarwidget.h"
 #include "toolbarmenu.h"
-#include "videoareawidget.h"
+#include "videoarea.h"
 #include "yt3.h"
 #include "ytregions.h"
 
+#ifdef MEDIA_QTAV
+#include "mediaqtav.h"
+#endif
+#ifdef MEDIA_MPV
+#include "mediampv.h"
+#endif
+
 namespace {
-static MainWindow *mainWindowInstance;
+MainWindow *mainWindowInstance;
 }
 
 MainWindow *MainWindow::instance() {
@@ -87,26 +95,23 @@ MainWindow *MainWindow::instance() {
 }
 
 MainWindow::MainWindow()
-    : updateChecker(0), aboutView(0), downloadView(0), regionsView(0), mainToolBar(0),
-#ifdef APP_PHONON
-      mediaObject(0), audioOutput(0),
-#endif
-      fullScreenActive(false), compactModeActive(false), initialized(false), toolbarMenu(0) {
-
+    : aboutView(nullptr), downloadView(nullptr), regionsView(nullptr), mainToolBar(nullptr),
+      fullScreenActive(false), compactModeActive(false), initialized(false), toolbarMenu(nullptr),
+      media(nullptr) {
     mainWindowInstance = this;
 
     // views mechanism
     views = new QStackedWidget();
-    views->hide();
     setCentralWidget(views);
 
 #ifdef APP_EXTRA
     Extra::windowSetup(this);
 #endif
 
-    messageLabel = new QLabel();
-    messageLabel->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint);
-    messageLabel->setStyleSheet("padding:5px;border:1px solid #808080;background:palette(window)");
+    messageLabel = new QLabel(this);
+    messageLabel->setWordWrap(false);
+    messageLabel->setStyleSheet("padding:5px;border:0;background:palette(window)");
+    messageLabel->setAlignment(Qt::AlignCenter);
     messageLabel->hide();
     adjustMessageLabelPosition();
     messageTimer = new QTimer(this);
@@ -126,7 +131,7 @@ MainWindow::MainWindow()
     // build ui
     createActions();
     createMenus();
-    createToolBars();
+    createToolBar();
     hideToolbar();
     createStatusBar();
 
@@ -146,25 +151,18 @@ MainWindow::MainWindow()
         views->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
     setMinimumWidth(0);
 
-    views->show();
-
 #ifdef APP_ACTIVATION
     Activation::instance().initialCheck();
 #else
     showHome();
 #endif
 
-    QTimer::singleShot(100, this, &MainWindow::lazyInit);
+    QTimer::singleShot(1000, this, &MainWindow::lazyInit);
 }
 
 void MainWindow::lazyInit() {
-#ifdef APP_PHONON
-    initPhonon();
-#endif
     mediaView->initialize();
-#ifdef APP_PHONON
-    mediaView->setMediaObject(mediaObject);
-#endif
+    initMedia();
     qApp->processEvents();
 
     // CLI
@@ -229,6 +227,12 @@ void MainWindow::changeEvent(QEvent *e) {
         getAction("minimize")->setEnabled(!isMinimized());
     }
 #endif
+    if (messageLabel->isVisible()) {
+        if (e->type() == QEvent::ActivationChange || e->type() == QEvent::WindowStateChange ||
+            e->type() == QEvent::WindowDeactivate || e->type() == QEvent::ApplicationStateChange) {
+            hideMessage();
+        }
+    }
     QMainWindow::changeEvent(e);
 }
 
@@ -303,27 +307,35 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
         toolbarMenu->move(mapToGlobal(p));
     }
 
+    if (obj == this && t == QEvent::StyleChange) {
+        qDebug() << "Style change detected";
+        qApp->paletteChanged(qApp->palette());
+        return false;
+    }
+
     // standard event processing
     return QMainWindow::eventFilter(obj, e);
 }
 
 void MainWindow::createActions() {
-    stopAct = new QAction(IconUtils::icon("media-playback-stop"), tr("&Stop"), this);
+    stopAct = new QAction(tr("&Stop"), this);
+    IconUtils::setIcon(stopAct, "media-playback-stop");
     stopAct->setStatusTip(tr("Stop playback and go back to the search view"));
-    stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape)
-                                                << QKeySequence(Qt::Key_MediaStop));
+    stopAct->setShortcuts(QList<QKeySequence>()
+                          << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
     stopAct->setEnabled(false);
     actionMap.insert("stop", stopAct);
     connect(stopAct, SIGNAL(triggered()), SLOT(stop()));
 
-    skipBackwardAct = new QAction(IconUtils::icon("media-skip-backward"), tr("P&revious"), this);
+    skipBackwardAct = new QAction(tr("P&revious"), this);
     skipBackwardAct->setStatusTip(tr("Go back to the previous track"));
     skipBackwardAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Left));
     skipBackwardAct->setEnabled(false);
     actionMap.insert("previous", skipBackwardAct);
     connect(skipBackwardAct, SIGNAL(triggered()), mediaView, SLOT(skipBackward()));
 
-    skipAct = new QAction(IconUtils::icon("media-skip-forward"), tr("S&kip"), this);
+    skipAct = new QAction(tr("S&kip"), this);
+    IconUtils::setIcon(skipAct, "media-skip-forward");
     skipAct->setStatusTip(tr("Skip to the next video"));
     skipAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_Right)
                                                 << QKeySequence(Qt::Key_MediaNext));
@@ -331,15 +343,17 @@ void MainWindow::createActions() {
     actionMap.insert("skip", skipAct);
     connect(skipAct, SIGNAL(triggered()), mediaView, SLOT(skip()));
 
-    pauseAct = new QAction(IconUtils::icon("media-playback-start"), tr("&Play"), this);
+    pauseAct = new QAction(tr("&Play"), this);
+    IconUtils::setIcon(pauseAct, "media-playback-start");
     pauseAct->setStatusTip(tr("Resume playback"));
-    pauseAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Space)
-                                                 << QKeySequence(Qt::Key_MediaPlay));
+    pauseAct->setShortcuts(QList<QKeySequence>()
+                           << QKeySequence(Qt::Key_Space) << QKeySequence(Qt::Key_MediaPlay));
     pauseAct->setEnabled(false);
     actionMap.insert("pause", pauseAct);
     connect(pauseAct, SIGNAL(triggered()), mediaView, SLOT(pause()));
 
-    fullscreenAct = new QAction(IconUtils::icon("view-fullscreen"), tr("&Full Screen"), this);
+    fullscreenAct = new QAction(tr("&Full Screen"), this);
+    IconUtils::setIcon(fullscreenAct, "view-fullscreen");
     fullscreenAct->setStatusTip(tr("Go full screen"));
     QList<QKeySequence> fsShortcuts;
 #ifdef APP_MAC
@@ -355,11 +369,7 @@ void MainWindow::createActions() {
 
     compactViewAct = new QAction(tr("&Compact Mode"), this);
     compactViewAct->setStatusTip(tr("Hide the playlist and the toolbar"));
-#ifdef APP_MAC
-    compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::META + Qt::Key_C));
-#else
     compactViewAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C));
-#endif
     compactViewAct->setCheckable(true);
     compactViewAct->setChecked(false);
     compactViewAct->setEnabled(false);
@@ -373,7 +383,8 @@ void MainWindow::createActions() {
     actionMap.insert("webpage", webPageAct);
     connect(webPageAct, SIGNAL(triggered()), mediaView, SLOT(openWebPage()));
 
-    copyPageAct = new QAction(IconUtils::icon("link"), tr("Copy the YouTube &Link"), this);
+    copyPageAct = new QAction(tr("Copy the YouTube &Link"), this);
+    IconUtils::setIcon(copyPageAct, "link");
     copyPageAct->setStatusTip(tr("Copy the current video YouTube link to the clipboard"));
     copyPageAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L));
     copyPageAct->setEnabled(false);
@@ -396,8 +407,8 @@ void MainWindow::createActions() {
 
     removeAct = new QAction(tr("&Remove"), this);
     removeAct->setStatusTip(tr("Remove the selected videos from the playlist"));
-    removeAct->setShortcuts(QList<QKeySequence>() << QKeySequence("Del")
-                                                  << QKeySequence("Backspace"));
+    removeAct->setShortcuts(QList<QKeySequence>()
+                            << QKeySequence("Del") << QKeySequence("Backspace"));
     removeAct->setEnabled(false);
     actionMap.insert("remove", removeAct);
     connect(removeAct, SIGNAL(triggered()), mediaView, SLOT(removeSelected()));
@@ -475,24 +486,47 @@ void MainWindow::createActions() {
     addAction(volumeDownAct);
 
     volumeMuteAct = new QAction(this);
-    volumeMuteAct->setIcon(IconUtils::icon("audio-volume-high"));
+    IconUtils::setIcon(volumeMuteAct, "audio-volume-high");
     volumeMuteAct->setStatusTip(tr("Mute volume"));
-    volumeMuteAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K));
+    volumeMuteAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M));
     actionMap.insert("volumeMute", volumeMuteAct);
-    connect(volumeMuteAct, SIGNAL(triggered()), SLOT(volumeMute()));
+    connect(volumeMuteAct, SIGNAL(triggered()), SLOT(toggleVolumeMute()));
     addAction(volumeMuteAct);
 
-    QAction *definitionAct = new QAction(this);
-    definitionAct->setIcon(IconUtils::icon("video-display"));
+    QToolButton *definitionButton = new QToolButton(this);
+    definitionButton->setText(YT3::instance().maxVideoDefinition().getName());
+    IconUtils::setIcon(definitionButton, "video-display");
+    definitionButton->setIconSize(QSize(16, 16));
+    definitionButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+    definitionButton->setPopupMode(QToolButton::InstantPopup);
+    QMenu *definitionMenu = new QMenu(this);
+    QActionGroup *group = new QActionGroup(this);
+    for (auto &defName : VideoDefinition::getDefinitionNames()) {
+        QAction *a = new QAction(defName);
+        a->setCheckable(true);
+        a->setActionGroup(group);
+        a->setChecked(defName == YT3::instance().maxVideoDefinition().getName());
+        connect(a, &QAction::triggered, this, [this, defName, definitionButton] {
+            setDefinitionMode(defName);
+            definitionButton->setText(defName);
+        });
+        connect(&YT3::instance(), &YT3::maxVideoDefinitionChanged, this,
+                [defName, definitionButton](const QString &name) {
+                    if (defName == name) definitionButton->setChecked(true);
+                });
+        definitionMenu->addAction(a);
+    }
+    definitionButton->setMenu(definitionMenu);
+    QWidgetAction *definitionAct = new QWidgetAction(this);
+    definitionAct->setDefaultWidget(definitionButton);
     definitionAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_D));
     actionMap.insert("definition", definitionAct);
-    connect(definitionAct, SIGNAL(triggered()), SLOT(toggleDefinitionMode()));
     addAction(definitionAct);
 
     QAction *action;
 
-    action = new QAction(IconUtils::icon("media-playback-start"), tr("&Manually Start Playing"),
-                         this);
+    action = new QAction(tr("&Manually Start Playing"), this);
+    IconUtils::setIcon(action, "media-playback-start");
     action->setStatusTip(tr("Manually start playing videos"));
     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T));
     action->setCheckable(true);
@@ -500,17 +534,17 @@ void MainWindow::createActions() {
     actionMap.insert("manualplay", action);
 
     action = new QAction(tr("&Downloads"), this);
+    IconUtils::setIcon(action, "document-save");
     action->setStatusTip(tr("Show details about video downloads"));
     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_J));
     action->setCheckable(true);
-    action->setIcon(IconUtils::icon("document-save"));
     connect(action, SIGNAL(toggled(bool)), SLOT(toggleDownloads(bool)));
     actionMap.insert("downloads", action);
 
     action = new QAction(tr("&Download"), this);
+    IconUtils::setIcon(action, "document-save");
     action->setStatusTip(tr("Download the current video"));
     action->setShortcut(QKeySequence::Save);
-    action->setIcon(IconUtils::icon("document-save"));
     action->setEnabled(false);
     action->setVisible(false);
     action->setPriority(QAction::LowPriority);
@@ -531,23 +565,26 @@ void MainWindow::createActions() {
     action->setEnabled(false);
     connect(action, SIGNAL(triggered()), mediaView, SLOT(toggleSubscription()));
     actionMap.insert("subscribeChannel", action);
-    mediaView->updateSubscriptionAction(0, false);
+    mediaView->updateSubscriptionActionForVideo(0, false);
 
     QString shareTip = tr("Share the current video using %1");
 
-    action = new QAction(IconUtils::icon("twitter"), "&Twitter", this);
+    action = new QAction("&Twitter", this);
+    IconUtils::setIcon(action, "twitter");
     action->setStatusTip(shareTip.arg("Twitter"));
     action->setEnabled(false);
     actionMap.insert("twitter", action);
     connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaTwitter()));
 
-    action = new QAction(IconUtils::icon("facebook"), "&Facebook", this);
+    action = new QAction("&Facebook", this);
+    IconUtils::setIcon(action, "facebook");
     action->setStatusTip(shareTip.arg("Facebook"));
     action->setEnabled(false);
     actionMap.insert("facebook", action);
     connect(action, SIGNAL(triggered()), mediaView, SLOT(shareViaFacebook()));
 
-    action = new QAction(IconUtils::icon("email"), tr("&Email"), this);
+    action = new QAction(tr("&Email"), this);
+    IconUtils::setIcon(action, "email");
     action->setStatusTip(shareTip.arg(tr("Email")));
     action->setEnabled(false);
     actionMap.insert("email", action);
@@ -563,13 +600,14 @@ void MainWindow::createActions() {
     actionMap.insert("restore", action);
     connect(action, SIGNAL(triggered()), SLOT(restore()));
 
-    action = new QAction(IconUtils::icon("go-top"), tr("&Float on Top"), this);
+    action = new QAction(tr("&Float on Top"), this);
+    IconUtils::setIcon(action, "go-top");
     action->setCheckable(true);
     actionMap.insert("ontop", action);
     connect(action, SIGNAL(toggled(bool)), SLOT(floatOnTop(bool)));
 
-    action =
-            new QAction(IconUtils::icon("media-playback-stop"), tr("&Stop After This Video"), this);
+    action = new QAction(tr("&Stop After This Video"), this);
+    IconUtils::setIcon(action, "media-playback-stop");
     action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Escape));
     action->setCheckable(true);
     action->setEnabled(false);
@@ -595,7 +633,8 @@ void MainWindow::createActions() {
     action = new QAction(tr("More..."), this);
     actionMap.insert("moreRegion", action);
 
-    action = new QAction(IconUtils::icon("view-list"), tr("&Related Videos"), this);
+    action = new QAction(tr("&Related Videos"), this);
+    IconUtils::setIcon(action, "view-list");
     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
     action->setStatusTip(tr("Watch videos related to the current one"));
     action->setEnabled(false);
@@ -609,7 +648,8 @@ void MainWindow::createActions() {
     actionMap.insert("openInBrowser", action);
     connect(action, SIGNAL(triggered()), mediaView, SLOT(openInBrowser()));
 
-    action = new QAction(IconUtils::icon("safesearch"), tr("Restricted Mode"), this);
+    action = new QAction(tr("Restricted Mode"), this);
+    IconUtils::setIcon(action, "safesearch");
     action->setStatusTip(tr("Hide videos that may contain inappropriate content"));
     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_K));
     action->setCheckable(true);
@@ -619,7 +659,8 @@ void MainWindow::createActions() {
     connect(action, SIGNAL(triggered()), SLOT(toggleMenuVisibilityWithMessage()));
     actionMap.insert("toggleMenu", action);
 
-    action = new QAction(IconUtils::icon("view-more"), tr("Menu"), this);
+    action = new QAction(tr("Menu"), this);
+    IconUtils::setIcon(action, "open-menu");
     connect(action, SIGNAL(triggered()), SLOT(toggleToolbarMenu()));
     actionMap.insert("toolbarMenu", action);
 
@@ -634,11 +675,11 @@ void MainWindow::createActions() {
 #endif
 
     // common action properties
-    for (QAction *action : actionMap) {
+    for (QAction *action : qAsConst(actionMap)) {
         // add actions to the MainWindow so that they work
         // when the menu is hidden
         addAction(action);
-        IconUtils::setupAction(action);
+        MainWindow::instance()->setupAction(action);
     }
 }
 
@@ -709,8 +750,10 @@ void MainWindow::createMenus() {
     menuMap.insert("view", viewMenu);
     viewMenu->addAction(getAction("ontop"));
     viewMenu->addAction(compactViewAct);
-#ifndef APP_MAC
+    viewMenu->addSeparator();
     viewMenu->addAction(fullscreenAct);
+#ifndef APP_MAC
+    viewMenu->addSeparator();
     viewMenu->addAction(getAction("toggleMenu"));
 #endif
 
@@ -733,46 +776,16 @@ void MainWindow::createMenus() {
 #endif
 }
 
-void MainWindow::createToolBars() {
+void MainWindow::createToolBar() {
     // Create widgets
+    currentTimeLabel = new QLabel("00:00", this);
 
-    currentTimeLabel = new QLabel("00:00");
-    currentTimeLabel->setFont(FontUtils::small());
-
-#ifdef APP_PHONON_SEEK
-    seekSlider = new Phonon::SeekSlider();
-#if APP_LINUX
+    seekSlider = new SeekSlider(this);
+    seekSlider->setEnabled(false);
     seekSlider->setTracking(false);
-#else
-    seekSlider->setTracking(true);
-#endif
-    // Phonon freezes the application with streaming videos if
-    // tracking is set to true and the seek slider is dragged.
-    seekSlider->setIconVisible(false);
-    seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
-#else
-    slider = new SeekSlider(this);
-    slider->setEnabled(false);
-    slider->setTracking(false);
-    slider->setMaximum(1000);
-    slider->setOrientation(Qt::Horizontal);
-    slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
-#endif
-
-#ifdef APP_PHONON
-    volumeSlider = new Phonon::VolumeSlider();
-    volumeSlider->setMuteVisible(false);
-    // qDebug() << volumeSlider->children();
-    // status tip for the volume slider
-    QSlider *volumeQSlider = volumeSlider->findChild<QSlider *>();
-    if (volumeQSlider)
-        volumeQSlider->setStatusTip(
-                tr("Press %1 to raise the volume, %2 to lower it")
-                        .arg(volumeUpAct->shortcut().toString(QKeySequence::NativeText),
-                             volumeDownAct->shortcut().toString(QKeySequence::NativeText)));
-    // this makes the volume slider smaller
-    volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-#endif
+    seekSlider->setMaximum(1000);
+    volumeSlider = new SeekSlider(this);
+    volumeSlider->setValue(volumeSlider->maximum());
 
 #if defined(APP_MAC_SEARCHFIELD) && !defined(APP_MAC_QMACTOOLBAR)
     SearchWrapper *searchWrapper = new SearchWrapper(this);
@@ -781,13 +794,14 @@ void MainWindow::createToolBars() {
     toolbarSearch = new SearchLineEdit(this);
 #endif
     toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize() * 15);
+    toolbarSearch->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
     toolbarSearch->setSuggester(new YTSuggester(this));
     connect(toolbarSearch, SIGNAL(search(const QString &)), SLOT(search(const QString &)));
     connect(toolbarSearch, SIGNAL(suggestionAccepted(Suggestion *)),
             SLOT(suggestionAccepted(Suggestion *)));
     toolbarSearch->setStatusTip(searchFocusAct->statusTip());
 
-// Add widgets to toolbar
+    // Add widgets to toolbar
 
 #ifdef APP_MAC_QMACTOOLBAR
     currentTimeLabel->hide();
@@ -826,32 +840,45 @@ void MainWindow::createToolBars() {
 
     mainToolBar->addWidget(new Spacer());
 
+    currentTimeLabel->setFont(FontUtils::small());
+    currentTimeLabel->setMinimumWidth(currentTimeLabel->fontInfo().pixelSize() * 4);
+    currentTimeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
     mainToolBar->addWidget(currentTimeLabel);
-    mainToolBar->addWidget(new Spacer(this, currentTimeLabel->sizeHint().height() / 2));
-#ifdef APP_PHONON_SEEK
-    mainToolBar->addWidget(seekSlider);
-#else
-    mainToolBar->addWidget(slider);
+
+#ifdef APP_WIN
+    mainToolBar->addWidget(new Spacer(nullptr, 10));
 #endif
 
-    /*
-    mainToolBar->addWidget(new Spacer());
-    totalTime = new QLabel(mainToolBar);
-    totalTime->setFont(smallerFont);
-    mainToolBar->addWidget(totalTime);
-    */
+    seekSlider->setOrientation(Qt::Horizontal);
+    seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
+    seekSlider->setFocusPolicy(Qt::NoFocus);
+    mainToolBar->addWidget(seekSlider);
 
     mainToolBar->addWidget(new Spacer());
+
     mainToolBar->addAction(volumeMuteAct);
-#ifdef APP_LINUX
+#ifndef APP_MAC_QMACTOOLBAR
     QToolButton *volumeMuteButton =
             qobject_cast<QToolButton *>(mainToolBar->widgetForAction(volumeMuteAct));
-    volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16));
+    volumeMuteButton->setIconSize(QSize(16, 16));
+    auto fixVolumeMuteIconSize = [volumeMuteButton] {
+        volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16));
+    };
+    fixVolumeMuteIconSize();
+    volumeMuteButton->connect(volumeMuteAct, &QAction::changed, volumeMuteButton,
+                              fixVolumeMuteIconSize);
 #endif
 
-#ifdef APP_PHONON
+    volumeSlider->setStatusTip(
+            tr("Press %1 to raise the volume, %2 to lower it")
+                    .arg(volumeUpAct->shortcut().toString(QKeySequence::NativeText),
+                         volumeDownAct->shortcut().toString(QKeySequence::NativeText)));
+
+    volumeSlider->setOrientation(Qt::Horizontal);
+    // this makes the volume slider smaller
+    volumeSlider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    volumeSlider->setFocusPolicy(Qt::NoFocus);
     mainToolBar->addWidget(volumeSlider);
-#endif
 
     mainToolBar->addWidget(new Spacer());
 
@@ -888,9 +915,8 @@ void MainWindow::createStatusBar() {
         regionMenu->addAction(moreRegionsAction);
         connect(moreRegionsAction, SIGNAL(triggered()), SLOT(showRegionsView()));
         regionAction->setMenu(regionMenu);
-    } else {
-        connect(regionAction, SIGNAL(triggered()), SLOT(showRegionsView()));
     }
+    connect(regionAction, SIGNAL(triggered()), SLOT(showRegionsView()));
 
     /* Stupid code that generates the QRC items
     foreach(YTRegion r, YTRegions::list())
@@ -903,31 +929,39 @@ void MainWindow::createStatusBar() {
 
 void MainWindow::showStopAfterThisInStatusBar(bool show) {
     QAction *action = getAction("stopafterthis");
-    showActionInStatusBar(action, show);
+    showActionsInStatusBar({action}, show);
 }
 
-void MainWindow::showActionInStatusBar(QAction *action, bool show) {
+void MainWindow::showActionsInStatusBar(const QVector<QAction *> &actions, bool show) {
 #ifdef APP_EXTRA
     Extra::fadeInWidget(statusBar(), statusBar());
 #endif
-    if (show) {
-        if (statusToolBar->actions().contains(action)) return;
-        if (statusToolBar->actions().isEmpty()) {
-            statusToolBar->addAction(action);
+    for (auto action : actions) {
+        if (show) {
+            if (statusToolBar->actions().contains(action)) continue;
+            if (statusToolBar->actions().isEmpty()) {
+                statusToolBar->addAction(action);
+            } else {
+                statusToolBar->insertAction(statusToolBar->actions().at(0), action);
+            }
         } else {
-            statusToolBar->insertAction(statusToolBar->actions().at(0), action);
+            statusToolBar->removeAction(action);
         }
+    }
+
+    if (show) {
         if (statusBar()->isHidden() && !fullScreenActive) setStatusBarVisibility(true);
     } else {
-        statusToolBar->removeAction(action);
         if (statusBar()->isVisible() && !needStatusBar()) setStatusBarVisibility(false);
     }
 }
 
 void MainWindow::setStatusBarVisibility(bool show) {
-    statusBar()->setVisible(show);
-    if (views->currentWidget() == mediaView)
-        QTimer::singleShot(0, mediaView, SLOT(adjustWindowSize()));
+    if (statusBar()->isVisible() != show) {
+        statusBar()->setVisible(show);
+        if (views->currentWidget() == mediaView)
+            QTimer::singleShot(0, mediaView, SLOT(adjustWindowSize()));
+    }
 }
 
 void MainWindow::adjustStatusBarVisibility() {
@@ -952,17 +986,18 @@ void MainWindow::showToolbar() {
 
 void MainWindow::readSettings() {
     QSettings settings;
-    if (settings.contains("geometry")) {
-        restoreGeometry(settings.value("geometry").toByteArray());
+    QByteArray geometrySettings = settings.value("geometry").toByteArray();
+    if (!geometrySettings.isEmpty()) {
+        restoreGeometry(geometrySettings);
     } else {
-        const QRect desktopSize = qApp->desktop()->availableGeometry();
+        const QRect desktopSize = QGuiApplication::primaryScreen()->availableGeometry();
         int w = desktopSize.width() * .9;
         int h = qMin(w / 2, desktopSize.height());
         setGeometry(
                 QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, QSize(w, h), desktopSize));
     }
-    const VideoDefinition &firstDefinition = VideoDefinition::getDefinitions().at(0);
-    setDefinitionMode(settings.value("definition", firstDefinition.getName()).toString());
+    setDefinitionMode(settings.value("definition", YT3::instance().maxVideoDefinition().getName())
+                              .toString());
     getAction("manualplay")->setChecked(settings.value("manualplay", false).toBool());
     getAction("safeSearch")->setChecked(settings.value("safeSearch", false).toBool());
 #ifndef APP_MAC
@@ -975,7 +1010,7 @@ void MainWindow::writeSettings() {
 
     if (!isReallyFullScreen()) {
         settings.setValue("geometry", saveGeometry());
-        mediaView->saveSplitterState();
+        if (mediaView) mediaView->saveSplitterState();
     }
 
     settings.setValue("manualplay", getAction("manualplay")->isChecked());
@@ -988,15 +1023,19 @@ void MainWindow::writeSettings() {
 void MainWindow::goBack() {
     if (history.size() > 1) {
         history.pop();
-        QWidget *widget = history.pop();
-        showWidget(widget);
+        showView(history.pop());
     }
 }
 
-void MainWindow::showWidget(QWidget *widget, bool transition) {
-    Q_UNUSED(transition);
+void MainWindow::showView(View *view, bool transition) {
+    if (!history.isEmpty() && view == history.top()) {
+        qDebug() << "Attempting to show same view" << view;
+        return;
+    }
 
-    setUpdatesEnabled(false);
+#ifdef APP_MAC
+    if (transition && !history.isEmpty()) CompositeFader::go(this, this->grab());
+#endif
 
     if (compactViewAct->isChecked()) compactViewAct->toggle();
 
@@ -1004,70 +1043,38 @@ void MainWindow::showWidget(QWidget *widget, bool transition) {
     View *oldView = qobject_cast<View *>(views->currentWidget());
     if (oldView) {
         oldView->disappear();
-        views->currentWidget()->setEnabled(false);
+        oldView->setEnabled(false);
+        oldView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
     } else
-        qDebug() << "Cannot cast view";
+        qDebug() << "Cannot cast old view";
 
-    const bool isMediaView = widget == mediaView;
+    view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    view->setEnabled(true);
+    views->setCurrentWidget(view);
+    view->appear();
 
+    QString title = view->getTitle();
+    if (title.isEmpty())
+        title = Constants::NAME;
+    else
+        title += QLatin1String(" - ") + Constants::NAME;
+    setWindowTitle(title);
+
+    const bool isMediaView = view == mediaView;
     stopAct->setEnabled(isMediaView);
     compactViewAct->setEnabled(isMediaView);
-    toolbarSearch->setEnabled(widget == homeView || isMediaView || widget == downloadView);
-
-    aboutAct->setEnabled(widget != aboutView);
-    getAction("downloads")->setChecked(widget == downloadView);
-
-    QWidget *oldWidget = views->currentWidget();
-    if (oldWidget) oldWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
-
-    views->setCurrentWidget(widget);
-    widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-
-    // call show method on the new view
-    View *newView = qobject_cast<View *>(widget);
-    if (newView) {
-        widget->setEnabled(true);
-
-        QString title = newView->getTitle();
-        if (title.isEmpty())
-            title = Constants::NAME;
-        else
-            title += QLatin1String(" - ") + Constants::NAME;
-        setWindowTitle(title);
-
-        statusToolBar->setUpdatesEnabled(false);
-
-        // dynamic view actions
-        /* Not currently used by any view
-        foreach (QAction* action, viewActions)
-            showActionInStatusBar(action, false);
-        viewActions = newView->getViewActions();
-        foreach (QAction* action, viewActions)
-            showActionInStatusBar(action, true);
-        */
-
-        adjustStatusBarVisibility();
-        messageLabel->hide();
-
-        newView->appear();
-
-        statusToolBar->setUpdatesEnabled(true);
-
-        /*
-        QString desc = metadata.value("description").toString();
-        if (!desc.isEmpty()) showMessage(desc);
-        */
-    }
-
-    setUpdatesEnabled(true);
-
-#ifdef APP_MAC
-    // Workaround cursor bug on macOS
-    unsetCursor();
-    mac::uncloseWindow(winId());
-#endif
+    toolbarSearch->setEnabled(isMediaView);
+    aboutAct->setEnabled(view != aboutView);
+    getAction("downloads")->setChecked(view == downloadView);
+
+    // dynamic view actions
+    /* Not currently used by any view
+    showActionsInStatusBar(viewActions, false);
+    viewActions = newView->getViewActions();
+    showActionsInStatusBar(viewActions, true);
+    */
 
-    history.push(widget);
+    history.push(view);
     emit viewChanged();
 }
 
@@ -1076,7 +1083,7 @@ void MainWindow::about() {
         aboutView = new AboutView(this);
         views->addWidget(aboutView);
     }
-    showWidget(aboutView);
+    showView(aboutView);
 }
 
 void MainWindow::visitSite() {
@@ -1092,7 +1099,7 @@ void MainWindow::donate() {
 }
 
 void MainWindow::reportIssue() {
-    QUrl url("http://flavio.tordini.org/forums/forum/minitube-forums/minitube-troubleshooting");
+    QUrl url("https://flavio.tordini.org/forums/forum/minitube-forums/minitube-troubleshooting");
     QDesktopServices::openUrl(url);
 }
 
@@ -1104,6 +1111,9 @@ void MainWindow::quit() {
 #endif
     // do not save geometry when in full screen or in compact mode
     if (!fullScreenActive && !compactViewAct->isChecked()) {
+#ifdef APP_MAC
+        hideToolbar();
+#endif
         writeSettings();
     }
     // mediaView->stop();
@@ -1111,6 +1121,7 @@ void MainWindow::quit() {
     ChannelAggregator::instance()->stop();
     ChannelAggregator::instance()->cleanup();
     Database::shutdown();
+    HttpUtils::clearCaches();
     qApp->quit();
 }
 
@@ -1139,7 +1150,7 @@ void MainWindow::showEvent(QShowEvent *e) {
 bool MainWindow::confirmQuit() {
     if (DownloadManager::instance()->activeItems() > 0) {
         QMessageBox msgBox(this);
-        msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png"));
+        msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png", devicePixelRatioF()));
         msgBox.setText(
                 tr("Do you want to exit %1 with a download in progress?").arg(Constants::NAME));
         msgBox.setInformativeText(
@@ -1162,12 +1173,13 @@ bool MainWindow::confirmQuit() {
 }
 
 void MainWindow::showHome() {
-    showWidget(homeView);
+    showView(homeView);
     currentTimeLabel->clear();
+    seekSlider->setValue(0);
 }
 
 void MainWindow::showMedia(SearchParams *searchParams) {
-    showWidget(mediaView);
+    showView(mediaView);
     if (getAction("safeSearch")->isChecked())
         searchParams->setSafeSearch(SearchParams::Strict);
     else
@@ -1176,71 +1188,59 @@ void MainWindow::showMedia(SearchParams *searchParams) {
 }
 
 void MainWindow::showMedia(VideoSource *videoSource) {
-    showWidget(mediaView);
+    showView(mediaView);
     mediaView->setVideoSource(videoSource);
 }
 
-#ifdef APP_PHONON
-void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */) {
-    // qDebug() << "Phonon state: " << newState;
+void MainWindow::stateChanged(Media::State newState) {
+    qDebug() << newState;
+
+    seekSlider->setEnabled(newState != Media::StoppedState);
 
     switch (newState) {
-    case Phonon::ErrorState:
-        if (mediaObject->errorType() == Phonon::FatalError) {
-            // Do not display because we try to play incomplete video files and sometimes trigger
-            // this
-            // We retry automatically (in MediaView) so no need to show it
-            // showMessage(tr("Fatal error: %1").arg(mediaObject->errorString()));
-        } else {
-            showMessage(tr("Error: %1").arg(mediaObject->errorString()));
-        }
+    case Media::ErrorState:
+        showMessage(tr("Error: %1").arg(media->errorString()));
         break;
 
-    case Phonon::PlayingState:
+    case Media::PlayingState:
         pauseAct->setEnabled(true);
         pauseAct->setIcon(IconUtils::icon("media-playback-pause"));
         pauseAct->setText(tr("&Pause"));
         pauseAct->setStatusTip(tr("Pause playback") + " (" +
                                pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
-        // stopAct->setEnabled(true);
         break;
 
-    case Phonon::StoppedState:
+    case Media::StoppedState:
         pauseAct->setEnabled(false);
         pauseAct->setIcon(IconUtils::icon("media-playback-start"));
         pauseAct->setText(tr("&Play"));
         pauseAct->setStatusTip(tr("Resume playback") + " (" +
                                pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
-        // stopAct->setEnabled(false);
         break;
 
-    case Phonon::PausedState:
+    case Media::PausedState:
         pauseAct->setEnabled(true);
         pauseAct->setIcon(IconUtils::icon("media-playback-start"));
         pauseAct->setText(tr("&Play"));
         pauseAct->setStatusTip(tr("Resume playback") + " (" +
                                pauseAct->shortcut().toString(QKeySequence::NativeText) + ")");
-        // stopAct->setEnabled(true);
         break;
 
-    case Phonon::BufferingState:
+    case Media::BufferingState:
         pauseAct->setEnabled(false);
         pauseAct->setIcon(IconUtils::icon("content-loading"));
         pauseAct->setText(tr("&Loading..."));
         pauseAct->setStatusTip(QString());
         break;
 
-    case Phonon::LoadingState:
+    case Media::LoadingState:
         pauseAct->setEnabled(false);
         currentTimeLabel->clear();
-        // totalTime->clear();
-        // stopAct->setEnabled(true);
         break;
 
     default:;
     }
 }
-#endif
 
 void MainWindow::stop() {
     showHome();
@@ -1264,16 +1264,11 @@ void MainWindow::resizeEvent(QResizeEvent *e) {
 #endif
 #ifdef APP_MAC_QMACTOOLBAR
     int moreButtonWidth = 40;
-    toolbarSearch->move(width() - toolbarSearch->width() - moreButtonWidth - 7, -38);
+    toolbarSearch->move(width() - toolbarSearch->width() - moreButtonWidth - 7, -34);
 #endif
     hideMessage();
 }
 
-void MainWindow::moveEvent(QMoveEvent *e) {
-    Q_UNUSED(e);
-    hideMessage();
-}
-
 void MainWindow::enterEvent(QEvent *e) {
     Q_UNUSED(e);
 #ifdef APP_MAC
@@ -1320,7 +1315,7 @@ void MainWindow::toggleFullscreen() {
 #endif
 
     } else {
-// Exit full screen
+        // Exit full screen
 
 #ifdef APP_MAC
         MacSupport::exitFullScreen(this, views);
@@ -1387,8 +1382,8 @@ void MainWindow::updateUIForFullscreen() {
     if (fullScreenActive) {
         stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_MediaStop));
     } else {
-        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape)
-                                                    << QKeySequence(Qt::Key_MediaStop));
+        stopAct->setShortcuts(QList<QKeySequence>()
+                              << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
     }
 
 #ifdef Q_OS_MAC
@@ -1418,23 +1413,29 @@ bool MainWindow::isReallyFullScreen() {
 }
 
 void MainWindow::missingKeyWarning() {
+    static bool shown = false;
+    if (shown) return;
+    shown = true;
     QMessageBox msgBox(this);
-    msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png"));
+    msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png", devicePixelRatioF()));
     msgBox.setText(QString("%1 was built without a Google API key.").arg(Constants::NAME));
     msgBox.setInformativeText(QString("It won't work unless you enter one."
                                       "<p>In alternative you can get %1 from the developer site.")
                                       .arg(Constants::NAME));
     msgBox.setModal(true);
     msgBox.setWindowModality(Qt::WindowModal);
+    msgBox.addButton(QMessageBox::Close);
     QPushButton *enterKeyButton =
             msgBox.addButton(QString("Enter API key..."), QMessageBox::AcceptRole);
     QPushButton *devButton = msgBox.addButton(QString("Get from %1").arg(Constants::WEBSITE),
                                               QMessageBox::AcceptRole);
     QPushButton *helpButton = msgBox.addButton(QMessageBox::Help);
+
     msgBox.exec();
+
     if (msgBox.clickedButton() == helpButton) {
-        QDesktopServices::openUrl(QUrl(
-                "https://github.com/flaviotordini/minitube/blob/master/README.md#google-api-key"));
+        QDesktopServices::openUrl(QUrl("https://github.com/flaviotordini/minitube/blob/master/"
+                                       "README.md#google-api-key"));
     } else if (msgBox.clickedButton() == enterKeyButton) {
         bool ok;
         QString text = QInputDialog::getText(this, QString(), "Google API key:", QLineEdit::Normal,
@@ -1447,9 +1448,12 @@ void MainWindow::missingKeyWarning() {
     } else if (msgBox.clickedButton() == devButton) {
         QDesktopServices::openUrl(QUrl(Constants::WEBSITE));
     }
+    shown = false;
 }
 
 void MainWindow::compactView(bool enable) {
+    setUpdatesEnabled(false);
+
     compactModeActive = enable;
 
     static QList<QKeySequence> compactShortcuts;
@@ -1468,7 +1472,7 @@ void MainWindow::compactView(bool enable) {
         if (settings.contains(key))
             restoreGeometry(settings.value(key).toByteArray());
         else
-            resize(320, 180);
+            resize(480, 270);
 
 #ifdef APP_MAC_QMACTOOLBAR
         mac::showToolBar(winId(), !enable);
@@ -1492,12 +1496,14 @@ void MainWindow::compactView(bool enable) {
         mediaView->setFocus();
 
     } else {
+        settings.setValue(key, saveGeometry());
+
         // unset minimum size
         setMinimumSize(0, 0);
+
 #ifdef Q_OS_MAC
         mac::SetupFullScreenWindow(winId());
 #endif
-        settings.setValue(key, saveGeometry());
 #ifdef APP_MAC_QMACTOOLBAR
         mac::showToolBar(winId(), !enable);
 #else
@@ -1525,6 +1531,8 @@ void MainWindow::compactView(bool enable) {
         menuBar()->setVisible(menuVisibleBeforeCompactMode);
     }
 #endif
+
+    setUpdatesEnabled(true);
 }
 
 void MainWindow::toggleToolbarMenu() {
@@ -1540,72 +1548,79 @@ void MainWindow::searchFocus() {
     toolbarSearch->setFocus();
 }
 
-#ifdef APP_PHONON
-void MainWindow::initPhonon() {
-    // Phonon initialization
-    if (mediaObject) delete mediaObject;
-    if (audioOutput) delete audioOutput;
-    mediaObject = new Phonon::MediaObject(this);
-    mediaObject->setTickInterval(100);
-    connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
-            SLOT(stateChanged(Phonon::State, Phonon::State)));
-    connect(mediaObject, SIGNAL(tick(qint64)), SLOT(tick(qint64)));
-    connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), SLOT(totalTimeChanged(qint64)));
-
-    audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this);
-    connect(audioOutput, SIGNAL(mutedChanged(bool)), SLOT(volumeMutedChanged(bool)));
-    Phonon::createPath(mediaObject, audioOutput);
-    volumeSlider->setAudioOutput(audioOutput);
-
-#ifdef APP_PHONON_SEEK
-    seekSlider->setMediaObject(mediaObject);
+void MainWindow::initMedia() {
+#ifdef MEDIA_QTAV
+    qFatal("QtAV has a showstopper bug. Audio stops randomly. See bug "
+           "https://github.com/wang-bin/QtAV/issues/1184");
+    media = new MediaQtAV(this);
+#elif defined MEDIA_MPV
+    media = new MediaMPV();
+#else
+    qFatal("No media backend defined");
 #endif
+    media->init();
+    media->setUserAgent(HttpUtils::stealthUserAgent());
 
     QSettings settings;
-    audioOutput->setVolume(settings.value("volume", 1.).toReal());
-    // audioOutput->setMuted(settings.value("volumeMute").toBool());
-
-    mediaObject->stop();
+    qreal volume = settings.value("volume", 1.).toReal();
+    media->setVolume(volume);
+
+    connect(media, &Media::error, this, &MainWindow::handleError);
+    connect(media, &Media::stateChanged, this, &MainWindow::stateChanged);
+    connect(media, &Media::positionChanged, this, &MainWindow::tick);
+
+    connect(seekSlider, &QSlider::sliderMoved, this, [this](int value) {
+        // value : maxValue = posit ion : duration
+        qint64 ms = (value * media->duration()) / seekSlider->maximum();
+        qDebug() << "Seeking to" << ms;
+        media->seek(ms);
+        if (media->state() == Media::PausedState) media->play();
+    });
+    connect(seekSlider, &QSlider::sliderPressed, this, [this]() {
+        // value : maxValue = position : duration
+        qint64 ms = (seekSlider->value() * media->duration()) / seekSlider->maximum();
+        media->seek(ms);
+        if (media->state() == Media::PausedState) media->play();
+    });
+    connect(media, &Media::started, this, [this]() { seekSlider->setValue(0); });
+
+    connect(media, &Media::volumeChanged, this, &MainWindow::volumeChanged);
+    connect(media, &Media::volumeMutedChanged, this, &MainWindow::volumeMutedChanged);
+    connect(volumeSlider, &QSlider::sliderMoved, this, [this](int value) {
+        qreal volume = (qreal)value / volumeSlider->maximum();
+        media->setVolume(volume);
+    });
+    connect(volumeSlider, &QSlider::sliderPressed, this, [this]() {
+        qreal volume = (qreal)volumeSlider->value() / volumeSlider->maximum();
+        media->setVolume(volume);
+    });
+
+    mediaView->setMedia(media);
 }
-#endif
 
 void MainWindow::tick(qint64 time) {
+#ifdef APP_MAC
+    bool isDown = seekSlider->property("down").isValid();
+#else
+    bool isDown = seekSlider->isSliderDown();
+#endif
+    if (!isDown && media->state() == Media::PlayingState) {
+        // value : maxValue = position : duration
+        qint64 duration = media->duration();
+        if (duration <= 0) return;
+        int value = (seekSlider->maximum() * media->position()) / duration;
+        seekSlider->setValue(value);
+    }
+
     const QString s = formatTime(time);
     if (s != currentTimeLabel->text()) {
         currentTimeLabel->setText(s);
         emit currentTimeChanged(s);
-    }
-
-// remaining time
-#ifdef APP_PHONON
-    const qint64 remainingTime = mediaObject->remainingTime();
-    currentTimeLabel->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime)));
-
-#ifndef APP_PHONON_SEEK
-    const qint64 totalTime = mediaObject->totalTime();
-    slider->blockSignals(true);
-    // qWarning() << totalTime << time << time * 100 / totalTime;
-    if (totalTime > 0 && time > 0 && !slider->isSliderDown() &&
-        mediaObject->state() == Phonon::PlayingState)
-        slider->setValue(time * slider->maximum() / totalTime);
-    slider->blockSignals(false);
-#endif
 
-#endif
-}
-
-void MainWindow::totalTimeChanged(qint64 time) {
-    if (time <= 0) {
-        // totalTime->clear();
-        return;
+        // remaining time
+        const qint64 remainingTime = media->remainingTime();
+        currentTimeLabel->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime)));
     }
-    // totalTime->setText(formatTime(time));
-
-    /*
-    slider->blockSignals(true);
-    slider->setMaximum(time/1000);
-    slider->blockSignals(false);
-    */
 }
 
 QString MainWindow::formatTime(qint64 duration) {
@@ -1621,31 +1636,31 @@ QString MainWindow::formatTime(qint64 duration) {
 }
 
 void MainWindow::volumeUp() {
-#ifdef APP_PHONON
-    qreal newVolume = volumeSlider->audioOutput()->volume() + .1;
-    if (newVolume > volumeSlider->maximumVolume()) newVolume = volumeSlider->maximumVolume();
-    volumeSlider->audioOutput()->setVolume(newVolume);
-#endif
+    qreal newVolume = media->volume() + .1;
+    if (newVolume > 1.) newVolume = 1.;
+    media->setVolume(newVolume);
 }
 
 void MainWindow::volumeDown() {
-#ifdef APP_PHONON
-    qreal newVolume = volumeSlider->audioOutput()->volume() - .1;
-    if (newVolume < 0.) newVolume = 0.;
-    volumeSlider->audioOutput()->setVolume(newVolume);
-#endif
+    qreal newVolume = media->volume() - .1;
+    if (newVolume < 0) newVolume = 0;
+    media->setVolume(newVolume);
 }
 
-void MainWindow::volumeMute() {
-#ifdef APP_PHONON
-    bool muted = volumeSlider->audioOutput()->isMuted();
-    volumeSlider->audioOutput()->setMuted(!muted);
-    qApp->processEvents();
-    if (muted && volumeSlider->audioOutput()->volume() == 0.) {
-        volumeSlider->audioOutput()->setVolume(volumeSlider->maximumVolume());
-    }
-    qDebug() << volumeSlider->audioOutput()->isMuted() << volumeSlider->audioOutput()->volume();
-#endif
+void MainWindow::toggleVolumeMute() {
+    bool muted = media->volumeMuted();
+    media->setVolumeMuted(!muted);
+}
+
+void MainWindow::volumeChanged(qreal newVolume) {
+    // automatically unmute when volume changes
+    if (media->volumeMuted()) media->setVolumeMuted(false);
+    showMessage(tr("Volume at %1%").arg((int)(newVolume * 100)));
+    // newVolume : 1.0 = x : 1000
+    int value = newVolume * volumeSlider->maximum();
+    volumeSlider->blockSignals(true);
+    volumeSlider->setValue(value);
+    volumeSlider->blockSignals(false);
 }
 
 void MainWindow::volumeMutedChanged(bool muted) {
@@ -1656,11 +1671,6 @@ void MainWindow::volumeMutedChanged(bool muted) {
         volumeMuteAct->setIcon(IconUtils::icon("audio-volume-high"));
         showMessage(tr("Volume is unmuted"));
     }
-#ifdef APP_LINUX
-    QToolButton *volumeMuteButton =
-            qobject_cast<QToolButton *>(mainToolBar->widgetForAction(volumeMuteAct));
-    volumeMuteButton->setIcon(volumeMuteButton->icon().pixmap(16));
-#endif
 }
 
 void MainWindow::setDefinitionMode(const QString &definitionName) {
@@ -1670,18 +1680,15 @@ void MainWindow::setDefinitionMode(const QString &definitionName) {
             tr("Maximum video definition set to %1").arg(definitionAct->text()) + " (" +
             definitionAct->shortcut().toString(QKeySequence::NativeText) + ")");
     showMessage(definitionAct->statusTip());
-    QSettings settings;
-    settings.setValue("definition", definitionName);
+    YT3::instance().setMaxVideoDefinition(definitionName);
+    if (views->currentWidget() == mediaView) {
+        mediaView->reloadCurrentVideo();
+    }
 }
 
 void MainWindow::toggleDefinitionMode() {
-    const QString definitionName = QSettings().value("definition").toString();
     const QVector<VideoDefinition> &definitions = VideoDefinition::getDefinitions();
-    const VideoDefinition &currentDefinition = VideoDefinition::forName(definitionName);
-    if (currentDefinition.isEmpty()) {
-        setDefinitionMode(definitions.at(0).getName());
-        return;
-    }
+    const VideoDefinition &currentDefinition = YT3::instance().maxVideoDefinition();
 
     int index = definitions.indexOf(currentDefinition);
     if (index != definitions.size() - 1) {
@@ -1689,7 +1696,6 @@ void MainWindow::toggleDefinitionMode() {
     } else {
         index = 0;
     }
-    // TODO: pass a VideoDefinition instead of QString.
     setDefinitionMode(definitions.at(index).getName());
 }
 
@@ -1712,7 +1718,7 @@ void MainWindow::setManualPlay(bool enabled) {
     if (views->currentWidget() == homeView &&
         homeView->currentWidget() == homeView->getSearchView())
         return;
-    showActionInStatusBar(getAction("manualplay"), enabled);
+    showActionsInStatusBar({getAction("manualplay")}, enabled);
 }
 
 void MainWindow::updateDownloadMessage(const QString &message) {
@@ -1733,8 +1739,8 @@ void MainWindow::toggleDownloads(bool show) {
     } else {
         getAction("downloads")
                 ->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::Key_J));
-        stopAct->setShortcuts(QList<QKeySequence>() << QKeySequence(Qt::Key_Escape)
-                                                    << QKeySequence(Qt::Key_MediaStop));
+        stopAct->setShortcuts(QList<QKeySequence>()
+                              << QKeySequence(Qt::Key_Escape) << QKeySequence(Qt::Key_MediaStop));
     }
 
     if (!downloadView) {
@@ -1742,7 +1748,7 @@ void MainWindow::toggleDownloads(bool show) {
         views->addWidget(downloadView);
     }
     if (show)
-        showWidget(downloadView);
+        showView(downloadView);
     else
         goBack();
 }
@@ -1796,36 +1802,27 @@ void MainWindow::checkForUpdate() {
     if (secondsSinceLastCheck < 86400) return;
 
     // check it out
-    if (updateChecker) delete updateChecker;
-    updateChecker = new UpdateChecker();
-    connect(updateChecker, SIGNAL(newVersion(QString)), this, SLOT(gotNewVersion(QString)));
+    UpdateChecker *updateChecker = new UpdateChecker();
+    connect(updateChecker, &UpdateChecker::newVersion, this,
+            [this, updateChecker](const QString &version) {
+                updateChecker->deleteLater();
+                QSettings settings;
+                QString checkedVersion = settings.value("checkedVersion").toString();
+                if (checkedVersion == version) return;
+#ifdef APP_SIMPLEUPDATE
+                simpleUpdateDialog(version);
+#elif defined(APP_EXTRA) && !defined(APP_MAC)
+                UpdateDialog *dialog = new UpdateDialog(version, this);
+                dialog->show();
+#endif
+            });
     updateChecker->checkForUpdate();
     settings.setValue(updateCheckKey, unixTime);
 }
 
-void MainWindow::gotNewVersion(const QString &version) {
-    if (updateChecker) {
-        delete updateChecker;
-        updateChecker = 0;
-    }
-
-    QSettings settings;
-    QString checkedVersion = settings.value("checkedVersion").toString();
-    if (checkedVersion == version) return;
-
-#ifdef APP_EXTRA
-#ifndef APP_MAC
-    UpdateDialog *dialog = new UpdateDialog(version, this);
-    dialog->show();
-#endif
-#else
-    simpleUpdateDialog(version);
-#endif
-}
-
 void MainWindow::simpleUpdateDialog(const QString &version) {
     QMessageBox msgBox(this);
-    msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png"));
+    msgBox.setIconPixmap(IconUtils::pixmap(":/images/64x64/app.png", devicePixelRatioF()));
     msgBox.setText(tr("%1 version %2 is now available.").arg(Constants::NAME, version));
     msgBox.setModal(true);
     msgBox.setWindowModality(Qt::WindowModal);
@@ -1852,7 +1849,7 @@ void MainWindow::adjustMessageLabelPosition() {
 }
 
 void MainWindow::floatOnTop(bool onTop, bool showAction) {
-    if (showAction) showActionInStatusBar(getAction("ontop"), onTop);
+    if (showAction) showActionsInStatusBar({getAction("ontop")}, onTop);
 #ifdef APP_MAC
     mac::floatOnTop(winId(), onTop);
 #else
@@ -1946,6 +1943,18 @@ void MainWindow::printHelp() {
     std::cout << msg.toLocal8Bit().data();
 }
 
+void MainWindow::setupAction(QAction *action) {
+    // never autorepeat.
+    // unexperienced users tend to keep keys pressed for a "long" time
+    action->setAutoRepeat(false);
+
+    // show keyboard shortcuts in the status bar
+    if (!action->shortcut().isEmpty())
+        action->setStatusTip(action->statusTip() + QLatin1String(" (") +
+                             action->shortcut().toString(QKeySequence::NativeText) +
+                             QLatin1String(")"));
+}
+
 QAction *MainWindow::getAction(const char *name) {
     return actionMap.value(QByteArray::fromRawData(name, strlen(name)));
 }
@@ -1965,13 +1974,13 @@ void MainWindow::showMessage(const QString &message) {
 #endif
     if (statusBar()->isVisible())
         statusBar()->showMessage(message, 60000);
-    else {
+    else if (isActiveWindow()) {
         messageLabel->setText(message);
         QSize size = messageLabel->sizeHint();
-        // round width to nearest 10 to avoid flicker with fast changing messages (e.g. volume
+        // round width to avoid flicker with fast changing messages (e.g. volume
         // changes)
-        int w = size.width();
-        const int multiple = 10;
+        int w = size.width() + 10;
+        const int multiple = 15;
         w = w + multiple / 2;
         w -= w % multiple;
         size.setWidth(w);
@@ -1991,11 +2000,16 @@ void MainWindow::hideMessage() {
     }
 }
 
+void MainWindow::handleError(const QString &message) {
+    qWarning() << message;
+    showMessage(message);
+}
+
 #ifdef APP_ACTIVATION
 void MainWindow::showActivationView() {
-    QWidget *activationView = ActivationView::instance();
+    View *activationView = ActivationView::instance();
     views->addWidget(activationView);
-    if (views->currentWidget() != activationView) showWidget(activationView);
+    if (views->currentWidget() != activationView) showView(activationView);
 }
 #endif
 
@@ -2006,5 +2020,5 @@ void MainWindow::showRegionsView() {
                 SLOT(load()));
         views->addWidget(regionsView);
     }
-    showWidget(regionsView);
+    showView(regionsView);
 }
index e377a2fe3fd5bd8bec8af55161382a42c8a5bd77..e7d80b67f2e5857dc8028347c69a4dfbe1a6ba3c 100644 (file)
@@ -22,56 +22,47 @@ $END_LICENSE */
 #define MAINWINDOW_H
 
 #include <QtWidgets>
-#ifdef APP_PHONON
-#include <phonon/audiooutput.h>
-#include <phonon/volumeslider.h>
-#include <phonon/mediaobject.h>
-#include <phonon/seekslider.h>
-#endif
 
+#include "media.h"
+
+class View;
 class HomeView;
 class MediaView;
 class DownloadView;
 
 class SearchLineEdit;
-class UpdateChecker;
 class SearchParams;
 class VideoSource;
 class Suggestion;
 class ToolbarMenu;
 
 class MainWindow : public QMainWindow {
-
     Q_OBJECT
 
 public:
-    static MainWindowinstance();
+    static MainWindow *instance();
     MainWindow();
-#ifdef APP_PHONON_SEEK
-    Phonon::SeekSlider* getSeekSlider() { return seekSlider; }
-#else
-    QSlider* getSlider() { return slider; }
-#endif
-#ifdef APP_PHONON
-    Phonon::AudioOutput* getAudioOutput() { return audioOutput; }
-    Phonon::VolumeSlider *getVolumeSlider() { return volumeSlider; }
-#endif
+
+    QSlider *getSeekSlider() { return seekSlider; }
+    QSlider *getVolumeSlider() { return volumeSlider; }
+
     QLabel *getCurrentTimeLabel() { return currentTimeLabel; }
     void readSettings();
     void writeSettings();
     static void printHelp();
     QStackedWidget *getViews() { return views; }
-    MediaViewgetMediaView() { return mediaView; }
-    HomeViewgetHomeView() { return homeView; }
-    QActiongetRegionAction() { return regionAction; }
+    MediaView *getMediaView() { return mediaView; }
+    HomeView *getHomeView() { return homeView; }
+    QAction *getRegionAction() { return regionAction; }
     SearchLineEdit *getToolbarSearch() { return toolbarSearch; }
 
+    void setupAction(QAction *action);
     QAction *getAction(const char *name);
     void addNamedAction(const QByteArray &name, QAction *action);
 
     QMenu *getMenu(const char *name);
 
-    void showActionInStatusBar(QAction*, bool show);
+    void showActionsInStatusBar(const QVector<QAction *> &actions, bool show);
     void setStatusBarVisibility(bool show);
     void adjustStatusBarVisibility();
 
@@ -96,10 +87,12 @@ public slots:
     void goBack();
     void showMessage(const QString &message);
     void hideMessage();
+    void handleError(const QString &message);
     bool isReallyFullScreen();
     bool isCompact() { return compactModeActive; }
     void missingKeyWarning();
     void visitSite();
+    void setDefinitionMode(const QString &definitionName);
 
 signals:
     void currentTimeChanged(const QString &s);
@@ -113,14 +106,12 @@ protected:
     void dragEnterEvent(QDragEnterEvent *e);
     void dropEvent(QDropEvent *e);
     void resizeEvent(QResizeEvent *e);
-    void moveEvent(QMoveEvent *e);
     void leaveEvent(QEvent *e);
     void enterEvent(QEvent *e);
 
 private slots:
     void lazyInit();
     void checkForUpdate();
-    void gotNewVersion(const QString &version);
     void donate();
     void reportIssue();
     void about();
@@ -128,19 +119,18 @@ private slots:
     void updateUIForFullscreen();
     void compactView(bool enable);
     void stop();
-#ifdef APP_PHONON
-    void stateChanged(Phonon::State newState, Phonon::State oldState);
-#endif
     void searchFocus();
-    void tick(qint64 time);
-    void totalTimeChanged(qint64 time);
-    void setDefinitionMode(const QString &definitionName);
     void toggleDefinitionMode();
     void clearRecentKeywords();
 
+    // media
+    void stateChanged(Media::State state);
+    void tick(qint64 time);
+
     void volumeUp();
     void volumeDown();
-    void volumeMute();
+    void toggleVolumeMute();
+    void volumeChanged(qreal newVolume);
     void volumeMutedChanged(bool muted);
 
     void updateDownloadMessage(const QString &);
@@ -161,36 +151,31 @@ private slots:
 #endif
 
 private:
-#ifdef APP_PHONON
-    void initPhonon();
-#endif
+    void initMedia();
     void createActions();
     void createMenus();
-    void createToolBars();
+    void createToolBar();
     void createStatusBar();
-    void showWidget(QWidget*, bool transition = true);
+    void showView(View *view, bool transition = false);
     static QString formatTime(qint64 duration);
     bool confirmQuit();
     void simpleUpdateDialog(const QString &version);
     bool needStatusBar();
     void adjustMessageLabelPosition();
 
-    UpdateChecker *updateChecker;
-
-    QHash<QByteArray, QAction*> actionMap;
-    QHash<QByteArray, QMenu*> menuMap;
+    QHash<QByteArray, QAction *> actionMap;
+    QHash<QByteArray, QMenu *> menuMap;
 
     // view mechanism
     QStackedWidget *views;
-    QStack<QWidget*> history;
-    // QVector<QAction*> viewActions;
+    QStack<View *> history;
 
     // view widgets
     HomeView *homeView;
     MediaView *mediaView;
-    QWidget *aboutView;
-    QWidget *downloadView;
-    QWidget *regionsView;
+    View *aboutView;
+    View *downloadView;
+    View *regionsView;
 
     // actions
     QAction *backAct;
@@ -233,18 +218,8 @@ private:
     SearchLineEdit *toolbarSearch;
     QToolBar *statusToolBar;
     QAction *regionAction;
-
-    // phonon
-#ifdef APP_PHONON
-#ifdef APP_PHONON_SEEK
-    Phonon::SeekSlider *seekSlider;
-#else
-    QSlider *slider;
-#endif
-    Phonon::VolumeSlider *volumeSlider;
-    Phonon::MediaObject *mediaObject;
-    Phonon::AudioOutput *audioOutput;
-#endif
+    QSlider *seekSlider;
+    QSlider *volumeSlider;
     QLabel *currentTimeLabel;
 
     bool fullScreenActive;
@@ -260,6 +235,8 @@ private:
 
     ToolbarMenu *toolbarMenu;
     QToolButton *toolbarMenuButton;
+
+    Media *media;
 };
 
 #endif
index 286d56f226c9b3f3b622f06b0cedef0956c31b84..e6343ab9b2c498507ff3973611047a040f157c1b 100644 (file)
@@ -20,7 +20,6 @@ $END_LICENSE */
 
 #include "mediaview.h"
 #include "constants.h"
-#include "downloaditem.h"
 #include "downloadmanager.h"
 #include "http.h"
 #include "loadingwidget.h"
@@ -32,7 +31,7 @@ $END_LICENSE */
 #include "sidebarheader.h"
 #include "sidebarwidget.h"
 #include "temporary.h"
-#include "videoareawidget.h"
+#include "videoarea.h"
 #ifdef APP_ACTIVATION
 #include "activation.h"
 #endif
@@ -51,6 +50,7 @@ $END_LICENSE */
 #endif
 #include "datautils.h"
 #include "idle.h"
+#include "videodefinition.h"
 
 MediaView *MediaView::instance() {
     static MediaView *i = new MediaView();
@@ -58,10 +58,10 @@ MediaView *MediaView::instance() {
 }
 
 MediaView::MediaView(QWidget *parent)
-    : View(parent), stopped(false), downloadItem(0)
+    : View(parent), splitter(nullptr), stopped(false)
 #ifdef APP_SNAPSHOT
       ,
-      snapshotSettings(0)
+      snapshotSettings(nullptr)
 #endif
       ,
       pauseTime(0) {
@@ -79,10 +79,11 @@ void MediaView::initialize() {
     playlistView = new PlaylistView();
     playlistView->setParent(this);
     connect(playlistView, SIGNAL(activated(const QModelIndex &)),
-            SLOT(itemActivated(const QModelIndex &)));
+            SLOT(onItemActivated(const QModelIndex &)));
 
     playlistModel = new PlaylistModel();
-    connect(playlistModel, SIGNAL(activeRowChanged(int)), SLOT(activeRowChanged(int)));
+    connect(playlistModel, &PlaylistModel::activeVideoChanged, this,
+            &MediaView::activeVideoChanged);
     // needed to restore the selection after dragndrop
     connect(playlistModel, SIGNAL(needSelectionFor(QVector<Video *>)),
             SLOT(selectVideos(QVector<Video *>)));
@@ -92,22 +93,18 @@ void MediaView::initialize() {
             SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
             SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
 
-    connect(playlistView, SIGNAL(authorPushed(QModelIndex)), SLOT(authorPushed(QModelIndex)));
+    connect(playlistView, SIGNAL(authorPushed(QModelIndex)), SLOT(onAuthorPushed(QModelIndex)));
 
     sidebar = new SidebarWidget(this);
     sidebar->setPlaylist(playlistView);
+    sidebar->setMaximumWidth(playlistView->minimumWidth() * 3);
     connect(sidebar->getRefineSearchWidget(), SIGNAL(searchRefined()), SLOT(searchAgain()));
     connect(playlistModel, SIGNAL(haveSuggestions(const QStringList &)), sidebar,
             SLOT(showSuggestions(const QStringList &)));
     connect(sidebar, SIGNAL(suggestionAccepted(QString)), mainWindow, SLOT(search(QString)));
     splitter->addWidget(sidebar);
 
-    videoAreaWidget = new VideoAreaWidget(this);
-
-#ifdef APP_PHONON
-    videoWidget = new Phonon::VideoWidget(this);
-    videoAreaWidget->setVideoWidget(videoWidget);
-#endif
+    videoAreaWidget = new VideoArea(this);
     videoAreaWidget->setListModel(playlistModel);
 
     loadingWidget = new LoadingWidget(this);
@@ -156,24 +153,19 @@ void MediaView::initialize() {
     for (auto *name : videoActionNames) {
         currentVideoActions.append(mainWindow->getAction(name));
     }
-
-#ifndef APP_PHONON_SEEK
-    QSlider *slider = mainWindow->getSlider();
-    connect(slider, SIGNAL(valueChanged(int)), SLOT(sliderMoved(int)));
-#endif
 }
 
-#ifdef APP_PHONON
-void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
-    this->mediaObject = mediaObject;
-    Phonon::createPath(mediaObject, videoWidget);
-    connect(mediaObject, SIGNAL(finished()), SLOT(playbackFinished()));
-    connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
-            SLOT(stateChanged(Phonon::State, Phonon::State)));
-    connect(mediaObject, SIGNAL(aboutToFinish()), SLOT(aboutToFinish()));
-    connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int)));
+void MediaView::setMedia(Media *media) {
+    this->media = media;
+
+    videoWidget = media->videoWidget();
+    videoAreaWidget->setVideoWidget(videoWidget);
+
+    connect(media, &Media::finished, this, &MediaView::onPlaybackFinished);
+    connect(media, &Media::stateChanged, this, &MediaView::mediaStateChanged);
+    connect(media, &Media::aboutToFinish, this, &MediaView::onAboutToFinish);
+    connect(media, &Media::bufferStatus, loadingWidget, &LoadingWidget::bufferStatus);
 }
-#endif
 
 SearchParams *MediaView::getSearchParams() {
     VideoSource *videoSource = playlistModel->getVideoSource();
@@ -181,7 +173,7 @@ SearchParams *MediaView::getSearchParams() {
         YTSearch *search = qobject_cast<YTSearch *>(videoSource);
         return search->getSearchParams();
     }
-    return 0;
+    return nullptr;
 }
 
 void MediaView::search(SearchParams *searchParams) {
@@ -212,6 +204,13 @@ void MediaView::setVideoSource(VideoSource *videoSource, bool addToHistory, bool
 
     // qDebug() << "Adding VideoSource" << videoSource->getName() << videoSource;
 
+    YTSearch * ytSearch = qobject_cast<YTSearch *>(videoSource);
+    if (nullptr != ytSearch) {
+        if (!ytSearch->getSearchParams()->channelId().isEmpty()) {
+            updateSubscriptionActionForChannel(ytSearch->getSearchParams()->channelId());
+        }
+    }
+
     if (addToHistory) {
         int currentIndex = getHistoryIndex();
         if (currentIndex >= 0 && currentIndex < history.size() - 1) {
@@ -219,7 +218,7 @@ void MediaView::setVideoSource(VideoSource *videoSource, bool addToHistory, bool
                 VideoSource *vs = history.takeLast();
                 if (!vs->parent()) {
                     qDebug() << "Deleting VideoSource" << vs->getName() << vs;
-                    delete vs;
+                    vs->deleteLater();
                 }
             }
         }
@@ -233,9 +232,11 @@ void MediaView::setVideoSource(VideoSource *videoSource, bool addToHistory, bool
 
     playlistModel->setVideoSource(videoSource);
 
-    QSettings settings;
-    if (settings.value("manualplay", false).toBool()) {
-        videoAreaWidget->showPickMessage();
+    if (media->state() == Media::StoppedState) {
+        QSettings settings;
+        if (settings.value("manualplay", false).toBool()) {
+            videoAreaWidget->showPickMessage();
+        }
     }
 
     sidebar->showPlaylist();
@@ -301,54 +302,48 @@ void MediaView::disappear() {
 
 void MediaView::handleError(const QString &message) {
     qWarning() << __PRETTY_FUNCTION__ << message;
-#ifdef APP_PHONON_SEEK
-    mediaObject->play();
-#else
-    QTimer::singleShot(500, this, SLOT(startPlaying()));
+#ifndef QT_NO_DEBUG_OUTPUT
+    MainWindow::instance()->showMessage(message);
 #endif
 }
 
-#ifdef APP_PHONON
-void MediaView::stateChanged(Phonon::State newState, Phonon::State oldState) {
-    if (pauseTime > 0 && (newState == Phonon::PlayingState || newState == Phonon::BufferingState)) {
-        mediaObject->seek(pauseTime);
+void MediaView::mediaStateChanged(Media::State state) {
+    if (pauseTime > 0 && (state == Media::PlayingState || state == Media::BufferingState)) {
+        qDebug() << "Seeking to" << pauseTime;
+        media->seek(pauseTime);
         pauseTime = 0;
     }
-    if (newState == Phonon::PlayingState) {
+    if (state == Media::PlayingState) {
         videoAreaWidget->showVideo();
-    } else if (newState == Phonon::ErrorState) {
-        qWarning() << "Phonon error:" << mediaObject->errorString() << mediaObject->errorType();
-        if (mediaObject->errorType() == Phonon::FatalError) handleError(mediaObject->errorString());
+    } else if (state == Media::ErrorState) {
+        handleError(media->errorString());
     }
 
-    if (newState == Phonon::PlayingState) {
+    if (state == Media::PlayingState) {
         bool res = Idle::preventDisplaySleep(QString("%1 is playing").arg(Constants::NAME));
         if (!res) qWarning() << "Error disabling idle display sleep" << Idle::displayErrorMessage();
-    } else if (oldState == Phonon::PlayingState) {
+    } else if (state == Media::PausedState || state == Media::StoppedState) {
         bool res = Idle::allowDisplaySleep();
         if (!res) qWarning() << "Error enabling idle display sleep" << Idle::displayErrorMessage();
     }
 }
-#endif
 
 void MediaView::pause() {
-#ifdef APP_PHONON
-    switch (mediaObject->state()) {
-    case Phonon::PlayingState:
-        mediaObject->pause();
+    switch (media->state()) {
+    case Media::PlayingState:
+        media->pause();
         pauseTimer.start();
         break;
     default:
         if (pauseTimer.hasExpired(60000)) {
             pauseTimer.invalidate();
-            connect(playlistModel->activeVideo(), SIGNAL(gotStreamUrl(QUrl)),
-                    SLOT(resumeWithNewStreamUrl(QUrl)));
+            connect(playlistModel->activeVideo(), &Video::gotStreamUrl, this,
+                    &MediaView::resumeWithNewStreamUrl);
             playlistModel->activeVideo()->loadStreamUrl();
         } else
-            mediaObject->play();
+            media->play();
         break;
     }
-#endif
 }
 
 QRegExp MediaView::wordRE(const QString &s) {
@@ -371,14 +366,9 @@ void MediaView::stop() {
     videoAreaWidget->update();
     errorTimer->stop();
     playlistView->selectionModel()->clearSelection();
-    if (downloadItem) {
-        downloadItem->stop();
-        delete downloadItem;
-        downloadItem = 0;
-        currentVideoSize = 0;
-    }
+
     MainWindow::instance()->getAction("refineSearch")->setChecked(false);
-    updateSubscriptionAction(0, false);
+    updateSubscriptionActionForVideo(nullptr, false);
 #ifdef APP_ACTIVATION
     demoTimer->stop();
 #endif
@@ -390,24 +380,14 @@ void MediaView::stop() {
     a->setEnabled(false);
     a->setVisible(false);
 
-#ifdef APP_PHONON
-    mediaObject->stop();
-    mediaObject->clear();
-#endif
+    media->stop();
+    media->clearQueue();
     currentVideoId.clear();
 
-#ifndef APP_PHONON_SEEK
-    QSlider *slider = MainWindow::instance()->getSlider();
-    slider->setEnabled(false);
-    slider->setValue(0);
-#else
-// Phonon::SeekSlider *slider = MainWindow::instance()->getSeekSlider();
-#endif
-
 #ifdef APP_SNAPSHOT
     if (snapshotSettings) {
         delete snapshotSettings;
-        snapshotSettings = 0;
+        snapshotSettings = nullptr;
     }
 #endif
 }
@@ -416,30 +396,22 @@ const QString &MediaView::getCurrentVideoId() {
     return currentVideoId;
 }
 
-void MediaView::activeRowChanged(int row) {
+void MediaView::activeVideoChanged(Video *video, Video *previousVideo) {
     if (stopped) return;
 
+    media->stop();
     errorTimer->stop();
 
-#ifdef APP_PHONON
-    mediaObject->stop();
-#endif
-    if (downloadItem) {
-        downloadItem->stop();
-        delete downloadItem;
-        downloadItem = 0;
-        currentVideoSize = 0;
+    if (previousVideo && previousVideo != video) {
+        if (previousVideo->isLoadingStreamUrl()) previousVideo->abortLoadStreamUrl();
     }
 
-    Video *video = playlistModel->videoAt(row);
-    if (!video) return;
-
     // optimize window for 16:9 video
     adjustWindowSize();
 
     videoAreaWidget->showLoading(video);
 
-    connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)), Qt::UniqueConnection);
+    connect(video, &Video::gotStreamUrl, this, &MediaView::gotStreamUrl, Qt::UniqueConnection);
     connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(skip()), Qt::UniqueConnection);
     video->loadStreamUrl();
 
@@ -448,6 +420,7 @@ void MediaView::activeRowChanged(int row) {
                                            QLatin1String(Constants::NAME));
 
     // ensure active item is visible
+    int row = playlistModel->rowForVideo(video);
     if (row != -1) {
         QModelIndex index = playlistModel->index(row, 0, QModelIndex());
         playlistView->scrollTo(index, QAbstractItemView::EnsureVisible);
@@ -456,7 +429,7 @@ void MediaView::activeRowChanged(int row) {
     // enable/disable actions
     MainWindow::instance()
             ->getAction("download")
-            ->setEnabled(DownloadManager::instance()->itemForVideo(video) == 0);
+            ->setEnabled(DownloadManager::instance()->itemForVideo(video) == nullptr);
     MainWindow::instance()->getAction("previous")->setEnabled(row > 0);
     MainWindow::instance()->getAction("stopafterthis")->setEnabled(true);
     MainWindow::instance()->getAction("relatedVideos")->setEnabled(true);
@@ -472,21 +445,15 @@ void MediaView::activeRowChanged(int row) {
     a->setEnabled(enableDownload);
     a->setVisible(enableDownload);
 
-    updateSubscriptionAction(video, YTChannel::isSubscribed(video->getChannelId()));
+    updateSubscriptionActionForVideo(video, YTChannel::isSubscribed(video->getChannelId()));
 
     for (QAction *action : currentVideoActions)
         action->setEnabled(true);
 
-#ifndef APP_PHONON_SEEK
-    QSlider *slider = MainWindow::instance()->getSlider();
-    slider->setEnabled(false);
-    slider->setValue(0);
-#endif
-
 #ifdef APP_SNAPSHOT
     if (snapshotSettings) {
         delete snapshotSettings;
-        snapshotSettings = 0;
+        snapshotSettings = nullptr;
         MainWindow::instance()->adjustStatusBarVisibility();
     }
 #endif
@@ -494,9 +461,10 @@ void MediaView::activeRowChanged(int row) {
     // see you in gotStreamUrl...
 }
 
-void MediaView::gotStreamUrl(QUrl streamUrl) {
+void MediaView::gotStreamUrl(const QString &streamUrl, const QString &audioUrl) {
     if (stopped) return;
-    if (!streamUrl.isValid()) {
+    if (streamUrl.isEmpty()) {
+        qWarning() << "Empty stream url";
         skip();
         return;
     }
@@ -510,12 +478,13 @@ void MediaView::gotStreamUrl(QUrl streamUrl) {
 
     currentVideoId = video->getId();
 
-#ifdef APP_PHONON_SEEK
-    mediaObject->setCurrentSource(streamUrl);
-    mediaObject->play();
-#else
-    startDownloading();
-#endif
+    if (audioUrl.isEmpty()) {
+        qDebug() << "Playing" << streamUrl;
+        media->play(streamUrl);
+    } else {
+        qDebug() << "Playing" << streamUrl << audioUrl;
+        media->playSeparateAudioAndVideo(streamUrl, audioUrl);
+    }
 
     // ensure we always have videos ahead
     playlistModel->searchNeeded();
@@ -541,79 +510,13 @@ void MediaView::gotStreamUrl(QUrl streamUrl) {
     ChannelAggregator::instance()->videoWatched(video);
 }
 
-void MediaView::downloadStatusChanged() {
-    // qDebug() << __PRETTY_FUNCTION__;
-    switch (downloadItem->status()) {
-    case Downloading:
-        // qDebug() << "Downloading";
-        if (downloadItem->offset() == 0)
-            startPlaying();
-        else {
-#ifdef APP_PHONON
-            // qDebug() << "Seeking to" << downloadItem->offset();
-            mediaObject->seek(offsetToTime(downloadItem->offset()));
-            mediaObject->play();
-#endif
-        }
-        break;
-    case Starting:
-        // qDebug() << "Starting";
-        break;
-    case Finished:
-// qDebug() << "Finished" << mediaObject->state();
-#ifdef APP_PHONON_SEEK
-        MainWindow::instance()->getSeekSlider()->setEnabled(mediaObject->isSeekable());
-#endif
-        break;
-    case Failed:
-        // qDebug() << "Failed";
-        skip();
-        break;
-    case Idle:
-        // qDebug() << "Idle";
-        break;
-    }
-}
-
-void MediaView::startPlaying() {
-    // qDebug() << __PRETTY_FUNCTION__;
-    if (stopped) return;
-    if (!downloadItem) {
-        skip();
-        return;
-    }
-
-    if (downloadItem->offset() == 0) {
-        currentVideoSize = downloadItem->bytesTotal();
-        // qDebug() << "currentVideoSize" << currentVideoSize;
-    }
-
-    // go!
-    QString source = downloadItem->currentFilename();
-    qDebug() << "Playing" << source << QFile::exists(source);
-#ifdef APP_PHONON
-    mediaObject->setCurrentSource(QUrl::fromLocalFile(source));
-    mediaObject->play();
-#endif
-#ifdef APP_PHONON_SEEK
-    MainWindow::instance()->getSeekSlider()->setEnabled(false);
-#else
-    QSlider *slider = MainWindow::instance()->getSlider();
-    slider->setEnabled(true);
-#endif
-}
-
-void MediaView::itemActivated(const QModelIndex &index) {
+void MediaView::onItemActivated(const QModelIndex &index) {
     if (playlistModel->rowExists(index.row())) {
         // if it's the current video, just rewind and play
         Video *activeVideo = playlistModel->activeVideo();
         Video *video = playlistModel->videoAt(index.row());
         if (activeVideo && video && activeVideo == video) {
-            // mediaObject->seek(0);
-            sliderMoved(0);
-#ifdef APP_PHONON
-            mediaObject->play();
-#endif
+            media->play();
         } else
             playlistModel->setActiveRow(index.row());
 
@@ -652,30 +555,27 @@ void MediaView::skipBackward() {
     playlistModel->setActiveRow(prevRow);
 }
 
-void MediaView::aboutToFinish() {
-#ifdef APP_PHONON
-    qint64 currentTime = mediaObject->currentTime();
-    qint64 totalTime = mediaObject->totalTime();
+void MediaView::onAboutToFinish() {
+    qint64 currentTime = media->position();
+    qint64 totalTime = media->duration();
     // qDebug() << __PRETTY_FUNCTION__ << currentTime << totalTime;
     if (totalTime < 1 || currentTime + 10000 < totalTime) {
         // QTimer::singleShot(500, this, SLOT(playbackResume()));
-        mediaObject->seek(currentTime);
-        mediaObject->play();
+        media->seek(currentTime);
+        media->play();
     }
-#endif
 }
 
-void MediaView::playbackFinished() {
+void MediaView::onPlaybackFinished() {
     if (stopped) return;
 
-#ifdef APP_PHONON
-    const qint64 totalTime = mediaObject->totalTime();
-    const qint64 currentTime = mediaObject->currentTime();
+    const qint64 totalTime = media->duration();
+    const qint64 currentTime = media->position();
     // qDebug() << __PRETTY_FUNCTION__ << mediaObject->currentTime() << totalTime;
     // add 10 secs for imprecise Phonon backends (VLC, Xine)
     if (currentTime > 0 && currentTime + 10000 < totalTime) {
         // mediaObject->seek(currentTime);
-        QTimer::singleShot(500, this, SLOT(playbackResume()));
+        QTimer::singleShot(500, this, SLOT(resumePlayback()));
     } else {
         QAction *stopAfterThisAction = MainWindow::instance()->getAction("stopafterthis");
         if (stopAfterThisAction->isChecked()) {
@@ -683,27 +583,22 @@ void MediaView::playbackFinished() {
         } else
             skip();
     }
-#endif
 }
 
-void MediaView::playbackResume() {
+void MediaView::resumePlayback() {
     if (stopped) return;
-#ifdef APP_PHONON
-    const qint64 currentTime = mediaObject->currentTime();
+    const qint64 currentTime = media->position();
     // qDebug() << __PRETTY_FUNCTION__ << currentTime;
-    if (currentTime > 0) mediaObject->seek(currentTime);
-    mediaObject->play();
-#endif
+    if (currentTime > 0) media->seek(currentTime);
+    media->play();
 }
 
 void MediaView::openWebPage() {
     Video *video = playlistModel->activeVideo();
     if (!video) return;
-#ifdef APP_PHONON
-    mediaObject->pause();
-#endif
-    QString url = video->getWebpage() + QLatin1String("&t=") +
-                  QString::number(mediaObject->currentTime() / 1000);
+    media->pause();
+    QString url =
+            video->getWebpage() + QLatin1String("&t=") + QString::number(media->position() / 1000);
     QDesktopServices::openUrl(url);
 }
 
@@ -719,7 +614,7 @@ void MediaView::copyWebPage() {
 void MediaView::copyVideoLink() {
     Video *video = playlistModel->activeVideo();
     if (!video) return;
-    QApplication::clipboard()->setText(video->getStreamUrl().toEncoded());
+    QApplication::clipboard()->setText(video->getStreamUrl());
     QString message = tr("You can now paste the video stream URL into another application") + ". " +
                       tr("The link will be valid only for a limited time.");
     MainWindow::instance()->showMessage(message);
@@ -728,9 +623,7 @@ void MediaView::copyVideoLink() {
 void MediaView::openInBrowser() {
     Video *video = playlistModel->activeVideo();
     if (!video) return;
-#ifdef APP_PHONON
-    mediaObject->pause();
-#endif
+    media->pause();
     QDesktopServices::openUrl(video->getStreamUrl());
 }
 
@@ -786,24 +679,22 @@ void MediaView::moveDownSelected() {
 void MediaView::setSidebarVisibility(bool visible) {
     if (sidebar->isVisible() == visible) return;
     sidebar->setVisible(visible);
-    sidebar->raise();
-    playlistView->setFocus();
+    if (visible) {
+        sidebar->move(0, 0);
+        sidebar->resize(sidebar->width(), window()->height());
+        sidebar->raise();
+        playlistView->setFocus();
+    }
 }
 
 void MediaView::removeSidebar() {
     sidebar->hide();
-#ifndef APP_MAC
     sidebar->setParent(window());
-    sidebar->move(0, 0);
-    sidebar->raise();
-#endif
 }
 
 void MediaView::restoreSidebar() {
     sidebar->show();
-#ifndef APP_MAC
     splitter->insertWidget(0, sidebar);
-#endif
 }
 
 bool MediaView::isSidebarVisible() {
@@ -812,91 +703,83 @@ bool MediaView::isSidebarVisible() {
 
 void MediaView::saveSplitterState() {
     QSettings settings;
-    settings.setValue("splitter", splitter->saveState());
+    if (splitter) settings.setValue("splitter", splitter->saveState());
 }
 
 void MediaView::downloadVideo() {
     Video *video = playlistModel->activeVideo();
     if (!video) return;
     DownloadManager::instance()->addItem(video);
-    MainWindow::instance()->showActionInStatusBar(MainWindow::instance()->getAction("downloads"),
-                                                  true);
+    MainWindow::instance()->showActionsInStatusBar({MainWindow::instance()->getAction("downloads")},
+                                                   true);
     QString message = tr("Downloading %1").arg(video->getTitle());
     MainWindow::instance()->showMessage(message);
 }
 
 #ifdef APP_SNAPSHOT
 void MediaView::snapshot() {
-    qint64 currentTime = mediaObject->currentTime() / 1000;
+    qint64 currentTime = media->position() / 1000;
 
-    QImage image = videoWidget->snapshot();
-    if (image.isNull()) {
-        qWarning() << "Null snapshot";
-        return;
-    }
+    QObject *context = new QObject();
+    connect(media, &Media::snapshotReady, context,
+            [this, currentTime, context](const QImage &image) {
+                context->deleteLater();
 
-    // QPixmap pixmap = QPixmap::grabWindow(videoWidget->winId());
-    QPixmap pixmap = QPixmap::fromImage(
-            image.scaled(videoWidget->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
-    videoAreaWidget->showSnapshotPreview(pixmap);
-
-    Video *video = playlistModel->activeVideo();
-    if (!video) return;
+                if (image.isNull()) {
+                    qWarning() << "Null snapshot";
+                    return;
+                }
 
-    QString location = SnapshotSettings::getCurrentLocation();
-    QDir dir(location);
-    if (!dir.exists()) dir.mkpath(location);
-    QString basename = video->getTitle();
-    QString format = video->getDuration() > 3600 ? "h_mm_ss" : "m_ss";
-    basename += " (" + QTime(0, 0, 0).addSecs(currentTime).toString(format) + ")";
-    basename = DataUtils::stringToFilename(basename);
-    QString filename = location + "/" + basename + ".png";
-    qDebug() << filename;
-    image.save(filename, "PNG");
-
-    if (snapshotSettings) delete snapshotSettings;
-    snapshotSettings = new SnapshotSettings(videoWidget);
-    snapshotSettings->setSnapshot(pixmap, filename);
-    QStatusBar *statusBar = MainWindow::instance()->statusBar();
+                QPixmap pixmap = QPixmap::fromImage(image.scaled(
+                        videoWidget->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
+                videoAreaWidget->showSnapshotPreview(pixmap);
+
+                Video *video = playlistModel->activeVideo();
+                if (!video) return;
+
+                QString location = SnapshotSettings::getCurrentLocation();
+                QDir dir(location);
+                if (!dir.exists()) dir.mkpath(location);
+                QString basename = video->getTitle();
+                QString format = video->getDuration() > 3600 ? "h_mm_ss" : "m_ss";
+                basename += " (" + QTime(0, 0, 0).addSecs(currentTime).toString(format) + ")";
+                basename = DataUtils::stringToFilename(basename);
+                QString filename = location + "/" + basename + ".png";
+                qDebug() << filename;
+                image.save(filename, "PNG");
+
+                if (snapshotSettings) delete snapshotSettings;
+                snapshotSettings = new SnapshotSettings(videoWidget);
+                snapshotSettings->setSnapshot(pixmap, filename);
+                QStatusBar *statusBar = MainWindow::instance()->statusBar();
 #ifdef APP_EXTRA
-    Extra::fadeInWidget(statusBar, statusBar);
+                Extra::fadeInWidget(statusBar, statusBar);
 #endif
-    statusBar->insertPermanentWidget(0, snapshotSettings);
-    snapshotSettings->show();
-    MainWindow::instance()->setStatusBarVisibility(true);
-}
+                statusBar->insertPermanentWidget(0, snapshotSettings);
+                snapshotSettings->show();
+                MainWindow::instance()->setStatusBarVisibility(true);
+            }
 #endif
+    );
+
+    media->snapshot();
+}
 
 void MediaView::fullscreen() {
-    videoAreaWidget->setParent(0);
+    videoAreaWidget->setParent(nullptr);
     videoAreaWidget->showFullScreen();
 }
 
-void MediaView::startDownloading() {
-    Video *video = playlistModel->activeVideo();
-    if (!video) return;
-    Video *videoCopy = video->clone();
-    if (downloadItem) {
-        downloadItem->stop();
-        delete downloadItem;
-    }
-    QString tempFile = Temporary::filename();
-    downloadItem = new DownloadItem(videoCopy, video->getStreamUrl(), tempFile, this);
-    connect(downloadItem, SIGNAL(statusChanged()), SLOT(downloadStatusChanged()),
-            Qt::UniqueConnection);
-    connect(downloadItem, SIGNAL(bufferProgress(int)), loadingWidget, SLOT(bufferStatus(int)),
-            Qt::UniqueConnection);
-    // connect(downloadItem, SIGNAL(finished()), SLOT(itemFinished()));
-    connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)),
-            Qt::UniqueConnection);
-    connect(downloadItem, SIGNAL(error(QString)), SLOT(handleError(QString)), Qt::UniqueConnection);
-    downloadItem->start();
-}
+void MediaView::resumeWithNewStreamUrl(const QString &streamUrl, const QString &audioUrl) {
+    pauseTime = media->position();
 
-void MediaView::resumeWithNewStreamUrl(const QUrl &streamUrl) {
-    pauseTime = mediaObject->currentTime();
-    mediaObject->setCurrentSource(streamUrl);
-    mediaObject->play();
+    if (audioUrl.isEmpty()) {
+        qDebug() << "Playing" << streamUrl;
+        media->play(streamUrl);
+    } else {
+        qDebug() << "Playing" << streamUrl << audioUrl;
+        media->playSeparateAudioAndVideo(streamUrl, audioUrl);
+    }
 
     Video *video = static_cast<Video *>(sender());
     if (!video) {
@@ -906,43 +789,6 @@ void MediaView::resumeWithNewStreamUrl(const QUrl &streamUrl) {
     video->disconnect(this);
 }
 
-void MediaView::sliderMoved(int value) {
-    Q_UNUSED(value);
-#ifdef APP_PHONON
-#ifndef APP_PHONON_SEEK
-
-    if (currentVideoSize <= 0 || !downloadItem || !mediaObject->isSeekable()) return;
-
-    QSlider *slider = MainWindow::instance()->getSlider();
-    if (slider->isSliderDown()) return;
-
-    qint64 offset = (currentVideoSize * value) / slider->maximum();
-
-    bool needsDownload = downloadItem->needsDownload(offset);
-    if (needsDownload) {
-        if (downloadItem->isBuffered(offset)) {
-            qint64 realOffset = downloadItem->blankAtOffset(offset);
-            if (offset < currentVideoSize) downloadItem->seekTo(realOffset, false);
-            mediaObject->seek(offsetToTime(offset));
-        } else {
-            mediaObject->pause();
-            downloadItem->seekTo(offset);
-        }
-    } else {
-        // qDebug() << "simple seek";
-        mediaObject->seek(offsetToTime(offset));
-    }
-#endif
-#endif
-}
-
-qint64 MediaView::offsetToTime(qint64 offset) {
-#ifdef APP_PHONON
-    const qint64 totalTime = mediaObject->totalTime();
-    return ((offset * totalTime) / currentVideoSize);
-#endif
-}
-
 void MediaView::findVideoParts() {
     Video *video = playlistModel->activeVideo();
     if (!video) return;
@@ -1045,7 +891,7 @@ void MediaView::shareViaEmail() {
     QDesktopServices::openUrl(url);
 }
 
-void MediaView::authorPushed(QModelIndex index) {
+void MediaView::onAuthorPushed(QModelIndex index) {
     Video *video = playlistModel->videoAt(index.row());
     if (!video) return;
 
@@ -1061,20 +907,22 @@ void MediaView::authorPushed(QModelIndex index) {
     search(searchParams);
 }
 
-void MediaView::updateSubscriptionAction(Video *video, bool subscribed) {
+
+void MediaView::updateSubscriptionAction(bool subscribed) {
     QAction *subscribeAction = MainWindow::instance()->getAction("subscribeChannel");
 
     QString subscribeTip;
     QString subscribeText;
-    if (!video) {
+
+    if (currentSubscriptionChannelId.isEmpty()) {
         subscribeText = subscribeAction->property("originalText").toString();
         subscribeAction->setEnabled(false);
     } else if (subscribed) {
-        subscribeText = tr("Unsubscribe from %1").arg(video->getChannelTitle());
+        subscribeText = tr("Unsubscribe from %1").arg(currentSubscriptionChannelTitle);
         subscribeTip = subscribeText;
         subscribeAction->setEnabled(true);
     } else {
-        subscribeText = tr("Subscribe to %1").arg(video->getChannelTitle());
+        subscribeText = tr("Subscribe to %1").arg(currentSubscriptionChannelTitle);
         subscribeTip = subscribeText;
         subscribeAction->setEnabled(true);
     }
@@ -1082,42 +930,97 @@ void MediaView::updateSubscriptionAction(Video *video, bool subscribed) {
     subscribeAction->setStatusTip(subscribeTip);
 
     if (subscribed) {
-#ifdef APP_LINUX_NO
-        static QIcon tintedIcon;
-        if (tintedIcon.isNull()) {
-            QVector<QSize> sizes;
-            sizes << QSize(16, 16);
-            tintedIcon = IconUtils::tintedIcon("bookmark-new", QColor(254, 240, 0), sizes);
-        }
-        subscribeAction->setIcon(tintedIcon);
-#else
         subscribeAction->setIcon(IconUtils::icon("bookmark-remove"));
-#endif
     } else {
         subscribeAction->setIcon(IconUtils::icon("bookmark-new"));
     }
 
-    IconUtils::setupAction(subscribeAction);
+    MainWindow::instance()->setupAction(subscribeAction);
 }
 
-void MediaView::toggleSubscription() {
+void MediaView::updateSubscriptionActionForChannel(const QString & channelId) {
+    QString channelTitle = tr("channel");
+    YTChannel *channel = YTChannel::forId(channelId);
+    if (nullptr != channel && !channel->getDisplayName().isEmpty()) {
+        channelTitle = channel->getDisplayName();
+    }
+
+    bool subscribed = YTChannel::isSubscribed(channelId);
+
+    currentSubscriptionChannelId = channelId;
+    currentSubscriptionChannelTitle = channelTitle;
+    updateSubscriptionAction(subscribed);
+}
+
+void MediaView::updateSubscriptionActionForVideo(Video *video, bool subscribed) {
+    if (!video) {
+        currentSubscriptionChannelId = "";
+        currentSubscriptionChannelTitle = "";
+        updateSubscriptionAction(false);
+    } else {
+        currentSubscriptionChannelId = video->getChannelId();
+        currentSubscriptionChannelTitle = video->getChannelTitle();
+        updateSubscriptionAction(subscribed);
+    }
+}
+
+void MediaView::reloadCurrentVideo() {
     Video *video = playlistModel->activeVideo();
     if (!video) return;
-    QString userId = video->getChannelId();
-    if (userId.isEmpty()) return;
-    bool subscribed = YTChannel::isSubscribed(userId);
+
+    int oldFormat = video->getDefinitionCode();
+
+    QObject *context = new QObject();
+    connect(video, &Video::gotStreamUrl, context,
+            [this, oldFormat, video, context](const QString &videoUrl, const QString &audioUrl) {
+                context->deleteLater();
+                if (oldFormat == video->getDefinitionCode()) return;
+                QObject *context2 = new QObject();
+                const qint64 position = media->position();
+                connect(media, &Media::stateChanged, context2,
+                        [position, this, context2](Media::State state) {
+                            if (state == Media::PlayingState) {
+                                media->seek(position);
+                                context2->deleteLater();
+                                Video *video = playlistModel->activeVideo();
+                                QString msg = tr("Switched to %1")
+                                                      .arg(VideoDefinition::forCode(
+                                                                   video->getDefinitionCode())
+                                                                   .getName());
+                                MainWindow::instance()->showMessage(msg);
+                            }
+                        });
+
+                if (audioUrl.isEmpty()) {
+                    media->play(videoUrl);
+                } else {
+                    media->playSeparateAudioAndVideo(videoUrl, audioUrl);
+                }
+            });
+    video->loadStreamUrl();
+}
+
+void MediaView::toggleSubscription() {
+    //Video *video = playlistModel->activeVideo();
+    if (currentSubscriptionChannelId.isEmpty()) {
+        return;
+    }
+
+    bool subscribed = YTChannel::isSubscribed(currentSubscriptionChannelId);
     if (subscribed) {
-        YTChannel::unsubscribe(userId);
+        YTChannel::unsubscribe(currentSubscriptionChannelId);
         MainWindow::instance()->showMessage(
-                tr("Unsubscribed from %1").arg(video->getChannelTitle()));
+                tr("Unsubscribed from %1").arg(currentSubscriptionChannelTitle));
     } else {
-        YTChannel::subscribe(userId);
-        MainWindow::instance()->showMessage(tr("Subscribed to %1").arg(video->getChannelTitle()));
+        YTChannel::subscribe(currentSubscriptionChannelId);
+        MainWindow::instance()->showMessage(tr("Subscribed to %1").arg(currentSubscriptionChannelTitle));
     }
-    updateSubscriptionAction(video, !subscribed);
+
+    updateSubscriptionAction(!subscribed);
 }
 
 void MediaView::adjustWindowSize() {
+    qDebug() << "Adjusting window size";
     Video *video = playlistModel->activeVideo();
     if (!video) return;
     QWidget *window = this->window();
@@ -1127,6 +1030,7 @@ void MediaView::adjustWindowSize() {
         const double h = (double)videoAreaWidget->height();
         const double currentVideoRatio = w / h;
         if (currentVideoRatio != ratio) {
+            qDebug() << "Adjust size";
             int newHeight = std::round((window->height() - h) + (w / ratio));
             window->resize(window->width(), newHeight);
         }
index ae0b6fd3c432fbc0156709ffdd1e76ed8c442cbe..25693055cf1ae44269024f49f38eb3ab921ef9a9 100644 (file)
@@ -18,24 +18,21 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 
 $END_LICENSE */
 
-#ifndef __MEDIAVIEW_H__
-#define __MEDIAVIEW_H__
+#ifndef MEDIAVIEW_H
+#define MEDIAVIEW_H
 
-#include <QtWidgets>
 #include <QtNetwork>
-#ifdef APP_PHONON
-#include <phonon/mediaobject.h>
-#include <phonon/videowidget.h>
-#include <phonon/seekslider.h>
-#endif
+#include <QtWidgets>
+
+#include "media.h"
+
 #include "view.h"
 
 class Video;
 class PlaylistModel;
 class SearchParams;
 class LoadingWidget;
-class VideoAreaWidget;
-class DownloadItem;
+class VideoArea;
 class PlaylistView;
 class SidebarWidget;
 class VideoSource;
@@ -44,25 +41,24 @@ class SnapshotSettings;
 #endif
 
 class MediaView : public View {
-
     Q_OBJECT
 
 public:
-    static MediaViewinstance();
+    static MediaView *instance();
     void initialize();
 
     void appear();
     void disappear();
 
-#ifdef APP_PHONON
-    void setMediaObject(Phonon::MediaObject *mediaObject);
-#endif
-    const QVector<VideoSource*> & getHistory() { return history; }
+    void setMedia(Media *media);
+    const QVector<VideoSource *> &getHistory() { return history; }
     int getHistoryIndex();
-    PlaylistModelgetPlaylistModel() { return playlistModel; }
+    PlaylistModel *getPlaylistModel() { return playlistModel; }
     const QString &getCurrentVideoId();
-    void updateSubscriptionAction(Video *video, bool subscribed);
-    VideoAreaWidget* getVideoArea() { return videoAreaWidget; }
+    void updateSubscriptionActionForVideo(Video *video, bool subscribed);
+    void updateSubscriptionActionForChannel(const QString & channelId);
+    VideoArea *getVideoArea() { return videoAreaWidget; }
+    void reloadCurrentVideo();
 
 public slots:
     void search(SearchParams *searchParams);
@@ -101,34 +97,26 @@ public slots:
     void goForward();
     void toggleSubscription();
     void adjustWindowSize();
+    void updateSubscriptionAction(bool subscribed);
 
 private slots:
-    // list/model
-    void itemActivated(const QModelIndex &index);
-    void selectionChanged (const QItemSelection & selected, const QItemSelection & deselected);
-    void activeRowChanged(int);
-    void selectVideos(const QVector<Video*> &videos);
-    void gotStreamUrl(QUrl streamUrl);
+    void onItemActivated(const QModelIndex &index);
+    void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+    void activeVideoChanged(Video *video, Video *previousVideo);
+    void selectVideos(const QVector<Video *> &videos);
+    void gotStreamUrl(const QString &streamUrl, const QString &audioUrl);
     void handleError(const QString &message);
-    // phonon
-#ifdef APP_PHONON
-    void stateChanged(Phonon::State newState, Phonon::State oldState);
-#endif
-    void aboutToFinish();
-    void startPlaying();
-    void downloadStatusChanged();
-    void playbackFinished();
-    void playbackResume();
-    void authorPushed(QModelIndex);
+    void mediaStateChanged(Media::State state);
+    void onAboutToFinish();
+    void onPlaybackFinished();
+    void resumePlayback();
+    void onAuthorPushed(QModelIndex);
     void searchAgain();
-    void sliderMoved(int value);
-    qint64 offsetToTime(qint64 offset);
-    void startDownloading();
-    void resumeWithNewStreamUrl(const QUrl &streamUrl);
+    void resumeWithNewStreamUrl(const QString &streamUrl, const QString &audioUrl);
 
 private:
-    MediaView(QWidget *parent = 0);
-    SearchParamsgetSearchParams();
+    MediaView(QWidget *parent = nullptr);
+    SearchParams *getSearchParams();
 
     static QRegExp wordRE(const QString &s);
 
@@ -136,26 +124,26 @@ private:
     SidebarWidget *sidebar;
     PlaylistView *playlistView;
     PlaylistModel *playlistModel;
-    VideoAreaWidget *videoAreaWidget;
+    VideoArea *videoAreaWidget;
     LoadingWidget *loadingWidget;
 
-#ifdef APP_PHONON
-    Phonon::MediaObject *mediaObject;
-    Phonon::VideoWidget *videoWidget;
-#endif
+    Media *media;
+    QWidget *videoWidget;
 
     bool stopped;
     QTimer *errorTimer;
     Video *skippedVideo;
     QString currentVideoId;
 
+    QString currentSubscriptionChannelId;
+    QString currentSubscriptionChannelTitle;
+
 #ifdef APP_ACTIVATION
     QTimer *demoTimer;
 #endif
 
-    DownloadItem *downloadItem;
-    QVector<VideoSource*> history;
-    QVector<QAction*> currentVideoActions;
+    QVector<VideoSource *> history;
+    QVector<QAction *> currentVideoActions;
 
     qint64 currentVideoSize;
 
@@ -167,4 +155,4 @@ private:
     qint64 pauseTime;
 };
 
-#endif // __MEDIAVIEW_H__
+#endif // MEDIAVIEW_H
index 71ce0124fdb7703d9b56f11821cd25dfacf6dd4c..7d145f79458b9e03b4e139973f1fd350bcff1541 100644 (file)
@@ -22,22 +22,24 @@ $END_LICENSE */
 #include "fontutils.h"
 #include "iconutils.h"
 
-PainterUtils::PainterUtils() { }
+PainterUtils::PainterUtils() {}
 
-void PainterUtils::centeredMessage(const QString &message, QWidgetwidget) {
+void PainterUtils::centeredMessage(const QString &message, QWidget *widget) {
     QPainter painter(widget);
     painter.setFont(FontUtils::big());
     QSize textSize(QFontMetrics(painter.font()).size(Qt::TextSingleLine, message));
-    QPoint topLeft(
-                (widget->width()-textSize.width())/2,
-                ((widget->height()-textSize.height())/2)
-                );
+    QPoint topLeft((widget->width() - textSize.width()) / 2,
+                   ((widget->height() - textSize.height()) / 2));
     QRect rect(topLeft, textSize);
     painter.setOpacity(.5);
     painter.drawText(rect, Qt::AlignCenter, message);
 }
 
-void PainterUtils::paintBadge(QPainter *painter, const QString &text, bool center, QColor backgroundColor) {
+void PainterUtils::paintBadge(QPainter *painter,
+                              const QString &text,
+                              bool center,
+                              const QColor &backgroundColor,
+                              bool literalColor) {
     painter->save();
 
     QRect textBox = painter->boundingRect(QRect(), Qt::AlignCenter, text);
@@ -48,11 +50,17 @@ void PainterUtils::paintBadge(QPainter *painter, const QString &text, bool cente
     if (rect.width() < rect.height() || text.length() == 1) rect.setWidth(rect.height());
 
     painter->setPen(Qt::NoPen);
-    painter->setBrush(backgroundColor);
+    QColor bg;
+    if (literalColor)
+        bg = backgroundColor;
+    else
+        bg = backgroundColor.value() > 128 ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64);
+    painter->setBrush(bg);
     painter->setRenderHint(QPainter::Antialiasing);
-    qreal borderRadius = rect.height()/2.;
+    qreal borderRadius = rect.height() / 2.;
     painter->drawRoundedRect(rect, borderRadius, borderRadius);
 
+    painter->setFont(FontUtils::small());
     painter->setPen(Qt::white);
     painter->drawText(rect, Qt::AlignCenter, text);
 
index 82c5eceed8ea353e2474795bf7e74fd6bbc350d6..edc0c93f26459ee48a2604b7b6f1172a33bcacb3 100644 (file)
@@ -24,15 +24,16 @@ $END_LICENSE */
 #include <QtWidgets>
 
 class PainterUtils {
-
 public:
-    static void centeredMessage(const QString &message, QWidget* widget);
-    static void paintBadge(QPainter *painter, const QString &text,
-                           bool center = false, QColor backgroundColor = QColor(230,36,41));
+    static void centeredMessage(const QString &message, QWidget *widget);
+    static void paintBadge(QPainter *painter,
+                           const QString &text,
+                           bool center,
+                           const QColor &backgroundColor,
+                           bool literalColor = false);
 
 private:
     PainterUtils();
-
 };
 
 #endif // PAINTERUTILS_H
index c7795d5b572d77e600c5c4507371f1ebd20a1e59..c111113c886ae32d204deba90ecb1409fec0aa9f 100644 (file)
@@ -40,10 +40,10 @@ bool drawElidedText(QPainter *painter, const QRect &textBox, const int flags, co
     painter->drawText(textBox, 0, elidedText);
     return elidedText.length() < text.length();
 }
-}
+} // namespace
 
 PlaylistItemDelegate::PlaylistItemDelegate(QObject *parent, bool downloadInfo)
-    : QStyledItemDelegate(parent), downloadInfo(downloadInfo), progressBar(0) {
+    : QStyledItemDelegate(parent), downloadInfo(downloadInfo), progressBar(nullptr) {
     listView = qobject_cast<PlaylistView *>(parent);
 
     smallerBoldFont = FontUtils::smallBold();
@@ -65,7 +65,7 @@ PlaylistItemDelegate::~PlaylistItemDelegate() {
 }
 
 void PlaylistItemDelegate::createPlayIcon() {
-    qreal maxRatio = IconUtils::maxSupportedPixelRatio();
+    qreal maxRatio = 2.0;
     playIcon = QPixmap(thumbWidth * maxRatio, thumbHeight * maxRatio);
     playIcon.setDevicePixelRatio(maxRatio);
     playIcon.fill(Qt::transparent);
@@ -99,26 +99,26 @@ void PlaylistItemDelegate::createPlayIcon() {
 
 QSize PlaylistItemDelegate::sizeHint(const QStyleOptionViewItem & /*option*/,
                                      const QModelIndex & /*index*/) const {
-    return QSize(thumbWidth, thumbHeight + 1);
+    return QSize(thumbWidth, thumbHeight);
 }
 
 void PlaylistItemDelegate::paint(QPainter *painter,
                                  const QStyleOptionViewItem &option,
                                  const QModelIndex &index) const {
     int itemType = index.data(ItemTypeRole).toInt();
-    if (itemType == ItemTypeVideo) {
-        QStyleOptionViewItem opt = QStyleOptionViewItem(option);
-        initStyleOption(&opt, index);
-        opt.text.clear();
-        opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
-        paintBody(painter, opt, index);
-    } else
+    if (itemType == ItemTypeVideo)
+        paintBody(painter, option, index);
+    else
         QStyledItemDelegate::paint(painter, option, index);
 }
 
 void PlaylistItemDelegate::paintBody(QPainter *painter,
                                      const QStyleOptionViewItem &option,
                                      const QModelIndex &index) const {
+    const bool isSelected = option.state & QStyle::State_Selected;
+    if (isSelected)
+        QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
+
     painter->save();
     painter->translate(option.rect.topLeft());
 
@@ -126,7 +126,6 @@ void PlaylistItemDelegate::paintBody(QPainter *painter,
     if (downloadInfo) line.setWidth(line.width() / 2);
 
     const bool isActive = index.data(ActiveTrackRole).toBool();
-    const bool isSelected = option.state & QStyle::State_Selected;
 
     // get the video metadata
     const Video *video = index.data(VideoRole).value<VideoPointer>().data();
@@ -134,23 +133,20 @@ void PlaylistItemDelegate::paintBody(QPainter *painter,
     // draw the "current track" highlight underneath the text
     if (isActive && !isSelected) paintActiveOverlay(painter, option, line);
 
-    // separator
-    painter->setPen(option.palette.color(QPalette::Midlight));
-    painter->drawLine(thumbWidth, thumbHeight, option.rect.width(), thumbHeight);
-    if (!video->getThumbnail().isNull()) painter->setPen(Qt::black);
-    painter->drawLine(0, thumbHeight, thumbWidth - 1, thumbHeight);
-
     // thumb
-    painter->drawPixmap(0, 0, video->getThumbnail());
+    const QPixmap &thumb = video->getThumbnail();
+    if (!thumb.isNull()) {
+        painter->drawPixmap(0, 0, thumb);
+        if (video->getDuration() > 0) drawTime(painter, video->getFormattedDuration(), line);
+    }
 
     const bool thumbsOnly = line.width() <= thumbWidth + 60;
     const bool isHovered = index.data(HoveredItemRole).toBool();
 
     // play icon overlayed on the thumb
-    if (isActive && (!isHovered && thumbsOnly)) painter->drawPixmap(0, 0, playIcon);
-
-    // time
-    if (video->getDuration() > 0) drawTime(painter, video->getFormattedDuration(), line);
+    bool needPlayIcon = isActive;
+    if (thumbsOnly) needPlayIcon = needPlayIcon && !isHovered;
+    if (needPlayIcon) painter->drawPixmap(0, 0, playIcon);
 
     if (!thumbsOnly) {
         // text color
@@ -163,7 +159,7 @@ void PlaylistItemDelegate::paintBody(QPainter *painter,
         QStringRef title(&video->getTitle());
         QString elidedTitle = video->getTitle();
         static const int titleFlags = Qt::AlignTop | Qt::TextWordWrap;
-        QRect textBox = line.adjusted(padding + thumbWidth, padding, 0, 0);
+        QRect textBox = line.adjusted(padding + thumbWidth, padding, -padding, 0);
         textBox = painter->boundingRect(textBox, titleFlags, elidedTitle);
         while (textBox.height() > 55 && elidedTitle.length() > 10) {
 #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
@@ -207,7 +203,7 @@ void PlaylistItemDelegate::paintBody(QPainter *painter,
             textSize = QSize(painter->fontMetrics().size(Qt::TextSingleLine, author));
             textBox = QRect(textPoint, textSize);
             authorRects.insert(index.row(), textBox);
-            if (textBox.right() > line.width()) {
+            if (textBox.right() > line.width() - padding) {
                 textBox.setRight(line.width());
                 elided = drawElidedText(painter, textBox, flags, author);
             } else {
@@ -218,11 +214,10 @@ void PlaylistItemDelegate::paintBody(QPainter *painter,
 
         // view count
         if (video->getViewCount() > 0) {
-            QLocale locale;
-            const QString viewCount = tr("%1 views").arg(locale.toString(video->getViewCount()));
+            const QString &viewCount = video->getFormattedViewCount();
             textPoint.setX(textBox.right() + padding);
             textSize = QSize(fontMetrics.size(Qt::TextSingleLine, viewCount));
-            if (elided || textPoint.x() + textSize.width() > line.width()) {
+            if (elided || textPoint.x() + textSize.width() > line.width() - padding) {
                 textPoint.setX(thumbWidth + padding);
                 textPoint.setY(textPoint.y() + textSize.height() + padding);
             }
index bd894dee21a97becf616387295c26a8ed9086585..1e4cebe25757a985a3582d71c719fda839758bd2 100644 (file)
@@ -38,6 +38,9 @@ public:
     QRect downloadButtonRect(const QRect &line) const;
     QRect authorRect(const QModelIndex &index) const;
 
+    static const int thumbWidth;
+    static const int thumbHeight;
+
 private:
     void createPlayIcon();
     void paintBody(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const;
@@ -49,8 +52,6 @@ private:
                             const QRect &line) const;
     void drawTime(QPainter *painter, const QString &time, const QRect &line) const;
 
-    static const int thumbWidth;
-    static const int thumbHeight;
     static const int padding;
 
     QPixmap playIcon;
index e2fcfe81a137388c15c1c66a647bc031d64e6f8b..c4a7d72b9fdfd096491c56de9ffa870fc4c8902e 100644 (file)
@@ -26,16 +26,18 @@ $END_LICENSE */
 #include "videosource.h"
 #include "ytsearch.h"
 
-static const int maxItems = 50;
-static const QString recentKeywordsKey = "recentKeywords";
-static const QString recentChannelsKey = "recentChannels";
+namespace {
+const int maxItems = 50;
+const QString recentKeywordsKey = "recentKeywords";
+const QString recentChannelsKey = "recentChannels";
+} // namespace
 
 PlaylistModel::PlaylistModel(QWidget *parent) : QAbstractListModel(parent) {
-    videoSource = 0;
+    videoSource = nullptr;
     searching = false;
     canSearchMore = true;
     firstSearch = false;
-    m_activeVideo = 0;
+    m_activeVideo = nullptr;
     m_activeRow = -1;
     startIndex = 1;
     max = 0;
@@ -64,7 +66,7 @@ QVariant PlaylistModel::data(const QModelIndex &index, int role) const {
             return ItemTypeShowMore;
         case Qt::DisplayRole:
             if (!errorMessage.isEmpty()) return errorMessage;
-            if (searching) return tr("Searching...");
+            if (searching) return QString(); // tr("Searching...");
             if (canSearchMore) return tr("Show %1 More").arg("").simplified();
             if (videos.isEmpty())
                 return tr("No videos");
@@ -118,6 +120,7 @@ QVariant PlaylistModel::data(const QModelIndex &index, int role) const {
 void PlaylistModel::setActiveRow(int row, bool notify) {
     if (rowExists(row)) {
         m_activeRow = row;
+        Video *previousVideo = m_activeVideo;
         m_activeVideo = videoAt(row);
 
         int oldactiverow = m_activeRow;
@@ -127,11 +130,11 @@ void PlaylistModel::setActiveRow(int row, bool notify) {
                              createIndex(oldactiverow, columnCount() - 1));
 
         emit dataChanged(createIndex(m_activeRow, 0), createIndex(m_activeRow, columnCount() - 1));
-        if (notify) emit activeRowChanged(row);
+        if (notify) emit activeVideoChanged(m_activeVideo, previousVideo);
 
     } else {
         m_activeRow = -1;
-        m_activeVideo = 0;
+        m_activeVideo = nullptr;
     }
 }
 
@@ -149,7 +152,7 @@ int PlaylistModel::previousRow() const {
 
 Video *PlaylistModel::videoAt(int row) const {
     if (rowExists(row)) return videos.at(row);
-    return 0;
+    return nullptr;
 }
 
 Video *PlaylistModel::activeVideo() const {
@@ -158,9 +161,14 @@ Video *PlaylistModel::activeVideo() const {
 
 void PlaylistModel::setVideoSource(VideoSource *videoSource) {
     beginResetModel();
-    while (!videos.isEmpty())
-        delete videos.takeFirst();
-    m_activeVideo = 0;
+
+    qDeleteAll(videos);
+    videos.clear();
+
+    qDeleteAll(deletedVideos);
+    deletedVideos.clear();
+
+    m_activeVideo = nullptr;
     m_activeRow = -1;
     startIndex = 1;
     endResetModel();
@@ -170,12 +178,19 @@ void PlaylistModel::setVideoSource(VideoSource *videoSource) {
             Qt::UniqueConnection);
     connect(videoSource, SIGNAL(finished(int)), SLOT(searchFinished(int)), Qt::UniqueConnection);
     connect(videoSource, SIGNAL(error(QString)), SLOT(searchError(QString)), Qt::UniqueConnection);
+    connect(videoSource, &QObject::destroyed, this,
+            [this, videoSource] {
+                if (this->videoSource == videoSource) {
+                    this->videoSource = nullptr;
+                }
+            },
+            Qt::UniqueConnection);
 
     searchMore();
 }
 
 void PlaylistModel::searchMore(int max) {
-    if (searching) return;
+    if (videoSource == nullptr || searching) return;
     searching = true;
     firstSearch = startIndex == 1;
     this->max = max;
@@ -203,7 +218,7 @@ void PlaylistModel::abortSearch() {
     videos.squeeze();
     searching = false;
     m_activeRow = -1;
-    m_activeVideo = 0;
+    m_activeVideo = nullptr;
     startIndex = 1;
     endResetModel();
 }
@@ -315,21 +330,21 @@ bool PlaylistModel::removeRows(int position, int rows, const QModelIndex & /*par
 
 void PlaylistModel::removeIndexes(QModelIndexList &indexes) {
     QVector<Video *> originalList(videos);
-    QVector<Video *> delitems;
-    delitems.reserve(indexes.size());
     for (const QModelIndex &index : indexes) {
         if (index.row() >= originalList.size()) continue;
         Video *video = originalList.at(index.row());
         int idx = videos.indexOf(video);
         if (idx != -1) {
             beginRemoveRows(QModelIndex(), idx, idx);
-            delitems.append(video);
+            deletedVideos.append(video);
+            if (m_activeVideo == video) {
+                m_activeVideo = nullptr;
+                m_activeRow = -1;
+            }
             videos.removeAll(video);
-            video->deleteLater();
             endRemoveRows();
         }
     }
-    qDeleteAll(delitems);
     videos.squeeze();
 }
 
index d11ee56342ce977a10ec85ceaa29bb2c6c2304b1..a510e6f5ed58896506dd30f28b18f51de2cdcfce 100644 (file)
@@ -38,20 +38,19 @@ enum DataRoles {
     AuthorPressedRole
 };
 
-enum ItemTypes {
-    ItemTypeVideo = 1,
-    ItemTypeShowMore
-};
+enum ItemTypes { ItemTypeVideo = 1, ItemTypeShowMore };
 
 class PlaylistModel : public QAbstractListModel {
-
     Q_OBJECT
 
 public:
     PlaylistModel(QWidget *parent = 0);
 
     int rowCount(const QModelIndex &parent = QModelIndex()) const;
-    int columnCount( const QModelIndex& parent = QModelIndex() ) const { Q_UNUSED( parent ); return 4; }
+    int columnCount(const QModelIndex &parent = QModelIndex()) const {
+        Q_UNUSED(parent);
+        return 4;
+    }
     QVariant data(const QModelIndex &index, int role) const;
     bool removeRows(int position, int rows, const QModelIndex &parent);
 
@@ -59,26 +58,28 @@ public:
     QStringList mimeTypes() const;
     Qt::DropActions supportedDropActions() const;
     Qt::DropActions supportedDragActions() const;
-    QMimeData* mimeData( const QModelIndexList &indexes ) const;
+    QMimeData *mimeData(const QModelIndexList &indexes) const;
     bool dropMimeData(const QMimeData *data,
-                      Qt::DropAction action, int row, int column,
+                      Qt::DropAction action,
+                      int row,
+                      int column,
                       const QModelIndex &parent);
 
-    void setActiveRow(int row , bool notify = true);
-    bool rowExists( int row ) const { return (( row >= 0 ) && ( row < videos.size() ) ); }
+    void setActiveRow(int row, bool notify = true);
+    bool rowExists(int row) const { return ((row >= 0) && (row < videos.size())); }
     int activeRow() const { return m_activeRow; } // returns -1 if there is no active row
     int nextRow() const;
     int previousRow() const;
     void removeIndexes(QModelIndexList &indexes);
-    int rowForVideo(Videovideo);
-    QModelIndex indexForVideo(Videovideo);
+    int rowForVideo(Video *video);
+    QModelIndex indexForVideo(Video *video);
     void move(QModelIndexList &indexes, bool up);
 
-    Video* videoAt( int row ) const;
-    VideoactiveVideo() const;
+    Video *videoAt(int row) const;
+    Video *activeVideo() const;
     int rowForCloneVideo(const QString &videoId) const;
 
-    VideoSourcegetVideoSource() { return videoSource; }
+    VideoSource *getVideoSource() { return videoSource; }
     void setVideoSource(VideoSource *videoSource);
     void abortSearch();
 
@@ -101,12 +102,12 @@ public slots:
     void exitAuthorPressed();
 
 signals:
-    void activeRowChanged(int);
-    void needSelectionFor(const QVector<Video*> &videos);
+    void activeVideoChanged(Video *video, Video *previousVideo);
+    void needSelectionFor(const QVector<Video *> &videos);
     void haveSuggestions(const QStringList &suggestions);
 
 private:
-    void handleFirstVideo(Videovideo);
+    void handleFirstVideo(Video *video);
     void searchMore(int max);
 
     VideoSource *videoSource;
@@ -114,7 +115,8 @@ private:
     bool canSearchMore;
     bool firstSearch;
 
-    QVector<Video*> videos;
+    QVector<Video *> videos;
+    QVector<Video *> deletedVideos;
     int startIndex;
     int max;
 
index f5a1677f5454a21a641cfc45a7882f56be9a3358..3655f4dd36ebbade5c3008d12160100e5bac4e20 100644 (file)
@@ -19,23 +19,16 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "playlistview.h"
-#include "playlistmodel.h"
-#include "playlistitemdelegate.h"
 #include "painterutils.h"
+#include "playlistitemdelegate.h"
+#include "playlistmodel.h"
 
-PlaylistView::PlaylistView(QWidget *parent) : QListView(parent),
-    clickableAuthors(true) {
+PlaylistView::PlaylistView(QWidget *parent) : QListView(parent), clickableAuthors(true) {
     setItemDelegate(new PlaylistItemDelegate(this));
     setSelectionMode(QAbstractItemView::ExtendedSelection);
 
     setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
 
-#if defined(APP_MAC)
-    setMinimumWidth(160);
-#else
-    setMinimumWidth(175);
-#endif
-
     // dragndrop
     setDragEnabled(true);
     setAcceptDrops(true);
@@ -49,9 +42,22 @@ PlaylistView::PlaylistView(QWidget *parent) : QListView(parent),
     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setUniformItemSizes(true);
 
-    connect(this, SIGNAL(entered(const QModelIndex &)),
-            SLOT(itemEntered(const QModelIndex &)));
+    connect(this, SIGNAL(entered(const QModelIndex &)), SLOT(itemEntered(const QModelIndex &)));
     setMouseTracking(true);
+
+    QScrollBar *vScrollbar = verticalScrollBar();
+    connect(vScrollbar, &QAbstractSlider::valueChanged, this, [this, vScrollbar](int value) {
+        if (isVisible() && value == vScrollbar->maximum()) {
+            PlaylistModel *listModel = qobject_cast<PlaylistModel *>(model());
+            listModel->searchMore();
+        }
+    });
+    setMinimumHeight(PlaylistItemDelegate::thumbHeight * 4);
+
+    setMinimumWidth(PlaylistItemDelegate::thumbWidth);
+#ifndef APP_MAC
+    setMinimumWidth(minimumWidth() + vScrollbar->width());
+#endif
 }
 
 void PlaylistView::itemEntered(const QModelIndex &index) {
@@ -84,19 +90,21 @@ void PlaylistView::mousePressEvent(QMouseEvent *event) {
     if (event->button() == Qt::LeftButton) {
         if (isHoveringAuthor(event)) {
             QMetaObject::invokeMethod(model(), "enterAuthorPressed");
+        } else if (isHoveringThumbnail(event)) {
+            const QModelIndex index = indexAt(event->pos());
+            emit activated(index);
+            unsetCursor();
+            return;
         }
+        QListView::mousePressEvent(event);
     }
-    QListView::mousePressEvent(event);
 }
 
 void PlaylistView::mouseReleaseEvent(QMouseEvent *event) {
     if (event->button() == Qt::LeftButton) {
         QMetaObject::invokeMethod(model(), "exitAuthorPressed");
-        const QModelIndex index =  indexAt(event->pos());
-        if (isHoveringThumbnail(event)) {
-            emit activated(index);
-            unsetCursor();
-        } else if (isHoveringAuthor(event)) {
+        const QModelIndex index = indexAt(event->pos());
+        if (isHoveringAuthor(event)) {
             emit authorPushed(index);
         } else if (isShowMoreItem(index)) {
             PlaylistModel *listModel = qobject_cast<PlaylistModel *>(model());
@@ -129,13 +137,13 @@ bool PlaylistView::isHoveringAuthor(QMouseEvent *event) {
 bool PlaylistView::isHoveringThumbnail(QMouseEvent *event) {
     const QModelIndex index = indexAt(event->pos());
     const QRect itemRect = visualRect(index);
-    static const QRect thumbRect(0, 0, 160, 90);
+    static const QRect thumbRect(0, 0, PlaylistItemDelegate::thumbWidth,
+                                 PlaylistItemDelegate::thumbHeight);
     const int x = event->x() - itemRect.x() - thumbRect.x();
     const int y = event->y() - itemRect.y() - thumbRect.y();
     return x > 0 && x < thumbRect.width() && y > 0 && y < thumbRect.height();
 }
 
 bool PlaylistView::isShowMoreItem(const QModelIndex &index) {
-    return model()->rowCount() > 1 &&
-            model()->rowCount() == index.row() + 1;
+    return model()->rowCount() > 1 && model()->rowCount() == index.row() + 1;
 }
index 361fe3a930c8be1fb680e3aacfdd04e2d351a79b..1091e56d9edeed41a7e1b6c74094202252ab869d 100644 (file)
@@ -21,9 +21,7 @@ $END_LICENSE */
 #include "refinesearchbutton.h"
 #include "iconutils.h"
 
-RefineSearchButton::RefineSearchButton(QWidget *parent) :
-    QPushButton(parent) {
-
+RefineSearchButton::RefineSearchButton(QWidget *parent) : QPushButton(parent) {
     hovered = false;
 
     const int refineButtonSize = 48;
@@ -32,23 +30,21 @@ RefineSearchButton::RefineSearchButton(QWidget *parent) :
     setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
 }
 
-void RefineSearchButton::paintBackground() const {
-
-}
-
 void RefineSearchButton::paintEvent(QPaintEvent *) {
+    QColor backgroundColor = palette().windowText().color();
+    backgroundColor.setAlpha(hovered ? 192 : 170);
+
     QPainter painter(this);
     painter.setRenderHints(QPainter::Antialiasing, true);
     painter.setPen(Qt::NoPen);
-    painter.setBrush(QColor(0,0,0, hovered ? 192 : 170));
-    painter.drawEllipse(QPoint(width(), height()), width()-2, height()-2);
+    painter.setBrush(backgroundColor);
+    painter.drawEllipse(QPoint(width(), height()), width() - 2, height() - 2);
 
-    QPixmap pixmap = IconUtils::pixmap(":/images/refine-search.png");
+    QPixmap pixmap =
+            IconUtils::iconPixmap("refine-search", 24, backgroundColor, devicePixelRatioF());
     int pw = pixmap.width() / pixmap.devicePixelRatio();
     int ph = pixmap.height() / pixmap.devicePixelRatio();
-    painter.drawPixmap(width() - pw - 6, height() - ph - 6,
-                       pw, ph,
-                       pixmap);
+    painter.drawPixmap(width() - pw - 6, height() - ph - 6, pw, ph, pixmap);
 }
 
 void RefineSearchButton::enterEvent(QEvent *) {
index a9835764a15da7d341f659383882426b6029ba48..795cce8e87498a3ee105bf66b44b19098058b2f8 100644 (file)
@@ -23,8 +23,7 @@ $END_LICENSE */
 
 #include <QtWidgets>
 
-class RefineSearchButton : public QPushButton
-{
+class RefineSearchButton : public QPushButton {
     Q_OBJECT
 public:
     RefineSearchButton(QWidget *parent = 0);
@@ -33,11 +32,9 @@ protected:
     void paintEvent(QPaintEvent *);
     void enterEvent(QEvent *);
     void leaveEvent(QEvent *);
-    
+
 private:
-    void paintBackground() const;
     bool hovered;
-
 };
 
 #endif // REFINESEARCHBUTTON_H
index 6723b5a282bb5457e2670d6b3f889b1fe0d92e82..4840d25c304b0885a50e252e6beae82709539eb9 100644 (file)
@@ -26,8 +26,7 @@ $END_LICENSE */
 #include "iconutils.h"
 #include "mainwindow.h"
 
-RefineSearchWidget::RefineSearchWidget(QWidget *parent) :
-    QWidget(parent) {
+RefineSearchWidget::RefineSearchWidget(QWidget *parent) : QWidget(parent) {
     dirty = false;
     // Fixes background painting in fullscreen
     setAutoFillBackground(true);
@@ -48,12 +47,9 @@ void RefineSearchWidget::setup() {
     QString paramName = "sortBy";
     setupLabel(tr("Sort by"), layout, paramName);
     QToolBar *sortBar = setupBar(paramName);
-    QActionGroup* sortGroup = new QActionGroup(this);
-    const QStringList sortOptions = QStringList()
-            << tr("Relevance")
-            << tr("Date")
-            << tr("View Count")
-            << tr("Rating");
+    QActionGroup *sortGroup = new QActionGroup(this);
+    const QStringList sortOptions = QStringList() << tr("Relevance") << tr("Date")
+                                                  << tr("View Count") << tr("Rating");
     int i = 0;
     for (const QString &actionName : sortOptions) {
         QAction *action = new QAction(actionName, sortBar);
@@ -68,12 +64,9 @@ void RefineSearchWidget::setup() {
     layout->addSpacing(spacing);
     setupLabel(tr("Date"), layout, paramName);
     QToolBar *timeBar = setupBar(paramName);
-    QActionGrouptimeGroup = new QActionGroup(this);
+    QActionGroup *timeGroup = new QActionGroup(this);
     const QStringList timeSpans = QStringList()
-            << tr("Anytime")
-            << tr("Today")
-            << tr("7 Days")
-            << tr("30 Days");
+                                  << tr("Anytime") << tr("Today") << tr("7 Days") << tr("30 Days");
     i = 0;
     for (const QString &actionName : timeSpans) {
         QAction *action = new QAction(actionName, timeBar);
@@ -88,17 +81,12 @@ void RefineSearchWidget::setup() {
     layout->addSpacing(spacing);
     setupLabel(tr("Duration"), layout, paramName);
     QToolBar *lengthBar = setupBar(paramName);
-    QActionGrouplengthGroup = new QActionGroup(this);
+    QActionGroup *lengthGroup = new QActionGroup(this);
     const QStringList lengthOptions = QStringList()
-            << tr("All")
-            << tr("Short")
-            << tr("Medium")
-            << tr("Long");
+                                      << tr("All") << tr("Short") << tr("Medium") << tr("Long");
     QStringList tips = QStringList()
-            << ""
-            << tr("Less than 4 minutes")
-            << tr("Between 4 and 20 minutes")
-            << tr("Longer than 20 minutes");
+                       << "" << tr("Less than 4 minutes") << tr("Between 4 and 20 minutes")
+                       << tr("Longer than 20 minutes");
     i = 0;
     for (const QString &actionName : lengthOptions) {
         QAction *action = new QAction(actionName, timeBar);
@@ -114,13 +102,9 @@ void RefineSearchWidget::setup() {
     layout->addSpacing(spacing);
     setupLabel(tr("Quality"), layout, paramName);
     QToolBar *qualityBar = setupBar(paramName);
-    QActionGroup* qualityGroup = new QActionGroup(this);
-    const QStringList qualityOptions = QStringList()
-            << tr("All")
-            << tr("High Definition");
-    tips = QStringList()
-            << ""
-            << tr("720p or higher");
+    QActionGroup *qualityGroup = new QActionGroup(this);
+    const QStringList qualityOptions = QStringList() << tr("All") << tr("High Definition");
+    tips = QStringList() << "" << tr("720p or higher");
     i = 0;
     for (const QString &actionName : qualityOptions) {
         QAction *action = new QAction(actionName, timeBar);
@@ -146,8 +130,10 @@ void RefineSearchWidget::setup() {
     layout->addWidget(doneButton, 0, Qt::AlignLeft);
 }
 
-void RefineSearchWidget::setupLabel(const QString &text, QBoxLayout *layout, const QString &paramName) {
-    QBoxLayout* hLayout = new QHBoxLayout();
+void RefineSearchWidget::setupLabel(const QString &text,
+                                    QBoxLayout *layout,
+                                    const QString &paramName) {
+    QBoxLayout *hLayout = new QHBoxLayout();
     hLayout->setSpacing(8);
     hLayout->setMargin(0);
     hLayout->setAlignment(Qt::AlignVCenter);
@@ -155,7 +141,10 @@ void RefineSearchWidget::setupLabel(const QString &text, QBoxLayout *layout, con
     QLabel *icon = new QLabel(this);
     icon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
     QString resource = paramName;
-    QPixmap pixmap = IconUtils::pixmap(":/images/search-" + resource + ".png");
+    QByteArray iconName = QByteArrayLiteral("search-") + resource.toLatin1();
+    QPixmap pixmap = IconUtils::iconPixmap(iconName.constData(), 16, palette().window().color(),
+                                           devicePixelRatioF());
+
     /*
     QPixmap translucentPixmap(pixmap.size());
     translucentPixmap.fill(Qt::transparent);
@@ -176,12 +165,12 @@ void RefineSearchWidget::setupLabel(const QString &text, QBoxLayout *layout, con
     layout->addLayout(hLayout);
 }
 
-QToolBarRefineSearchWidget::setupBar(const QString &paramName) {
-    QToolBarbar = new QToolBar(this);
+QToolBar *RefineSearchWidget::setupBar(const QString &paramName) {
+    QToolBar *bar = new QToolBar(this);
     bar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
     // bar->setProperty("segmented", true);
     bar->setProperty("paramName", paramName);
-    connect(bar, SIGNAL(actionTriggered(QAction*)), SLOT(actionTriggered(QAction*)));
+    connect(bar, SIGNAL(actionTriggered(QAction *)), SLOT(actionTriggered(QAction *)));
     bars.insert(paramName, bar);
     layout()->addWidget(bar);
     return bar;
@@ -211,8 +200,8 @@ void RefineSearchWidget::setSearchParams(SearchParams *params) {
 
     if (!params) return;
 
-    QToolBarbar;
-    QActionaction;
+    QToolBar *bar;
+    QAction *action;
 
     bar = bars.value("sortBy");
     action = bar->actions().at(params->sortBy());
@@ -230,10 +219,9 @@ void RefineSearchWidget::setSearchParams(SearchParams *params) {
     action = bar->actions().at(params->quality());
     if (action) action->setChecked(true);
 
-    disconnect(SIGNAL(paramChanged(QString,QVariant)));
-    connect(this, SIGNAL(paramChanged(QString,QVariant)),
-            params, SLOT(setParam(QString,QVariant)),
-            Qt::UniqueConnection);
+    disconnect(SIGNAL(paramChanged(QString, QVariant)));
+    connect(this, SIGNAL(paramChanged(QString, QVariant)), params,
+            SLOT(setParam(QString, QVariant)), Qt::UniqueConnection);
 
     dirty = false;
 
index b2b41c3afd0c5115ff996be0eece33b94de658bd..b5c33430eb0a83e9a151aee32cc8e16cd9402d7a 100644 (file)
@@ -19,8 +19,8 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "regionsview.h"
-#include "ytregions.h"
 #include "mainwindow.h"
+#include "ytregions.h"
 
 RegionsView::RegionsView(QWidget *parent) : View(parent) {
     QBoxLayout *l = new QVBoxLayout(this);
@@ -33,7 +33,7 @@ RegionsView::RegionsView(QWidget *parent) : View(parent) {
     l->addLayout(layout);
 
     addRegion(YTRegions::worldwideRegion());
-    foreach(YTRegion region, YTRegions::list())
+    foreach (YTRegion region, YTRegions::list())
         addRegion(region);
 
     doneButton = new QPushButton(tr("Done"));
@@ -67,7 +67,7 @@ void RegionsView::appear() {
     QString currentRegionId = YTRegions::currentRegionId();
     for (int i = 0; i < layout->count(); i++) {
         QLayoutItem *item = layout->itemAt(i);
-        QPushButton *b = static_cast<QPushButton*>(item->widget());
+        QPushButton *b = static_cast<QPushButton *>(item->widget());
         QString regionId = b->property("regionId").toString();
         b->setChecked(currentRegionId == regionId);
     }
@@ -77,7 +77,7 @@ void RegionsView::paintEvent(QPaintEvent *e) {
     QWidget::paintEvent(e);
     QBrush brush;
     if (window()->isActiveWindow()) {
-        brush = Qt::white;
+        brush = palette().base();
     } else {
         brush = palette().window();
     }
@@ -87,7 +87,7 @@ void RegionsView::paintEvent(QPaintEvent *e) {
 }
 
 void RegionsView::buttonClicked() {
-    QObjecto = sender();
+    QObject *o = sender();
     QString regionId = o->property("regionId").toString();
     YTRegions::setRegion(regionId);
     emit regionChanged();
index 06d201d7f3e80379c9caa5bccf6712e133089205..5f3abd315c84be201d46ac277796c191e7f5563d 100644 (file)
 #include "autocomplete.h"
 #include "iconutils.h"
 
-class SearchButton : public QAbstractButton {
+SearchLineEdit::SearchLineEdit(QWidget *parent) : QLineEdit(parent) {
+    setClearButtonEnabled(true);
+    setPlaceholderText(tr("Search"));
 
-public:
-    SearchButton(QWidget *parent = 0);
-    QMenu *m_menu;
-
-protected:
-    void paintEvent(QPaintEvent *e);
-    void mousePressEvent(QMouseEvent *e);
-
-};
-
-SearchButton::SearchButton(QWidget *parent)
-    : QAbstractButton(parent),
-    m_menu(0) {
-    setObjectName(QLatin1String("SearchButton"));
-    setCursor(Qt::ArrowCursor);
-    setFocusPolicy(Qt::NoFocus);
-}
-
-void SearchButton::mousePressEvent(QMouseEvent *e) {
-    if (m_menu && e->button() == Qt::LeftButton) {
-        QWidget *p = parentWidget();
-        if (p) {
-            QPoint r = p->mapToGlobal(QPoint(0, p->height()));
-            m_menu->exec(QPoint(r.x() + height() / 2, r.y()));
-        }
-        e->accept();
-    }
-    QAbstractButton::mousePressEvent(e);
-}
-
-void SearchButton::paintEvent(QPaintEvent *e) {
-    Q_UNUSED(e);
-    QPainter painter(this);
-    const int h = height();
-    int iconSize = 16;
-    if (h > 30) iconSize = 22;
-    QPixmap p = IconUtils::icon("edit-find").pixmap(iconSize, iconSize);
-    int x = (width() - p.width()) / 2;
-    int y = (h - p.height()) / 2;
-    painter.drawPixmap(x, y, p);
-}
-
-SearchLineEdit::SearchLineEdit(QWidget *parent) : ExLineEdit(parent), searchButton(new SearchButton(this)) {
-    connect(m_lineEdit, SIGNAL(textChanged(const QString &)), SIGNAL(textChanged(const QString &)));
-    connect(m_lineEdit, SIGNAL(textEdited(const QString &)), SIGNAL(textEdited(const QString &)));
-    connect(m_lineEdit, SIGNAL(returnPressed()), SLOT(returnPressed()));
-
-    setLeftWidget(searchButton);
-    inactiveText = tr("Search");
-
-    QSizePolicy policy = sizePolicy();
-    setSizePolicy(QSizePolicy::Preferred, policy.verticalPolicy());
+    QAction *searchAction = new QAction();
+    IconUtils::setIcon(searchAction, "edit-find");
+    addAction(searchAction, QLineEdit::LeadingPosition);
 
     // completion
-    autoComplete = new AutoComplete(this, m_lineEdit);
-    connect(autoComplete, SIGNAL(suggestionAccepted(Suggestion*)), SIGNAL(suggestionAccepted(Suggestion*)));
-}
-
-void SearchLineEdit::paintEvent(QPaintEvent *e) {
-    ExLineEdit::paintEvent(e);
-    if (m_lineEdit->text().isEmpty() && !hasFocus() && !inactiveText.isEmpty()) {
-        QStyleOptionFrame panel;
-        initStyleOption(&panel);
-        QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
-        QFontMetrics fm = fontMetrics();
-        int horizontalMargin = m_lineEdit->x();
-        QRect lineRect(horizontalMargin + r.x(), r.y() + (r.height() - fm.height() + 1) / 2,
-                       r.width() - 2 * horizontalMargin, fm.height());
-        QPainter painter(this);
-        painter.setPen(palette().brush(QPalette::Disabled, QPalette::Text).color());
-        painter.drawText(lineRect, Qt::AlignLeft | Qt::AlignVCenter, inactiveText);
-    }
-}
+    autoComplete = new AutoComplete(this, this);
+    connect(autoComplete, SIGNAL(suggestionAccepted(Suggestion *)),
+            SIGNAL(suggestionAccepted(Suggestion *)));
 
-void SearchLineEdit::resizeEvent(QResizeEvent *e) {
-    updateGeometries();
-    ExLineEdit::resizeEvent(e);
-}
-
-void SearchLineEdit::updateGeometries() {
-    int menuHeight = height();
-    int menuWidth = menuHeight + 1;
-    if (!searchButton->m_menu)
-        menuWidth = (menuHeight / 5) * 4;
-    searchButton->resize(QSize(menuWidth, menuHeight));
-}
-
-void SearchLineEdit::setInactiveText(const QString &text) {
-    inactiveText = text;
-}
-
-void SearchLineEdit::setText(const QString &text) {
-    m_lineEdit->setText(text);
-}
-
-AutoComplete *SearchLineEdit::getAutoComplete() {
-    return autoComplete;
-}
-
-void SearchLineEdit::setMenu(QMenu *menu) {
-    if (searchButton->m_menu)
-        searchButton->m_menu->deleteLater();
-    searchButton->m_menu = menu;
-    updateGeometries();
+    connect(this, SIGNAL(returnPressed()), SLOT(returnPressed()));
 }
 
 QMenu *SearchLineEdit::menu() const {
-    if (!searchButton->m_menu) {
-        searchButton->m_menu = new QMenu(searchButton);
-        if (isVisible())
-            (const_cast<SearchLineEdit*>(this))->updateGeometries();
-    }
-    return searchButton->m_menu;
+    return nullptr;
 }
 
-void SearchLineEdit::returnPressed() {
-    QString text = m_lineEdit->text().simplified();
-    if (!text.isEmpty()) {
-        autoComplete->preventSuggest();
-        emit search(text);
-    }
+void SearchLineEdit::setMenu(QMenu *menu) {
+    Q_UNUSED(menu);
 }
 
 void SearchLineEdit::enableSuggest() {
@@ -135,33 +34,31 @@ void SearchLineEdit::preventSuggest() {
     autoComplete->preventSuggest();
 }
 
-void SearchLineEdit::selectAll() {
-    m_lineEdit->selectAll();
-}
-
 void SearchLineEdit::setSuggester(Suggester *suggester) {
     autoComplete->setSuggester(suggester);
 }
 
-void SearchLineEdit::focusInEvent(QFocusEvent *e) {
-    ExLineEdit::focusInEvent(e);
-    enableSuggest();
+AutoComplete *SearchLineEdit::getAutoComplete() {
+    return autoComplete;
 }
 
 void SearchLineEdit::emitTextChanged(const QString &text) {
     autoComplete->enableSuggest();
-    emit textEdited(text);
+    emit QLineEdit::textEdited(text);
 }
 
-QString SearchLineEdit::text() {
-    return m_lineEdit->text();
+void SearchLineEdit::returnPressed() {
+    QString s = text().simplified();
+    if (!s.isEmpty()) {
+        autoComplete->preventSuggest();
+        emit search(s);
+    }
 }
 
 QLineEdit *SearchLineEdit::getLineEdit() {
-    return m_lineEdit;
+    return this;
 }
 
-void SearchLineEdit::setEnabled(bool enabled) {
-    ExLineEdit::setEnabled(enabled);
-    emit enabledChanged(enabled);
+QWidget *SearchLineEdit::toWidget() {
+    return this;
 }
index e3a71d02d5903b71ded9a48b8da58cfdc33c59a9..a116f2faac290d2be6fa60a0fdd40919bf5f0687 100644 (file)
@@ -3,57 +3,39 @@
 
 #include <QtWidgets>
 
-#include "exlineedit.h"
 #include "searchwidget.h"
 
-class SearchButton;
-class Suggester;
-class AutoComplete;
-
-class SearchLineEdit : public ExLineEdit, public SearchWidget {
-
+class SearchLineEdit : public QLineEdit, public SearchWidget {
     Q_OBJECT
 
 public:
-    SearchLineEdit(QWidget *parent = 0);
+    explicit SearchLineEdit(QWidget *parent = nullptr);
+
+    // SearchWidget interface
     QMenu *menu() const;
     void setMenu(QMenu *menu);
     void enableSuggest();
     void preventSuggest();
-    void selectAll();
     void setSuggester(Suggester *suggester);
-    void setInactiveText(const QString &text);
-    void setText(const QString &text);
     AutoComplete *getAutoComplete();
     void emitTextChanged(const QString &text);
-    QString text();
     QLineEdit *getLineEdit();
-    QWidget *toWidget() { return qobject_cast<QWidget*>(this); }
+    QWidget *toWidget();
 
-    void setEnabled(bool enabled);
+    void setPlaceholderText(const QString &text) { QLineEdit::setPlaceholderText(text); }
+    void selectAll() { QLineEdit::selectAll(); }
+    void setText(const QString &text) { QLineEdit::setText(text); }
+    QString text() { return QLineEdit::text(); }
 
 public slots:
     void returnPressed();
 
 signals:
-    void textChanged(const QString &text);
-    void textEdited(const QString &text);
     void search(const QString &text);
     void suggestionAccepted(Suggestion *suggestion);
 
-    void enabledChanged(bool enabled);
-
-protected:
-    void updateGeometries();
-    void resizeEvent(QResizeEvent *e);
-    void paintEvent(QPaintEvent *e);
-    void focusInEvent(QFocusEvent *e);
-
 private:
-    SearchButton *searchButton;
-    QString inactiveText;
     AutoComplete *autoComplete;
 };
 
 #endif // SEARCHLINEEDIT_H
-
index 2ca81d3cfe5c1b5cfce15698190d5d59d84f8485..1bcdac9b06c734266811a30c320d2da8ddc3c46e 100644 (file)
@@ -24,7 +24,6 @@ $END_LICENSE */
 #include <QtCore>
 
 class SearchParams : public QObject {
-
     Q_OBJECT
     Q_PROPERTY(int sortBy READ sortBy WRITE setSortBy)
     Q_PROPERTY(int duration READ duration WRITE setDuration)
@@ -32,40 +31,17 @@ class SearchParams : public QObject {
     Q_PROPERTY(int time READ time WRITE setTime)
 
 public:
+    enum SortBy { SortByRelevance = 0, SortByNewest, SortByViewCount, SortByRating };
+
+    enum Duration { DurationAny = 0, DurationShort, DurationMedium, DurationLong };
+
+    enum Quality { QualityAny = 0, QualityHD };
 
-    enum SortBy {
-        SortByRelevance = 0,
-        SortByNewest,
-        SortByViewCount,
-        SortByRating
-    };
-
-    enum Duration {
-        DurationAny = 0,
-        DurationShort,
-        DurationMedium,
-        DurationLong
-    };
-
-    enum Quality {
-        QualityAny = 0,
-        QualityHD
-    };
-
-    enum Time {
-        TimeAny = 0,
-        TimeToday,
-        TimeWeek,
-        TimeMonth
-    };
-
-    enum SafeSearch {
-        None = 0,
-        Moderate,
-        Strict
-    };
-
-    SearchParams(QObject *parent = 0);
+    enum Time { TimeAny = 0, TimeToday, TimeWeek, TimeMonth };
+
+    enum SafeSearch { None = 0, Moderate, Strict };
+
+    SearchParams(QObject *parent = nullptr);
 
     const QString &keywords() const { return m_keywords; }
     void setKeywords(const QString &keywords) { m_keywords = keywords; }
@@ -74,29 +50,28 @@ public:
     void setChannelId(const QString &value) { m_channelId = value; }
 
     int sortBy() const { return m_sortBy; }
-    void setSortBy( int sortBy ) { m_sortBy = sortBy; }
+    void setSortBy(int sortBy) { m_sortBy = sortBy; }
 
     int isTransient() const { return m_transient; }
-    void setTransient( int transient ) { m_transient = transient; }
+    void setTransient(int transient) { m_transient = transient; }
 
     int duration() const { return m_duration; }
-    void setDuration( int duration ) { m_duration = duration; }
+    void setDuration(int duration) { m_duration = duration; }
 
     int quality() const { return m_quality; }
-    void setQuality( int quality ) { m_quality = quality; }
+    void setQuality(int quality) { m_quality = quality; }
 
     int time() const { return m_time; }
-    void setTime( int time ) { m_time = time; }
+    void setTime(int time) { m_time = time; }
 
     uint publishedAfter() const { return m_publishedAfter; }
     void setPublishedAfter(uint value) { m_publishedAfter = value; }
 
     int safeSearch() const { return m_safeSearch; }
-    void setSafeSearch( int safeSearch ) { m_safeSearch = safeSearch; }
+    void setSafeSearch(int safeSearch) { m_safeSearch = safeSearch; }
 
     bool operator==(const SearchParams &other) const {
-        return m_keywords == other.keywords() &&
-                m_channelId == other.channelId();
+        return m_keywords == other.keywords() && m_channelId == other.channelId();
     }
 
 public slots:
@@ -112,7 +87,6 @@ private:
     int m_time;
     uint m_publishedAfter;
     int m_safeSearch;
-
 };
 
 #endif // SEARCHPARAMS_H
index bf48028953b1a01f0b66e2f1fb97adb7df614159..6536d0b4edaebb0bf69401ccec918f6b33772185 100644 (file)
@@ -19,16 +19,19 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "searchview.h"
+#include "channelsuggest.h"
 #include "constants.h"
 #include "fontutils.h"
 #include "searchparams.h"
 #include "ytsuggester.h"
-#include "channelsuggest.h"
 #ifdef APP_MAC_SEARCHFIELD
 #include "searchlineedit_mac.h"
 #else
 #include "searchlineedit.h"
 #endif
+#ifdef APP_MAC
+#include "macutils.h"
+#endif
 #ifdef APP_EXTRA
 #include "extra.h"
 #endif
@@ -36,22 +39,22 @@ $END_LICENSE */
 #include "activation.h"
 #include "activationview.h"
 #endif
+#include "clickablelabel.h"
+#include "iconutils.h"
 #include "mainwindow.h"
 #include "painterutils.h"
-#include "iconutils.h"
-#include "clickablelabel.h"
 
 namespace {
-static const QString recentKeywordsKey = "recentKeywords";
-static const QString recentChannelsKey = "recentChannels";
-}
+const QString recentKeywordsKey = "recentKeywords";
+const QString recentChannelsKey = "recentChannels";
+} // namespace
 
 SearchView::SearchView(QWidget *parent) : View(parent) {
-    const int padding = 30;
+    setBackgroundRole(QPalette::Base);
+    setForegroundRole(QPalette::Text);
+    setAutoFillBackground(true);
 
-    // speedup painting since we'll paint the whole background
-    // by ourselves anyway in paintEvent()
-    setAttribute(Qt::WA_OpaquePaintEvent);
+    const int padding = 30;
 
     QBoxLayout *vLayout = new QVBoxLayout(this);
     vLayout->setMargin(padding);
@@ -71,8 +74,12 @@ SearchView::SearchView(QWidget *parent) : View(parent) {
 
     hLayout->addStretch();
 
-    logo = new ClickableLabel(this);
-    logo->setPixmap(IconUtils::pixmap(":/images/app.png"));
+    logo = new ClickableLabel();
+    auto setLogoPixmap = [this] {
+        logo->setPixmap(IconUtils::pixmap(":/images/app.png", logo->devicePixelRatioF()));
+    };
+    setLogoPixmap();
+    connect(window()->windowHandle(), &QWindow::screenChanged, this, setLogoPixmap);
     connect(logo, &ClickableLabel::clicked, MainWindow::instance(), &MainWindow::visitSite);
     hLayout->addWidget(logo, 0, Qt::AlignTop);
     hLayout->addSpacing(padding);
@@ -81,65 +88,41 @@ SearchView::SearchView(QWidget *parent) : View(parent) {
     layout->setAlignment(Qt::AlignCenter);
     hLayout->addLayout(layout);
 
-    QColor titleColor = palette().color(QPalette::WindowText);
-    titleColor.setAlphaF(.75);
-    int r,g,b,a;
-    titleColor.getRgb(&r,&g,&b,&a);
-    QString cssColor = QString::asprintf("rgba(%d,%d,%d,%d)", r, g, b, a);
-
-    QLabel *welcomeLabel =
-            new QLabel(QString("<h1 style='font-weight:300;color:%1'>").arg(cssColor) +
-                       tr("Welcome to <a href='%1'>%2</a>,")
-                       .replace("<a ", "<a style='text-decoration:none; color:palette(text)' ")
-                       .arg(Constants::WEBSITE, Constants::NAME)
-                       + "</h1>");
+    QLabel *welcomeLabel = new QLabel();
+    auto setupWelcomeLabel = [this, welcomeLabel] {
+        QColor titleColor = palette().color(QPalette::WindowText);
+        titleColor.setAlphaF(.75);
+        int r, g, b, a;
+        titleColor.getRgb(&r, &g, &b, &a);
+        QString cssColor = QString::asprintf("rgba(%d,%d,%d,%d)", r, g, b, a);
+        QString text =
+                QString("<h1 style='font-weight:300;color:%1'>").arg(cssColor) +
+                tr("Welcome to <a href='%1'>%2</a>,")
+                        .replace("<a ", "<a style='text-decoration:none; color:palette(text)' ")
+                        .arg(Constants::WEBSITE, Constants::NAME) +
+                "</h1>";
+        welcomeLabel->setText(text);
+    };
+    setupWelcomeLabel();
+    connect(qApp, &QGuiApplication::paletteChanged, this, setupWelcomeLabel);
     welcomeLabel->setOpenExternalLinks(true);
-    welcomeLabel->setProperty("heading", true);
-    welcomeLabel->setFont(FontUtils::light(welcomeLabel->font().pointSize() * 1.25));
+    welcomeLabel->setFont(FontUtils::light(welcomeLabel->font().pointSize()));
     layout->addWidget(welcomeLabel);
 
     layout->addSpacing(padding / 2);
 
-#ifndef APP_MAC
-    const QFont &biggerFont = FontUtils::big();
-#endif
-
     //: "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos"
     // QLabel *tipLabel = new QLabel(tr("Enter"), this);
-
     QString tip;
     if (qApp->layoutDirection() == Qt::RightToLeft) {
         tip = tr("to start watching videos.") + " " + tr("a keyword") + " " + tr("Enter");
     } else {
         tip = tr("Enter") + " " + tr("a keyword") + " " + tr("to start watching videos.");
     }
-    QLabel *tipLabel = new QLabel(tip);
-
-#ifndef APP_MAC
-    tipLabel->setFont(biggerFont);
-#endif
-    layout->addWidget(tipLabel);
-
-    /*
-    typeCombo = new QComboBox(this);
-    typeCombo->addItem(tr("a keyword"));
-    typeCombo->addItem(tr("a channel"));
-#ifndef APP_MAC
-    typeCombo->setFont(biggerFont);
-#endif
-    connect(typeCombo, SIGNAL(currentIndexChanged(int)), SLOT(searchTypeChanged(int)));
-    tipLayout->addWidget(typeCombo);
-
-    tipLabel = new QLabel(tr("to start watching videos."), this);
-#ifndef APP_MAC
-    tipLabel->setFont(biggerFont);
-#endif
-    tipLayout->addWidget(tipLabel);
-    */
 
     layout->addSpacing(padding / 2);
 
-    QHBoxLayout *searchLayout = new QHBoxLayout();
+    QBoxLayout *searchLayout = new QHBoxLayout();
     searchLayout->setAlignment(Qt::AlignVCenter);
 
 #ifdef APP_MAC_SEARCHFIELD
@@ -148,34 +131,31 @@ SearchView::SearchView(QWidget *parent) : View(parent) {
     setFocusProxy(slem);
 #else
     SearchLineEdit *sle = new SearchLineEdit(this);
-    sle->setFont(biggerFont);
+    sle->setFont(FontUtils::medium());
+    int tipWidth = sle->fontMetrics().size(Qt::TextSingleLine, tip).width();
+    sle->setMinimumWidth(tipWidth + sle->fontMetrics().width('m') * 6);
+    sle->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
     queryEdit = sle;
 #endif
 
-    connect(queryEdit->toWidget(), SIGNAL(search(const QString&)), SLOT(watch(const QString&)));
-    connect(queryEdit->toWidget(), SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &)));
-    connect(queryEdit->toWidget(), SIGNAL(textEdited(const QString &)), SLOT(textChanged(const QString &)));
-    connect(queryEdit->toWidget(), SIGNAL(suggestionAccepted(Suggestion*)), SLOT(suggestionAccepted(Suggestion*)));
+    connect(queryEdit->toWidget(), SIGNAL(search(const QString &)), SLOT(watch(const QString &)));
+    connect(queryEdit->toWidget(), SIGNAL(textChanged(const QString &)),
+            SLOT(textChanged(const QString &)));
+    connect(queryEdit->toWidget(), SIGNAL(textEdited(const QString &)),
+            SLOT(textChanged(const QString &)));
+    connect(queryEdit->toWidget(), SIGNAL(suggestionAccepted(Suggestion *)),
+            SLOT(suggestionAccepted(Suggestion *)));
+    queryEdit->setPlaceholderText(tip);
 
     youtubeSuggest = new YTSuggester(this);
     channelSuggest = new ChannelSuggest(this);
-    connect(channelSuggest, SIGNAL(ready(QVector<Suggestion*>)), SLOT(onChannelSuggestions(QVector<Suggestion*>)));
+    connect(channelSuggest, SIGNAL(ready(QVector<Suggestion *>)),
+            SLOT(onChannelSuggestions(QVector<Suggestion *>)));
     searchTypeChanged(0);
 
     searchLayout->addWidget(queryEdit->toWidget(), 0, Qt::AlignBaseline);
-    searchLayout->addSpacing(padding);
 
-    watchButton = new QPushButton(tr("Watch"));
-#ifndef APP_MAC
-    watchButton->setFont(biggerFont);
-#endif
-    watchButton->setDefault(true);
-    watchButton->setEnabled(false);
-    watchButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-    connect(watchButton, SIGNAL(clicked()), this, SLOT(watch()));
-    searchLayout->addWidget(watchButton, 0, Qt::AlignBaseline);
-
-    layout->addItem(searchLayout);
+    layout->addLayout(searchLayout);
 
     layout->addSpacing(padding);
 
@@ -188,9 +168,9 @@ SearchView::SearchView(QWidget *parent) : View(parent) {
     recentKeywordsLayout->setSpacing(0);
     recentKeywordsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
     recentKeywordsLabel = new QLabel(tr("Recent keywords"));
-    recentKeywordsLabel->setEnabled(false);
     recentKeywordsLabel->setProperty("recentHeader", true);
     recentKeywordsLabel->hide();
+    recentKeywordsLabel->setEnabled(false);
     recentKeywordsLayout->addWidget(recentKeywordsLabel);
     recentLayout->addLayout(recentKeywordsLayout);
 
@@ -199,9 +179,9 @@ SearchView::SearchView(QWidget *parent) : View(parent) {
     recentChannelsLayout->setSpacing(0);
     recentChannelsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
     recentChannelsLabel = new QLabel(tr("Recent channels"));
-    recentChannelsLabel->setEnabled(false);
     recentChannelsLabel->setProperty("recentHeader", true);
     recentChannelsLabel->hide();
+    recentChannelsLabel->setEnabled(false);
     recentChannelsLayout->addWidget(recentChannelsLabel);
     recentLayout->addLayout(recentChannelsLayout);
 
@@ -213,39 +193,30 @@ SearchView::SearchView(QWidget *parent) : View(parent) {
 
 #ifdef APP_ACTIVATION
     if (!Activation::instance().isActivated())
-        vLayout->addWidget(ActivationView::buyButton(tr("Get the full version")), 0, Qt::AlignRight);
+        vLayout->addWidget(ActivationView::buyButton(tr("Get the full version")), 0,
+                           Qt::AlignRight);
 #endif
 }
 
 void SearchView::appear() {
     MainWindow *w = MainWindow::instance();
-    w->showActionInStatusBar(w->getAction("manualplay"), true);
-    w->showActionInStatusBar(w->getAction("safeSearch"), true);
-    w->showActionInStatusBar(w->getAction("definition"), true);
+    w->showActionsInStatusBar(
+            {w->getAction("manualplay"), w->getAction("safeSearch"), w->getAction("definition")},
+            true);
 
     updateRecentKeywords();
     updateRecentChannels();
 
     queryEdit->selectAll();
     queryEdit->enableSuggest();
-    if (!queryEdit->toWidget()->hasFocus()) queryEdit->toWidget()->setFocus();
-
-    connect(window()->windowHandle(), SIGNAL(screenChanged(QScreen*)), SLOT(screenChanged()), Qt::UniqueConnection);
-
-    qApp->processEvents();
-    update();
-
-#ifdef APP_MAC
-    // Workaround cursor bug on macOS
-    window()->unsetCursor();
-#endif
+    QTimer::singleShot(0, queryEdit->toWidget(), SLOT(setFocus()));
 }
 
 void SearchView::disappear() {
     MainWindow *w = MainWindow::instance();
-    w->showActionInStatusBar(w->getAction("safeSearch"), false);
-    w->showActionInStatusBar(w->getAction("definition"), false);
-    w->showActionInStatusBar(w->getAction("manualplay"), false);
+    w->showActionsInStatusBar(
+            {w->getAction("manualplay"), w->getAction("safeSearch"), w->getAction("definition")},
+            false);
 }
 
 void SearchView::updateRecentKeywords() {
@@ -257,7 +228,8 @@ void SearchView::updateRecentKeywords() {
 
     // cleanup
     QLayoutItem *item;
-    while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
+    while (recentKeywordsLayout->count() - 1 > recentKeywords.size() &&
+           (item = recentKeywordsLayout->takeAt(1)) != nullptr) {
         item->widget()->close();
         delete item;
     }
@@ -267,37 +239,66 @@ void SearchView::updateRecentKeywords() {
 
     const int maxDisplayLength = 25;
 
+#ifdef APP_MAC
+    QPalette p = palette();
+    p.setColor(QPalette::Highlight, mac::accentColor());
+#endif
+
+    int counter = 1;
     for (const QString &keyword : keywords) {
         QString link = keyword;
         QString display = keyword;
-        if (keyword.startsWith(QLatin1String("http://")) || keyword.startsWith(QLatin1String("https://"))) {
+        if (keyword.startsWith(QLatin1String("http://")) ||
+            keyword.startsWith(QLatin1String("https://"))) {
             int separator = keyword.indexOf('|');
             if (separator > 0 && separator + 1 < keyword.length()) {
                 link = keyword.left(separator);
-                display = keyword.mid(separator+1);
+                display = keyword.mid(separator + 1);
             }
         }
-        bool needStatusTip = false;
-        if (display.length() > maxDisplayLength) {
+        bool needStatusTip = display.length() > maxDisplayLength;
+        if (needStatusTip) {
             display.truncate(maxDisplayLength);
             display.append(QStringLiteral("\u2026"));
-            needStatusTip = true;
         }
-        QPushButton *itemButton = new QPushButton(display);
-        itemButton->setAttribute(Qt::WA_DeleteOnClose);
-        itemButton->setProperty("recentItem", true);
-        itemButton->setCursor(Qt::PointingHandCursor);
-        itemButton->setFocusPolicy(Qt::TabFocus);
-        itemButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
+
+        ClickableLabel *item;
+        if (recentKeywordsLayout->count() - 1 >= counter) {
+            item = qobject_cast<ClickableLabel *>(recentKeywordsLayout->itemAt(counter)->widget());
+
+        } else {
+            item = new ClickableLabel();
+#ifdef APP_MAC
+            item->setPalette(p);
+#endif
+            item->setAttribute(Qt::WA_DeleteOnClose);
+            item->setProperty("recentItem", true);
+            item->setFocusPolicy(Qt::TabFocus);
+            connect(item, &ClickableLabel::hovered, this, [item, this](bool value) {
+                item->setForegroundRole(value ? QPalette::Highlight : QPalette::WindowText);
+                if (value) {
+                    for (int i = 1; i < recentKeywordsLayout->count(); ++i) {
+                        QWidget *w = recentKeywordsLayout->itemAt(i)->widget();
+                        if (w != item) {
+                            w->setForegroundRole(QPalette::WindowText);
+                        }
+                    }
+                }
+            });
+            recentKeywordsLayout->addWidget(item);
+        }
+
+        item->setText(display);
         if (needStatusTip)
-            itemButton->setStatusTip(link);
-        connect(itemButton, &QPushButton::clicked, [this,link]() {
-            watchKeywords(link);
-        });
+            item->setStatusTip(link);
+        else
+            item->setStatusTip(QString());
 
-        recentKeywordsLayout->addWidget(itemButton);
-    }
+        disconnect(item, &ClickableLabel::clicked, nullptr, nullptr);
+        connect(item, &ClickableLabel::clicked, this, [this, link]() { watchKeywords(link); });
 
+        counter++;
+    }
 }
 
 void SearchView::updateRecentChannels() {
@@ -309,13 +310,17 @@ void SearchView::updateRecentChannels() {
 
     // cleanup
     QLayoutItem *item;
-    while ((item = recentChannelsLayout->takeAt(1)) != 0) {
+    while ((item = recentChannelsLayout->takeAt(1)) != nullptr) {
         item->widget()->close();
         delete item;
     }
 
     recentChannelsLabel->setVisible(!keywords.isEmpty());
-    // TODO MainWindow::instance()->getAction("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
+
+#ifdef APP_MAC
+    QPalette p = palette();
+    p.setColor(QPalette::Highlight, mac::accentColor());
+#endif
 
     for (const QString &keyword : keywords) {
         QString link = keyword;
@@ -323,18 +328,21 @@ void SearchView::updateRecentChannels() {
         int separator = keyword.indexOf('|');
         if (separator > 0 && separator + 1 < keyword.length()) {
             link = keyword.left(separator);
-            display = keyword.mid(separator+1);
+            display = keyword.mid(separator + 1);
         }
-        QPushButton *itemButton = new QPushButton(display);
-        itemButton->setAttribute(Qt::WA_DeleteOnClose);
-        itemButton->setProperty("recentItem", true);
-        itemButton->setCursor(Qt::PointingHandCursor);
-        itemButton->setFocusPolicy(Qt::TabFocus);
-        itemButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
-        connect(itemButton, &QPushButton::clicked, [this,link]() {
-            watchChannel(link);
+
+        ClickableLabel *item = new ClickableLabel(display);
+#ifdef APP_MAC
+        item->setPalette(p);
+#endif
+        item->setAttribute(Qt::WA_DeleteOnClose);
+        item->setProperty("recentItem", true);
+        item->setFocusPolicy(Qt::TabFocus);
+        connect(item, &ClickableLabel::clicked, [this, link]() { watchChannel(link); });
+        connect(item, &ClickableLabel::hovered, item, [item](bool value) {
+            item->setForegroundRole(value ? QPalette::Highlight : QPalette::WindowText);
         });
-        recentChannelsLayout->addWidget(itemButton);
+        recentChannelsLayout->addWidget(item);
     }
 }
 
@@ -344,7 +352,6 @@ void SearchView::watch() {
 }
 
 void SearchView::textChanged(const QString &text) {
-    watchButton->setEnabled(!text.simplified().isEmpty());
     lastChannelSuggestions.clear();
 }
 
@@ -357,14 +364,6 @@ void SearchView::watch(const QString &query) {
         return;
     }
 
-    /*
-    if (typeCombo->currentIndex() == 1) {
-        // Channel search
-        MainWindow::instance()->channelSearch(q);
-        return;
-    }
-    */
-
     SearchParams *searchParams = new SearchParams();
     searchParams->setKeywords(q);
 
@@ -401,10 +400,7 @@ void SearchView::watchKeywords(const QString &query) {
         return;
     }
 
-    // if (typeCombo->currentIndex() == 0) {
-        queryEdit->setText(q);
-        watchButton->setEnabled(true);
-    // }
+    queryEdit->setText(q);
 
     SearchParams *searchParams = new SearchParams();
     searchParams->setKeywords(q);
@@ -413,18 +409,6 @@ void SearchView::watchKeywords(const QString &query) {
     emit search(searchParams);
 }
 
-void SearchView::paintEvent(QPaintEvent *event) {
-    QWidget::paintEvent(event);
-    QBrush brush;
-    if (window()->isActiveWindow()) {
-        brush = palette().base();
-    } else {
-        brush = palette().window();
-    }
-    QPainter painter(this);
-    painter.fillRect(0, 0, width(), height(), brush);
-}
-
 void SearchView::searchTypeChanged(int index) {
     if (index == 0) {
         queryEdit->setSuggester(youtubeSuggest);
@@ -438,11 +422,8 @@ void SearchView::searchTypeChanged(int index) {
 void SearchView::suggestionAccepted(Suggestion *suggestion) {
     if (suggestion->type == QLatin1String("channel")) {
         watchChannel(suggestion->userData);
-    } else watch(suggestion->value);
-}
-
-void SearchView::screenChanged() {
-    logo->setPixmap(IconUtils::pixmap(":/images/app.png"));
+    } else
+        watch(suggestion->value);
 }
 
 void SearchView::onChannelSuggestions(const QVector<Suggestion *> &suggestions) {
index 172b0cacc765795302bf37964e3f82533359c895..d6841d287e0256292b345d1c0aac4c9eae81f400 100644 (file)
@@ -18,8 +18,8 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 
 $END_LICENSE */
 
-#ifndef __SEARCHVIEW_H__
-#define __SEARCHVIEW_H__
+#ifndef SEARCHVIEW_H
+#define SEARCHVIEW_H
 
 #include <QtWidgets>
 
@@ -33,11 +33,10 @@ class Suggestion;
 class ClickableLabel;
 
 class SearchView : public View {
-
     Q_OBJECT
 
 public:
-    SearchView(QWidget *parent = 0);
+    SearchView(QWidget *parent = nullptr);
     void updateRecentKeywords();
     void updateRecentChannels();
 
@@ -49,38 +48,32 @@ public slots:
     void watchKeywords(const QString &query);
 
 signals:
-    void search(SearchParams*);
-
-protected:
-    void paintEvent(QPaintEvent *);
+    void search(SearchParams *);
 
 private slots:
     void watch();
     void textChanged(const QString &text);
     void searchTypeChanged(int index);
     void suggestionAccepted(Suggestion *suggestion);
-    void screenChanged();
-    void onChannelSuggestions(const QVector<Suggestion*> &suggestions);
+    void onChannelSuggestions(const QVector<Suggestion *> &suggestions);
 
 private:
     YTSuggester *youtubeSuggest;
     ChannelSuggest *channelSuggest;
 
-    QComboBox *typeCombo;
     SearchWidget *queryEdit;
     QLabel *recentKeywordsLabel;
     QBoxLayout *recentKeywordsLayout;
     QLabel *recentChannelsLabel;
     QBoxLayout *recentChannelsLayout;
     QLabel *message;
-    QPushButton *watchButton;
 
     QStringList recentKeywords;
     QStringList recentChannels;
 
-    QVector<Suggestion*> lastChannelSuggestions;
+    QVector<Suggestion *> lastChannelSuggestions;
 
     ClickableLabel *logo;
 };
 
-#endif // __SEARCHVIEW_H__
+#endif // SEARCHVIEW_H
index dd42b7014f0ab8b29423c23b57e1ea115cef3104..b429d38bea4078a00bcca4953e42ececc1f1dc62 100644 (file)
@@ -9,7 +9,6 @@ class Suggestion;
 class AutoComplete;
 
 class SearchWidget {
-
 public:
     virtual QMenu *menu() const = 0;
     virtual void setMenu(QMenu *menu) = 0;
@@ -17,7 +16,7 @@ public:
     virtual void preventSuggest() = 0;
     virtual void selectAll() = 0;
     virtual void setSuggester(Suggester *suggester) = 0;
-    virtual void setInactiveText(const QString &text) = 0;
+    virtual void setPlaceholderText(const QString &text) = 0;
     virtual void setText(const QString &text) = 0;
     virtual AutoComplete *getAutoComplete() = 0;
     virtual void emitTextChanged(const QString &text) = 0;
@@ -31,7 +30,6 @@ signals:
     void textEdited(const QString &text);
     void search(const QString &text);
     void suggestionAccepted(Suggestion *suggestion);
-
 };
 
 #endif // SEARCHWIDGET
index 281673ffc119993b74e535e36d7143e0a7ae9dce..81fb00a2b8d2d71cb63e18f1b11884bda7caf304 100644 (file)
@@ -2,14 +2,20 @@
 
 class MyProxyStyle : public QProxyStyle {
 public:
-    int styleHint(StyleHint hint, const QStyleOption *option = 0,
-                  const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const {
-        if (hint == SH_Slider_AbsoluteSetButtons)
-                return Qt::LeftButton;
-        return QProxyStyle::styleHint(hint, option, widget, returnData);
-    }
+    int styleHint(StyleHint hint,
+                  const QStyleOption *option = nullptr,
+                  const QWidget *widget = nullptr,
+                  QStyleHintReturn *returnData = nullptr) const;
 };
 
+int MyProxyStyle::styleHint(QStyle::StyleHint hint,
+                            const QStyleOption *option,
+                            const QWidget *widget,
+                            QStyleHintReturn *returnData) const {
+    if (hint == SH_Slider_AbsoluteSetButtons) return Qt::LeftButton;
+    return QProxyStyle::styleHint(hint, option, widget, returnData);
+}
+
 SeekSlider::SeekSlider(QWidget *parent) : QSlider(parent) {
     setStyle(new MyProxyStyle());
 }
index a56b6c0f9ebf6b482206d544f7c4d11c468c7afb..52835f994a09bad1aa6fefc8bf1ccfacdb73a548 100644 (file)
@@ -4,12 +4,10 @@
 #include <QtWidgets>
 
 class SeekSlider : public QSlider {
-
     Q_OBJECT
 
 public:
-    SeekSlider(QWidget *parent = 0);
-    
+    SeekSlider(QWidget *parent = nullptr);
 };
 
 #endif // SEEKSLIDER_H
index 1af157e950d77cccd6b83a128a1ed9c477d5cf04..e763f12877da3ff73dc1699d1f33ebbd9401ee46 100644 (file)
@@ -19,30 +19,25 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "segmentedcontrol.h"
-#include "mainwindow.h"
 #include "fontutils.h"
 #include "iconutils.h"
+#include "mainwindow.h"
 #include "painterutils.h"
 
-SegmentedControl::SegmentedControl (QWidget *parent) : QWidget(parent) {
+SegmentedControl::SegmentedControl(QWidget *parent) : QWidget(parent) {
     setAttribute(Qt::WA_OpaquePaintEvent);
 
     setMouseTracking(true);
 
-    hoveredAction = 0;
-    checkedAction = 0;
-    pressedAction = 0;
+    hoveredAction = nullptr;
+    checkedAction = nullptr;
+    pressedAction = nullptr;
 
-#ifdef APP_WIN
-    selectedColor = palette().color(QPalette::Base);
-#else
-    selectedColor = palette().color(QPalette::Window);
-#endif
-    int darkerFactor = 105;
-    backgroundColor = selectedColor.darker(darkerFactor);
-    borderColor = backgroundColor;
-    hoveredColor = backgroundColor.darker(darkerFactor);
-    pressedColor = hoveredColor.darker(darkerFactor);
+    setupColors();
+    connect(qApp, &QGuiApplication::paletteChanged, this, [this] {
+        setupColors();
+        update();
+    });
 }
 
 QAction *SegmentedControl::addAction(QAction *action) {
@@ -54,10 +49,10 @@ QAction *SegmentedControl::addAction(QAction *action) {
 
 bool SegmentedControl::setCheckedAction(int index) {
     if (index < 0) {
-        checkedAction = 0;
+        checkedAction = nullptr;
         return true;
     }
-    QActionnewCheckedAction = actionList.at(index);
+    QAction *newCheckedAction = actionList.at(index);
     return setCheckedAction(newCheckedAction);
 }
 
@@ -65,20 +60,19 @@ bool SegmentedControl::setCheckedAction(QAction *action) {
     if (checkedAction == action) {
         return false;
     }
-    if (checkedAction)
-        checkedAction->setChecked(false);
+    if (checkedAction) checkedAction->setChecked(false);
     checkedAction = action;
     checkedAction->setChecked(true);
     update();
     return true;
 }
 
-QSize SegmentedControl::minimumSizeHint (void) const {
+QSize SegmentedControl::minimumSizeHint() const {
     int itemsWidth = calculateButtonWidth() * actionList.size() * 1.2;
-    return(QSize(itemsWidth, QFontMetrics(font()).height() * 1.8));
+    return (QSize(itemsWidth, QFontMetrics(font()).height() * 1.8));
 }
 
-void SegmentedControl::paintEvent (QPaintEvent * /*event*/) {
+void SegmentedControl::paintEvent(QPaintEvent * /*event*/) {
     const int height = rect().height();
     const int width = rect().width();
 
@@ -87,8 +81,7 @@ void SegmentedControl::paintEvent (QPaintEvent * /*event*/) {
     // Calculate Buttons Size & Location
     const int buttonWidth = width / actionList.size();
 
-    const qreal pixelRatio = IconUtils::pixelRatio();
-
+    const qreal pixelRatio = devicePixelRatioF();
     QPen pen(borderColor);
     const qreal penWidth = 1. / pixelRatio;
     pen.setWidthF(penWidth);
@@ -101,22 +94,20 @@ void SegmentedControl::paintEvent (QPaintEvent * /*event*/) {
         QAction *action = actionList.at(i);
         if (i + 1 == actionCount) {
             // last button
-            rect.setWidth(width - buttonWidth * (actionCount-1));
+            rect.setWidth(width - buttonWidth * (actionCount - 1));
             paintButton(&p, rect, action);
         } else {
             paintButton(&p, rect, action);
             rect.moveLeft(rect.x() + rect.width());
         }
     }
-    const qreal y = height - penWidth;
-    p.drawLine(QPointF(0, y), QPointF(width, y));
 }
 
-void SegmentedControl::mouseMoveEvent (QMouseEvent *event) {
+void SegmentedControl::mouseMoveEvent(QMouseEvent *event) {
     QAction *action = findHoveredAction(event->pos());
 
     if (!action && hoveredAction) {
-        hoveredAction = 0;
+        hoveredAction = nullptr;
         update();
     } else if (action && action != hoveredAction) {
         hoveredAction = action;
@@ -138,7 +129,7 @@ void SegmentedControl::mousePressEvent(QMouseEvent *event) {
 
 void SegmentedControl::mouseReleaseEvent(QMouseEvent *event) {
     QWidget::mouseReleaseEvent(event);
-    pressedAction = 0;
+    pressedAction = nullptr;
     if (hoveredAction) {
         bool changed = setCheckedAction(hoveredAction);
         if (changed) hoveredAction->trigger();
@@ -149,22 +140,37 @@ void SegmentedControl::leaveEvent(QEvent *event) {
     QWidget::leaveEvent(event);
     // status tip
     MainWindow::instance()->statusBar()->clearMessage();
-    hoveredAction = 0;
-    pressedAction = 0;
+    hoveredAction = nullptr;
+    pressedAction = nullptr;
     update();
 }
 
-QAction *SegmentedControl::findHoveredAction(const QPoint& pos) const {
+void SegmentedControl::setupColors() {
+    selectedColor = palette().color(QPalette::Base);
+    if (selectedColor.value() > 128) {
+        int factor = 105;
+        backgroundColor = selectedColor.darker(factor);
+        borderColor = backgroundColor;
+        hoveredColor = backgroundColor.darker(factor);
+        pressedColor = hoveredColor.darker(factor);
+    } else {
+        int factor = 130;
+        backgroundColor = selectedColor.lighter(factor);
+        borderColor = backgroundColor;
+        hoveredColor = backgroundColor.lighter(factor);
+        pressedColor = hoveredColor.lighter(factor);
+    }
+}
+
+QAction *SegmentedControl::findHoveredAction(const QPoint &pos) const {
     const int w = width();
-    if (pos.y() <= 0 || pos.x() >= w || pos.y() >= height())
-        return 0;
+    if (pos.y() <= 0 || pos.x() >= w || pos.y() >= height()) return nullptr;
 
     int buttonWidth = w / actionList.size();
 
     int buttonIndex = pos.x() / buttonWidth;
 
-    if (buttonIndex >= actionList.size())
-        return 0;
+    if (buttonIndex >= actionList.size()) return nullptr;
     return actionList[buttonIndex];
 }
 
@@ -178,7 +184,7 @@ int SegmentedControl::calculateButtonWidth() const {
     return itemWidth;
 }
 
-void SegmentedControl::paintButton(QPainter *painter, const QRectrect, const QAction *action) {
+void SegmentedControl::paintButton(QPainter *painter, const QRect &rect, const QAction *action) {
     painter->save();
     painter->translate(rect.topLeft());
 
@@ -203,11 +209,9 @@ void SegmentedControl::paintButton(QPainter *painter, const QRect& rect, const Q
     painter->drawText(0, 0, width, height, Qt::AlignCenter, text);
 
     if (action->property("notifyCount").isValid()) {
-        QRect textBox = painter->boundingRect(rect,
-                                              Qt::AlignCenter,
-                                              text);
+        QRect textBox = painter->boundingRect(rect, Qt::AlignCenter, text);
         painter->translate((width + textBox.width()) / 2 + 10, (height - textBox.height()) / 2);
-        PainterUtils::paintBadge(painter, action->property("notifyCount").toString(), false, QColor(0,0,0,64));
+        PainterUtils::paintBadge(painter, action->property("notifyCount").toString(), false, c);
     }
 
     painter->restore();
index 06391466a3f48a9a0955fc53ed6b2da1df6e8a3a..8e9813fb67590e1010584023b70b63cce62318fa 100644 (file)
@@ -24,18 +24,17 @@ $END_LICENSE */
 #include <QtWidgets>
 
 class SegmentedControl : public QWidget {
-
     Q_OBJECT
 
 public:
-    SegmentedControl(QWidget *parent = 0);
+    SegmentedControl(QWidget *parent = nullptr);
     QAction *addAction(QAction *action);
     bool setCheckedAction(int index);
     bool setCheckedAction(QAction *action);
     QSize minimumSizeHint(void) const;
 
 signals:
-    void checkedActionChanged(QAction & action);
+    void checkedActionChanged(QAction &action);
 
 protected:
     void paintEvent(QPaintEvent *event);
@@ -44,11 +43,12 @@ protected:
     void mouseReleaseEvent(QMouseEvent *event);
     void leaveEvent(QEvent *event);
 
+private slots:
+    void setupColors();
+
 private:
-    void paintButton(QPainter *painter,
-                    const QRect& rect,
-                    const QAction *action);
-    QAction *findHoveredAction(const QPoint& pos) const;
+    void paintButton(QPainter *painter, const QRect &rect, const QAction *action);
+    QAction *findHoveredAction(const QPoint &pos) const;
     int calculateButtonWidth() const;
 
     QVector<QAction *> actionList;
@@ -61,7 +61,6 @@ private:
     QColor selectedColor;
     QColor hoveredColor;
     QColor pressedColor;
-
 };
 
 #endif /* !SEGMENTEDCONTROL_H */
index e8d1b0dcb448c4f552af46618367e58fd25318ca..84f232f2fc97623a065463d8fa5571d39616b479 100644 (file)
@@ -13,6 +13,6 @@ ShareToolbar::ShareToolbar(QWidget *parent) : QToolBar(parent) {
 }
 
 void ShareToolbar::setLeftMargin(int value) {
-    setStyleSheet("border:0;margin-left:" + QString::number(value) + "px");
-    disconnect(sender(), 0, this, 0);
+    setStyleSheet("QToolButton {border:0;margin-left:" + QString::number(value) + "px}");
+    disconnect(sender(), nullptr, this, nullptr);
 }
index 399d65f244687dc05b96bac9809f2b0a527fc6c3..150db794ef696ee549f0c532439f5db287f1c9c6 100644 (file)
@@ -19,12 +19,13 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "sidebarheader.h"
+#include "fontutils.h"
 #include "iconutils.h"
+#include "mainwindow.h"
 #include "mediaview.h"
 #include "videosource.h"
-#include "fontutils.h"
 
-SidebarHeader::SidebarHeader(QWidget *parent) : QToolBar(parent) { }
+SidebarHeader::SidebarHeader(QWidget *parent) : QToolBar(parent) {}
 
 void SidebarHeader::setup() {
     static bool isSetup = false;
@@ -33,24 +34,22 @@ void SidebarHeader::setup() {
 
     setIconSize(QSize(16, 16));
 
-    backAction = new QAction(
-                IconUtils::icon("go-previous"),
-                tr("&Back"), this);
+    backAction = new QAction(tr("&Back"), this);
+    IconUtils::setIcon(backAction, "go-previous");
     backAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Left));
     connect(backAction, SIGNAL(triggered()), MediaView::instance(), SLOT(goBack()));
     addAction(backAction);
 
-    forwardAction = new QAction(
-                IconUtils::icon("go-next"),
-                tr("&Back"), this);
+    forwardAction = new QAction(tr("&Forward"), this);
+    IconUtils::setIcon(forwardAction, "go-next");
     forwardAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Right));
     connect(forwardAction, SIGNAL(triggered()), MediaView::instance(), SLOT(goForward()));
     addAction(forwardAction);
 
     const auto a = actions();
-    for (QActionaction : a) {
+    for (QAction *action : a) {
         window()->addAction(action);
-        IconUtils::setupAction(action);
+        MainWindow::instance()->setupAction(action);
     }
 
     QWidget *spacerWidget = new QWidget(this);
@@ -65,7 +64,7 @@ QSize SidebarHeader::minimumSizeHint() const {
 void SidebarHeader::updateInfo() {
     setup();
 
-    const QVector<VideoSource*> &history = MediaView::instance()->getHistory();
+    const QVector<VideoSource *> &history = MediaView::instance()->getHistory();
     int currentIndex = MediaView::instance()->getHistoryIndex();
 
     bool canGoForward = MediaView::instance()->canGoForward();
@@ -73,11 +72,9 @@ void SidebarHeader::updateInfo() {
     forwardAction->setEnabled(canGoForward);
     if (canGoForward) {
         VideoSource *nextVideoSource = history.at(currentIndex + 1);
-        forwardAction->setStatusTip(
-                    tr("Forward to %1")
-                    .arg(nextVideoSource->getName())
-                    + " (" + forwardAction->shortcut().toString(QKeySequence::NativeText) + ")"
-                    );
+        forwardAction->setStatusTip(tr("Forward to %1").arg(nextVideoSource->getName()) + " (" +
+                                    forwardAction->shortcut().toString(QKeySequence::NativeText) +
+                                    ")");
     }
 
     bool canGoBack = MediaView::instance()->canGoBack();
@@ -86,16 +83,13 @@ void SidebarHeader::updateInfo() {
     backAction->setEnabled(canGoBack);
     if (canGoBack) {
         VideoSource *previousVideoSource = history.at(currentIndex - 1);
-        backAction->setStatusTip(
-                    tr("Back to %1")
-                    .arg(previousVideoSource->getName())
-                    + " (" + backAction->shortcut().toString(QKeySequence::NativeText) + ")"
-                    );
+        backAction->setStatusTip(tr("Back to %1").arg(previousVideoSource->getName()) + " (" +
+                                 backAction->shortcut().toString(QKeySequence::NativeText) + ")");
     }
 
     VideoSource *currentVideoSource = history.at(currentIndex);
-    connect(currentVideoSource, SIGNAL(nameChanged(QString)),
-            SLOT(updateTitle(QString)), Qt::UniqueConnection);
+    connect(currentVideoSource, SIGNAL(nameChanged(QString)), SLOT(updateTitle(QString)),
+            Qt::UniqueConnection);
     setTitle(currentVideoSource->getName());
 }
 
@@ -108,20 +102,19 @@ void SidebarHeader::setTitle(const QString &title) {
     this->title = title;
     update();
 
-    QVector<VideoSource*> history = MediaView::instance()->getHistory();
+    QVector<VideoSource *> history = MediaView::instance()->getHistory();
     int currentIndex = MediaView::instance()->getHistoryIndex();
     VideoSource *currentVideoSource = history.at(currentIndex);
-    for (QActionaction : videoSourceActions)
+    for (QAction *action : videoSourceActions)
         removeAction(action);
     videoSourceActions = currentVideoSource->getActions();
     addActions(videoSourceActions);
 }
 
 void SidebarHeader::paintEvent(QPaintEvent *event) {
-    QToolBar::paintEvent(event);
     if (title.isEmpty()) return;
     QPainter p(this);
-    p.setPen(Qt::white);
+    p.setPen(palette().windowText().color());
 
     const QRect r = rect();
 
@@ -129,12 +122,10 @@ void SidebarHeader::paintEvent(QPaintEvent *event) {
     QRect textBox = p.boundingRect(r, Qt::AlignCenter, t);
     int i = 1;
     const int margin = forwardAction->isVisible() ? 50 : 25;
-    while (textBox.width() > r.width() - margin*2 && t.length() > 3) {
+    while (textBox.width() > r.width() - margin * 2 && t.length() > 3) {
         t = t.left(t.length() - i).trimmed() + QStringLiteral("\u2026");
         textBox = p.boundingRect(r, Qt::AlignCenter, t);
         i++;
     }
     p.drawText(r, Qt::AlignCenter, t);
-
-
 }
index 27283d7e17cd60a6db6145a1c7841b4a6a0f0c7b..2b1d546e1ab3aaa6df3e66f16713b8799af63b22 100644 (file)
@@ -19,22 +19,24 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "sidebarwidget.h"
+#include "mainwindow.h"
 #include "refinesearchbutton.h"
 #include "refinesearchwidget.h"
 #include "sidebarheader.h"
-#include "mainwindow.h"
 #ifdef APP_EXTRA
 #include "extra.h"
 #endif
 
-SidebarWidget::SidebarWidget(QWidget *parent) :
-    QWidget(parent), playlistWidth(0) {
-    playlist = 0;
+SidebarWidget::SidebarWidget(QWidget *parent) : QWidget(parent), playlistWidth(0) {
+    playlist = nullptr;
 
     QBoxLayout *layout = new QVBoxLayout(this);
     layout->setSpacing(0);
     layout->setMargin(0);
 
+    setBackgroundRole(QPalette::Base);
+    setAutoFillBackground(true);
+
     sidebarHeader = new SidebarHeader();
     layout->addWidget(sidebarHeader);
 
@@ -46,11 +48,9 @@ SidebarWidget::SidebarWidget(QWidget *parent) :
     messageLabel->setAutoFillBackground(true);
     messageLabel->setWordWrap(true);
     messageLabel->setTextFormat(Qt::RichText);
-    messageLabel->setTextInteractionFlags(
-                Qt::LinksAccessibleByKeyboard |
-                Qt::LinksAccessibleByMouse);
-    connect(messageLabel, SIGNAL(linkActivated(QString)),
-            SIGNAL(suggestionAccepted(QString)));
+    messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard |
+                                          Qt::LinksAccessibleByMouse);
+    connect(messageLabel, SIGNAL(linkActivated(QString)), SIGNAL(suggestionAccepted(QString)));
     messageLabel->hide();
     layout->addWidget(messageLabel);
 
@@ -62,8 +62,9 @@ SidebarWidget::SidebarWidget(QWidget *parent) :
 
 void SidebarWidget::setup() {
     refineSearchButton = new RefineSearchButton(this);
-    refineSearchButton->setStatusTip(tr("Refine Search")
-                                     + " (" + QKeySequence(Qt::CTRL + Qt::Key_R).toString(QKeySequence::NativeText) + ")");
+    refineSearchButton->setStatusTip(
+            tr("Refine Search") + " (" +
+            QKeySequence(Qt::CTRL + Qt::Key_R).toString(QKeySequence::NativeText) + ")");
     refineSearchButton->hide();
     connect(refineSearchButton, SIGNAL(clicked()), SLOT(showRefineSearchWidget()));
 
@@ -115,20 +116,20 @@ void SidebarWidget::hideRefineSearchWidget() {
 }
 
 void SidebarWidget::toggleRefineSearch(bool show) {
-    if (show) showRefineSearchWidget();
-    else hideRefineSearchWidget();
+    if (show)
+        showRefineSearchWidget();
+    else
+        hideRefineSearchWidget();
 }
 
 void SidebarWidget::resizeEvent(QResizeEvent *event) {
     QWidget::resizeEvent(event);
-    refineSearchButton->move(
-                playlist->viewport()->width() - refineSearchButton->minimumWidth(),
-                height() - refineSearchButton->minimumHeight());
+    refineSearchButton->move(playlist->viewport()->width() - refineSearchButton->minimumWidth(),
+                             height() - refineSearchButton->minimumHeight());
 }
 
 void SidebarWidget::enterEvent(QEvent *) {
-    if (stackedWidget->currentWidget() != refineSearchWidget)
-        showRefineSearchButton();
+    if (stackedWidget->currentWidget() != refineSearchWidget) showRefineSearchButton();
 }
 
 void SidebarWidget::leaveEvent(QEvent *) {
@@ -154,9 +155,8 @@ void SidebarWidget::handleMouseMove() {
 
 void SidebarWidget::showRefineSearchButton() {
     if (!refineSearchWidget->isEnabled()) return;
-    refineSearchButton->move(
-                playlist->viewport()->width() - refineSearchButton->minimumWidth(),
-                height() - refineSearchButton->minimumHeight());
+    refineSearchButton->move(playlist->viewport()->width() - refineSearchButton->minimumWidth(),
+                             height() - refineSearchButton->minimumHeight());
     refineSearchButton->show();
 }
 
@@ -169,13 +169,12 @@ void SidebarWidget::showSuggestions(const QStringList &suggestions) {
     }
     message = message.arg(suggestionLinks);
 
-    QString html =
-            "<html>"
-            "<style>"
-            "a { color: palette(text); text-decoration: none; font-weight: bold }"
-            "</style>"
-            "<body>%1</body>"
-            "</html>";
+    QString html = "<html>"
+                   "<style>"
+                   "a { color: palette(text); text-decoration: none; font-weight: bold }"
+                   "</style>"
+                   "<body>%1</body>"
+                   "</html>";
     html = html.arg(message);
     messageLabel->setText(html);
     messageLabel->show();
index f15e4da01b5e5bf7d7b76cf84c4410ee922587ad..aa66ee29f2819a8a06f4f433433e2e3f835e5c26 100644 (file)
@@ -28,16 +28,15 @@ class RefineSearchWidget;
 class SidebarHeader;
 
 class SidebarWidget : public QWidget {
-
     Q_OBJECT
 
 public:
-    SidebarWidget(QWidget *parent = 0);
+    SidebarWidget(QWidget *parent = nullptr);
     QListView *getPlaylist() { return playlist; }
     void setPlaylist(QListView *playlist);
     void showPlaylist();
-    RefineSearchWidgetgetRefineSearchWidget() { return refineSearchWidget; }
-    SidebarHeadergetHeader() { return sidebarHeader; }
+    RefineSearchWidget *getRefineSearchWidget() { return refineSearchWidget; }
+    SidebarHeader *getHeader() { return sidebarHeader; }
     void hideSuggestions();
 
 public slots:
index 36626483a4f9a96953fcf7e0946b9185d952269d..373c0d64875627e230ec31e109ea0c88c2b91d91 100644 (file)
@@ -21,14 +21,17 @@ $END_LICENSE */
 #include "snapshotpreview.h"
 #include "mainwindow.h"
 
-SnapshotPreview::SnapshotPreview(QWidget *parent) : QWidget(parent),
-#ifdef APP_PHONON
-    mediaObject(0),
-    audioOutput(0)
+#ifdef MEDIA_QTAV
+#include "mediaqtav.h"
 #endif
-    {
+#ifdef MEDIA_MPV
+#include "mediampv.h"
+#endif
+
+SnapshotPreview::SnapshotPreview(QWidget *parent) : QWidget(parent), mediaObject(nullptr) {
+    setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
+                   Qt::WindowTransparentForInput | Qt::WindowDoesNotAcceptFocus);
     setAttribute(Qt::WA_ShowWithoutActivating);
-    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
     setAttribute(Qt::WA_StaticContents);
     setAttribute(Qt::WA_OpaquePaintEvent);
     setAttribute(Qt::WA_NoSystemBackground);
@@ -49,15 +52,29 @@ SnapshotPreview::SnapshotPreview(QWidget *parent) : QWidget(parent),
 }
 
 void SnapshotPreview::start(QWidget *widget, const QPixmap &pixmap, bool soundOnly) {
-#ifdef APP_PHONON
     if (!mediaObject) {
-        mediaObject = new Phonon::MediaObject(this);
-        audioOutput = new Phonon::AudioOutput(Phonon::NotificationCategory, this);
-        Phonon::createPath(mediaObject, audioOutput);
+#ifdef MEDIA_QTAV
+        mediaObject = new MediaQtAV(this);
+#elif defined MEDIA_MPV
+        mediaObject = new MediaMPV(this);
+#else
+        qFatal("No media backend defined");
+#endif
+        if (mediaObject) {
+            mediaObject->setAudioOnly(true);
+            mediaObject->init();
+        }
     }
-    mediaObject->setCurrentSource(QUrl("qrc:///sounds/snapshot.wav"));
-    mediaObject->play();
+
+#ifdef APP_MAC
+    QString soundPath = QCoreApplication::applicationDirPath() + "/../Resources";
+#elif defined PKGDATADIR
+    QString soundPath = QLatin1String(PKGDATADIR) + "/sounds";
+#else
+    QString soundPath = QCoreApplication::applicationDirPath() + "/sounds";
 #endif
+
+    if (mediaObject) mediaObject->play(soundPath + "/snapshot.wav");
     if (soundOnly) return;
 
     resize(pixmap.size());
@@ -79,8 +96,10 @@ void SnapshotPreview::start(QWidget *widget, const QPixmap &pixmap, bool soundOn
     timeLine->start();
 #endif
     timer->start();
-    if (isVisible()) update();
-    else show();
+    if (isVisible())
+        update();
+    else
+        show();
 }
 
 void SnapshotPreview::paintEvent(QPaintEvent *e) {
index 7bfab08d9fe089dd195c61d89222f10b27fe0696..2039c0d0039d81a0262dd9efd60634211d9c398c 100644 (file)
@@ -23,18 +23,13 @@ $END_LICENSE */
 
 #include <QtWidgets>
 
-#ifdef APP_PHONON
-#include <phonon/audiooutput.h>
-#include <phonon/mediaobject.h>
-#include <phonon/videowidget.h>
-#endif
+#include "media.h"
 
 class SnapshotPreview : public QWidget {
-
     Q_OBJECT
 
 public:
-    SnapshotPreview(QWidget *parent = 0);
+    SnapshotPreview(QWidget *parent = nullptr);
     void start(QWidget *widget, const QPixmap &pixmap, bool soundOnly);
 
 signals:
@@ -51,10 +46,7 @@ private:
     QTimeLine *timeLine;
     QPoint offset;
     QTimer *timer;
-#ifdef APP_PHONON
-    Phonon::MediaObject *mediaObject;
-    Phonon::AudioOutput *audioOutput;
-#endif
+    Media *mediaObject;
 };
 
 #endif // SNAPSHOTPREVIEW_H
index cb87b186f6d005cf0529fcfc779071be3bc5ddbc..5b33b2ee020178a0ae1fb03b3eff7b5d2cfc6c3b 100644 (file)
@@ -24,9 +24,8 @@ $END_LICENSE */
 #include <QtWidgets>
 
 class Spacer : public QWidget {
-
 public:
-    Spacer(QWidget *parent = 0, int minWidth = 60);
+    Spacer(QWidget *parent = nullptr, int minWidth = 60);
 
 protected:
     QSize sizeHint() const;
index 3e83208640c18b54e8496a67838196fe4793fc9f..9080f1ce0be2beef85219a91bca750f1465f06e7 100644 (file)
@@ -27,9 +27,7 @@ $END_LICENSE */
 #include "ytstandardfeed.h"
 
 StandardFeedsView::StandardFeedsView(QWidget *parent) : View(parent), layout(0) {
-    QPalette p = palette();
-    p.setBrush(QPalette::Window, Qt::black);
-    setPalette(p);
+    setBackgroundRole(QPalette::Base);
     setAutoFillBackground(true);
 
     connect(MainWindow::instance()->getAction("worldwideRegion"), SIGNAL(triggered()),
@@ -95,7 +93,7 @@ void StandardFeedsView::removeVideoSourceWidget(VideoSourceWidget *videoSourceWi
     }
 
     const int itemCount = items.size();
-    const int cols = itemCount / 3;
+    const int cols = 4; // itemCount / 3;
     for (int i = itemCount - 1; i >= 0; i--) {
         QLayoutItem *item = items.at(i);
         int index = itemCount - 1 - i;
@@ -114,7 +112,7 @@ void StandardFeedsView::resetLayout() {
 
     layout = new QGridLayout(this);
     layout->setMargin(0);
-    layout->setSpacing(1);
+    layout->setSpacing(0);
 }
 
 YTStandardFeed *
@@ -134,12 +132,12 @@ void StandardFeedsView::appear() {
         load();
     }
     QAction *regionAction = MainWindow::instance()->getRegionAction();
-    MainWindow::instance()->showActionInStatusBar(regionAction, true);
+    MainWindow::instance()->showActionsInStatusBar({regionAction}, true);
 }
 
 void StandardFeedsView::disappear() {
     QAction *regionAction = MainWindow::instance()->getRegionAction();
-    MainWindow::instance()->showActionInStatusBar(regionAction, false);
+    MainWindow::instance()->showActionsInStatusBar({regionAction}, false);
 }
 
 void StandardFeedsView::selectWorldwideRegion() {
index e03dbacd28338e5d6e7c24b59490f127beede1ef..da26af2507e901d5c3e5939327877352d2156848 100644 (file)
@@ -1,6 +1,8 @@
 #include "toolbarmenu.h"
 #include "mainwindow.h"
 #include "sharetoolbar.h"
+#include "videodefinition.h"
+#include "yt3.h"
 
 ToolbarMenu::ToolbarMenu(QWidget *parent) : QMenu(parent) {
     MainWindow *w = MainWindow::instance();
@@ -22,16 +24,46 @@ ToolbarMenu::ToolbarMenu(QWidget *parent) : QMenu(parent) {
     widgetAction->setDefaultWidget(shareToolbar);
     addAction(widgetAction);
     addSeparator();
+
     addAction(w->getAction("compactView"));
     addAction(w->getAction("ontop"));
     addSeparator();
+
+    QToolBar *definitionToolbar = new QToolBar();
+    definitionToolbar->setStyleSheet("QToolButton { padding: 0}");
+    definitionToolbar->setToolButtonStyle(Qt::ToolButtonTextOnly);
+    definitionToolbar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    QActionGroup *definitionGroup = new QActionGroup(this);
+    const VideoDefinition &preferredDefinition = YT3::instance().maxVideoDefinition();
+    int counter = 0;
+    for (auto defName : VideoDefinition::getDefinitionNames()) {
+        QAction *a = new QAction(defName);
+        a->setCheckable(true);
+        a->setChecked(preferredDefinition.getName() == defName);
+        connect(a, &QAction::triggered, this, [this, defName] {
+            MainWindow::instance()->setDefinitionMode(defName);
+            close();
+        });
+        definitionGroup->addAction(a);
+        definitionToolbar->addAction(a);
+        if (counter == 0) {
+            QWidget *w = definitionToolbar->widgetForAction(a);
+            w->setProperty("first", true);
+            counter++;
+        }
+    }
+    QWidgetAction *definitionAction = new QWidgetAction(this);
+    definitionAction->setDefaultWidget(definitionToolbar);
+    addAction(definitionAction);
+    addSeparator();
+
     addAction(w->getAction("clearRecentKeywords"));
 #ifndef APP_MAC
     addSeparator();
     addAction(w->getAction("toggleMenu"));
-#endif
     addSeparator();
     addMenu(w->getMenu("help"));
+#endif
 }
 
 void ToolbarMenu::showEvent(QShowEvent *e) {
@@ -40,9 +72,10 @@ void ToolbarMenu::showEvent(QShowEvent *e) {
     QStyleOptionMenuItem option;
     initStyleOption(&option, a);
     int leftMargin = option.maxIconWidth;
-#ifndef APP_MAC
-    // On Win & Linux the value is wrong
+#ifdef APP_WIN
     leftMargin *= 1.5;
 #endif
+    setStyleSheet("QToolBar > QToolButton[first] {margin-left:" + QString::number(leftMargin) +
+                  "px}");
     emit leftMarginChanged(leftMargin);
 }
index ed76ba877dda4cd23c3fd2fab9e246c98b780d69..fa8ddb02a085ba39bc139fe5fc1b7527edf741fc 100644 (file)
@@ -7,7 +7,7 @@ class ToolbarMenu : public QMenu {
     Q_OBJECT
 
 public:
-    ToolbarMenu(QWidget *parent = 0);
+    ToolbarMenu(QWidget *parent = nullptr);
 
 signals:
     void leftMarginChanged(int value);
index 52432b8d0efcf950566c4eb27f941d2448240f22..05d9761b58eb52ea899706c959587dd49114d466 100644 (file)
@@ -23,12 +23,13 @@ $END_LICENSE */
 #include "http.h"
 #include "httputils.h"
 #include "jsfunctions.h"
+#include "playlistitemdelegate.h"
 #include "videodefinition.h"
 #include "ytvideo.h"
 
 Video::Video()
     : duration(0), viewCount(-1), license(LicenseYouTube), definitionCode(0),
-      loadingThumbnail(false), ytVideo(0) {}
+      loadingThumbnail(false), ytVideo(nullptr) {}
 
 Video::~Video() {
     qDebug() << "Deleting" << id;
@@ -50,6 +51,7 @@ Video *Video::clone() {
     clone->published = published;
     clone->formattedPublished = formattedPublished;
     clone->viewCount = viewCount;
+    clone->formattedViewCount = formattedViewCount;
     clone->id = id;
     clone->definitionCode = definitionCode;
     return clone;
@@ -89,6 +91,11 @@ void Video::setDuration(int value) {
     formattedDuration = DataUtils::formatDuration(duration);
 }
 
+void Video::setViewCount(int value) {
+    viewCount = value;
+    formattedViewCount = DataUtils::formatCount(viewCount);
+}
+
 void Video::setPublished(const QDateTime &value) {
     published = value;
     formattedPublished = DataUtils::formatDateTime(published);
@@ -98,19 +105,20 @@ void Video::setThumbnail(const QByteArray &bytes) {
     qreal ratio = qApp->devicePixelRatio();
     thumbnail.loadFromData(bytes);
     thumbnail.setDevicePixelRatio(ratio);
-    const int thumbWidth = 160 * ratio;
+    const int thumbWidth = PlaylistItemDelegate::thumbWidth * ratio;
     if (thumbnail.width() > thumbWidth)
         thumbnail = thumbnail.scaledToWidth(thumbWidth, Qt::SmoothTransformation);
     emit gotThumbnail();
     loadingThumbnail = false;
 }
 
-void Video::streamUrlLoaded(const QUrl &streamUrl) {
+void Video::streamUrlLoaded(const QString &streamUrl, const QString &audioUrl) {
+    qDebug() << "Streams loaded";
     definitionCode = ytVideo->getDefinitionCode();
     this->streamUrl = streamUrl;
-    emit gotStreamUrl(this->streamUrl);
-    delete ytVideo;
-    ytVideo = 0;
+    emit gotStreamUrl(streamUrl, audioUrl);
+    ytVideo->deleteLater();
+    ytVideo = nullptr;
 }
 
 void Video::loadStreamUrl() {
@@ -120,7 +128,18 @@ void Video::loadStreamUrl() {
     }
     ytVideo = new YTVideo(id, this);
     connect(ytVideo, &YTVideo::gotStreamUrl, this, &Video::streamUrlLoaded);
-    connect(ytVideo, &YTVideo::errorStreamUrl, this, &Video::errorStreamUrl);
-    connect(ytVideo, &YTVideo::errorStreamUrl, ytVideo, &QObject::deleteLater);
+    connect(ytVideo, &YTVideo::errorStreamUrl, this, [this](const QString &msg) {
+        emit errorStreamUrl(msg);
+        ytVideo->deleteLater();
+        ytVideo = nullptr;
+    });
     ytVideo->loadStreamUrl();
 }
+
+void Video::abortLoadStreamUrl() {
+    if (ytVideo) {
+        ytVideo->disconnect(this);
+        ytVideo->deleteLater();
+        ytVideo = nullptr;
+    }
+}
index e0c8e44d097e171ecd9b31e36e1136ced0f6ed4e..723be191d592764c0f6667d6431254dd4a59a96a 100644 (file)
@@ -69,7 +69,8 @@ public:
     const QString &getFormattedDuration() const { return formattedDuration; }
 
     int getViewCount() const { return viewCount; }
-    void setViewCount(int value) { viewCount = value; }
+    void setViewCount(int value);
+    const QString &getFormattedViewCount() const { return formattedViewCount; }
 
     const QDateTime &getPublished() const { return published; }
     void setPublished(const QDateTime &value);
@@ -78,7 +79,9 @@ public:
     int getDefinitionCode() const { return definitionCode; }
 
     void loadStreamUrl();
-    const QUrl &getStreamUrl() { return streamUrl; }
+    const QString &getStreamUrl() { return streamUrl; }
+    bool isLoadingStreamUrl() const { return ytVideo != nullptr; }
+    void abortLoadStreamUrl();
 
     const QString &getId() const { return id; }
     void setId(const QString &value) { id = value; }
@@ -90,12 +93,12 @@ signals:
     void gotThumbnail();
     void gotMediumThumbnail(const QByteArray &bytes);
     void gotLargeThumbnail(const QByteArray &bytes);
-    void gotStreamUrl(const QUrl &streamUrl);
+    void gotStreamUrl(const QString &videoUrl, const QString &audioUrl);
     void errorStreamUrl(const QString &message);
 
 private slots:
     void setThumbnail(const QByteArray &bytes);
-    void streamUrlLoaded(const QUrl &streamUrl);
+    void streamUrlLoaded(const QString &streamUrl, const QString &audioUrl);
 
 private:
     QString title;
@@ -103,7 +106,7 @@ private:
     QString channelTitle;
     QString channelId;
     QString webpage;
-    QUrl streamUrl;
+    QString streamUrl;
     QPixmap thumbnail;
     QString thumbnailUrl;
     QString mediumThumbnailUrl;
@@ -114,6 +117,7 @@ private:
     QDateTime published;
     QString formattedPublished;
     int viewCount;
+    QString formattedViewCount;
     License license;
     QString id;
     int definitionCode;
diff --git a/src/videoarea.cpp b/src/videoarea.cpp
new file mode 100644 (file)
index 0000000..1b496ca
--- /dev/null
@@ -0,0 +1,147 @@
+/* $BEGIN_LICENSE
+
+This file is part of Minitube.
+Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
+
+Minitube is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Minitube is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
+
+$END_LICENSE */
+
+#include "videoarea.h"
+#include "loadingwidget.h"
+#include "mainwindow.h"
+#include "playlistmodel.h"
+#include "video.h"
+#include "videomimedata.h"
+#ifdef Q_OS_MAC
+#include "macutils.h"
+#endif
+#include "fontutils.h"
+#include "snapshotpreview.h"
+
+namespace {
+
+class PickMessage : public QWidget {
+public:
+    PickMessage(QWidget *parent = nullptr) : QWidget(parent) {
+        setAutoFillBackground(true);
+
+        QBoxLayout *l = new QHBoxLayout(this);
+        l->setMargin(32);
+        l->setSpacing(32);
+        l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+
+        QLabel *arrowLabel = new QLabel("←");
+        arrowLabel->setFont(FontUtils::light(64));
+        l->addWidget(arrowLabel);
+
+        QLabel *msgLabel = new QLabel(tr("Pick a video"));
+        msgLabel->setFont(FontUtils::light(32));
+        l->addWidget(msgLabel);
+    }
+};
+} // namespace
+
+VideoArea::VideoArea(QWidget *parent)
+    : QWidget(parent), videoWidget(nullptr), messageWidget(nullptr) {
+    setAttribute(Qt::WA_OpaquePaintEvent);
+
+    QBoxLayout *layout = new QVBoxLayout(this);
+    layout->setMargin(0);
+    layout->setSpacing(0);
+
+    stackedLayout = new QStackedLayout();
+    layout->addLayout(stackedLayout);
+
+#ifdef APP_SNAPSHOT
+    snapshotPreview = new SnapshotPreview();
+    connect(stackedLayout, SIGNAL(currentChanged(int)), snapshotPreview, SLOT(hide()));
+#endif
+
+    setAcceptDrops(true);
+    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+    setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
+            SLOT(showContextMenu(const QPoint &)));
+}
+
+void VideoArea::setVideoWidget(QWidget *videoWidget) {
+    this->videoWidget = videoWidget;
+    stackedLayout->addWidget(videoWidget);
+}
+
+void VideoArea::setLoadingWidget(LoadingWidget *loadingWidget) {
+    this->loadingWidget = loadingWidget;
+    stackedLayout->addWidget(loadingWidget);
+    stackedLayout->setCurrentWidget(loadingWidget);
+}
+
+void VideoArea::showVideo() {
+    if (videoWidget) stackedLayout->setCurrentWidget(videoWidget);
+    loadingWidget->clear();
+}
+
+void VideoArea::showPickMessage() {
+    if (!messageWidget) {
+        messageWidget = new PickMessage();
+        stackedLayout->addWidget(messageWidget);
+    }
+    stackedLayout->setCurrentWidget(messageWidget);
+}
+
+void VideoArea::showLoading(Video *video) {
+    loadingWidget->setVideo(video);
+    stackedLayout->setCurrentWidget(loadingWidget);
+}
+
+#ifdef APP_SNAPSHOT
+void VideoArea::showSnapshotPreview(const QPixmap &pixmap) {
+    bool soundOnly = MainWindow::instance()->isReallyFullScreen();
+    snapshotPreview->start(videoWidget, pixmap, soundOnly);
+}
+#endif
+
+void VideoArea::clear() {
+    loadingWidget->clear();
+    stackedLayout->setCurrentWidget(loadingWidget);
+}
+
+void VideoArea::mouseDoubleClickEvent(QMouseEvent *event) {
+    if (event->button() == Qt::LeftButton) emit doubleClicked();
+}
+
+void VideoArea::dragEnterEvent(QDragEnterEvent *event) {
+    // qDebug() << event->mimeData()->formats();
+    if (event->mimeData()->hasFormat("application/x-minitube-video")) {
+        event->acceptProposedAction();
+    }
+}
+
+void VideoArea::dropEvent(QDropEvent *event) {
+    const VideoMimeData *videoMimeData = qobject_cast<const VideoMimeData *>(event->mimeData());
+    if (!videoMimeData) return;
+
+    QVector<Video *> droppedVideos = videoMimeData->getVideos();
+    if (droppedVideos.isEmpty()) return;
+    Video *video = droppedVideos.at(0);
+    int row = listModel->rowForVideo(video);
+    if (row != -1) listModel->setActiveRow(row);
+    event->acceptProposedAction();
+}
+
+void VideoArea::showContextMenu(const QPoint &point) {
+    QMenu *menu = MainWindow::instance()->getMenu("video");
+    menu->exec(mapToGlobal(point));
+}
diff --git a/src/videoarea.h b/src/videoarea.h
new file mode 100644 (file)
index 0000000..33bbf83
--- /dev/null
@@ -0,0 +1,71 @@
+/* $BEGIN_LICENSE
+
+This file is part of Minitube.
+Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
+
+Minitube is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Minitube is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
+
+$END_LICENSE */
+
+#ifndef VIDEOAREAWIDGET_H
+#define VIDEOAREAWIDGET_H
+
+#include <QtWidgets>
+
+class Video;
+class LoadingWidget;
+class PlaylistModel;
+class SnapshotPreview;
+
+class VideoArea : public QWidget {
+    Q_OBJECT
+
+public:
+    VideoArea(QWidget *parent = nullptr);
+    void setVideoWidget(QWidget *videoWidget);
+    void setLoadingWidget(LoadingWidget *loadingWidget);
+    void showLoading(Video *video);
+    void showVideo();
+    void showPickMessage();
+    void clear();
+    void setListModel(PlaylistModel *listModel) { this->listModel = listModel; }
+#ifdef APP_SNAPSHOT
+    void showSnapshotPreview(const QPixmap &pixmap);
+#endif
+    bool isVideoShown() { return stackedLayout->currentWidget() == videoWidget; }
+
+signals:
+    void doubleClicked();
+    void rightClicked();
+
+protected:
+    void mouseDoubleClickEvent(QMouseEvent *event);
+    void dragEnterEvent(QDragEnterEvent *event);
+    void dropEvent(QDropEvent *event);
+
+private slots:
+    void showContextMenu(const QPoint &point);
+
+private:
+    QStackedLayout *stackedLayout;
+    QWidget *videoWidget;
+    LoadingWidget *loadingWidget;
+    QWidget *messageWidget;
+#ifdef APP_SNAPSHOT
+    SnapshotPreview *snapshotPreview;
+#endif
+    PlaylistModel *listModel;
+};
+
+#endif // VIDEOAREAWIDGET_H
diff --git a/src/videoareawidget.cpp b/src/videoareawidget.cpp
deleted file mode 100644 (file)
index 19973fa..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/* $BEGIN_LICENSE
-
-This file is part of Minitube.
-Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
-
-Minitube is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Minitube is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
-
-$END_LICENSE */
-
-#include "videoareawidget.h"
-#include "loadingwidget.h"
-#include "mainwindow.h"
-#include "playlistmodel.h"
-#include "video.h"
-#include "videomimedata.h"
-#ifdef Q_OS_MAC
-#include "macutils.h"
-#endif
-#include "snapshotpreview.h"
-#include "fontutils.h"
-
-namespace {
-
-class MessageWidget : public QWidget {
-public:
-    MessageWidget(QWidget *parent = nullptr) : QWidget(parent) {
-        QPalette p = palette();
-        p.setColor(QPalette::Window, Qt::black);
-        p.setColor(QPalette::WindowText, Qt::darkGray);
-        p.setColor(QPalette::Base, Qt::black);
-        p.setColor(QPalette::Text, Qt::darkGray);
-        setPalette(p);
-        setAutoFillBackground(true);
-
-        QBoxLayout *l = new QHBoxLayout(this);
-        l->setMargin(32);
-        l->setSpacing(32);
-        l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
-
-        QLabel *arrowLabel = new QLabel("←");
-        arrowLabel->setFont(FontUtils::light(64));
-        arrowLabel->setPalette(p);
-        l->addWidget(arrowLabel);
-
-        QLabel *msgLabel = new QLabel(tr("Pick a video"));
-        msgLabel->setFont(FontUtils::light(32));
-        msgLabel->setPalette(p);
-        l->addWidget(msgLabel);
-    }
-};
-}
-
-VideoAreaWidget::VideoAreaWidget(QWidget *parent)
-    : QWidget(parent), videoWidget(0), messageWidget(0) {
-    setAttribute(Qt::WA_OpaquePaintEvent);
-
-    QBoxLayout *layout = new QVBoxLayout(this);
-    layout->setMargin(0);
-    layout->setSpacing(0);
-
-    // hidden message widget
-    messageLabel = new QLabel(this);
-    messageLabel->setOpenExternalLinks(true);
-    messageLabel->setMargin(7);
-    messageLabel->setBackgroundRole(QPalette::ToolTipBase);
-    messageLabel->setForegroundRole(QPalette::ToolTipText);
-    messageLabel->setAutoFillBackground(true);
-    messageLabel->setWordWrap(true);
-    messageLabel->hide();
-    layout->addWidget(messageLabel);
-
-    stackedLayout = new QStackedLayout();
-    layout->addLayout(stackedLayout);
-
-#ifdef APP_SNAPSHOT
-    snapshotPreview = new SnapshotPreview();
-    connect(stackedLayout, SIGNAL(currentChanged(int)), snapshotPreview, SLOT(hide()));
-#endif
-
-    setAcceptDrops(true);
-    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-
-    setContextMenuPolicy(Qt::CustomContextMenu);
-    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
-            SLOT(showContextMenu(const QPoint &)));
-}
-
-void VideoAreaWidget::setVideoWidget(QWidget *videoWidget) {
-    this->videoWidget = videoWidget;
-    stackedLayout->addWidget(videoWidget);
-}
-
-void VideoAreaWidget::setLoadingWidget(LoadingWidget *loadingWidget) {
-    this->loadingWidget = loadingWidget;
-    stackedLayout->addWidget(loadingWidget);
-    stackedLayout->setCurrentWidget(loadingWidget);
-}
-
-void VideoAreaWidget::showVideo() {
-    if (videoWidget) stackedLayout->setCurrentWidget(videoWidget);
-    loadingWidget->clear();
-}
-
-void VideoAreaWidget::showError(const QString &message) {
-    messageLabel->setText(message);
-    messageLabel->show();
-    stackedLayout->setCurrentWidget(loadingWidget);
-}
-
-void VideoAreaWidget::showPickMessage() {
-    if (!messageWidget) {
-        messageWidget = new MessageWidget();
-        stackedLayout->addWidget(messageWidget);
-    }
-    stackedLayout->setCurrentWidget(messageWidget);
-}
-
-void VideoAreaWidget::showLoading(Video *video) {
-    messageLabel->hide();
-    messageLabel->clear();
-    stackedLayout->setCurrentWidget(loadingWidget);
-    loadingWidget->setVideo(video);
-}
-
-#ifdef APP_SNAPSHOT
-void VideoAreaWidget::showSnapshotPreview(const QPixmap &pixmap) {
-    bool soundOnly = false;
-#ifdef APP_MAC
-    soundOnly = MainWindow::instance()->isReallyFullScreen();
-#endif
-    snapshotPreview->start(videoWidget, pixmap, soundOnly);
-}
-
-void VideoAreaWidget::hideSnapshotPreview() {}
-#endif
-
-void VideoAreaWidget::clear() {
-    loadingWidget->clear();
-    messageLabel->hide();
-    messageLabel->clear();
-    stackedLayout->setCurrentWidget(loadingWidget);
-}
-
-void VideoAreaWidget::mouseDoubleClickEvent(QMouseEvent *event) {
-    if (event->button() == Qt::LeftButton) emit doubleClicked();
-}
-
-void VideoAreaWidget::dragEnterEvent(QDragEnterEvent *event) {
-    // qDebug() << event->mimeData()->formats();
-    if (event->mimeData()->hasFormat("application/x-minitube-video")) {
-        event->acceptProposedAction();
-    }
-}
-
-void VideoAreaWidget::dropEvent(QDropEvent *event) {
-    const VideoMimeData *videoMimeData = qobject_cast<const VideoMimeData *>(event->mimeData());
-    if (!videoMimeData) return;
-
-    QVector<Video *> droppedVideos = videoMimeData->getVideos();
-    if (droppedVideos.isEmpty()) return;
-    Video *video = droppedVideos.at(0);
-    int row = listModel->rowForVideo(video);
-    if (row != -1) listModel->setActiveRow(row);
-    event->acceptProposedAction();
-}
-
-void VideoAreaWidget::showContextMenu(const QPoint &point) {
-    QMenu *menu = MainWindow::instance()->getMenu("video");
-    menu->exec(mapToGlobal(point));
-}
diff --git a/src/videoareawidget.h b/src/videoareawidget.h
deleted file mode 100644 (file)
index 21b3d13..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/* $BEGIN_LICENSE
-
-This file is part of Minitube.
-Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
-
-Minitube is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Minitube is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
-
-$END_LICENSE */
-
-#ifndef VIDEOAREAWIDGET_H
-#define VIDEOAREAWIDGET_H
-
-#include <QtWidgets>
-
-class Video;
-class LoadingWidget;
-class PlaylistModel;
-class SnapshotPreview;
-
-class VideoAreaWidget : public QWidget {
-
-    Q_OBJECT
-
-public:
-    VideoAreaWidget(QWidget *parent = 0);
-    void setVideoWidget(QWidget *videoWidget);
-    void setLoadingWidget(LoadingWidget *loadingWidget);
-    void showLoading(Video* video);
-    void showVideo();
-    void showError(const QString &message);
-    void showPickMessage();
-    void clear();
-    void setListModel(PlaylistModel *listModel) {
-        this->listModel = listModel;
-    }
-#ifdef APP_SNAPSHOT
-    void showSnapshotPreview(const QPixmap &pixmap);
-#endif
-    bool isVideoShown() { return stackedLayout->currentWidget() == videoWidget; }
-
-signals:
-    void doubleClicked();
-    void rightClicked();
-
-protected:
-    void mouseDoubleClickEvent(QMouseEvent *event);
-    void dragEnterEvent(QDragEnterEvent *event);
-    void dropEvent(QDropEvent *event);
-
-private slots:
-    void showContextMenu(const QPoint &point);
-#ifdef APP_SNAPSHOT
-    void hideSnapshotPreview();
-#endif
-
-private:
-    QStackedLayout *stackedLayout;
-    QWidget *videoWidget;
-    LoadingWidget *loadingWidget;
-
-#ifdef APP_SNAPSHOT
-    SnapshotPreview *snapshotPreview;
-#endif
-
-    PlaylistModel *listModel;
-    QLabel *messageLabel;
-
-    QWidget *messageWidget;
-
-    QPoint dragPosition;
-};
-
-#endif // VIDEOAREAWIDGET_H
index 12cfd12c1f0c2e914fff892b049e17ae0cefd4e5..360817f99963db0b7033d672b930afab3a8bbf0c 100644 (file)
@@ -21,40 +21,56 @@ $END_LICENSE */
 #include "videodefinition.h"
 
 namespace {
-static const int kEmptyDefinitionCode = -1;
-
-static const VideoDefinition kEmptyDefinition(QString(), kEmptyDefinitionCode);
+const int kEmptyDefinitionCode = -1;
 
 template <typename T, T (VideoDefinition::*Getter)() const>
 const VideoDefinition &getDefinitionForImpl(T matchValue) {
     const auto &defs = VideoDefinition::getDefinitions();
+
+    for (auto i = defs.rbegin(); i != defs.rend(); ++i) {
+        if ((*i.*Getter)() == matchValue) return *i;
+    }
+    /*
     for (const VideoDefinition &def : defs) {
         if ((def.*Getter)() == matchValue) return def;
     }
+    */
+    static const VideoDefinition kEmptyDefinition(QString(), kEmptyDefinitionCode);
     return kEmptyDefinition;
 }
-}
+} // namespace
 
-// static
 const QVector<VideoDefinition> &VideoDefinition::getDefinitions() {
+    // List preferred equivalent format last:
+    // algo selects the last format with same name first
     static const QVector<VideoDefinition> definitions = {
-            VideoDefinition(QLatin1String("360p"), 18), VideoDefinition(QLatin1String("720p"), 22),
-            VideoDefinition(QLatin1String("1080p"), 37)};
+            VideoDefinition("240p", 242),      VideoDefinition("240p", 133),
+            VideoDefinition("360p", 243),      VideoDefinition("360p", 396),
+            VideoDefinition("360p", 18, true), VideoDefinition("480p", 244),
+            VideoDefinition("480p", 135),      VideoDefinition("720p", 247),
+            VideoDefinition("720p", 136),      VideoDefinition("720p", 22, true),
+            VideoDefinition("1080p", 248),     VideoDefinition("1080p", 137),
+            VideoDefinition("1440p", 271),     VideoDefinition("2160p", 313),
+    };
     return definitions;
 }
 
-// static
+const QVector<QString> &VideoDefinition::getDefinitionNames() {
+    static const QVector<QString> names = {"480p", "720p", "1080p", "1440p", "2160p"};
+    return names;
+}
+
 const VideoDefinition &VideoDefinition::forName(const QString &name) {
     return getDefinitionForImpl<const QString &, &VideoDefinition::getName>(name);
 }
 
-// static
 const VideoDefinition &VideoDefinition::forCode(int code) {
     return getDefinitionForImpl<int, &VideoDefinition::getCode>(code);
 }
 
-VideoDefinition::VideoDefinition(const QString &name, int code) : m_name(name), m_code(code) {}
+VideoDefinition::VideoDefinition(const QString &name, int code, bool hasAudioStream)
+    : name(name), code(code), hasAudioStream(hasAudioStream) {}
 
 bool VideoDefinition::isEmpty() const {
-    return m_code == kEmptyDefinitionCode && m_name.isEmpty();
+    return code == kEmptyDefinitionCode && name.isEmpty();
 }
index 321a116e09d4b6a3328b33331acebddeacdc1398..b35a545cbfb2d1ca6d27dc1255ef8e6e50cbfd46 100644 (file)
@@ -26,20 +26,23 @@ $END_LICENSE */
 class VideoDefinition {
 public:
     static const QVector<VideoDefinition> &getDefinitions();
+    static const QVector<QString> &getDefinitionNames();
     static const VideoDefinition &forName(const QString &name);
     static const VideoDefinition &forCode(int code);
 
-    VideoDefinition(const QString &name, int code);
+    VideoDefinition(const QString &name, int code, bool hasAudioStream = false);
 
-    const QString &getName() const { return m_name; }
-    int getCode() const { return m_code; }
+    const QString &getName() const { return name; }
+    int getCode() const { return code; }
+    bool hasAudio() const { return hasAudioStream; }
     bool isEmpty() const;
 
     VideoDefinition &operator=(const VideoDefinition &);
 
 private:
-    const QString m_name;
-    const int m_code;
+    const QString name;
+    const int code;
+    const bool hasAudioStream;
 };
 
 inline bool operator==(const VideoDefinition &lhs, const VideoDefinition &rhs) {
index 3703a8cf2b127ce70b464785d8c8b3d50b82083c..c8081b6282cec1c7763a0459e8a1f002d405d1ce 100644 (file)
@@ -3,11 +3,12 @@
 #include <algorithm>
 #include <ctime>
 
-#include "jsfunctions.h"
+#include "constants.h"
 #include "http.h"
 #include "httputils.h"
-#include "constants.h"
+#include "jsfunctions.h"
 #include "mainwindow.h"
+#include "videodefinition.h"
 
 #ifdef APP_EXTRA
 #include "extra.h"
@@ -55,14 +56,14 @@ void YT3::initApiKeys() {
 #endif
 
 #ifdef APP_EXTRA
-    if (keys.isEmpty())
-        keys << Extra::apiKeys();
+    if (keys.isEmpty()) keys << Extra::apiKeys();
 #endif
 
     if (keys.isEmpty()) {
         qWarning() << "No available API keys";
 #ifdef APP_LINUX
-        QMetaObject::invokeMethod(MainWindow::instance(), "missingKeyWarning", Qt::QueuedConnection);
+        QMetaObject::invokeMethod(MainWindow::instance(), "missingKeyWarning",
+                                  Qt::QueuedConnection);
 #endif
     } else {
         key = keys.takeFirst();
@@ -99,6 +100,18 @@ QUrl YT3::method(const QString &name) {
     return url;
 }
 
+const VideoDefinition &YT3::maxVideoDefinition() {
+    const QString name = QSettings().value("definition", "720p").toString();
+    const VideoDefinition &definition = VideoDefinition::forName(name);
+    return definition;
+}
+
+void YT3::setMaxVideoDefinition(const QString &name) {
+    QSettings settings;
+    settings.setValue("definition", name);
+    emit maxVideoDefinitionChanged(name);
+}
+
 void YT3::testResponse(const HttpReply &reply) {
     int status = reply.statusCode();
     if (status != 200) {
index c7de235ac88da1b38832140aad23e6d39852c81e..1750c8cb2fbe2f74c3f2d64d649816496ea39226 100644 (file)
--- a/src/yt3.h
+++ b/src/yt3.h
@@ -4,9 +4,9 @@
 #include <QtCore>
 
 class HttpReply;
+class VideoDefinition;
 
 class YT3 : public QObject {
-
     Q_OBJECT
 
 public:
@@ -18,6 +18,12 @@ public:
     void addApiKey(QUrl &url);
     QUrl method(const QString &name);
 
+    const VideoDefinition &maxVideoDefinition();
+    void setMaxVideoDefinition(const QString &name);
+
+signals:
+    void maxVideoDefinitionChanged(const QString &name);
+
 private slots:
     void testResponse(const HttpReply &reply);
 
index 74e11e6a3c816b884e550f9fd168b8efa1612b43..53e9e244f1e056c6c880cfa37e31ab462aa53636 100644 (file)
@@ -2,6 +2,16 @@
 #include "datautils.h"
 #include "video.h"
 
+#include <QRegularExpression>
+
+namespace {
+
+QString decodeEntities(const QString &s) {
+    return QTextDocumentFragment::fromHtml(s).toPlainText();
+}
+
+} // namespace
+
 YT3ListParser::YT3ListParser(const QByteArray &bytes) {
     QJsonDocument doc = QJsonDocument::fromJson(bytes);
     QJsonObject obj = doc.object();
@@ -42,7 +52,10 @@ void YT3ListParser::parseItem(const QJsonObject &item) {
 
     video->setChannelId(snippet[QLatin1String("channelId")].toString());
 
-    video->setTitle(snippet[QLatin1String("title")].toString());
+    QString title = snippet[QLatin1String("title")].toString();
+    static const QChar ampersand('&');
+    if (title.contains(ampersand)) title = decodeEntities(title);
+    video->setTitle(title);
     video->setDescription(snippet[QLatin1String("description")].toString());
 
     QJsonObject thumbnails = snippet[QLatin1String("thumbnails")].toObject();
index 2c0a745659eff552baf884a7424cc88d02ce8913..bfc2d42e101105b311b7cff6f09722b727bf527e 100644 (file)
@@ -19,28 +19,22 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "ytchannel.h"
+#include "database.h"
 #include "http.h"
 #include "httputils.h"
-#include "database.h"
 #include <QtSql>
 
 #include "yt3.h"
 
 #include "iconutils.h"
 
-YTChannel::YTChannel(const QString &channelId, QObject *parent) : QObject(parent),
-    id(0),
-    channelId(channelId),
-    loadingThumbnail(false),
-    notifyCount(0),
-    checked(0),
-    watched(0),
-    loaded(0),
-    loading(false) { }
+YTChannel::YTChannel(const QString &channelId, QObject *parent)
+    : QObject(parent), id(0), channelId(channelId), loadingThumbnail(false), notifyCount(0),
+      checked(0), watched(0), loaded(0), loading(false) {}
 
-QHash<QString, YTChannel*> YTChannel::cache;
+QHash<QString, YTChannel *> YTChannel::cache;
 
-YTChannelYTChannel::forId(const QString &channelId) {
+YTChannel *YTChannel::forId(const QString &channelId) {
     if (channelId.isEmpty()) return 0;
 
     auto i = cache.constFind(channelId);
@@ -54,7 +48,7 @@ YTChannel* YTChannel::forId(const QString &channelId) {
     bool success = query.exec();
     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
 
-    YTChannelchannel = 0;
+    YTChannel *channel = 0;
     if (query.next()) {
         // Change userId to ChannelId
 
@@ -68,7 +62,7 @@ YTChannel* YTChannel::forId(const QString &channelId) {
         channel->checked = query.value(6).toUInt();
         channel->loaded = query.value(7).toUInt();
         channel->thumbnail = QPixmap(channel->getThumbnailLocation());
-        channel->thumbnail.setDevicePixelRatio(IconUtils::maxSupportedPixelRatio());
+        channel->thumbnail.setDevicePixelRatio(2.0);
         channel->maybeLoadfromAPI();
         cache.insert(channelId, channel);
     }
@@ -127,8 +121,9 @@ void YTChannel::loadThumbnail() {
     connect(reply, SIGNAL(error(QString)), SLOT(requestError(QString)));
 }
 
-const QString & YTChannel::getThumbnailDir() {
-    static const QString thumbDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/channels/";
+const QString &YTChannel::getThumbnailDir() {
+    static const QString thumbDir =
+            QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/channels/";
     return thumbDir;
 }
 
@@ -139,7 +134,8 @@ QString YTChannel::getThumbnailLocation() {
 QString YTChannel::latestVideoId() {
     QSqlDatabase db = Database::instance().getConnection();
     QSqlQuery query(db);
-    query.prepare("select video_id from subscriptions_videos where user_id=? order by published desc limit 1");
+    query.prepare("select video_id from subscriptions_videos where user_id=? order by published "
+                  "desc limit 1");
     query.bindValue(0, channelId);
     bool success = query.exec();
     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
@@ -153,7 +149,7 @@ void YTChannel::unsubscribe() {
 
 void YTChannel::storeThumbnail(const QByteArray &bytes) {
     thumbnail.loadFromData(bytes);
-    qreal maxRatio = IconUtils::maxSupportedPixelRatio();
+    qreal maxRatio = 2.0;
     thumbnail.setDevicePixelRatio(maxRatio);
     const int maxWidth = 88 * maxRatio;
 
@@ -251,8 +247,7 @@ bool YTChannel::isSubscribed(const QString &channelId) {
     query.bindValue(0, channelId);
     bool success = query.exec();
     if (!success) qWarning() << query.lastQuery() << query.lastError().text();
-    if (query.next())
-        return query.value(0).toInt() > 0;
+    if (query.next()) return query.value(0).toInt() > 0;
     return false;
 }
 
@@ -281,7 +276,8 @@ void YTChannel::updateWatched() {
 
     QSqlDatabase db = Database::instance().getConnection();
     QSqlQuery query(db);
-    query.prepare("update subscriptions set watched=?, notify_count=0, views=views+1 where user_id=?");
+    query.prepare(
+            "update subscriptions set watched=?, notify_count=0, views=views+1 where user_id=?");
     query.bindValue(0, now);
     query.bindValue(1, channelId);
     bool success = query.exec();
@@ -289,8 +285,7 @@ void YTChannel::updateWatched() {
 }
 
 void YTChannel::storeNotifyCount(int count) {
-    if (notifyCount != count)
-        emit notifyCountChanged();
+    if (notifyCount != count) emit notifyCountChanged();
     notifyCount = count;
 
     QSqlDatabase db = Database::instance().getConnection();
index e24147b3f0860a6c8dbb4c72e24aa2abb609eb26..f968a72981dd3349cddf6b56f9cbce46a0357c9c 100644 (file)
@@ -19,6 +19,7 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "ytregions.h"
+#include "iconutils.h"
 
 YTRegions::YTRegions() : QObject() {}
 
@@ -84,7 +85,7 @@ YTRegion YTRegions::r(const QString &name, const QString &id) {
 }
 
 const YTRegion &YTRegions::localRegion() {
-    static const YTRegion region = [] {
+    static const YTRegion region = []() -> YTRegion {
         QString country = QLocale::system().name().right(2);
         for (const YTRegion &r : list()) {
             if (r.id == country) return r;
@@ -122,6 +123,6 @@ const YTRegion &YTRegions::regionById(const QString &id) {
 }
 
 QIcon YTRegions::iconForRegionId(const QString &regionId) {
-    if (regionId.isEmpty()) return QIcon(":images/worldwide.png");
+    if (regionId.isEmpty()) return IconUtils::icon("worldwide");
     return QIcon(":flags/" + regionId.toLower() + ".png");
 }
index e7bf33e2168f9d6d09b426e873a5a1a50863d9fe..09936cad8f1a541deb831f31331c4e45f351219c 100644 (file)
@@ -26,10 +26,8 @@ $END_LICENSE */
 #include "yt3.h"
 #include "yt3listparser.h"
 
-YTSingleVideoSource::YTSingleVideoSource(QObject *parent) : PaginatedVideoSource(parent),
-    video(0),
-    startIndex(0),
-    max(0) { }
+YTSingleVideoSource::YTSingleVideoSource(QObject *parent)
+    : PaginatedVideoSource(parent), video(nullptr), startIndex(0), max(0) {}
 
 void YTSingleVideoSource::loadVideos(int max, int startIndex) {
     aborted = false;
@@ -39,9 +37,8 @@ void YTSingleVideoSource::loadVideos(int max, int startIndex) {
     QUrl url;
 
     if (startIndex == 1) {
-
         if (video) {
-            QVector<Video*> videos;
+            QVector<Video *> videos;
             videos << video->clone();
             if (name.isEmpty()) {
                 name = videos.at(0)->getTitle();
@@ -84,15 +81,17 @@ void YTSingleVideoSource::parseResults(QByteArray data) {
     if (aborted) return;
 
     YT3ListParser parser(data);
-    const QVector<Video*> &videos = parser.getVideos();
+    const QVector<Video *> &videos = parser.getVideos();
 
     bool tryingWithNewToken = setPageToken(parser.getNextPageToken());
     if (tryingWithNewToken) return;
 
     if (asyncDetails) {
         emit gotVideos(videos);
-        if (startIndex == 2) emit finished(videos.size() + 1);
-        else emit finished(videos.size());
+        if (startIndex == 2)
+            emit finished(videos.size() + 1);
+        else
+            emit finished(videos.size());
     }
     loadVideoDetails(videos);
 }
index e84f7e16bc4540b4efac4c710c9092c616549a18..f311011ef86f2d37794588b03e86dbdb734e8ebe 100644 (file)
@@ -6,6 +6,7 @@
 #include "jsfunctions.h"
 #include "temporary.h"
 #include "videodefinition.h"
+#include "yt3.h"
 
 #include <QJSEngine>
 #include <QJSValue>
@@ -36,7 +37,7 @@ void YTVideo::getVideoInfo() {
 
     QUrl url;
     if (elIndex == elTypes.size()) {
-        // qDebug() << "Trying special embedded el param";
+        qDebug() << "Trying special embedded el param";
         url = QUrl("https://www.youtube.com/get_video_info");
         QUrlQuery q;
         q.addQueryItem("video_id", videoId);
@@ -81,7 +82,6 @@ void YTVideo::gotVideoInfo(const QByteArray &bytes) {
     }
 
     QString videoToken = videoTokeRE.cap(1);
-    qDebug() << "got token" << videoToken;
     while (videoToken.contains('%'))
         videoToken = QByteArray::fromPercentEncoding(videoToken.toLatin1());
     qDebug() << "videoToken" << videoToken;
@@ -106,12 +106,12 @@ void YTVideo::gotVideoInfo(const QByteArray &bytes) {
 }
 
 void YTVideo::parseFmtUrlMap(const QString &fmtUrlMap, bool fromWebPage) {
-    const QString definitionName = QSettings().value("definition", "360p").toString();
-    const VideoDefinition &definition = VideoDefinition::forName(definitionName);
+    int videoFormat = 0;
+    const VideoDefinition &definition = YT3::instance().maxVideoDefinition();
 
-    qDebug() << "fmtUrlMap" << fmtUrlMap;
+    // qDebug() << "fmtUrlMap" << fmtUrlMap;
     const QVector<QStringRef> formatUrls = fmtUrlMap.splitRef(',', QString::SkipEmptyParts);
-    QMap<int, QString> urlMap;
+
     for (const QStringRef &formatUrl : formatUrls) {
         // qDebug() << "formatUrl" << formatUrl;
         const QVector<QStringRef> urlParams = formatUrl.split('&', QString::SkipEmptyParts);
@@ -120,8 +120,13 @@ void YTVideo::parseFmtUrlMap(const QString &fmtUrlMap, bool fromWebPage) {
         int format = -1;
         QString url;
         QString sig;
+        QStringRef sp;
         for (const QStringRef &urlParam : urlParams) {
-            // qWarning() << urlParam;
+            qDebug() << "urlParam" << urlParam;
+            if (sp.isNull() && urlParam.startsWith(QLatin1String("sp"))) {
+                int separator = urlParam.indexOf('=');
+                sp = urlParam.mid(separator + 1);
+            }
             if (urlParam.startsWith(QLatin1String("itag="))) {
                 int separator = urlParam.indexOf('=');
                 format = urlParam.mid(separator + 1).toInt();
@@ -135,52 +140,58 @@ void YTVideo::parseFmtUrlMap(const QString &fmtUrlMap, bool fromWebPage) {
                 if (fromWebPage || ageGate) {
                     int separator = urlParam.indexOf('=');
                     sig = QByteArray::fromPercentEncoding(urlParam.mid(separator + 1).toUtf8());
-                    if (ageGate)
-                        sig = JsFunctions::instance()->decryptAgeSignature(sig);
-                    else {
-                        sig = decryptSignature(sig);
-                        if (sig.isEmpty()) sig = JsFunctions::instance()->decryptSignature(sig);
-                    }
+                    sig = decryptSignature(sig);
+                    if (sig.isEmpty()) sig = JsFunctions::instance()->decryptSignature(sig);
+                    if (sig.isEmpty()) qWarning() << "Empty signature";
                 } else {
-                    QUrl url("https://www.youtube.com/watch");
-                    QUrlQuery q;
-                    q.addQueryItem("v", videoId);
-                    q.addQueryItem("gl", "US");
-                    q.addQueryItem("hl", "en");
-                    q.addQueryItem("has_verified", "1");
-                    url.setQuery(q);
-                    qDebug() << "Loading webpage" << url;
-                    QObject *reply = HttpUtils::yt().get(url);
-                    connect(reply, SIGNAL(data(QByteArray)), SLOT(scrapeWebPage(QByteArray)));
-                    connect(reply, SIGNAL(error(QString)), SLOT(errorVideoInfo(QString)));
-                    // see you in scrapWebPage(QByteArray)
+                    loadWebPage();
                     return;
                 }
             }
         }
         if (format == -1 || url.isNull()) continue;
 
-        url += QLatin1String("&signature=") + sig;
+        if (!sig.isEmpty()) {
+            if (sp.isEmpty())
+                url += QLatin1String("&signature=") + sig;
+            else
+                url += '&' + sp + '=' + sig;
+        }
 
         if (!url.contains(QLatin1String("ratebypass"))) url += QLatin1String("&ratebypass=yes");
 
-        qDebug() << url;
-
+        qDebug() << format;
         if (format == definition.getCode()) {
-            qDebug() << "Found format" << definitionCode;
-            saveDefinitionForUrl(url, definition);
-            return;
+            qDebug() << "Found format" << format;
+            if (definition.hasAudio()) {
+                // we found the exact match with an audio/video stream
+                saveDefinitionForUrl(url, definition);
+                return;
+            }
+            videoFormat = format;
         }
-
         urlMap.insert(format, url);
     }
 
+    if (!fromWebPage && !ageGate) {
+        loadWebPage();
+        return;
+    }
+
+    if (videoFormat != 0) {
+        // exact match with video stream was found
+        const VideoDefinition &definition = VideoDefinition::forCode(videoFormat);
+        saveDefinitionForUrl(urlMap.value(videoFormat), definition);
+        return;
+    }
+
     const QVector<VideoDefinition> &definitions = VideoDefinition::getDefinitions();
     int previousIndex = std::max(definitions.indexOf(definition) - 1, 0);
     for (; previousIndex >= 0; previousIndex--) {
         const VideoDefinition &previousDefinition = definitions.at(previousIndex);
+        qDebug() << "Testing format" << previousDefinition.getCode();
         if (urlMap.contains(previousDefinition.getCode())) {
-            // qDebug() << "Found format" << definitionCode;
+            qDebug() << "Found format" << previousDefinition.getCode();
             saveDefinitionForUrl(urlMap.value(previousDefinition.getCode()), previousDefinition);
             return;
         }
@@ -189,56 +200,61 @@ void YTVideo::parseFmtUrlMap(const QString &fmtUrlMap, bool fromWebPage) {
     emit errorStreamUrl(tr("Cannot get video stream for %1").arg(videoId));
 }
 
+void YTVideo::loadWebPage() {
+    QUrl url("https://www.youtube.com/watch");
+    QUrlQuery q;
+    q.addQueryItem("v", videoId);
+    q.addQueryItem("gl", "US");
+    q.addQueryItem("hl", "en");
+    q.addQueryItem("has_verified", "1");
+    q.addQueryItem("bpctr", "9999999999");
+    url.setQuery(q);
+    qDebug() << "Loading webpage" << url;
+    QObject *reply = HttpUtils::yt().get(url);
+    connect(reply, SIGNAL(data(QByteArray)), SLOT(scrapeWebPage(QByteArray)));
+    connect(reply, SIGNAL(error(QString)), SLOT(errorVideoInfo(QString)));
+    // see you in scrapWebPage(QByteArray)
+}
+
 void YTVideo::errorVideoInfo(const QString &message) {
     loadingStreamUrl = false;
     emit errorStreamUrl(message);
 }
 
 void YTVideo::scrapeWebPage(const QByteArray &bytes) {
+    qDebug() << "scrapeWebPage";
+
     const QString html = QString::fromUtf8(bytes);
 
     static const QRegExp ageGateRE(JsFunctions::instance()->ageGateRE());
     if (ageGateRE.indexIn(html) != -1) {
-        // qDebug() << "Found ageGate";
+        qDebug() << "Found ageGate";
         ageGate = true;
         elIndex = 4;
         getVideoInfo();
         return;
     }
 
+    // "\"url_encoded_fmt_stream_map\":\s*\"([^\"]+)\""
     static const QRegExp fmtMapRE(JsFunctions::instance()->webPageFmtMapRE());
-    if (fmtMapRE.indexIn(html) == -1) {
-        qWarning() << "Error parsing video page";
-        // emit errorStreamUrl("Error parsing video page");
-        // loadingStreamUrl = false;
+    if (fmtMapRE.indexIn(html) != -1) {
+        fmtUrlMap = fmtMapRE.cap(1);
+        fmtUrlMap.replace("\\u0026", "&");
+    }
+
+    QRegExp adaptiveFormatsRE("\"adaptive_fmts\":\\s*\"([^\"]+)\"");
+    if (adaptiveFormatsRE.indexIn(html) != -1) {
+        qDebug() << "Found adaptive_fmts";
+        if (!fmtUrlMap.isEmpty()) fmtUrlMap += ',';
+        fmtUrlMap += adaptiveFormatsRE.cap(1).replace("\\u0026", "&");
+    }
+
+    if (fmtUrlMap.isEmpty()) {
+        qWarning() << "Cannot get fmtUrlMap from video page. Trying next el";
         elIndex++;
         getVideoInfo();
         return;
     }
-    fmtUrlMap = fmtMapRE.cap(1);
-    fmtUrlMap.replace("\\u0026", "&");
-// parseFmtUrlMap(fmtUrlMap, true);
-
-#ifdef APP_DASH
-    QSettings settings;
-    QString definitionName = settings.value("definition", "360p").toString();
-    if (definitionName == QLatin1String("1080p")) {
-        QRegExp dashManifestRe("\"dashmpd\":\\s*\"([^\"]+)\"");
-        if (dashManifestRe.indexIn(html) != -1) {
-            dashManifestUrl = dashManifestRe.cap(1);
-            dashManifestUrl.remove('\\');
-            qDebug() << "dashManifestUrl" << dashManifestUrl;
-        } else {
-            qWarning() << "DASH manifest not found in webpage";
-            if (dashManifestRe.indexIn(fmtUrlMap) != -1) {
-                dashManifestUrl = dashManifestRe.cap(1);
-                dashManifestUrl.remove('\\');
-                qDebug() << "dashManifestUrl" << dashManifestUrl;
-            } else
-                qWarning() << "DASH manifest not found in fmtUrlMap" << fmtUrlMap;
-        }
-    }
-#endif
 
     static const QRegExp jsPlayerRe(JsFunctions::instance()->jsPlayerRE());
     if (jsPlayerRe.indexIn(html) != -1) {
@@ -266,60 +282,34 @@ void YTVideo::parseJsPlayer(const QByteArray &bytes) {
     // qDebug() << "jsPlayer" << jsPlayer;
 
     // QRegExp funcNameRe("[\"']signature[\"']\\s*,\\s*([" + jsNameChars + "]+)\\(");
-    static const QRegExp funcNameRe(
-            JsFunctions::instance()->signatureFunctionNameRE().arg(jsNameChars));
-
-    if (funcNameRe.indexIn(jsPlayer) == -1) {
-        qWarning() << "Cannot capture signature function name" << jsPlayer;
-    } else {
-        sigFuncName = funcNameRe.cap(1);
-        captureFunction(sigFuncName, jsPlayer);
-        // qWarning() << sigFunctions << sigObjects;
-    }
-
-#ifdef APP_DASH
-    if (!dashManifestUrl.isEmpty()) {
-        QRegExp sigRe("/s/([\\w\\.]+)");
-        if (sigRe.indexIn(dashManifestUrl) != -1) {
-            qDebug() << "Decrypting signature for dash manifest";
-            QString sig = sigRe.cap(1);
-            sig = decryptSignature(sig);
-            dashManifestUrl.replace(sigRe, "/signature/" + sig);
-            qWarning() << "dash manifest" << dashManifestUrl;
-
-            if (true) {
-                // let phonon play the manifest
-                m_streamUrl = dashManifestUrl;
-                this->definitionCode = 37;
-                emit gotStreamUrl(m_streamUrl);
-                loadingStreamUrl = false;
-            } else {
-                // download the manifest
-                QObject *reply = HttpUtils::yt().get(QUrl::fromEncoded(dashManifestUrl.toUtf8()));
-                connect(reply, SIGNAL(data(QByteArray)), SLOT(parseDashManifest(QByteArray)));
-                connect(reply, SIGNAL(error(QString)), SLOT(errorVideoInfo(QString)));
+    static const QVector<QRegExp> funcNameRes = [] {
+        QVector<QRegExp> res;
+        for (const QString &s : JsFunctions::instance()->signatureFunctionNameREs()) {
+            res << QRegExp(s.arg(jsNameChars));
+        }
+        return res;
+    }();
+    for (const QRegExp &funcNameRe : funcNameRes) {
+        if (funcNameRe.indexIn(jsPlayer) == -1) {
+            qWarning() << "Cannot capture signature function name" << funcNameRe;
+            continue;
+        } else {
+            sigFuncName = funcNameRe.cap(1);
+            qDebug() << "Captures" << funcNameRe.captureCount() << funcNameRe.capturedTexts();
+            if (sigFuncName.isEmpty()) {
+                qDebug() << "Empty capture for" << funcNameRe;
+                continue;
             }
-
-            return;
+            captureFunction(sigFuncName, jsPlayer);
+            qDebug() << sigFunctions << sigObjects;
+            break;
         }
     }
-#endif
+    if (sigFuncName.isEmpty()) qDebug() << "Empty signature function name";
 
     parseFmtUrlMap(fmtUrlMap, true);
 }
 
-void YTVideo::parseDashManifest(const QByteArray &bytes) {
-    QFile file(Temporary::filename() + ".mpd");
-    if (!file.open(QIODevice::WriteOnly)) qWarning() << file.errorString() << file.fileName();
-    QDataStream stream(&file);
-    stream.writeRawData(bytes.constData(), bytes.size());
-
-    m_streamUrl = "file://" + file.fileName();
-    this->definitionCode = 37;
-    emit gotStreamUrl(m_streamUrl);
-    loadingStreamUrl = false;
-}
-
 void YTVideo::captureFunction(const QString &name, const QString &js) {
     qDebug() << __PRETTY_FUNCTION__ << name;
     const QString argsAndBody =
@@ -423,8 +413,26 @@ QString YTVideo::decryptSignature(const QString &s) {
 }
 
 void YTVideo::saveDefinitionForUrl(const QString &url, const VideoDefinition &definition) {
-    m_streamUrl = QUrl::fromEncoded(url.toUtf8(), QUrl::StrictMode);
+    qDebug() << "Selected video format" << definition.getCode() << definition.getName()
+             << definition.hasAudio();
+    m_streamUrl = url;
     definitionCode = definition.getCode();
-    emit gotStreamUrl(m_streamUrl);
+
+    QString audioUrl;
+    if (!definition.hasAudio()) {
+        qDebug() << "Finding audio format";
+        static const QVector<int> audioFormats({251, 171, 140});
+        for (int audioFormat : audioFormats) {
+            qDebug() << "Trying audio format" << audioFormat;
+            auto i = urlMap.constFind(audioFormat);
+            if (i != urlMap.constEnd()) {
+                qDebug() << "Found audio format" << i.value();
+                audioUrl = i.value();
+                break;
+            }
+        }
+    }
+
     loadingStreamUrl = false;
+    emit gotStreamUrl(url, audioUrl);
 }
index 9f18e43c3094c9238396380caa4058d645bba072..772df5f90a6c1eb60b791e50ffa3dbfd9c29e1df 100644 (file)
@@ -14,7 +14,7 @@ public:
     int getDefinitionCode() const { return definitionCode; }
 
 signals:
-    void gotStreamUrl(const QUrl &streamUrl);
+    void gotStreamUrl(const QString &videoUrl, const QString &audioUrl);
     void errorStreamUrl(const QString &message);
 
 private slots:
@@ -22,11 +22,11 @@ private slots:
     void errorVideoInfo(const QString &message);
     void scrapeWebPage(const QByteArray &bytes);
     void parseJsPlayer(const QByteArray &bytes);
-    void parseDashManifest(const QByteArray &bytes);
 
 private:
     void getVideoInfo();
     void parseFmtUrlMap(const QString &fmtUrlMap, bool fromWebPage = false);
+    void loadWebPage();
     void captureFunction(const QString &name, const QString &js);
     void captureObject(const QString &name, const QString &js);
     QString decryptSignature(const QString &s);
@@ -36,8 +36,6 @@ private:
     QUrl m_streamUrl;
     int definitionCode;
     bool loadingStreamUrl;
-    // current index for the elTypes list
-    // needed to iterate on elTypes
     int elIndex;
     bool ageGate;
     QString videoToken;
@@ -47,6 +45,7 @@ private:
     QHash<QString, QString> sigObjects;
     QString dashManifestUrl;
     QString jsPlayer;
+    QMap<int, QString> urlMap;
 };
 
 #endif // YTVIDEO_H
index d6651f6077ef7c977ceed95a45a305e8f54bbb41..ab926ac6e72a986d71c53cbd510a431521522b66 100644 (file)
--- a/style.css
+++ b/style.css
@@ -1,5 +1,12 @@
 QMainWindow > QToolBar QToolButton::menu-indicator { image: none; }
 
+QStatusBar {
+    border: 0;
+    padding: 0;
+    background: palette(base);
+    color: palette(text);
+}
+
 QStatusBar QToolBar {
     padding:0;
     spacing:0;
@@ -8,7 +15,9 @@ QStatusBar QToolBar {
 }
 
 QStatusBar::item {
-    border:0;
+    border: 0;
+    padding: 0;
+    margin: 0;
 }
 
 QStatusBar QToolButton::menu-indicator {
@@ -17,7 +26,7 @@ QStatusBar QToolButton::menu-indicator {
 
 AppsWidget {
     background-color: palette(window);
-    border-top: 1px solid #808080;
+    border-top: 1px solid palette(mid);
 }
 
 RegionsView QPushButton[regionId] {
@@ -29,26 +38,17 @@ RegionsView QPushButton[regionId] {
     border-radius: 3px;
 }
 
-RegionsView QPushButton[regionId=""] {
-
-}
-
-RegionsView QPushButton[regionId]:hover {
-    background: rgba(0,0,0,16);
+RegionsView QPushButton[regionId]:hover, RegionsView QPushButton[regionId]:checked {
+    background: palette(highlight);
+    color: palette(highlighted-text);
 }
 
 RegionsView QPushButton[regionId]:focus {
-    background: palette(highlight);
-    color: palette(highlightText);
+    background: palette(mid);
     outline: 0;
 }
 
-RegionsView QPushButton[regionId]:checked {
-    background: rgba(0,0,0,32)
-}
-
 SidebarHeader {
-    background: #3c3c3c;
     padding: 0;
     margin: 0;
     spacing: 0;
@@ -66,7 +66,6 @@ SidebarHeader QToolButton {
 
 SidebarHeader QPushButton {
     background: transparent;
-    color: #fff;
     text-align: center;
 }
 
@@ -75,29 +74,11 @@ SidebarHeader QComboBox::drop-down {
     border-style: none;
 }
 
-SearchView QLabel[recentHeader="true"] {
-    margin-left: -3px;
-    margin-bottom: 4px;
-    padding: 0;
-}
-
-SearchView QPushButton[recentItem="true"] {
+SearchView *[recentItem="true"], SearchView *[recentHeader="true"] {
     border: 0;
-    padding: 4px;
-    text-align: left;
-    outline: 0;
-}
-
-SearchView QPushButton[recentItem="true"]:hover {
-    background: rgba(0,0,0,16);
-}
-
-SearchView QPushButton[recentItem="true"]:pressed {
-    background: palette(highlight);
-    color: white; /* SHOULD BE color: palette(highlightText); */
+    padding: 4px 0;
 }
 
-SearchView QPushButton[recentItem="true"]:focus {
+SearchView *[recentItem="true"]:focus {
     outline: 1px solid palette(highlight);
-    outline-offset: 4px;
 }