From ccc918169d49d046c29001c21c492dfbbde063aa Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Sun, 16 Jun 2019 20:27:40 +0000 Subject: [PATCH] New upstream version 3.1 --- .clang-format | 11 - .gitignore | 16 - README.md | 34 +- empty.ts | 125 +- icons/dark/16/audio-volume-high.png | Bin 0 -> 588 bytes icons/dark/16/audio-volume-high@2x.png | Bin 0 -> 994 bytes icons/dark/16/audio-volume-muted.png | Bin 0 -> 430 bytes icons/dark/16/audio-volume-muted@2x.png | Bin 0 -> 523 bytes icons/dark/16/bookmark-new.png | Bin 0 -> 491 bytes icons/dark/16/bookmark-new@2x.png | Bin 0 -> 736 bytes icons/dark/16/bookmark-new_active.png | Bin 0 -> 436 bytes icons/dark/16/bookmark-new_active@2x.png | Bin 0 -> 626 bytes icons/dark/16/bookmark-remove.png | Bin 0 -> 564 bytes icons/dark/16/bookmark-remove@2x.png | Bin 0 -> 898 bytes icons/dark/16/edit-find.png | Bin 0 -> 441 bytes icons/dark/16/edit-find@2x.png | Bin 0 -> 654 bytes icons/dark/16/email.png | Bin 0 -> 436 bytes icons/dark/16/email@2x.png | Bin 0 -> 590 bytes icons/dark/16/facebook.png | Bin 0 -> 385 bytes icons/dark/16/facebook@2x.png | Bin 0 -> 482 bytes icons/dark/16/go-next.png | Bin 0 -> 348 bytes icons/dark/16/go-next@2x.png | Bin 0 -> 436 bytes icons/dark/16/go-previous.png | Bin 0 -> 346 bytes icons/dark/16/go-previous@2x.png | Bin 0 -> 431 bytes icons/dark/16/link.png | Bin 0 -> 485 bytes icons/dark/16/link@2x.png | Bin 0 -> 705 bytes icons/dark/16/mark-watched.png | Bin 0 -> 442 bytes icons/dark/16/mark-watched@2x.png | Bin 0 -> 724 bytes icons/dark/16/media-playback-start.png | Bin 0 -> 344 bytes icons/dark/16/media-playback-start@2x.png | Bin 0 -> 394 bytes .../dark/16/media-playback-start_checked.png | Bin 0 -> 344 bytes .../16/media-playback-start_checked@2x.png | Bin 0 -> 394 bytes icons/dark/16/safesearch.png | Bin 0 -> 405 bytes icons/dark/16/safesearch@2x.png | Bin 0 -> 550 bytes icons/dark/16/search-duration.png | Bin 0 -> 496 bytes icons/dark/16/search-duration@2x.png | Bin 0 -> 833 bytes icons/dark/16/search-quality.png | Bin 0 -> 425 bytes icons/dark/16/search-quality@2x.png | Bin 0 -> 605 bytes icons/dark/16/search-sortBy.png | Bin 0 -> 424 bytes icons/dark/16/search-sortBy@2x.png | Bin 0 -> 493 bytes icons/dark/16/search-time.png | Bin 0 -> 532 bytes icons/dark/16/search-time@2x.png | Bin 0 -> 715 bytes icons/dark/16/show-updated.png | Bin 0 -> 484 bytes icons/dark/16/show-updated@2x.png | Bin 0 -> 679 bytes icons/dark/16/sort.png | Bin 0 -> 348 bytes icons/dark/16/sort@2x.png | Bin 0 -> 410 bytes icons/dark/16/twitter.png | Bin 0 -> 474 bytes icons/dark/16/twitter@2x.png | Bin 0 -> 704 bytes icons/dark/16/unwatched.png | Bin 0 -> 442 bytes icons/dark/16/unwatched@2x.png | Bin 0 -> 655 bytes icons/dark/16/video-display.png | Bin 0 -> 352 bytes icons/dark/16/video-display@2x.png | Bin 0 -> 407 bytes icons/dark/16/worldwide.png | Bin 0 -> 520 bytes icons/dark/16/worldwide@2x.png | Bin 0 -> 899 bytes icons/dark/24/edit-find.png | Bin 0 -> 546 bytes icons/dark/24/edit-find@2x.png | Bin 0 -> 899 bytes icons/dark/24/refine-search.png | Bin 0 -> 546 bytes icons/dark/24/refine-search@2x.png | Bin 0 -> 899 bytes icons/dark/32/content-loading.png | Bin 0 -> 355 bytes icons/dark/32/content-loading@2x.png | Bin 0 -> 490 bytes icons/dark/32/document-save.png | Bin 0 -> 462 bytes icons/dark/32/document-save@2x.png | Bin 0 -> 612 bytes icons/dark/32/media-playback-pause.png | Bin 0 -> 348 bytes icons/dark/32/media-playback-pause@2x.png | Bin 0 -> 433 bytes icons/dark/32/media-playback-start.png | Bin 0 -> 443 bytes icons/dark/32/media-playback-start@2x.png | Bin 0 -> 571 bytes .../dark/32/media-playback-start_checked.png | Bin 0 -> 420 bytes .../32/media-playback-start_checked@2x.png | Bin 0 -> 484 bytes icons/dark/32/media-playback-stop.png | Bin 0 -> 325 bytes icons/dark/32/media-playback-stop@2x.png | Bin 0 -> 379 bytes icons/dark/32/media-skip-forward.png | Bin 0 -> 640 bytes icons/dark/32/media-skip-forward@2x.png | Bin 0 -> 930 bytes icons/dark/32/open-menu.png | Bin 0 -> 428 bytes icons/dark/32/open-menu@2x.png | Bin 0 -> 432 bytes icons/dark/32/view-fullscreen.png | Bin 0 -> 591 bytes icons/dark/32/view-fullscreen@2x.png | Bin 0 -> 787 bytes icons/dark/32/view-list.png | Bin 0 -> 694 bytes icons/dark/32/view-list@2x.png | Bin 0 -> 1101 bytes icons/dark/32/view-restore.png | Bin 0 -> 573 bytes icons/dark/32/view-restore@2x.png | Bin 0 -> 840 bytes icons/dark/88/channels.png | Bin 0 -> 449 bytes icons/dark/88/channels@2x.png | Bin 0 -> 872 bytes icons/dark/88/unwatched.png | Bin 0 -> 1770 bytes icons/dark/88/unwatched@2x.png | Bin 0 -> 3504 bytes icons/light/16/audio-volume-high.png | Bin 0 -> 577 bytes icons/light/16/audio-volume-high@2x.png | Bin 0 -> 1164 bytes icons/light/16/audio-volume-muted.png | Bin 0 -> 259 bytes icons/light/16/audio-volume-muted@2x.png | Bin 0 -> 390 bytes icons/light/16/bookmark-new.png | Bin 0 -> 392 bytes icons/light/16/bookmark-new@2x.png | Bin 0 -> 746 bytes icons/light/16/bookmark-new_active.png | Bin 0 -> 293 bytes icons/light/16/bookmark-new_active@2x.png | Bin 0 -> 495 bytes icons/light/16/bookmark-remove.png | Bin 0 -> 383 bytes icons/light/16/bookmark-remove@2x.png | Bin 0 -> 708 bytes icons/light/16/edit-find.png | Bin 0 -> 270 bytes icons/light/16/edit-find@2x.png | Bin 0 -> 528 bytes icons/light/16/email.png | Bin 0 -> 294 bytes icons/light/16/email@2x.png | Bin 0 -> 463 bytes icons/light/16/facebook.png | Bin 0 -> 229 bytes icons/light/16/facebook@2x.png | Bin 0 -> 340 bytes icons/light/16/go-next.png | Bin 0 -> 158 bytes icons/light/16/go-next@2x.png | Bin 0 -> 225 bytes icons/light/16/go-previous.png | Bin 0 -> 167 bytes icons/light/16/go-previous@2x.png | Bin 0 -> 235 bytes icons/light/16/link.png | Bin 0 -> 323 bytes icons/light/16/link@2x.png | Bin 0 -> 574 bytes icons/light/16/mark-watched.png | Bin 0 -> 350 bytes .../light/16}/mark-watched@2x.png | Bin 791 -> 735 bytes icons/light/16/media-playback-start.png | Bin 0 -> 170 bytes icons/light/16/media-playback-start@2x.png | Bin 0 -> 244 bytes .../light/16/media-playback-start_checked.png | Bin 0 -> 170 bytes .../16/media-playback-start_checked@2x.png | Bin 0 -> 244 bytes icons/light/16/safesearch.png | Bin 0 -> 249 bytes icons/light/16/safesearch@2x.png | Bin 0 -> 402 bytes icons/light/16/search-duration.png | Bin 0 -> 370 bytes icons/light/16/search-duration@2x.png | Bin 0 -> 721 bytes icons/light/16/search-quality.png | Bin 0 -> 289 bytes .../light/16}/search-quality@2x.png | Bin 547 -> 491 bytes icons/light/16/search-sortBy.png | Bin 0 -> 286 bytes icons/light/16/search-sortBy@2x.png | Bin 0 -> 374 bytes icons/light/16/search-time.png | Bin 0 -> 394 bytes icons/light/16/search-time@2x.png | Bin 0 -> 617 bytes icons/light/16/show-updated.png | Bin 0 -> 423 bytes icons/light/16/show-updated@2x.png | Bin 0 -> 725 bytes icons/light/16/sort.png | Bin 0 -> 176 bytes icons/light/16/sort@2x.png | Bin 0 -> 238 bytes icons/light/16/twitter.png | Bin 0 -> 322 bytes icons/light/16/twitter@2x.png | Bin 0 -> 553 bytes icons/light/16/unwatched.png | Bin 0 -> 280 bytes icons/light/16/unwatched@2x.png | Bin 0 -> 513 bytes icons/light/16/video-display.png | Bin 0 -> 188 bytes icons/light/16/video-display@2x.png | Bin 0 -> 240 bytes icons/light/16/worldwide.png | Bin 0 -> 377 bytes icons/light/16/worldwide@2x.png | Bin 0 -> 821 bytes icons/light/24/edit-find.png | Bin 0 -> 387 bytes icons/light/24/edit-find@2x.png | Bin 0 -> 822 bytes icons/light/24/refine-search.png | Bin 0 -> 387 bytes icons/light/24/refine-search@2x.png | Bin 0 -> 822 bytes icons/light/32/content-loading.png | Bin 0 -> 187 bytes icons/light/32/content-loading@2x.png | Bin 0 -> 372 bytes icons/light/32/document-save.png | Bin 0 -> 344 bytes icons/light/32/document-save@2x.png | Bin 0 -> 535 bytes icons/light/32/media-playback-pause.png | Bin 0 -> 190 bytes icons/light/32/media-playback-pause@2x.png | Bin 0 -> 351 bytes .../light/32}/media-playback-start.png | Bin 369 -> 313 bytes icons/light/32/media-playback-start@2x.png | Bin 0 -> 454 bytes .../light/32/media-playback-start_checked.png | Bin 0 -> 313 bytes .../32/media-playback-start_checked@2x.png | Bin 0 -> 475 bytes icons/light/32/media-playback-stop.png | Bin 0 -> 171 bytes icons/light/32/media-playback-stop@2x.png | Bin 0 -> 246 bytes .../light/32}/media-skip-forward.png | Bin 648 -> 592 bytes icons/light/32/media-skip-forward@2x.png | Bin 0 -> 1001 bytes icons/light/32/open-menu.png | Bin 0 -> 311 bytes icons/light/32/open-menu@2x.png | Bin 0 -> 354 bytes icons/light/32/view-fullscreen.png | Bin 0 -> 561 bytes icons/light/32/view-fullscreen@2x.png | Bin 0 -> 799 bytes icons/light/32/view-list.png | Bin 0 -> 696 bytes icons/light/32/view-list@2x.png | Bin 0 -> 1276 bytes {images => icons/light/32}/view-restore.png | Bin 560 -> 504 bytes .../light/32}/view-restore@2x.png | Bin 955 -> 899 bytes icons/light/88/channels.png | Bin 0 -> 373 bytes icons/light/88/channels@2x.png | Bin 0 -> 1168 bytes icons/light/88/unwatched.png | Bin 0 -> 2163 bytes {images => icons/light/88}/unwatched@2x.png | Bin 4459 -> 4403 bytes images/audio-volume-high.png | Bin 282 -> 0 bytes images/audio-volume-high@2x.png | Bin 831 -> 0 bytes images/audio-volume-muted.png | Bin 300 -> 0 bytes images/audio-volume-muted@2x.png | Bin 412 -> 0 bytes images/bookmark-new.png | Bin 440 -> 0 bytes images/bookmark-new@2x.png | Bin 809 -> 0 bytes images/bookmark-new_active.png | Bin 348 -> 0 bytes images/bookmark-new_active@2x.png | Bin 572 -> 0 bytes images/bookmark-remove.png | Bin 450 -> 0 bytes images/bookmark-remove@2x.png | Bin 804 -> 0 bytes images/channels.png | Bin 466 -> 0 bytes images/channels@2x.png | Bin 1200 -> 0 bytes images/content-loading.png | Bin 286 -> 0 bytes images/content-loading@2x.png | Bin 471 -> 0 bytes images/document-save.png | Bin 400 -> 0 bytes images/document-save@2x.png | Bin 591 -> 0 bytes images/edit-clear.png | Bin 431 -> 0 bytes images/edit-find.png | Bin 693 -> 0 bytes images/email.png | Bin 374 -> 0 bytes images/email@2x.png | Bin 629 -> 0 bytes images/facebook.png | Bin 254 -> 0 bytes images/facebook@2x.png | Bin 351 -> 0 bytes images/go-next.png | Bin 358 -> 0 bytes images/go-next@2x.png | Bin 583 -> 0 bytes images/go-next_active.png | Bin 253 -> 0 bytes images/go-next_active@2x.png | Bin 361 -> 0 bytes images/go-previous.png | Bin 372 -> 0 bytes images/go-previous@2x.png | Bin 618 -> 0 bytes images/go-previous_active.png | Bin 264 -> 0 bytes images/go-previous_active@2x.png | Bin 386 -> 0 bytes images/go-top.png | Bin 406 -> 0 bytes images/go-top@2x.png | Bin 564 -> 0 bytes images/link.png | Bin 518 -> 0 bytes images/link@2x.png | Bin 903 -> 0 bytes images/mark-watched.png | Bin 419 -> 0 bytes images/media-playback-pause.png | Bin 246 -> 0 bytes images/media-playback-pause@2x.png | Bin 407 -> 0 bytes images/media-playback-start@2x.png | Bin 510 -> 0 bytes images/media-playback-stop.png | Bin 227 -> 0 bytes images/media-playback-stop@2x.png | Bin 302 -> 0 bytes images/media-skip-forward@2x.png | Bin 1057 -> 0 bytes images/refine-search.png | Bin 571 -> 0 bytes images/refine-search@2x.png | Bin 1021 -> 0 bytes images/safesearch.png | Bin 338 -> 0 bytes images/safesearch@2x.png | Bin 485 -> 0 bytes images/search-duration.png | Bin 391 -> 0 bytes images/search-duration@2x.png | Bin 777 -> 0 bytes images/search-quality.png | Bin 343 -> 0 bytes images/search-sortBy.png | Bin 369 -> 0 bytes images/search-sortBy@2x.png | Bin 430 -> 0 bytes images/search-time.png | Bin 485 -> 0 bytes images/search-time@2x.png | Bin 673 -> 0 bytes images/show-updated.png | Bin 486 -> 0 bytes images/show-updated@2x.png | Bin 766 -> 0 bytes images/sort.png | Bin 396 -> 0 bytes images/sort@2x.png | Bin 602 -> 0 bytes images/system-search.png | Bin 548 -> 0 bytes images/system-search_active.png | Bin 533 -> 0 bytes images/system-search_selected.png | Bin 457 -> 0 bytes images/twitter.png | Bin 438 -> 0 bytes images/twitter@2x.png | Bin 723 -> 0 bytes images/unwatched.png | Bin 1904 -> 0 bytes images/video-display.png | Bin 157 -> 0 bytes images/video-display@2x.png | Bin 212 -> 0 bytes images/view-fullscreen.png | Bin 617 -> 0 bytes images/view-fullscreen@2x.png | Bin 855 -> 0 bytes images/view-list.png | Bin 866 -> 0 bytes images/view-list@2x.png | Bin 1433 -> 0 bytes images/view-more.png | Bin 140 -> 0 bytes images/view-more@2x.png | Bin 204 -> 0 bytes images/view-refresh.png | Bin 528 -> 0 bytes images/view-refresh_active.png | Bin 531 -> 0 bytes images/view-refresh_selected.png | Bin 509 -> 0 bytes images/window-close.png | Bin 320 -> 0 bytes images/window-close_active.png | Bin 327 -> 0 bytes images/window-close_selected.png | Bin 342 -> 0 bytes images/worldwide.png | Bin 630 -> 0 bytes images/worldwide@2x.png | Bin 1424 -> 0 bytes lib/http/.gitignore | 2 + {src => lib}/http/README.md | 0 {src => lib}/http/http.pri | 0 lib/http/http.pro | 2 + {src => lib}/http/src/cachedhttp.cpp | 8 +- {src => lib}/http/src/cachedhttp.h | 18 +- {src => lib}/http/src/http.cpp | 28 +- {src => lib}/http/src/http.h | 63 +- {src => lib}/http/src/localcache.cpp | 6 +- {src => lib}/http/src/localcache.h | 0 {src => lib}/http/src/throttledhttp.cpp | 15 +- {src => lib}/http/src/throttledhttp.h | 2 +- lib/idle/README.md | 9 + {src => lib}/idle/idle.pri | 0 {src => lib}/idle/src/idle.h | 2 - {src => lib}/idle/src/idle_linux.cpp | 33 +- {src => lib}/idle/src/idle_mac.cpp | 8 +- {src => lib}/idle/src/idle_win.cpp | 0 lib/media/.gitignore | 1 + lib/media/README.md | 9 + lib/media/media.pri | 37 + lib/media/src/media.h | 82 + lib/media/src/mpv/mediampv.cpp | 410 +++++ lib/media/src/mpv/mediampv.h | 64 + lib/media/src/mpv/mpvwidget.cpp | 102 ++ lib/media/src/mpv/mpvwidget.h | 36 + lib/media/src/qtav/mediaqtav.cpp | 369 +++++ lib/media/src/qtav/mediaqtav.h | 79 + locale/ar.ts | 146 +- locale/ast.ts | 146 +- locale/be.ts | 146 +- locale/bg_BG.ts | 146 +- locale/ca.ts | 162 +- locale/ca_ES.ts | 146 +- locale/cs_CZ.ts | 158 +- locale/da.ts | 152 +- locale/de_DE.ts | 146 +- locale/el.ts | 146 +- locale/en.ts | 4 +- locale/en_GB.ts | 1362 +++++++++++++++++ locale/es.ts | 148 +- locale/es_AR.ts | 146 +- locale/es_ES.ts | 146 +- locale/es_MX.ts | 152 +- locale/fi.ts | 162 +- locale/fi_FI.ts | 150 +- locale/fr.ts | 158 +- locale/gl.ts | 198 ++- locale/he_IL.ts | 182 ++- locale/hr.ts | 146 +- locale/hu.ts | 152 +- locale/id.ts | 146 +- locale/it.ts | 158 +- locale/ja_JP.ts | 148 +- locale/ko_KR.ts | 208 ++- locale/ky.ts | 146 +- locale/locale.pri | 2 +- locale/ms_MY.ts | 158 +- locale/nb.ts | 146 +- locale/nl.ts | 154 +- locale/nn.ts | 146 +- locale/pl.ts | 146 +- locale/pl_PL.ts | 146 +- locale/pt.ts | 146 +- locale/pt_BR.ts | 164 +- locale/pt_PT.ts | 1362 +++++++++++++++++ locale/ro.ts | 154 +- locale/ru.ts | 146 +- locale/sk.ts | 158 +- locale/sl.ts | 152 +- locale/sq.ts | 146 +- locale/sr.ts | 146 +- locale/sv_SE.ts | 146 +- locale/th.ts | 146 +- locale/tr.ts | 162 +- locale/uk.ts | 158 +- locale/uk_UA.ts | 158 +- locale/vi.ts | 146 +- locale/zh_CN.ts | 146 +- locale/zh_TW.ts | 158 +- minitube.appdata.xml | 19 +- minitube.pro | 44 +- resources.qrc | 86 -- src/aboutview.cpp | 123 +- src/aboutview.h | 9 - src/appwidget.cpp | 2 +- src/autocomplete.cpp | 43 +- src/channelitemdelegate.cpp | 71 +- src/channellistview.cpp | 17 +- src/channellistview.h | 2 - src/channelview.cpp | 73 +- src/channelview.h | 8 +- src/clickablelabel.cpp | 16 +- src/clickablelabel.h | 8 +- src/constants.cpp | 2 +- src/datautils.cpp | 26 +- src/datautils.h | 5 +- src/exlineedit.cpp | 186 --- src/exlineedit.h | 58 - src/fontutils.cpp | 9 +- src/gridwidget.cpp | 8 +- src/gridwidget.h | 9 +- src/homeview.cpp | 65 +- src/httputils.cpp | 13 +- src/iconutils.cpp | 131 +- src/iconutils.h | 39 +- src/jsfunctions.cpp | 22 +- src/jsfunctions.h | 10 +- src/loadingwidget.cpp | 12 +- src/loadingwidget.h | 4 +- src/main.cpp | 58 +- src/mainwindow.cpp | 740 ++++----- src/mainwindow.h | 91 +- src/mediaview.cpp | 570 +++---- src/mediaview.h | 88 +- src/painterutils.cpp | 26 +- src/painterutils.h | 11 +- src/playlistitemdelegate.cpp | 51 +- src/playlistitemdelegate.h | 5 +- src/playlistmodel.cpp | 53 +- src/playlistmodel.h | 40 +- src/playlistview.cpp | 50 +- src/refinesearchbutton.cpp | 22 +- src/refinesearchbutton.h | 7 +- src/refinesearchwidget.cpp | 70 +- src/regionsview.cpp | 10 +- src/searchlineedit.cpp | 153 +- src/searchlineedit.h | 36 +- src/searchparams.h | 60 +- src/searchview.cpp | 287 ++-- src/searchview.h | 21 +- src/searchwidget.h | 4 +- src/seekslider.cpp | 18 +- src/seekslider.h | 4 +- src/segmentedcontrol.cpp | 88 +- src/segmentedcontrol.h | 15 +- src/sharetoolbar.cpp | 4 +- src/sidebarheader.cpp | 51 +- src/sidebarwidget.cpp | 55 +- src/sidebarwidget.h | 7 +- src/snapshotpreview.cpp | 47 +- src/snapshotpreview.h | 14 +- src/spacer.h | 3 +- src/standardfeedsview.cpp | 12 +- src/toolbarmenu.cpp | 39 +- src/toolbarmenu.h | 2 +- src/video.cpp | 35 +- src/video.h | 14 +- src/{videoareawidget.cpp => videoarea.cpp} | 76 +- src/{videoareawidget.h => videoarea.h} | 23 +- src/videodefinition.cpp | 38 +- src/videodefinition.h | 13 +- src/yt3.cpp | 23 +- src/yt3.h | 8 +- src/yt3listparser.cpp | 15 +- src/ytchannel.cpp | 41 +- src/ytregions.cpp | 5 +- src/ytsinglevideosource.cpp | 17 +- src/ytvideo.cpp | 230 +-- src/ytvideo.h | 7 +- style.css | 55 +- 403 files changed, 9673 insertions(+), 6528 deletions(-) delete mode 100644 .clang-format delete mode 100644 .gitignore create mode 100644 icons/dark/16/audio-volume-high.png create mode 100644 icons/dark/16/audio-volume-high@2x.png create mode 100644 icons/dark/16/audio-volume-muted.png create mode 100644 icons/dark/16/audio-volume-muted@2x.png create mode 100644 icons/dark/16/bookmark-new.png create mode 100644 icons/dark/16/bookmark-new@2x.png create mode 100644 icons/dark/16/bookmark-new_active.png create mode 100644 icons/dark/16/bookmark-new_active@2x.png create mode 100644 icons/dark/16/bookmark-remove.png create mode 100644 icons/dark/16/bookmark-remove@2x.png create mode 100644 icons/dark/16/edit-find.png create mode 100644 icons/dark/16/edit-find@2x.png create mode 100644 icons/dark/16/email.png create mode 100644 icons/dark/16/email@2x.png create mode 100644 icons/dark/16/facebook.png create mode 100644 icons/dark/16/facebook@2x.png create mode 100644 icons/dark/16/go-next.png create mode 100644 icons/dark/16/go-next@2x.png create mode 100644 icons/dark/16/go-previous.png create mode 100644 icons/dark/16/go-previous@2x.png create mode 100644 icons/dark/16/link.png create mode 100644 icons/dark/16/link@2x.png create mode 100644 icons/dark/16/mark-watched.png create mode 100644 icons/dark/16/mark-watched@2x.png create mode 100644 icons/dark/16/media-playback-start.png create mode 100644 icons/dark/16/media-playback-start@2x.png create mode 100644 icons/dark/16/media-playback-start_checked.png create mode 100644 icons/dark/16/media-playback-start_checked@2x.png create mode 100644 icons/dark/16/safesearch.png create mode 100644 icons/dark/16/safesearch@2x.png create mode 100644 icons/dark/16/search-duration.png create mode 100644 icons/dark/16/search-duration@2x.png create mode 100644 icons/dark/16/search-quality.png create mode 100644 icons/dark/16/search-quality@2x.png create mode 100644 icons/dark/16/search-sortBy.png create mode 100644 icons/dark/16/search-sortBy@2x.png create mode 100644 icons/dark/16/search-time.png create mode 100644 icons/dark/16/search-time@2x.png create mode 100644 icons/dark/16/show-updated.png create mode 100644 icons/dark/16/show-updated@2x.png create mode 100644 icons/dark/16/sort.png create mode 100644 icons/dark/16/sort@2x.png create mode 100644 icons/dark/16/twitter.png create mode 100644 icons/dark/16/twitter@2x.png create mode 100644 icons/dark/16/unwatched.png create mode 100644 icons/dark/16/unwatched@2x.png create mode 100644 icons/dark/16/video-display.png create mode 100644 icons/dark/16/video-display@2x.png create mode 100644 icons/dark/16/worldwide.png create mode 100644 icons/dark/16/worldwide@2x.png create mode 100644 icons/dark/24/edit-find.png create mode 100644 icons/dark/24/edit-find@2x.png create mode 100644 icons/dark/24/refine-search.png create mode 100644 icons/dark/24/refine-search@2x.png create mode 100644 icons/dark/32/content-loading.png create mode 100644 icons/dark/32/content-loading@2x.png create mode 100644 icons/dark/32/document-save.png create mode 100644 icons/dark/32/document-save@2x.png create mode 100644 icons/dark/32/media-playback-pause.png create mode 100644 icons/dark/32/media-playback-pause@2x.png create mode 100644 icons/dark/32/media-playback-start.png create mode 100644 icons/dark/32/media-playback-start@2x.png create mode 100644 icons/dark/32/media-playback-start_checked.png create mode 100644 icons/dark/32/media-playback-start_checked@2x.png create mode 100644 icons/dark/32/media-playback-stop.png create mode 100644 icons/dark/32/media-playback-stop@2x.png create mode 100644 icons/dark/32/media-skip-forward.png create mode 100644 icons/dark/32/media-skip-forward@2x.png create mode 100644 icons/dark/32/open-menu.png create mode 100644 icons/dark/32/open-menu@2x.png create mode 100644 icons/dark/32/view-fullscreen.png create mode 100644 icons/dark/32/view-fullscreen@2x.png create mode 100644 icons/dark/32/view-list.png create mode 100644 icons/dark/32/view-list@2x.png create mode 100644 icons/dark/32/view-restore.png create mode 100644 icons/dark/32/view-restore@2x.png create mode 100644 icons/dark/88/channels.png create mode 100644 icons/dark/88/channels@2x.png create mode 100644 icons/dark/88/unwatched.png create mode 100644 icons/dark/88/unwatched@2x.png create mode 100644 icons/light/16/audio-volume-high.png create mode 100644 icons/light/16/audio-volume-high@2x.png create mode 100644 icons/light/16/audio-volume-muted.png create mode 100644 icons/light/16/audio-volume-muted@2x.png create mode 100644 icons/light/16/bookmark-new.png create mode 100644 icons/light/16/bookmark-new@2x.png create mode 100644 icons/light/16/bookmark-new_active.png create mode 100644 icons/light/16/bookmark-new_active@2x.png create mode 100644 icons/light/16/bookmark-remove.png create mode 100644 icons/light/16/bookmark-remove@2x.png create mode 100644 icons/light/16/edit-find.png create mode 100644 icons/light/16/edit-find@2x.png create mode 100644 icons/light/16/email.png create mode 100644 icons/light/16/email@2x.png create mode 100644 icons/light/16/facebook.png create mode 100644 icons/light/16/facebook@2x.png create mode 100644 icons/light/16/go-next.png create mode 100644 icons/light/16/go-next@2x.png create mode 100644 icons/light/16/go-previous.png create mode 100644 icons/light/16/go-previous@2x.png create mode 100644 icons/light/16/link.png create mode 100644 icons/light/16/link@2x.png create mode 100644 icons/light/16/mark-watched.png rename {images => icons/light/16}/mark-watched@2x.png (81%) create mode 100644 icons/light/16/media-playback-start.png create mode 100644 icons/light/16/media-playback-start@2x.png create mode 100644 icons/light/16/media-playback-start_checked.png create mode 100644 icons/light/16/media-playback-start_checked@2x.png create mode 100644 icons/light/16/safesearch.png create mode 100644 icons/light/16/safesearch@2x.png create mode 100644 icons/light/16/search-duration.png create mode 100644 icons/light/16/search-duration@2x.png create mode 100644 icons/light/16/search-quality.png rename {images => icons/light/16}/search-quality@2x.png (64%) create mode 100644 icons/light/16/search-sortBy.png create mode 100644 icons/light/16/search-sortBy@2x.png create mode 100644 icons/light/16/search-time.png create mode 100644 icons/light/16/search-time@2x.png create mode 100644 icons/light/16/show-updated.png create mode 100644 icons/light/16/show-updated@2x.png create mode 100644 icons/light/16/sort.png create mode 100644 icons/light/16/sort@2x.png create mode 100644 icons/light/16/twitter.png create mode 100644 icons/light/16/twitter@2x.png create mode 100644 icons/light/16/unwatched.png create mode 100644 icons/light/16/unwatched@2x.png create mode 100644 icons/light/16/video-display.png create mode 100644 icons/light/16/video-display@2x.png create mode 100644 icons/light/16/worldwide.png create mode 100644 icons/light/16/worldwide@2x.png create mode 100644 icons/light/24/edit-find.png create mode 100644 icons/light/24/edit-find@2x.png create mode 100644 icons/light/24/refine-search.png create mode 100644 icons/light/24/refine-search@2x.png create mode 100644 icons/light/32/content-loading.png create mode 100644 icons/light/32/content-loading@2x.png create mode 100644 icons/light/32/document-save.png create mode 100644 icons/light/32/document-save@2x.png create mode 100644 icons/light/32/media-playback-pause.png create mode 100644 icons/light/32/media-playback-pause@2x.png rename {images => icons/light/32}/media-playback-start.png (54%) create mode 100644 icons/light/32/media-playback-start@2x.png create mode 100644 icons/light/32/media-playback-start_checked.png create mode 100644 icons/light/32/media-playback-start_checked@2x.png create mode 100644 icons/light/32/media-playback-stop.png create mode 100644 icons/light/32/media-playback-stop@2x.png rename {images => icons/light/32}/media-skip-forward.png (70%) create mode 100644 icons/light/32/media-skip-forward@2x.png create mode 100644 icons/light/32/open-menu.png create mode 100644 icons/light/32/open-menu@2x.png create mode 100644 icons/light/32/view-fullscreen.png create mode 100644 icons/light/32/view-fullscreen@2x.png create mode 100644 icons/light/32/view-list.png create mode 100644 icons/light/32/view-list@2x.png rename {images => icons/light/32}/view-restore.png (58%) rename {images => icons/light/32}/view-restore@2x.png (81%) create mode 100644 icons/light/88/channels.png create mode 100644 icons/light/88/channels@2x.png create mode 100644 icons/light/88/unwatched.png rename {images => icons/light/88}/unwatched@2x.png (89%) delete mode 100644 images/audio-volume-high.png delete mode 100644 images/audio-volume-high@2x.png delete mode 100644 images/audio-volume-muted.png delete mode 100644 images/audio-volume-muted@2x.png delete mode 100644 images/bookmark-new.png delete mode 100644 images/bookmark-new@2x.png delete mode 100644 images/bookmark-new_active.png delete mode 100644 images/bookmark-new_active@2x.png delete mode 100644 images/bookmark-remove.png delete mode 100644 images/bookmark-remove@2x.png delete mode 100644 images/channels.png delete mode 100644 images/channels@2x.png delete mode 100644 images/content-loading.png delete mode 100644 images/content-loading@2x.png delete mode 100644 images/document-save.png delete mode 100644 images/document-save@2x.png delete mode 100644 images/edit-clear.png delete mode 100644 images/edit-find.png delete mode 100644 images/email.png delete mode 100644 images/email@2x.png delete mode 100644 images/facebook.png delete mode 100644 images/facebook@2x.png delete mode 100644 images/go-next.png delete mode 100644 images/go-next@2x.png delete mode 100644 images/go-next_active.png delete mode 100644 images/go-next_active@2x.png delete mode 100644 images/go-previous.png delete mode 100644 images/go-previous@2x.png delete mode 100644 images/go-previous_active.png delete mode 100644 images/go-previous_active@2x.png delete mode 100644 images/go-top.png delete mode 100644 images/go-top@2x.png delete mode 100644 images/link.png delete mode 100644 images/link@2x.png delete mode 100644 images/mark-watched.png delete mode 100644 images/media-playback-pause.png delete mode 100644 images/media-playback-pause@2x.png delete mode 100644 images/media-playback-start@2x.png delete mode 100644 images/media-playback-stop.png delete mode 100644 images/media-playback-stop@2x.png delete mode 100644 images/media-skip-forward@2x.png delete mode 100644 images/refine-search.png delete mode 100644 images/refine-search@2x.png delete mode 100644 images/safesearch.png delete mode 100644 images/safesearch@2x.png delete mode 100644 images/search-duration.png delete mode 100644 images/search-duration@2x.png delete mode 100644 images/search-quality.png delete mode 100644 images/search-sortBy.png delete mode 100644 images/search-sortBy@2x.png delete mode 100644 images/search-time.png delete mode 100644 images/search-time@2x.png delete mode 100644 images/show-updated.png delete mode 100644 images/show-updated@2x.png delete mode 100644 images/sort.png delete mode 100644 images/sort@2x.png delete mode 100644 images/system-search.png delete mode 100644 images/system-search_active.png delete mode 100644 images/system-search_selected.png delete mode 100644 images/twitter.png delete mode 100644 images/twitter@2x.png delete mode 100644 images/unwatched.png delete mode 100644 images/video-display.png delete mode 100644 images/video-display@2x.png delete mode 100644 images/view-fullscreen.png delete mode 100644 images/view-fullscreen@2x.png delete mode 100644 images/view-list.png delete mode 100644 images/view-list@2x.png delete mode 100644 images/view-more.png delete mode 100644 images/view-more@2x.png delete mode 100644 images/view-refresh.png delete mode 100644 images/view-refresh_active.png delete mode 100644 images/view-refresh_selected.png delete mode 100644 images/window-close.png delete mode 100644 images/window-close_active.png delete mode 100644 images/window-close_selected.png delete mode 100644 images/worldwide.png delete mode 100644 images/worldwide@2x.png create mode 100644 lib/http/.gitignore rename {src => lib}/http/README.md (100%) rename {src => lib}/http/http.pri (100%) create mode 100644 lib/http/http.pro rename {src => lib}/http/src/cachedhttp.cpp (92%) rename {src => lib}/http/src/cachedhttp.h (69%) rename {src => lib}/http/src/http.cpp (93%) rename {src => lib}/http/src/http.h (86%) rename {src => lib}/http/src/localcache.cpp (95%) rename {src => lib}/http/src/localcache.h (100%) rename {src => lib}/http/src/throttledhttp.cpp (84%) rename {src => lib}/http/src/throttledhttp.h (95%) create mode 100644 lib/idle/README.md rename {src => lib}/idle/idle.pri (100%) rename {src => lib}/idle/src/idle.h (99%) rename {src => lib}/idle/src/idle_linux.cpp (59%) rename {src => lib}/idle/src/idle_mac.cpp (77%) rename {src => lib}/idle/src/idle_win.cpp (100%) create mode 100644 lib/media/.gitignore create mode 100644 lib/media/README.md create mode 100644 lib/media/media.pri create mode 100644 lib/media/src/media.h create mode 100644 lib/media/src/mpv/mediampv.cpp create mode 100644 lib/media/src/mpv/mediampv.h create mode 100644 lib/media/src/mpv/mpvwidget.cpp create mode 100644 lib/media/src/mpv/mpvwidget.h create mode 100644 lib/media/src/qtav/mediaqtav.cpp create mode 100644 lib/media/src/qtav/mediaqtav.h create mode 100644 locale/en_GB.ts create mode 100644 locale/pt_PT.ts delete mode 100644 src/exlineedit.cpp delete mode 100644 src/exlineedit.h rename src/{videoareawidget.cpp => videoarea.cpp} (63%) rename src/{videoareawidget.h => videoarea.h} (82%) diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 2409f52..0000000 --- a/.clang-format +++ /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 index 48266c8..0000000 --- a/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -build/ -Makefile* -minitube.pro.user -.settings/ -.DS_Store -.cproject -.project -local/ -*.swp -.tx -android -qtc_packaging -debian - - -*.stash diff --git a/README.md b/README.md index d238fe0..1580dc6 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,46 @@ +

+ +

+ # 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 diff --git a/empty.ts b/empty.ts index ec0b121..ce7cd4a 100644 --- a/empty.ts +++ b/empty.ts @@ -27,6 +27,14 @@ Translate %1 to your native language using %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. @@ -110,7 +118,7 @@ You have %n new video(s) - + @@ -159,6 +167,10 @@ Show Updated + + You have no subscriptions. Use the star symbol to subscribe to channels. + + All Videos @@ -179,17 +191,6 @@ There are no updated subscriptions at this time. - - You have no subscriptions. Use the star symbol to subscribe to channels. - - - - - ClearButton - - Clear - - DataUtils @@ -200,25 +201,44 @@ %n hour(s) ago - + %n day(s) ago - + - %n weeks(s) ago + %n month(s) ago - + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + + - %n month(s) ago + %n week(s) ago - + @@ -274,7 +294,7 @@ %n Download(s) - + @@ -418,6 +438,14 @@ MainWindow + + &Window + + + + &Minimize + + &Stop @@ -658,6 +686,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! @@ -746,6 +782,10 @@ Remaining time: %1 + + Volume at %1% + + Volume is muted @@ -778,26 +818,10 @@ Update - - Toggle &Menu Bar - - You can still access the menu bar by pressing the ALT key - - &Window - - - - &Minimize - - - - Menu - - MediaView @@ -844,6 +868,10 @@ Subscribe to %1 + + Switched to %1 + + Unsubscribed from %1 @@ -879,10 +907,6 @@ Install Update - - Pick a video - - PasteLineEdit @@ -892,11 +916,14 @@ - PlaylistItemDelegate + PickMessage - %1 views + Pick a video + + + PlaylistItemDelegate %1 of %2 (%3) — %4 @@ -936,10 +963,6 @@ PlaylistModel - - Searching... - - Show %1 More @@ -1061,7 +1084,7 @@ - Enter + to start watching videos. @@ -1069,11 +1092,7 @@ - to start watching videos. - - - - Watch + Enter @@ -1095,6 +1114,10 @@ &Back + + &Forward + + Forward to %1 diff --git a/icons/dark/16/audio-volume-high.png b/icons/dark/16/audio-volume-high.png new file mode 100644 index 0000000000000000000000000000000000000000..26cd8cd20dc587edec3c4059af33fcfd2ada2fbb GIT binary patch literal 588 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4YWhYR zMC`ViV*QgL=c%xUbJo4%8x*D+vCTZXL?>nOiiv8Qlb)IxdYnxDDCPF7-faKnoh5$t zCjXPoF6Yem+I&NybyM~Yo%^A!4n@tIR;G17>C9W)H{m$rw)sV3t8F&%XElc#HgzvP zV_CJ&d_@gk#>NQ!%LmJzg!o^~4ddpm@b0nIVvt|F>ucHFn=TIi97lP*+o}TikFkng z`;xx#gOzR3`~?yd3Y$zMo=j}FE%dyvYJ8$Krq*NXrb5@uQ|51fP3K+Y(y1DhuR3|g zCr=ZhY4TG9MWrHiJicFM_;K&J(Vgwte&W_W`+vW=uED(E|G%@Y3<)20mM>k=YU(tx z^0$Cx#lC!ry3Q?z`5Rv(y*?WociO~G@?Y|#?RJjKZuiVgFXrpLpt&!gR7QA7@IBSa ziEDoBTk4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuxRZO=PaQ7D^C~45Rcdz%^mVLhJeOh`1v`~;lwM8Q z;Nfw~{>gnngv2sa{+~H><=+gkm8`Bg zNvBF?`|F$CotN&P_pCNqcH%v&YsWQ=J(eeO-L}(ySy?83S+W8xzj% zXIhXt$N!Es!*+##V%xX&D9^Eu%4c^|i_C9TWhgBAweuF!%A2~cE;n!N+iE+_vSC$; zv!{dE>AdY?ov*hpKDExGx!|0$)kc-sb{rFQ{p>v?z1cPHhwMIn&o@PWb;d(8}i$u(w=m%t=@RIc82Yt+}iM!CG$Qrt4+1%k7NAq z{7>5;wdf2Ek5U7Eg@``v@Ltgb>QJWl68ElWqwX0`$Oe&tH4{Q?#Ky$ zaKA46z%w<2Th>*r&G}W0wMK29zVE@v?yx_;VT&c#XkX+%^#0wp-5xQiJpay|=ly*n zVy27b9^r3O-*_y1{KY0;EaK;?_}t*jA6O07Rygzi`y`?A-*D!n9mno9s(oA6J5@A4 zvLTQA@HuTp0r%E(PO7tKZJE_nvu|f&@qNF;^LuYTdXk&^Vw2LAxS;h(W@_G39=1Gv z__*$?7Q?mOHy?gJZ!%Z!K-}w!|KfWs?gz*I34R952&yHn5hW>!C8<`)MX5lF!N|bS zQrEyp*U%`$(9Fuv(#ptC+rZGuz~GO4$Ym4_x%nxXX_dG&^d`N@2WnvOboFyt=akR{ E06EOBmH+?% literal 0 HcmV?d00001 diff --git a/icons/dark/16/audio-volume-muted.png b/icons/dark/16/audio-volume-muted.png new file mode 100644 index 0000000000000000000000000000000000000000..e15b3e2bae8d356ef49f1d6b0db2db19e50245be GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLgE>*e3=tLm*!v*(1o8 zfuTx`fuW&=f#DZWsNn?zL#Y7+!>a@a2CEqi4B`cIb_Lo1C0ql1LR|m<|9>ep6^KrT zhhIub0iv^UahH;luOuf!xIpqUSo}g}CXjtDCkH5dxVjpoD?J@30yN=lwcRFTd|n$^IDrPLmbgZgq$HN4S|t~y0x1R~ z14Bz)10!8SqYy(gD?>{wBSUQiLn{M=KlUM)Q8eV{r(~v8;?~fc^d=vufx*+&&t;uc GLK6T~DvMG8 literal 0 HcmV?d00001 diff --git a/icons/dark/16/audio-volume-muted@2x.png b/icons/dark/16/audio-volume-muted@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d2436953d2280758e14b95aa812a0ef11ab164 GIT binary patch literal 523 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fgkssg_;`;yp|1%eb$e;FI@K3=u?U+8zI4?2&eE(jc0#^Gc% zuK}o>G0EHAg`tC0)&t04FY)wsWq-`XBPc9dd&D^lD3s;t;uvCa`s&4!C8<`)MX5lF!N|bSQrEyp*U%`$(9Fuv(#ptC+rZGuz~GO4$Ym4_x%nxX YX_dG&^d`N@2WnvOboFyt=akR{0IOQUHvj+t literal 0 HcmV?d00001 diff --git a/icons/dark/16/bookmark-new.png b/icons/dark/16/bookmark-new.png new file mode 100644 index 0000000000000000000000000000000000000000..26faefd62887afe324e0454446719319bf26c5ad GIT binary patch literal 491 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e40`}dl)I4_tbEXoob+{)0b$h&go z0!Pza&uxl-SdXZBgqZRbzn0?ZyApamR^Vz!l2(>}IMe!N+uv+!*`8K@Z~9zG)g7M$ znvUm(YgSihi7Wa1V|=kf*I}pFvNP%j7ghcLx|&r_*H=mF;?nfQZ{dj_Z@gx@P(3e7 zHTa|==k-6f`KxDN%C7q~DQ(rm*^GxK?7p2NY?Qe|aN_2;OV^Xvw_2vTO;lUF&VF08 z%nj2P`)_%SnY$UAzVR(s*Dzq@+LV-UWJJwZt`|BqgyV)hf9t6-Y4{85mmX8W`ys8ig2| uSs7Yd85wFD7+M(^{IL(YjG`eoKP5A*61Rrlq&N9M4Gf;HelF{r5}E*mPpzf^ literal 0 HcmV?d00001 diff --git a/icons/dark/16/bookmark-new@2x.png b/icons/dark/16/bookmark-new@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7bc263745f44c794cf10f3071a6bc73c69695a9c GIT binary patch literal 736 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuxRZO=PU*W#uuJ0jv*e$mrmX9FXAY2-2U*rw=Y$*3<5v6U+I_{(U7Qj)Hw24hC940 zzr5`Ijq1y9W>?-fzyE%=`DUlfbHA$SuikU2e5yl#!Q#~{i##^^x&-mAYA>4Iara+! zw5H^fW0ofvt4;Q{EK*mV+j29mnprx-#Ba*}WeF4J9{BftVquWyhn4D`9a9a;b$(Cm zd2Uiaf7kBJzS9RbZ=RMKq4DnE!A)vcolKV9oD*}};k>2Ti|&BzuUa8WKlHEVUTa~y zl9?5gSikLv@6K1Ef!DUL`1?@OU9W=cm65*1eYSn; z)V=a+wlC#>V&uHXr)7VV28a3A#)9RpM{gv|l9W4Xzpx-!d1X0|#IpORxi5Mi_IszQ zEW>u?XrIu7BVh+#YBWl8`zAzmUU8VaMDA7ceUtQ*y|T?08Dfmj+x(ML`sL;>{KLxr z;fYN;ubJc@xwfc;^gpqyejm`w{%N-QYlHroP7J(#r{y_$idb_JoUK1ytG76jTNy9D zf;rN5j!q)a3R5=k?!;9~ukUUS;Z0w{oUHbGU#YC`pMv$fwb#kc$=uuPUjK6CdzD)` z>vd}ibL(>F-iWthoO#xK;U!mKGEgmXjVMV;EJ?LWE=mPb3`PcqmbwN;x`sv}hGtfV smR3fF+6IPJ1_poZLoTCe$jwj5OsmALp*QJGK2QULr>mdKI;Vst0JC){^#A|> literal 0 HcmV?d00001 diff --git a/icons/dark/16/bookmark-new_active.png b/icons/dark/16/bookmark-new_active.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7214ba8cd6fbaf8441e51fca6e4e6a0e66e5df GIT binary patch literal 436 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4l}0#}JM4r4tPK7z0II-}g+EXm!!8-C`JT&|>m%-p$0a@awvkD>6EN_a2rzaq`58#@dsocQ_yHv~&7m5p-eep-HBt-=mLo zwTdQv*((2uEql)eXZtyIlYstEEpd$~Nl7e8wMs5Z1yT$~28Ncp21dGuMj?h~R)&^V qMuyr3hE@g!f9yjpqiD#@PsvQH#I2z>=}kUR1B0ilpUXO@geCy={+w_C literal 0 HcmV?d00001 diff --git a/icons/dark/16/bookmark-new_active@2x.png b/icons/dark/16/bookmark-new_active@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5283eceee2d1ff7a0e1b3b0a7b4c3d5d7a646ae7 GIT binary patch literal 626 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuxRZO=PU*W#wt%2#}JR>OQ-JFcMcRezTZE3OJ|t2(-KbO?xQ)u%%O@a z532lQ|KD^qV}nEoUrxu3)+1>KR|16Wl@!?om;R0T_1N=%?fb7^_dI#JuloGYeeb{D zDL%h7ApP3YjdHV%ZPVUO_QmvAUQh^kMk%6J5u7Q!Rp;3sTnU$fXm64&gfuWUw j!5{mO%P1Oh^HVa@DsgM*O?s0L)WG2B>gTe~DWM4fKwtfI literal 0 HcmV?d00001 diff --git a/icons/dark/16/bookmark-remove.png b/icons/dark/16/bookmark-remove.png new file mode 100644 index 0000000000000000000000000000000000000000..f98a670ee08c43a932853a80bdc243cd9171add7 GIT binary patch literal 564 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLgE>*e3=tLm*!v*(1o8 zfuTx`fuW&=f#DZWsNn?zL#Y7+!>a@a2CEqi4B`cIb_Lo1B^m>KLR|j?!C{8~=NSH< zW%z%Q>;DC=|Cbp4pJxN1J7Dn(3?PwH3?Qj9Fsc7Q28<09UkGMHWP$2|Qa}UY3ZM+Q z8nD!LF!%reTN`&~03D-Q666=m!0hBWa|X-H*Q}l%9D8-*yEzQb` z<3FS>7@U$xN#snu+SCEm#F*sm?!wT)D(eB{u$OrHy0SlJ;t>=Utv%wL1r)0Gba4!k zxE$MeTdYYzz|~!M<(_GdS)3Is=luWgIQe$dxyv`J&+;(5a1m_joNQlc(Kct&WlI6^ zBWJGcxe=^1ZPk;2t1q5LG2EH8+x3)OmbT)o=xVlIbUO_QmvAUQh^kMk%6J5 zu7Q!Rp;3sTnU$fXm64&gfuWUw!5{mO%P1Oh^HVa@DsgM*O?s0L)WG2B>gTe~DWM4f Dvx(x> literal 0 HcmV?d00001 diff --git a/icons/dark/16/bookmark-remove@2x.png b/icons/dark/16/bookmark-remove@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5650d2878e2d0c607fa71fc80db4392321bbd41a GIT binary patch literal 898 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6|34fl)BPC&cwX5S(QAe~#h* zc_3l~p%X9$NCZjz6ioaASP-NIto{s)0TjQ;@E=Gbn+7xiqTm7>hyg?wxIpMS*btBj za0VMlFGvB{*s~1(|07Fr{a*;S2q+G507xTPCBy`vnaEryo9+KauK$-HPJ-A4v<2*d zJ79}|P6e69_Wv-L{Qtj8#HR-sYLO*De!&b};bH66@_zfy9~ZlIi{Q^+!l}u7c8UJ} zBQCF0Y5(H2o6PaE3hWaXamIX2F}VBClyChuA;$8*|2mlNXlqqhojNI%F=xj!Klex5 zSIrcNc<%pr`7+iU0UlS_<`nVU0xe-o@^*J&=wOxg0CLz%Jbhi+A2aa?3X9eran1rt zb9%ZshFF|VPEZguFcJzlaKy)fkz2d2(yWZnZx7cFCFPwTwH|c#JUOa<{`zlLd} zODON|J-gWM$j*wIaY^RZ3B9WqU8i5a?Eba8QE}CtiH21YGiJEsGGjp{!kSU`n}(dsxD{j)ey}l^(v~34GDA@BpjIn^(sUJb1^L zRJrJMF_Uog5^;+Xo;{6i96uIle(3G_^0~e64zrT-%nKqcZdHn^VSevVD1`icFd-nM zal;1=k$r~pyB7a2NaAK^VCePD^gDn4yFM_SR7+eVN>UO_QmvAUQh^kMk%6J5u7Q!R zp;3sTnU$fXm64&gfuWUw!5{mO%P1Oh^HVa@DsgM*O?s0L)WG2B>gTe~DWM4f5jcV0 literal 0 HcmV?d00001 diff --git a/icons/dark/16/edit-find.png b/icons/dark/16/edit-find.png new file mode 100644 index 0000000000000000000000000000000000000000..84a5f7a6c027da29efce6c96fd485dab3e8850d7 GIT binary patch literal 441 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4-w6%O`x;#s85=m{`R6^#-FmTX z^0HG>Oo}>cKP*yC7(Q@fwN<`T_2WqVbYDId-wnqqpDV=dI$wOFS@-;k*L#0+91P85 zUQ%k(xcb}iVz1J*XEbxpZIKHNJ`>@xTtM}kj-+AQ@i`4qA{{KUZC_3*cd>*lKG4I& z*qiS#<))Iz@89O{GaVKMUj3h<4)ly_iEBhjN@7W>RdP`(kYX@0FtpS)Fw!+N3NbXZ uGPJZZGSoIOv@$UGV;^!EMMG|WN@iLmZVkOjZ}Nc}7(8A5T-G@yGywqe#+ID` literal 0 HcmV?d00001 diff --git a/icons/dark/16/edit-find@2x.png b/icons/dark/16/edit-find@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0300a6abd39ffe9ca99567b5aa74547a2efd1aa3 GIT binary patch literal 654 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuxRZO=PU*W#%Z1|jv*e$-(EfI9TF(Q@WHrg)K@* zx?&%&<@5R#rL!}xs|mD(ZOyp6Prqq{?>imtiL*9!NEvWxwQjk$zSY_2@}A#1=DJqt zU-iEKdmwji=Xw=0v0Q1J|NTtNg?4I>V{7`Oe2%tC_;E>XKCJ33m3*`%cTuTX-M0#k zcap^p{fW!(PI)-f_NYUUBBKm$WqBpMe12}O=r-Q&(}*D?~bZmExD&)MdsxNngwerq}tCU9SuER$~m`hSI$PeYwO?YF&!$q{&$7^k3X+n zcPvhl`rOvlAfUBw#i;|;*IzAt{gp-ktrlC@=Ggsbd9_oGS4hRkU(gEgZ0r6bDe%kY zXfbm=cZ+PA;osjT>&&wXmE_j*hQ9p2->{D9_Ts%PL66tX0Y;i?iEBhjN@7W>RdP`( zkYX@0FtpS)Fw!+N3NbXZGPJZZGSoIOv@$UGV;^!EMMG|WN@iLmZVkOjZ}Nc}7(8A5 KT-G@yGywpISO6^m literal 0 HcmV?d00001 diff --git a/icons/dark/16/email.png b/icons/dark/16/email.png new file mode 100644 index 0000000000000000000000000000000000000000..90f9cb2dff3112da23d80bb3174da94fb386d733 GIT binary patch literal 436 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4l}0#}JM4TPL39Vq}y!Qonokl=|zwnfyVgj?VpFP@8c9GwTpM`sNjifR>^v65s@^x`%G!tue4{zN6KgX(du9j(@m1!A)LI9!*bP$ zV2SY1ryRm7CNA&$|4d9bC##>Q*2pAIWs&ckhuJ3>&3r{J$S&$Rq`PU&%LflHraCId z6rbzSmFyDn+8V@zFa4ghr1{jX73KMbipRS`9vCoqY*IP?)9wmO_mTF6 zeLe?&{NJ3%=@)u$B9Hqspg&YgTq8@`+m*4;Z literal 0 HcmV?d00001 diff --git a/icons/dark/16/email@2x.png b/icons/dark/16/email@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d7520a458d5f740d2b7d22394a224fdde85de7c0 GIT binary patch literal 590 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuxRZO=PU*W#xPG8#}JR>Z>OHuKAb3X{Ql&K2$$T#M@KSu7Du+IE}9wU za?){<3g^%GCu;Q#OLm+{6u-o`tZY_&g6iR%YReya`+nalzrWV<w?O3tU=2Y^8qq`L6tu%|{5ZS@BuU}$a(8RPO3O-C0EG z`-u8JkL&-Be-dYz-!&(r?8XZ96kwpMmbgZgq$HN4S|t~y0x1R~14Bz)10!8SqYy(g uD?>{wBSUQiLn{M=KlUM)Q8eV{r(~v8;?~fc^d=vufx*+&&t;ucLK6V}0^N`R literal 0 HcmV?d00001 diff --git a/icons/dark/16/facebook.png b/icons/dark/16/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..6e70bdd4066af95692a73acd6fd37fd85118b5c7 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^d_XL~!2%?E=C*AFQY`6?zK#rxTw$LW#0-Iag=CK) zUj~LMH3o);76yi2K%s^g3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tW#W6(Ua_>Y#zNP>Hm;2)99GOK~H+~l|QTrw`htV%N z^{J?j#*5xx{tq}U8YQ)aV*c2l3!8m1r7UeOM{>J+W2L3$vMoD`6rOW5>?;gcYSHrC zbV^!Y>s}ATN=5YrDz~fbUPv^k%cj4sjC`80Cf)J0+=#@40XiNU@|l`Z_W&a)o_j5HkcxCVK?= zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R3&|Mvbf`++?6 z5>H=O_Qy;-g2JM;N1U^OLa#hs978NlznyBEcgR5GczvIist!-=DYl19cbM4v4jejQ zE5JQfKsw0UsIVt)!7JTe8x$Ecc_hzVNU)nbA$)4*^MiHU{#U(!?w>kgruvnKh7QSG zV!rW`z4{z0rV2WBNzTuk@%ii&f2EpJCNG!nRXd-iW)U_0+&*jlN8+(UoR44Z(wYBJ zdF|>R&c<}>Nhiglw|)P-@J-0}ZK0Z{UYLkKp2ZRoX`jUC(5Wzc<6DdSlW$At>rOGc zw&c*OeGhkq71uSG_Fk!9%(}vP)wQFEV~EE2=f1e+PHH31`YW&~)|NpOgkx0&6OdXSAIqV$s7ro|pDraTP3}JPA!RHeG zfyG!PKvd&T`tc9K%*sbMax#3>+!C8<`)MX5lF!N|bSQrEyp z*U%`$(9Fuv(#ptC+rZGuz~GO4$Ym4_x%nxXX_dG&^d`N@2WnvOboFyt=akR{0I=|F AVgLXD literal 0 HcmV?d00001 diff --git a/icons/dark/16/go-next@2x.png b/icons/dark/16/go-next@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3f5925f5b9516628462a3b744d38626be723e6 GIT binary patch literal 436 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuxRZO=PaPm9#0p?5Rc<;r=I3LWFX*je2W9;L%E&>EL>i-UnVelaH=e6 zInLVJFZ2G?KE*xk-rJsKUbo)Hyh?hu*WdR{Ql(jQSb_{0lm%~2Jg4As>(!!&S&k`8 z@nYH1)ou)FeyI%RO77)~pG2($!e;xl+kdXwJd12r&sy85}Sb4q9e07_q#S^xk5 literal 0 HcmV?d00001 diff --git a/icons/dark/16/go-previous.png b/icons/dark/16/go-previous.png new file mode 100644 index 0000000000000000000000000000000000000000..f582862e3620aa82f17e837ea4d03786a64a4cf5 GIT binary patch literal 346 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4bP0l+XkK+Ang* literal 0 HcmV?d00001 diff --git a/icons/dark/16/go-previous@2x.png b/icons/dark/16/go-previous@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..85523c865e6b3c09356e7b6342c4a1734a9a561e GIT binary patch literal 431 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuxRZO=PaPmc25__5Rc<;r=I0KWWduJ8~6U!QT(mPsM3=TDU!@Vsneeqz(~r7Nxc+vlx$ zsrvg@qWsNQ)=l5eH%zZhe$RC)x8ZUgqmeDc+*q%WLTKU@ZbgirE1v}^~Zn2U+xRrqT;*qUwpJo z0dsF!Pv1l7*#$r^sFt`!l%yn~RsL!%HwGb=+&DH3d-q7|xqC^DQd)bP zznGZalIDNF=+kqIc}C6}-!&d9TA0d}Tg5+Yb`UEwwF>sR_vgvkGpkSB5BsaS-SdnX z=Xr*M{Y6K(ZOe8hb2Pa2``8L?e0StR(BZz7b7f+!HaG}gc+m1&Cjaw3KS8UaH)oD4 z>xfmq7ABHw{Ci&1*E=pfXLC+&T9}%qV^{gGE&bcs9DO&zY`-^MH@;jnKixD%we}X zbt&ZEw6roZ p(l#)(GB7xM-Omq2LvDUbW?Cg~4STx2eFAD=@O1TaS?83{1OQo@wax$l literal 0 HcmV?d00001 diff --git a/icons/dark/16/link@2x.png b/icons/dark/16/link@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e32d82687b6b4aa363c07721ba972174864ba0c7 GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKheq7*Be-IEHu}zk1c)BQ#Os_{ZmIODor3l+fjs3cp-%X5~_c z&DWkyTXwBUW63Qi!JPgK&!&GuXHyked>s`pUJ@v}%H}zH&%?haFTT$E{k$ZP!}0y@ zJ$v?l{(Go;-t#@`>SgBN-c0&m-t@6T?sQ<^(HXrHce#9f^DUw%m+BTXe@*iK_)xY|N$hkUJ8LJW`_v*@P?n<}V`*3wP z@2vCcJQ2rwa}Fw%PInWHKNPbz_Y&*o8NaIcs7W~9%uuqr$T#mz^KOmVW^X1e;r$u~zBz@5*;0D;^&&x#WC5N24;Ve2)E@JOBR||K&Y=YI%3T_2i?#cvdZOjVMV; zEJ?LWE=mPb3`PcqmbwN;x`sv}hGtfVmR3ea+6IPJ1_o!Z`}v`0$jwj5OsmALVNch$ QPe2U}p00i_>zopr0MWEDCIA2c literal 0 HcmV?d00001 diff --git a/icons/dark/16/mark-watched.png b/icons/dark/16/mark-watched.png new file mode 100644 index 0000000000000000000000000000000000000000..216f85529e0abed361c3457bd7dcd79221ae7fc9 GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4kxxL+y2QayUWTuOAA)>FREr=m=HVn zmq=b;n5G_Io?XXQjglQV6nDSS5v*%>(GuC!X)b&G;P?7+wSVh{%;P!q&X!xgS(R(5 zaPg5zrp%|EoCp3KWebVON^$E~tKNInUO?f}iRF6J^|vqdndbKIWzkQQUKh^3Vk?F{ z$Csx}t_d|h#!%At*(RlIs%MP68uJmW9SWCLIIJl;Vih1b>B&q*{xuSbdp;fM`}g?G z^+gw!xbpn@b9K*q{c~USV~p0=Wv&7GMzzE>q9i4;B-JXpC>2OC7#SE^>KYj78XAQd wnpqiIS{WH>8yH#{7@WQC=ZB&pH$NpatrE9}Jzd{E0W~mqy85}Sb4q9e08NIRxc~qF literal 0 HcmV?d00001 diff --git a/icons/dark/16/mark-watched@2x.png b/icons/dark/16/mark-watched@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9b38fe2de31c77ac25b31d338db60d18e873d4db GIT binary patch literal 724 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKheq81HzxIEHu}e>>IQN5oO&SpDX56)7$$CKp>Co$3h^%3>fIB;_ z#de=LSLm&gp_8}k``-7zpFQ1czT2sAQ}Wz^c*7t1Jd-|txK+7&c2C9=p04>>(Pttq ze=Cy-o}wT0&TR1__Ck)ZJ;9G(_8*rx;oVal-2QqE!}QFnC+0|b*4rL>P`v1$~(A5o_F^s>-w4#y3{!`CVFn*KEz|Z+RvC(XaKBUw>Fzdj6jN%%&WT zbCN}W9y+`WTdT(SCRR?Wl>f))O$QAoXo|fw_>56I`ZixYxX8y< z)aPQzrPKNc-})5H%8FwTWm#e8>*LDiUMq6tV2SGKl8)#H_WW+$?LU0DeHT~Cnocs?8ob`gigE7 z-*he3I^M`_!)L$wRbGlbv-hgbv^;UrC&}|PcaLQ)`=?EzWw-ZmUR>!cxo*?qiTsZ| zPw!gUQR%a3vf-{%@{_Cd<1CHaZz|Qf?JQn2{q+)y?xgnjS&ccr++~78{&9;kKajuo zby|M$M8oI6s8=mzopr0BS2Ce*gdg literal 0 HcmV?d00001 diff --git a/icons/dark/16/media-playback-start.png b/icons/dark/16/media-playback-start.png new file mode 100644 index 0000000000000000000000000000000000000000..0d1dd2acd2d534cd70ee3f7d6d654cbffb1d27cf GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4mdKI;Vst0LsN{8UO$Q literal 0 HcmV?d00001 diff --git a/icons/dark/16/media-playback-start@2x.png b/icons/dark/16/media-playback-start@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8ee8e5fb10da4b1585d7f5cf3ee1fbd101a63a GIT binary patch literal 394 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKlESlRRA6#~}(r4!-n9>>WCY+K=cy^xo@X<7_H=#w1k}Lb>FVdQ&MBb@0A_=Q7ytkO literal 0 HcmV?d00001 diff --git a/icons/dark/16/media-playback-start_checked.png b/icons/dark/16/media-playback-start_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..0d1dd2acd2d534cd70ee3f7d6d654cbffb1d27cf GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4mdKI;Vst0LsN{8UO$Q literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..5a8ee8e5fb10da4b1585d7f5cf3ee1fbd101a63a GIT binary patch literal 394 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKlESlRRA6#~}(r4!-n9>>WCY+K=cy^xo@X<7_H=#w1k}Lb>FVdQ&MBb@0A_=Q7ytkO literal 0 HcmV?d00001 diff --git a/icons/dark/16/safesearch.png b/icons/dark/16/safesearch.png new file mode 100644 index 0000000000000000000000000000000000000000..a3597010d166fed9835ed383c1eb807a5b047ac3 GIT binary patch literal 405 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4UO_QmvAUQh^kM zk%6J5u7Q!Rp;3sTnU$fXm64IQfuWUw!P)D6ekdAp^HVa@DsgMr)Aj8WPy>UftDnm{ Hr-UW|caDNh literal 0 HcmV?d00001 diff --git a/icons/dark/16/safesearch@2x.png b/icons/dark/16/safesearch@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0e2257f0f5b89ae06906da2b687570da8843ab04 GIT binary patch literal 550 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKheq81+0|978;gZ=JH&@34bFi@ILS!UmQ_55hYdf&?nOOSUMu zHcdO;$mS%had079_n~PI7I)0KaqaF=@xw8XCzc)Gyg9u*z}iLk_gl$Q$HPhyHiiRRl;~l&q8gF?+gBRIoSVEdMcN8*Gcxa=r&Qkf`G*KhfK{} z*1X~t+1dq4p1UrVcHVptah{Dqa&^f`h8sN5k4+k@YhMJHPxBG$Gno4`aFWR<-iPHM zLWDR<3^ca(?Wx}2COna8L!UtQABK3Bv-Zp681jw(aXGC2DRzqa%;pWt62xz;o=I zEp-iybPbI{49%9nO2Eg!=A2hpMV+|JYD@<);T3K F0RT6^%bNfI literal 0 HcmV?d00001 diff --git a/icons/dark/16/search-duration.png b/icons/dark/16/search-duration.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec10d232b77d8682c9d6d8e1bdf84299f3dcc1a GIT binary patch literal 496 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4^S;09ymB>IqUvkbiVN9aUS2JH$-JkM zc`El@wii1NPG-(ly7r~FaIG<`USL%2*<}Hp#hvp`1>cR^^qX07b;;rbj=JxDZn>aT zJLib**77&W8(GZ^W}Gcjt2|Nv+-~xkGp=by!fR`$d=IfaojmEtm889LyB3=m1>F*x zEpbHi*ZzGst|;~`UTu^&|7WrAd97$cagF~9uipijtloDl;(l9)Z~MH$)PL4%ibG|u zvFy=4Q@>pDVrkNYquq_k(Y^mzr@gn$|6=9W2Mhz%64!{5l*E!$tK_0oAjM#0U}&jp zV5Dnk6k=#*WoT(-WTb6iXk}n<_PU=RiiX_$l+3hB+#2?DeftE|z~JfX=d#Wzp$PyM Cw!a?$ literal 0 HcmV?d00001 diff --git a/icons/dark/16/search-duration@2x.png b/icons/dark/16/search-duration@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..21cb8744c1f11349cac7c08417711efd2ac2c21f GIT binary patch literal 833 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKlGYo}Mm_As)x4PCe@v94OMdKTh*U^8t@T4!K*6Jrh_SZLr#C zWojsx&iAL1A$^4llfMGvy@N*@WfO8)f_&#p_{G4ngtO+W_#`YaqTJV+1DP+ zBYnN4UZ_mCwBD@3Q}ybcyG?G3S=3516nWRa-nmX&ttH9pMeDv5iRZtYzT;Zr^d*0m zkYIAuY_ZMa6%Xa-Uf;I2+A~h&?)Rr#{dhj@{;&V1xzfA-(nOh_Na<5a3I=Ka|J<2b z(sECtB-FgNF;%*H4QoaG?q33u9v}3>Sfz|IFJI_k{-fe4)j45?QKC%dg~yEIj3+)G z_Pk`l@K^BV@5wd{(@#_^Is44LY9mAK_2ff7n{7E+UU2rSFqC@Pa=!V@->~Gk00X;8 zl~6_j!-0bNH#jTe5+_+B>8@V-)2j!ew^ReVhojn}wm1f*`>aoMzX z*UVEKYb6_bm(Q>&Z`C~<@o}|b>h62D*RkA_{IYS9qjTgY-qdeCzk+WEh|QAKT-Q4< zI!f`*r3)**P2wZt`|BqgyV)hf9t6-Y4{85mmX8W`ys8ig2|Ss7Yd r85wCC7+M(^oW1VnhoT`jKP5A*61RpuUEe+dH86O(`njxgN@xNA!?a&_ literal 0 HcmV?d00001 diff --git a/icons/dark/16/search-quality.png b/icons/dark/16/search-quality.png new file mode 100644 index 0000000000000000000000000000000000000000..a602e45e0cd9831020494e479639f923f673dfac GIT binary patch literal 425 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4)~U_S@|RFkQBpIuhNOaah9=D zR=Mn*m(|=9=Ks}ARC}7p9oufnK3i*Vmopp>mEDfGtO#DHmT_@KK>Es)JB1#3`Q=JZ zp2)(=DVcnlVOpDT|ARLNJJSqOj&v(?KeNcMKhz%E; zH+vJ%@v0@R5hW>!C8<`)MX5lF!N|bSQrEyp*U%`$(9Fuv(#pt4+rZGuz~Jn4KR*-= cx%nxXX_dG&?CJXU38;a=)78&qol`;+0QfM8xBvhE literal 0 HcmV?d00001 diff --git a/icons/dark/16/search-quality@2x.png b/icons/dark/16/search-quality@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..db43613153a620ad382f195169777ee5e8714f06 GIT binary patch literal 605 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKheq7*jo6978;gznyC9$K)t;w0`qVrpzq{6I%{RmLv5SKKs8`tFYjf~SH3>JqRGe29G^6&rm zw0nCDZ$0Mrw=tIO)^Wcl;u;vylT^~hf2XMT#-l5XesfH}lW391?pZ3iaE);DnhAln zDFF)}T(EYj{_=o1-=;2lRwl=EYgZZ1P(=}sRS}*mJh)l{LXCuKXVpwdny>g>?5=rd z;bCSwcb%UHn09xtC}{s;3hPLo!CxC7?X3~*snM@`IrGk)3H78wsDZ)L)z4*} HQ$iB}D#+Fx literal 0 HcmV?d00001 diff --git a/icons/dark/16/search-sortBy.png b/icons/dark/16/search-sortBy.png new file mode 100644 index 0000000000000000000000000000000000000000..55f6c5860d9656d983141cb2117490348b9611e1 GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4fg=e#~>ytHw;HG#Q; z%eHCjlGCb1OwqG9OkLq~Nvfx&+c%)(=(U;8`2X|r)H?{UO!ch#dTp<+W#zp(<|$89 zUb@BQm_JILozUs$&+tI)-=8C= zj2SLmwV1tbi#-?Pg6wzK)4or!C8<`)MX5lF!N|bSQrEyp*U%`$(9Fuv(#pt4+rZGuz~Jn4KR*-= cx%nxXX_dG&?CJXU38;a=)78&qol`;+0DVD_7ytkO literal 0 HcmV?d00001 diff --git a/icons/dark/16/search-sortBy@2x.png b/icons/dark/16/search-sortBy@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7e6acdd33c64846eb6115345983d98b534cdfd37 GIT binary patch literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKlESUp-wMLp+Wr|2W^krN41O_k(sH9*qY^?SJN7-z@3DsIjfM z#^%F+AXuL!{GLhYmZve(>aw7o0}JO?C>La@U*oX3)oIMMzAULHp~kTDJg?BgoJ(hH zI$r$!`|G?l$7|o|Z}!~YbjFKg=l=YV^+}%W@5_~M|5+n?b6)z|6GiXKANzDJX%1v| zRdP`(kYX@0FtpS)Fw!+N3NbXZ uGPJZZGSW6Mv@$R_d)?0uMMG|WN@iLmZVh|7zI_5}VDNPHb6Mw<&;$TtDYR?= literal 0 HcmV?d00001 diff --git a/icons/dark/16/search-time.png b/icons/dark/16/search-time.png new file mode 100644 index 0000000000000000000000000000000000000000..b1cd0c3a8ff05f2e7720b8d9fa8b164fb38b4094 GIT binary patch literal 532 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4=6aVkEIcXPQ~I3D z+0}Q8a_Q}D_B*$$bglS;@CT+6B_H|TELdEuG+$}btOMW6e(VzyuRZsPkN^1|_bs2z z+PD8S$j>X{7kM*NLrP6fYlXni87Y(Xcb?&$|Jii5io8>s=cLqgpI(Jk%-pk4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu-KEcLKheq7%zLeIEHu}e>?S{cSxbYar@W1T(rN5-Es{!3{dg5mC(z4 z;Ml<@w|LRN4y!Ni6(8b1Tw0i{zIyV42p6tJ(=uFUvq5UUvNHj;GCPtvUy!m-CnSU)mFv^zQVm=_T>) z=W@ckB@CY2j(KhykfJ&5fRO70+3a&M3LQZwuB{QyirD<>VptA)^s9?&+C;AvL{1c# zle#=@>4eZ@FR}uT^yd_`o|8)J@aNpRpqH_K)|Y)Jr|FoVYC^eCwtS&^>wI)BNUUO_QmvAUQh^kMk%6J5u7Q!Rp;3sTnU$fXm64IQfuWUw!P)D6ekdAp^HVa@ XDsgMr)Aj8WPy>UftDnm{r-UW|%|aj3 literal 0 HcmV?d00001 diff --git a/icons/dark/16/show-updated.png b/icons/dark/16/show-updated.png new file mode 100644 index 0000000000000000000000000000000000000000..f9b36c3d5e54be50767cbdfa96840316d94df440 GIT binary patch literal 484 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4DylSg;yM9*7PK5NBKpOTD8%1I1DdoMCaYY;OE?pOJ z=-Dyt?8?JE;c0U|2l@D4a;tu<@H&Wn$D@g_W0{vv<+IpuEwEp=pkglj;qEJo*PsvQH#H~Tk4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dEQg#`T^qjv*e$uTJ&%W^$A`Ha~LmySqp3bpQ9TdS8_Lk%|@3Q`W_ate!o6LQ&>&=csdU*6gat^4q=n z{pZ=wYtz5UIvL)Y;C7{-PeRx~{EtbSL8*|q!|NrDGAFCN7I`GCX|!r)5o6g{uJUfD zEW?UhUq#FueEtNVh)V`V1d}+#& zoBoeja%b=gt@=@a&h>bs$W!NgzfUkW=xx?)`O#1s^MHx{S&PABrj&oXnHeq^u%B4N z%)W+CoqNMQu@bQlS)u;%5)5l2Stm#_D|Dr@TV6XjGnM^0&w=Gi`I)sxqth4|%665e zUSnjq!Bh3-uiKxu^Jg7=Hi7NZJzd5HS=aSj_fC*c<2|}IbF~J~l;>Ml-1$7;@VnCj zP1gx8i?vFZ*emUo4ZLwp`|=k7Z#UD>7wi5X3`qM}l&asU=o6bFfr% z@$2sIa;X1tWm+#V?io}|Tq8QFEV~E7%+Wy^w4GIFR;?fFghYkpB5ng-0f$Pl7N;aPT zf;H3k++OwzW7R%38=Ie9I-aLz6*(`2Yt?2SZ zMsAjd?-RjKZ)b;|Wb$9-d|upJqfNnR;~XiVeX1p{5hW>!C8<`)MX5lF!N|bSQrEyp z*U%`$(9Fuv(#pt0+rZGuz+fGFbqR`w-29Zxv`X9>?nfOy0o1_Y>FVdQ&MBb@0Jzm@ AL;wH) literal 0 HcmV?d00001 diff --git a/icons/dark/16/sort@2x.png b/icons/dark/16/sort@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0152e7b4adf3f8960fab9c6012ecfa29f03f18db GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^B0#Lb!2%?AWKXaKQY`6?zK#rxTw$LW#0)`_$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4~M4#}JFtXD=J_H5>4-UdZoj-{EsrG%TU}%DaLtx#d~! z8GfgCXgL-DNBFJkHa%^zcnj#KYN}z3FunY64!{5l*E!$ ztK_0oAjM#0U}&jpV5Dnk6k=#*WoT(-WTI_gXk}oqj=j1BMMG|WN@iLmZVmUN4xa#O OVDNPHb6Mw<&;$TTC5vPL literal 0 HcmV?d00001 diff --git a/icons/dark/16/twitter.png b/icons/dark/16/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..f789e6173e714214de8c2de22d0d7867349e58a4 GIT binary patch literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4hwt9l`^oVBqWylM53~Hawbnf1-nL3geGQs!VU@L^o*8 zYn#6F?Yg5^JRgSGlug;VUdQ+3qj}{!@6Kayll-ONFjLm-=fkP)zkUWEs(=5Fb?XNK zS>8&cV4!bROI#yLQW8s2t&)pUffR$0fuW_YfswAEQHY_Lm7%4Tk%_i}p_PHbI`--k e6b-rgDVb@NxHa65I(!1Cfx*+&&t;ucLK6T5tg6%i literal 0 HcmV?d00001 diff --git a/icons/dark/16/twitter@2x.png b/icons/dark/16/twitter@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8267aaed14046eb80b9363c9558432175090eb1b GIT binary patch literal 704 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dEQg#uJ_{jv*e$uTI_X9UUlgeE<8*s4c-EOtTy(=si*hS;fb9 z%ZhdDrG?!ZUEQwR{hSK3{u=(~w|IG>?B%5dv6DBnA}%IrH;HskS=21VZu-~4CO&CW zb@C#GFTdNW@731qe?F)DyybPr^5$yyf3=)87qX9*E?Ht`y870ay6zaoQxeXs^N!~f z${ydNazwyTY@&dEL!q|q5xJcn6PbjrEGqKMo%Brfigogj;#KBq`xcvbRcPwOysT52 z@!bA|W!fCZ)gfGl)9?8hv`kjL&@5ZHYES)z`CIp%)abv&q#X9kihrWIz=QI0$5rHK zE|{A2YYkJpjrhFzc|oj3rjMJ|A9&3QiO4TavuSSFx`S`e(wLy$W%+@J_Zz88?F|)P z6SJ11J%379?f)IIo*4n{s=wmJW^%M-BrRUTVfEMRBkQV+l(xl6FSBPxeeJEvK090B zq_}{w>t*}+_tSbJj|ByO*?(mwYs+Dc-R2k0SI)cpVdvlXA6i1S|28$=>74lY)uq$? z+JC2n|5QG2uJt0J>n+p4|4&wCrgiBpnsaAD<)lolv(DG0H4Y#7nXaf_;eW&K%iJ$_ zZ!Ad93g7IsD$GIs*W%pTaG&_8T31BQ{jc8{$Hcev@%PI+U%dfFvucTJL`h0wNvc(H zQ7VvPFfuT-)HN{DH8ct_G_x|av@$Z$HZZg@Fj&W4U4o(^H$NpatrE9}`%#Ba05vdp My85}Sb4q9e0Ld~QssI20 literal 0 HcmV?d00001 diff --git a/icons/dark/16/unwatched.png b/icons/dark/16/unwatched.png new file mode 100644 index 0000000000000000000000000000000000000000..6195ece992f0324531095c30d8d77b37145c5b9d GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4>G3#}JM4TQ3@VIWzJc`?!96P(^8Ww*}XtFH(1|Q`Zu`mey9Px~L$#m0x-3rB>FkJQ1O#GjF*iEznovovD?4Pi5+*1G~*_e_!LB zE55RB&ED!6T2}97z1#TWb?51}J1;_LtOzq0C?Y=7MwRH6#>jcSQ&L`h0wNvc(HQ7VvPFfuT-)HN{DH8ct_ wG_x|av@$Z$HZZg@Fj&W4U4o(^H$NpatrE9}`%#Ba05vdpy85}Sb4q9e0A*L6)&Kwi literal 0 HcmV?d00001 diff --git a/icons/dark/16/unwatched@2x.png b/icons/dark/16/unwatched@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7768f24b66ffb11b8741187e6b80e429acc41fa2 GIT binary patch literal 655 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dEQg#_66ejv*e$-%dTN-4ZBrY`)>*DVp-e!OJ>Z1t;&_H2vd8 z@eLDO1?qc6y{3rPH%!%7c+*3K+k21Qp+nc5B>(EfRK2ghZmLMn{zsza7wd>$qpH zukrD2{il-r{ps_cw^~Yh3Gx&_Z=Pr1r0aCW@YG8EqSF(+1=4S7m3LRI-o|n}tfn)| z-ck5>*pDeKmuIV_i>37+D{nIMV|puP>N)kxMdveb)R>I}!=alXT7%6o>mwA}WWN5|^p&a#yLoQOWi0|kO zO-ncZ%E@|abHCjAEn)nI&B-&pNAsNS89mAU6IMlTICPt_ON3S1E}#2lE^kqCsA1Z% z$n!f?7iF_~#V%tp4qMz+F(+_~&E;(;?!LYg^U2D7qlaEcf6G4Z!lzf)^KD(_b!l?n zmc^oLUm3cXq{>b6TKFivp@=2*?f3uJ{xclZ{+jW@b=w5CPrz7HEpd$~Nl7e8wMs5Z z1yT$~28Ncp21dGuMj?h~R)&^VMkd+@hE@g!>)5MHP&DM`r(~v8;?{6K>hKAm1_n=8 KKbLh*2~7aoP5V6n literal 0 HcmV?d00001 diff --git a/icons/dark/16/video-display.png b/icons/dark/16/video-display.png new file mode 100644 index 0000000000000000000000000000000000000000..ea291f2537fd8be6929e25a2c1c22c4383b2ff44 GIT binary patch literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e42OC7#SE^ z>KYj78XAQdnpqiIS{a#W8yH#{7_4KjEk4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dJ(wd`}n05Rc<;r#NyQG7xZ$Kho;je<_>up7K0LewBF(+%KNx z__(_xj7zs9s)gh4O%;Rxsc8vn9hFgMzlbw;NEFFE=9mz_?AGgWU124S;A<-7?>Yng zgvAdqX|Qf6w7%bW-o)>6M`ljOu_+&BeAz#7vv`+LWop;`UK8iTwkvjQ=lTfjfXWYZE=Ib_&cZdIO06JB*#5JNMC9x#cD!C{X zNHG{07+UHY80i`sg&3My8CqHynP?jrS{WFuW3Mhj(U6;;l9^VCTf_aR!zX|m7(8A5 KT-G@yGywn!1d8?m literal 0 HcmV?d00001 diff --git a/icons/dark/16/worldwide.png b/icons/dark/16/worldwide.png new file mode 100644 index 0000000000000000000000000000000000000000..cdd59e67ba6a30a6e42484d9e220e5fa12a799ff GIT binary patch literal 520 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4WY@Sj2^;wGdg*8(yTrTilv*KJN-*cIY{Y*c1A9H`k zqkTqEcHK+f?P{_WDQ^yJla24SHSo}DWMF*0=VHoi>x79DbQ8pXR#>FWRT7Rd;Qeyr zL2LRf8Fhi+W6ykVZ+*9RV(IQ>XQXEf9x-(;Jm#?`O7y*M$3js__ES?<7<%_faXZY? z;hm>pc>d~*0KX62f{#`$jQGV^n5O!0!cT>t8HZ1tXp0RNt0^#gc;WFB?=GGbmz;`t zGCrQ+tlcNM)tN;{a1-a7hbyjzCbJ(atJ(VYU-)YK!%@q6j@-E*-}PJnUHRRhb)QYX zF#ZMxmuiV?L`h0wNvc(HQ7VvPFfuT-)HN{DH8ct_G_x|av@$Z$HZZg@Fj&W4U4o(^ cH$NpatrE9}`%#Ba05vdpy85}Sb4q9e01(H*`2YX_ literal 0 HcmV?d00001 diff --git a/icons/dark/16/worldwide@2x.png b/icons/dark/16/worldwide@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e8b632f09fa6b7fd0215ecb90988aa93c1dd6f91 GIT binary patch literal 899 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dJ&xx2KC^h{y4(Q|*0%6Ge{tdp3HRI0-C1G5?@Ml4FmMWUp+& z?Ru#ow3t~b&&^FiZIZ*)CAzYO0^&?uY}_47o!7E8u{z&B-`IbB-@a*% zYVWt-srkO|bJ2V2=QjMe;-|d%{PQn!MfaWGS67~1b(wQvepH~x;#vAPI)865KA0Dg zqU>ko{yOF}#j4#`owS21W^;Ag zjN-c1)VV>G=eD@V%Jt=g;iI@ z!Q!`H>m4b-rxT8PWTP}JuVG~w*yw?G%Q~8k(1xJk(Ux%nxXX_dG&+>bhZ0;qw()78&qol`;+ E076D&g8%>k literal 0 HcmV?d00001 diff --git a/icons/dark/24/edit-find.png b/icons/dark/24/edit-find.png new file mode 100644 index 0000000000000000000000000000000000000000..ac08a4280c26e92fe5cca01a72073f3348c8a009 GIT binary patch literal 546 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>M z2g5gP4{;0-kalEwET42Ftfu(~lN8e=JEbPGPG2s?ZZ^B_4JY%8#D#L^MBknH`R%!# z8FlAh6Vt|j!-RP*dflb=MV$_6AbH7=RL zT9a{!rN;ApnEW|T31gd-pG%G#*}C>t$8^{5-tf)3V^`o>@AJ>K^`OcDW!^m-4=Zh6 z^O<2vInKtXJ<|TgZ`eXnzTlg#dd`2W4&57rdb2Cb?V&4s{#R4s9hC`m~yNwrEYN(E93Mh1qKx&}tN zhDITVW>$ulRz@b;28LD!2J6_XOHefA=BH$)RpQogKkD!apaup{S3j3^P614Ba#1H&(%P{RubhEkx$R|yOZRx=nF#0%!^3bX-AFeQ1ryDd9QrqFEcxe^?AFVAJN&za~8NpjPyONegiWo&m{K_^A|M+x| z`S)i$A{Fn_?pe$4uRedL`rb~tRclAhd zgQeeWUVu+sLF?NSc`TY)5(yI%W-BPjklW?; z!M}c*YkEyG{p@pjpO^Gg3;EumHO z%5!2LLMLrG%P25iffw1A};tOmC z^s{pLZ>2qBut?;r;5g9Xt^3L_d&O3c`-!hI+qoCad(Y4#q$_CW$kveluxxdF>Fmn8 zi>yp)9&Vnp{HmzJFP=x1)=YQ4&Axd!+iWNEDoN9K#W6|`FIT=vy>LCgr(LRZS(S8Em6sgh@DmLIq~g> z9eH7fQ8}X7>}z~N1YIl{ln&)K@A#i?94Xh(QE*)0-A}=%OdT4`Y!|~N8#;=v-;vc~ z5iqPe`Fr=LOV1N}KmCtSzRsxn)O=>eN;}h%>vw=DRkg%5q9i4;B-JXpC>2OC7#SE^ z>KYj78XAQdnpqiIS{a#W8yH#{7_4KjE14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>M z2g5gP4{;0-kalEwET42Ftfu(~lN8e=JEbPGPG2s?ZZ^B_4JY%8#D#L^MBknH`R%!# z8FlAh6Vt|j!-RP*dflb=MV$_6AbH7=RL zT9a{!rN;ApnEW|T31gd-pG%G#*}C>t$8^{5-tf)3V^`o>@AJ>K^`OcDW!^m-4=Zh6 z^O<2vInKtXJ<|TgZ`eXnzTlg#dd`2W4&57rdb2Cb?V&4s{#R4s9hC`m~yNwrEYN(E93Mh1qKx&}tN zhDITVW>$ulRz@b;28LD!2J6_XOHefA=BH$)RpQogKkD!apaup{S3j3^P614Ba#1H&(%P{RubhEkx$R|yOZRx=nF#0%!^3bX-AFeQ1ryDd9QrqFEcxe^?AFVAJN&za~8NpjPyONegiWo&m{K_^A|M+x| z`S)i$A{Fn_?pe$4uRedL`rb~tRclAhd zgQeeWUVu+sLF?NSc`TY)5(yI%W-BPjklW?; z!M}c*YkEyG{p@pjpO^Gg3;EumHO z%5!2LLMLrG%P25iffw1A};tOmC z^s{pLZ>2qBut?;r;5g9Xt^3L_d&O3c`-!hI+qoCad(Y4#q$_CW$kveluxxdF>Fmn8 zi>yp)9&Vnp{HmzJFP=x1)=YQ4&Axd!+iWNEDoN9K#W6|`FIT=vy>LCgr(LRZS(S8Em6sgh@DmLIq~g> z9eH7fQ8}X7>}z~N1YIl{ln&)K@A#i?94Xh(QE*)0-A}=%OdT4`Y!|~N8#;=v-;vc~ z5iqPe`Fr=LOV1N}KmCtSzRsxn)O=>eN;}h%>vw=DRkg%5q9i4;B-JXpC>2OC7#SE^ z>KYj78XAQdnpqiIS{a#W8yH#{7_4KjEk4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dJ&Fwx^3@h{y4_Qw+Hp40v4QUH7EE)xEN^X`V}wx8tiNY%x)< zR8D@WoWOqaL+qlwS*L1+XFSkv`oy_$kF#?1CQ}48#TIgwEph`Oy1-C2@(yo5D8XSwL%5OI#yLQW8s2t&)pUffR$0 zfuW_YfswAEQHY_Lm7%4Tk%_i}p_PHbI`--k6b-rgDVb@NxHa65I(!1Cfx*+&&t;uc GLK6Tes%($| literal 0 HcmV?d00001 diff --git a/icons/dark/32/content-loading@2x.png b/icons/dark/32/content-loading@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8014704ea210d1d587efd87a54f76e272f9552 GIT binary patch literal 490 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>ihY}=?eXNhVXvqG7O=!j9_7j|E6gXZ@ zJDdWfZb^R;$dixQa%CM|io^Z4n>>-8HLm^c*R z#DN1=k-e9Ai;W&^wvBoH>DhIakIQeC{rPn;`ngNSr>E2XlNnY8R{b$$=9nHCem|V~ znC*nTMUp<3r@ALIX>8~_wA7GMxOm1+lRtkA_f(r2KFHxauh+|(@%`C{2N!2FWczlc zHQrim$oO>aN%>_9Pm14{wOpPPeFE&!h!wle;90^)|N?$ z+8hJ=UbVzEq9i4;B-JXpC>2OC7#SE^>KYj78XAQdnpqiIS{a#W8yH#{7_4KjEnW&i*H literal 0 HcmV?d00001 diff --git a/icons/dark/32/document-save.png b/icons/dark/32/document-save.png new file mode 100644 index 0000000000000000000000000000000000000000..0af10d861d26edeb7f2657eb475b1a90d450272d GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dJ(wHBT4E5Rc<;rz-L>849?@b4CiW%t_V_cT|enxk`eEQ>dF! zBS4}7YL61TPrNhFys33Is9iu8uT(3d^;ey zy6kPbJxTFJeUE}s*=-O0H*Gv3Ee$<)UwI3Mx?GT(<0P=~##F;Pg^=B|nuU~-`dLoq zefYkQIc~H2bP0l+XkKw1=C< literal 0 HcmV?d00001 diff --git a/icons/dark/32/document-save@2x.png b/icons/dark/32/document-save@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..35054eebcdef744a365f646372fc76c892dd5f99 GIT binary patch literal 612 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>H=_`euFnass< z%J1_;=?$E|2UvW-C`vry@`M!SIf?6w9!o4wxu(7+WZk|G59E$A-DJprVPmFpc=Iy^jiK4784qnE`FD;yrPvQKnyY!IK@vZRSa-=>R`RWUAxn~5uF&mxJn zfdLOfuWoR2Yz?25Y=8LbdauX>*&klj9eDadw3X@J(_KEV+LkO5XcYguIEpd$~Nl7e8 zwMs5Z1yT$~28Ncp21dGuMj?h~R)&^VMkd+@hE@g!>)5MHP&DM`r(~v8;?{6K>hKAm O1_n=8KbLh*2~7aP8R;(o literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-playback-pause.png b/icons/dark/32/media-playback-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..133fab74442a868d15ca8b4b8bd8d68d95a78532 GIT binary patch literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dJ&Fil>WXh{y5dKj$4J6BY&hU%`0Apn-Mj?hQ=`L+5w?)5MHP&DM`r(~v8;?{6K>hKAm1_n=8KbLh*2~7YeJZO;s literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-playback-pause@2x.png b/icons/dark/32/media-playback-pause@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..91af18a13e97acb0fb7c57a5c4e186b729df4dab GIT binary patch literal 433 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>RdP`(kYX@0FtpS)Fw!+N3NbXZGPJZZGSN0Lv@$SQ$6j56q9HdwB{QuOw}$&s Rhfjd4^mO%eS?83{1OO^9i_QQ5 literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-playback-start.png b/icons/dark/32/media-playback-start.png new file mode 100644 index 0000000000000000000000000000000000000000..70c3c6ef6a58d472552739fc2e7831e4bce0bb6d GIT binary patch literal 443 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVump3d{dJ(wVNVyw5Rc<;uWsZ$WFXM`uzb0PiO&Sq8w^vp%oM#Z95A?1 z+`^=hbWo0`QKZmBL?hvMPI*W1-}=_tNuB{_F*p9pB)nEius?jZtZW|d2bKd|=VXE= ze3T7Ih&pt0bL107eHPxYR!6>bWvP7W(NfvQ9_X^!!_j4TXPipvRBZ#(E4R=2eP-xi zHJ?$(&+M%#yTpMX9a|gBuQ437U~_pau%N59!Rj9yPlDyevf8UhlphqPKj?^E-N(rA zsmw>$!}iPnTc;Y;JtT8D`103f0KKDH;u=wsl30>zm0Xkxq!^403@vpHjC2i+LJZBU t3@xpUOtcLQtqcs-u~(O%Xvob^$xN%nt>J#u;S)d&44$rjF6*2UngCoOlW70| literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-playback-start@2x.png b/icons/dark/32/media-playback-start@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..32f13184cb207e2f10c605c76425d36c1cd445bd GIT binary patch literal 571 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>BWvqoHgzbS-7U(2+a_=cJBD^Hy&rUX#f1bX`^wz+f$C+lMejl=x6{UdA1k#)&#OT z)Y@^E$VaC{JiUC;^n~<@*9<~SUab-T9L8|qcCpc+C;l@OLdEx9n;XEu5Fn9o-r+_~ zn@G!BJB7Z2n;kBK>#P?_97@*~bNn%n^&Us(;S=17rBYVPD*egoEnjRBdJ}k5!Z@zE zr1T{8rMPi?^I2hVz)!!#SmeP~kHT5yhZ(&}{!C5m{PyC*&%|Dt^BWJQROtL;6b_I| zXX<&R(@;2vaYL6k^P0z#7?$r4Ibf;B_@V1J)1Jq63=?-~JLtS)66n3nbmXxagV!Aq zhrMgGm@nj?;$nCe=PIMIN1dfazq%*?&W~`1(AWCQ9zi_4{x#>hXA|4X86Fw~Ls+%M zHKHUXu_V85pc%uP#B+kei>9nO2Eg V!~LkkCx99lJYD@<);T3K0RZZq&V>K~ literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-playback-start_checked.png b/icons/dark/32/media-playback-start_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..4b7e59e7f603cb53f729a3bb9c6d359baa23490d GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+Qc&uulwPh9Jpgk04(L zhAK4%hK3dfhF?ITh8GMBr3MTPuM!v-tY$DUh!@P+6=(yLFbMDoasB`Qzvub?j%WTm zpZ)I$L}&iHoCC34&i;2j4`REW1ChR$KqL@U_-^n3>g6j5@(X5=Qr~T+_xtaj--pb1 z^T?h%cnK)OnB?v5!qCAg3sS~j;_2(k{+NkJP*}30h~E(?6z=Kb7@~1LIbngIfWu>B z+cF2ygxau1twX=wrCwzVVKfn3z_5zLgTa+Wr$L8F&_Sq?OTi_9>HWN`F$eS;*0J6V zD9OGk+xD)cFdD{kqCIRIMEBP6w&&Y}wI{8o^XoYHtYeY#(Vo9o1a#1RfVlXl= zwA3{)(ls;+F*LI>w6roZ)iyA+GBDsMC^&$kAvZrIGp!Q0hDzNNH9!pvp00i_>zopr E0Dm8fD*ylh literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..55f6afb6662ee9509e8bfcf1600142a9bfaca20e GIT binary patch literal 484 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|emUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>%4H5yefu!qMs5p=dL_m-dsHFhZ%U2TQ7tG+G>GXHCZtTC!MmoQZpZshC z+QPt?21sKW5?)6qYP0;&%iJP4{$h4DmSr_R3D)1_PefM3t^D zJno0ne2xleYD`FHD127%N8DrOBeVHaPS?d=UzNwZ;ok3yY<>!s`|09iK9W!X+al9h7wzshl5_wgif9%O6-r+88+$KGv+$BFUh{-d@=ZP z`o;3#!vUuQF9+Se{NTciiytoi2yO^qaqLLsNt&>2Re<%!uXClUf83D;I#IR6HKHUX zu_V9nO2EgL#6JC Q8lVOSPgg&ebxsLQ02|P)6#xJL literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-playback-stop.png b/icons/dark/32/media-playback-stop.png new file mode 100644 index 0000000000000000000000000000000000000000..b86d233212f3a36edcf20da8fd4a0c3ab0ee3687 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuw+RQzavn{*VDx@#N&AKpYslq35x>$uVB1l(7-x%x58Y-DGhyBIgLL` z91uA3q9^|U|9Tz`Hl7-Z9w~#Y11lIqj2QOXMEssVVPbaR&Ys1hrwf4gGjegA{`4x8 z18A^niEBhjN@7W>RdP`(kYX@0FtpS)Fw!+N3NbXZGPJZZGSxOPv@$T@C@469q9Hdw ZB{QuOw}wjH6E#2$44$rjF6*2UngH>7T?YUF literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-playback-stop@2x.png b/icons/dark/32/media-playback-stop@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..28de286fd698c1f20b2385ad1201e02c3894a689 GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>>Z&0N0Ob3WJIz2a_Qape9Q zAv>rth+;6wyFyR0Nc6J2*g=1j{42-PAyR)B3z*I)3sx&+`d$QzsFt`!l%yn~RsL!%HwGb=+&Dk4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuw+RQzas+!V~3}UV~EG`w^I!}gdIiN_HVzb+97bzNjFD_$*E6-t89{) zYfx9~qho3sMNbssLbI!@NL<}EShsfjpNi@mWkeiYejYoyQGwVUbjQ( zWZEPV#~n(aPyMgIf98+znGU1-1<&5ZGw=x=c#^;LO`F1Z1zX{SzD;xgCUu1!4qGN2 z%N)R z{QRmlnl7i$^<~=G8L=*4kjs>6uU?ilN&U&gS6{z;UyydkQ(I!q6N&5^u5;?FA5Rp- z@1J$)!nubh3%@UC6@L7pcxOuBuSYMWz3RUF&s(JB<<33zw3z0#dDH$blJ{kE`SVCB zUF-evUCwpE(=Y#frm*f|M*zpm;_v78O4lFXps3XU@9w&Hea~#Iin~g~{(kgGmbuxH zANN+ZmC?cVi>!9t@k#8TCO*}c&)Hqdct_wx*4cX7ya^ul0pH^dUj7UdQ9WTE)U`Hh zXWFfGZ>H<~Yh`7)!Tj^nq_4&&iUonuq*~${QIe8al4_M)lnSI6j0_Adbq$Pk4UIw! v&8!S9t&B{y4GgUe3^)o34xnhr%}>cptHiCLQujm+Py>UftDnm{r-UW|O1Jr! literal 0 HcmV?d00001 diff --git a/icons/dark/32/media-skip-forward@2x.png b/icons/dark/32/media-skip-forward@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..982da70427cb951d8b2ec7de5520308568314089 GIT binary patch literal 930 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>j6KLFMC4uhQ-s86Q!486dQXW9=F}K{m0iK^qct zcN@oEk&P_ApI@H9dGEd1p6|c^+I>z-ugGt0J=7NV;2+ll1}F-sXgFQx_c;CUM-DT(WV6~_~ZB>xe2j09j3%KW2*@!p170gH#VEEQyJ%e-Z z9=62B= zCb1Q3m$$U85DUIyz2Q111H&7a1tv~$kM^w7tBB>k;$}UEf#HDNmq)sDlh!(GGId;V z`suG9&v+f9De?!ce|+Ig`|SGTYx{{$_6v9(2;aK6e)gJYg(VA}|4*$=GCSaZr>8tFF45FK z$>3(xch|g6VGSZ|lXW+>UVFUqX3GC-&-o;_*Bs;7Tq)7}i$g!P_(1jQixtOm{z>Y7 zKKNQQZJKO?o_J)oyRq}HjJAk6cjLr*Z>|~(U7tVKe>1WMFhEg(%0DKBZw6roZ)iyA+GBDsMC^&$kAvZrI YGp!Q0hDzNNH9!pvp00i_>zopr03`@&dH?_b literal 0 HcmV?d00001 diff --git a/icons/dark/32/open-menu.png b/icons/dark/32/open-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..917da50459d72f9394774b15215a3f890db7455a GIT binary patch literal 428 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuw+RQzavm+i>HfYh{y4_lNE)U0tDLjm&$b9VeC&>@WS+n+DWyW$~SUV zdSB_v@QCC)ER{Cg!PfQE@Ql&6@7K4#zyEjtT+!4yYRdnsnG~RsL!%HwGb=+&DgTe~DWM4f>nxC5 literal 0 HcmV?d00001 diff --git a/icons/dark/32/open-menu@2x.png b/icons/dark/32/open-menu@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7a95cba0cd713cbc82a59827c9a826d92281f977 GIT binary patch literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>FNO7zlGXK@jOQCR zv23_}QD~`x)zO{LcAoYu+~+be-stGM*bko>f!Y}wzWs~Nz80!JGtR7JaryKoy@9&B z{>Dr@T^sChw#rX^T5bHn-~3-To4>TZ{e!Wcb@DQXXoF0yit}poeHqRE%r{{zS$rxb zV2^s^ZrUi(3efrEWQa*-pW%$AL?nlbs zX7QdhAV;;tHKHUXu_V9nO2EgL#6JC8lVOSPgg&ebxsLQ01Z!&0{{R3 literal 0 HcmV?d00001 diff --git a/icons/dark/32/view-fullscreen.png b/icons/dark/32/view-fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..87864c90753f5d5fc6921f125da476d4bb902fc9 GIT binary patch literal 591 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVuw+RQzas+!W4Nb_V~EG`yI1W!4m*gjeRvP4Bw6?Y=DS55a+F-(Hy|X;Cv}D7AM~^N>6-EWMxhM&W$C^|}?g ztBf8jka2Ul>lSo7B$e@DeaP+<``ER|*v{DSJ-ehMZd$T&>6)W#J%ZD3a=N@0%G{~z z@Ls>j@znQD3H?d;j_axYP~_h@&*bgI^9xlN-Uvy3ICMRzkikS#{KX8}$b|XVnLj_C zd~i{>Pg02P|Kpo#9tdyvm8ZG-jIJ#()KyDdBT7;dOH!?pi&B9UgOP!urLKXIuAxzg wp_!GTrInGXwt=CQfdNNB!2uKvx%nxXX_dG&RO+6n0cv3IboFyt=akR{01g4$vH$=8 literal 0 HcmV?d00001 diff --git a/icons/dark/32/view-fullscreen@2x.png b/icons/dark/32/view-fullscreen@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..48f63ac2f46b60e559ed63f7eac911abf6299057 GIT binary patch literal 787 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>G zl$Ocn3pxtl9T8~Lw(VuKsXw;6d}k2P z!ynI_coSOw&#Dyr9H^;Zv+n1-qf9qUr_Y_DzNw#=v1L-7Fvla-G{>_J6OM5E9+qcW zRMDvTL*slBYtMrfZ>}8EUv9N4Pj5QQcP~eWlAify%sh$n{kv<|aWNk{?LRwOf`vh* z$i2nkQ+$A^{vHQmhPor%!c*E8sQp{KitSLoad_f~oF4}?z8xq!b%&>O{j1iR-8;K2 z&lsuy;q0o8bT>B=^flwQX&~;a^UNlivV4mPk{~EVHu?n_Jk~FtuvHZxr z;kN!oY?t@P@~5}Y#(u0gB^NQV^Z)+lQ(Wr~Nq5+tWH_HC*)4hD_0P*}%qiZ#BMTvM zmchPSUX;oOTd()TH+c}l9E`GYL#4+3Zxi}3=A!G4UBXRjY15~tPCx!j7+r+ m46O_dI0_05plHa=PsvQH#I2!H_e2d)1B0ilpUXO@geCy@VJ3b6 literal 0 HcmV?d00001 diff --git a/icons/dark/32/view-list.png b/icons/dark/32/view-list.png new file mode 100644 index 0000000000000000000000000000000000000000..9508f9861d63cbb224227abf581e0e365f4d7a3c GIT binary patch literal 694 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu+-b?xZey6jQc!Y978;gznx;}aivh?xc&YA|BbY{y;>F>Tp>15%yHI~ z%L^S<#HF=f)z7>raYdn{tI6U;h{D9czyA|>b#=L(D!6VA-l{S^vLoiHbhtD-7nw zi&)q^TxPyk(!uS4K!M!TmG(1El*G?EyH!4AlGUy~4=2dq*l|v5<gwN3&Vk)igW2S+yqHydEGSHs)7Vb=SMqnK*bl$&{Ag`&{dD8rreZ7K zwcl=3&%TiR{&IfKpWi?JvoB?7h(B|0!q>zPf1Uv&SGB}7q9i4;B-JXpC>2OC7#SE^ z>KYj78XAQdnpqiIS{a#X8yH#{7+B88ibc_oo1c=IR*74K$I(j14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>e2J3>Q6j@Q4w!@u|P#sK%D3ymCu1(+QL zm@iHd;jKEM@#bLdR<|}cmqq5fZo(lSlokf7I2ZHV&1e=eQVN?ip+i_n%Va4(YuKVi zi+ImmpKDqEv*7sco%g@XG*?=_e*5n4{n!7K-`8Hd6+Y!7Z}yzO&J6+zEF6qZaH=8W z<%G=Y3Cv#OUZpKW4wSUET* z*ePePI`u*3s_$C~s~eZ*o992_zV>{5u|UI@>`#;DyEYWfUj60cq%#*+cBCIucX+4W zA>NxNsn(t0lO(!*N#`<|lb2mHWeXU)S-eut*affezM{UGX#y*o<&jU0w|$I+je|>M z=U?vUunMxC*~hf~FXNMU2c#^Sceq%%78gFu^uO``(jlRQmWZh_l8<5@>3;SPV7QTc zODlK#<{A7q54e=Ndhz?QZD|w?db27_GmGKH@slB?g~zYl_`StII%Ms?HQEMVb37YP zbC%7%|7YfNMyvGb=O$BaLe4WCPg2#6IH&z&Rhs;COW)}~Pw%gkQ;682{wDLwO!r1% z8{Gr{g;n3~D4w^qcdE}Zx5@+ZbDqyGy1Ku{;`on?HL?}#m$;v@=uZ#4`?%uS*p&VJ_xv>aH^qOezh)J& z#&f^k{f|5XJO?&DZ8N*NSM9VY-{n*`hBCKj>(cp)KW}cl^>MOSg!dcW$)}t}JCnq} zE^v6e!(fVIW`ILzo9eo2^}?(s!A&LmqZsrAS-vQhcb@bSTgA#_yl`L1s=9?-j4?`I zHq5`QHd(qrs$^N;I^`Q6=2V+ZrQ=elzqGN*Aw2|CAi zGFabu&VQhq^Z!-zN#a_Zb1m{OpIUujgPUrQfu)N~_vsu{TRZ=RH^wn%zkhl8>WTi= zCof$uovKzjR+si^{mHm*Gaft2bu2nC@wwWPFRPy{{WT33qN=-mo_%KOz4Lt9zg741 ze%;-_l|8{G`ObgYb_PWU4gsbX2E_x(3-2pWTREw;ADHJMfx*+&&t;uc GLK6TI^V!S* literal 0 HcmV?d00001 diff --git a/icons/dark/32/view-restore.png b/icons/dark/32/view-restore.png new file mode 100644 index 0000000000000000000000000000000000000000..dcde5ee33df2f6f1534b3193bf0a243dba98357c GIT binary patch literal 573 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtq=kVV@Yp3_+5~9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!Ib3f?!xfDz5mR9AdkJo z)7O>#F%yrVu+-b?xZey6jIN$8jv*e$?_O2(KkOjF{^9>?FHb`Urn^D#5s z=laH&Hao%N#6Ffs@0b+~gj*gO2pD8+<8OG;bmP#pZ;E2qc00Y0_2qbYC;Q#ss_*aO zynBicYtHU5gtI5Dod&lOu#xFbEeRr(b(7!K-;gJK+ zlUara(MS0>7&81^N*qmRFdx^?NNh@RoHKLIL^IVr&-eB)JbS&7q2$o64tbVcE2c0! zQS?i^xM<3JZMKl$Ln?R6?rkcN+MZmyZ?<^)?t4{YOHZGhyldf7#cf;-Wjs%1H?3M? z98|)U{o!qy|Dn@D4C*4adB)rgVzx=#1`A?7$RskU$Y_e+d7k}saxhCz#R>Ps@SP8Q z_&<8AU6aV_GB@u*^UrDDnm_V0I9$1SeDSK{GxJHzuB$lLFB^RXvDF!10LrYx)BV9wI5JNL7LrW_oGi?JyD+2?|8CkI? d8glbfGSez?Yw$RF=^;=9gQu&X%Q~loCIGZe)#LyG literal 0 HcmV?d00001 diff --git a/icons/dark/32/view-restore@2x.png b/icons/dark/32/view-restore@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad55908ac65eaf66202cfebdf9feee52de7187c GIT binary patch literal 840 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+Qc&uulwPhCseTvPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>|0`!6*2>Luy**h$HNbzVs>Y-% zYn2RNc%HwkbYkA8M4PEj8$a{5L}X7~=CVPlOMK}Tg^Eql!Gc@X+`e)2t>d=b6`##E zS`UyL@6I@3$%D}IQ$INztSFwzaq;}BiXKjzE-^+=$z?_1r`S1E7mL3y zWM}vzeUU*$(4w+C^kq8NF)s!AyDAJ%+pPT*c#f$0Pg(eM0>?(li|n_W9n6$YI|_#O zGBDU5Zi#BSFkR5TSUvDE2SY?(^da|NhK7sXcD)Qr-BW+O`gePh*kOilM} zyDj3{=X3IZm=Iz6weM(0&c$-S?WcP?7}PJ%l*#iFvwg3zwe9w$7Ye}#Q(PK~nzN28 z=}%{8xWoPXUq^A{`4dmAb|f)e+V@+zCS2Y@_SxpS1?Hs;)$i)G^IkEqH9uah$FLwl zXaAn1!VD5#`mKs9Ml{ba-YBD%#^n~7AOclVqTFv*J+0LYStBxS6X|`k^MY# zkr2Z-?Fr9!PkTIV3CFhW3)PsSS0r3E*RS3uawF9fqg2& z+F9RrKVnf}^k5KTc+0S#(e&_|O`-K){{xeoYKdz^NlIc#s#S7PDv)9@GBC8%H89dO zGzu{^vof@_GBVRPFtjo-u$++-W+3_Uq4z#rM0vs@r(fZReN%Eq{|v zGD#RD9B5!X&v&o=dvv+dyXj%_Avj68yu1Zgs{z5+U_sf4^ z<;-k0)-z(2ww>2w1nIxE!Q18gP1QxrFvHTOcf8%~&A`SZvBB(((8b!a&KDpKQ0eC8 zo2p5_^HZ(%3&%OIJOp}5wZt`|BqgyV)hf9t6-Y4{85mmX8W`ys8ig2|Ss7Yd8JTGt m7+M(^SkB0bMbVI(pOTqYiCcrm(Mup37(8A5T-G@yGywo9C89I{ literal 0 HcmV?d00001 diff --git a/icons/dark/88/channels@2x.png b/icons/dark/88/channels@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8172783810f6e184b0616e098c687d2705ee683c GIT binary patch literal 872 zcmeAS@N?(olHy`uVBq!ia0vp^8$g(Y1xT{))42+ySkfJR9T^z8!agyG83OqV$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZEE?e4{2>bT!P`2tTD$B>F!Z|`i(6$zAZySRPx5(US1Qa+-~cv7Mi3WZOI zIxq$>HdZT?@~livJie`yqhns$TJ4RqiXYANUoE{x?N_<~$=;n#}7 zI@|h!+rM|8SN|>l`0T0a|990|{QXe;>$BP3{crU7>uP(h|MuVPzHQ(Bp3Tki^2t5r zm(|Vo?O*dR|L679FNLM=$Its}70h&;rH@la*g~zq=fR8vhKQuloMpjtVdicOk}cNX{?TSGre@il1pK;rHWCal!Yy z#UI8UDFjj=rr`VET^+=S4cPj3u19{O;TqQ?_=EVn(xck%N_V;~-T^5P^HFWQAfcE* z@+>Gw&%lBTO8@6qo3zPjo4$bt+$)_r%B?zD z3>gRKG$kZCg~JG8g&da=LOT7{tG|AK{Qh{J_v?9{*XQ};`Q!8Cd3m@f$iid+08k(j za6V#^zJ@edoL}GEc_apy=IHJS9FX5RrU1ao1b{tpi?s*n5oLqB~wnC?T(+23xrp{0RUc8$_I=@7d{< zU+B?LKWOil^GOHX*vGpho{mgJbRGZ4tQu-{T&3B_=_Pc8)eM;^%juW0kbtxVDrYG< zLtOQ_*UGwU3<_EA*+%)DfUW6doLr<}o%??D0q z4853r6Bt9IW$Vh|pcgf?Yvp~@As(F2L$zazfIBh!mu@jRtGzRP1yDRZK4dFRX15YNYrC5nL^q1% z!7Qf%1Nn3 zShqW58rYuor18xCQ4Wbb>yeb3`=d`QSyofIkIyU#2P5Xj$xp@>WEL`W(lP?O-?~@e z`UNWpNEqizt}#si&-J7pt5|%4?M!jVJ1Thw^j#Z&jvabr+`#N}s^^4OR6Il(gXKW~>(l&Zq?>7V$(+-7dZ|GE2 zbG9y+VV{Gm{Say4x8msbb?-itI0YNFlOt9!kAmu|FK2c7zS$!XV>9#?YJ=1sTC`}N zS)7P*9n)FgXxMwhz1s?Br6?vn?B8vIr%cQ%n7=arD2$&{Zdcjw3l9nSYm1W*Wm3W< z_S<6uXi!=TQci~{tbcqtvHb2*XPlktUvBT{SySGSWOavnoWIB907w+D|F)1jaot^d zgsXbS)^aRvy9FGh7VLxQ_&G&g)N!ugThQk?Xy9#S6U0PB8T*f&?yMVqvn%vdx;uKI zLH1x9Z9FM(QcdBFZ)7csk z)BN#p4gd2n;WrJq;*`V)?S8&e&$aHWF4`LRUZlT}bq@R(iiL+Bn;xSh_8^kL82Ku@ zI1~JWblLu2Q%pTS{Y6a#g-wXp-AKb6@3>OvtT?0TW@NvVHLq>BRy?V$PU;I z#VQ*upX{+t<)|hZZl1W#WWZi~sML@-3Cc%neR_lUAsnUM#(+i!);I+^B*m^@5Wog5 z;fLHku}ngA(!Rw4b#d{k3p*hnT0*CAT`S#CYv@CIuU{K8 zaD@AYDM`x!DWF83OLJCl=zsInZ`P`Bj{4@Z+dq6cP#HBc8-=s<6h8!$1UxB$8j=u> z4T}pG13)8Dn8Qf)VHDaIWsOB)uxM)p5`{$~G38M~|06gTOO1@U@c#vbr$e)10YJoi K;OdJ;&U0>znaN!ydR}?}0KlXV*Ri-@!hZ`ybJ1xV zZ;oCNjfUW-sp(@~HL0%%XWojroJehMwI}ZLk$yEMnx0#$nq8`Z z&4d(+pe?eG$WcK?3QkP&(GGNB)oeXT2;3)Iv6tGhevOihY|4p0mw- z6#+ISLMRV9=|wyKS17R|LI#xZ-J1YY_E5)L`5!YHx@xxrnE3hTV$Ef#;yeK&A;1n>t&_DQ?E zt2@7~v_dx%Ht$8Zduxu(1nuv3-MO%q72GZ;9NHR)FyHpmb5oLK%$lD$>QaT#d=Y$0 zET9kK$4$7O{-*rmx!h84MGr0h#CxB)gcsU1J_!zP-|(>GT;CwcctiU!s(mW2e@gzM z9M0f}NA?{dgWLRiEchOZP`aMl@F5os ze6?Km_)i*7sadk>3>Ekhaw!Q_`mRsZy8EyUe+Wr=6iff7r*tp8xxjJb_EKUYS?Yx< zBQOC5&L4>hxsfT;2~->uPh~T3PkyGSEk}cM zr(Bj&oS7+xmt;5at6`b+BVPuKrDP9SGf0F?>hfyHRUTd~8Zu#CkQWgm%z6(puM2rq zKYmzjN}!-cugT}0z1f*9e9fCM6`D<4Ng$76Cw@~>d!n>83DG2nQxcZ?JLT?q?RKfg z_IcO~1K6*xylqqiH6UzEmphJ$CLoObWR*7i<1cNRhrs!l^`TsSfIKvry_|7wcWg&R z@$AnvKovbTsC6@qI=kYW{^*Pbr-3jn`0^=0P1CArB(4n;PDm%P=-v*YJq2;LZ7U#? zom53OnuIZDiC-bI`c$0`#gyS)aw*zNl#^#OTCH27S0n}!x^t6Hn2s;rxquvq*Q3ET zRZUv4>p5}0KdQd$G-7@XHg}D@DLs0a7o_hb2r*4wdQQQbP`vt;{ewZWvXeM*q3`a= z8B6Y>@V0LtQTt{@^&`N*(+#K+n+W?Pzi`1-BtX3FT0Ji9DE-{>U00NL#MvaXOL zf_2d8q=k!kP&t|I|2~@H3`m)%A+~m+{JOeL^6fWf8{_=^C}5@fwYcSRE;Z?bLM|aV zgbt>3RerKJ1%w4U zXn=WJu}t^aT{7Y#3UWt>Z}BV(Mi6~g_B@t43gHh+%&1~N1s@q5R>-$&Zn}})+efFv zlIz##gYZZztl<5l`W*#f0Z~)~o%J8h+hw<})R;En+1Fptvn1bV2+Eh96QEVRk$!ct z!MfJp^&<;}g5n*s?>0Fk~XKDSN3XoMM4LOF~_sLl>1_D^-H*i7e zqJ|RhMZG$mkx7BrkJeJ(hSO|$vbHuDusVjHyJ}a5dbD?qz5$UxuSSZ2^+S;S}&sTd=&NH%bZ7FIii}s!$ z%AVD;vQ4#2gGdZHYfY0NJ9VjR*Thy^n7Q07v+tdpU0NBqIT!Ls3$4cpc*l~FLyGdhSevc@CL_UAlaDXLxBMWanB1uIew*ZglZmLn6**DDvZ8Zx%J z#nZaY&U_+zwyYU?6rX9k*K5~Z5wH-k#AX7$#{@@<^S+vVpiRKHuppBZRkM1(2nrYR zE#WKv;O233o%7)ap0*J|NQs@FywD_xA8>1prJFP+`z71IDfJAsqNRs3)goAw@}|tb zr-KgUlfS@lU4KpOEt7rV?p?Dx_MTR~`yX_lGi{o$%THJYH|ar6h=WzN>wARlRW_y= zAR)4TO=C^>c;Ri>#8tw?o3$`x3Yxw9X%OUeuhh1IEEHVe#2Ond(Pa4Ed#%6c524>H zsdZ;oWti{^=+Ekcu#zF38#L8KZs>B@lYSI2t&kMm(_ChS58?(MlyCUNZW0=Zx@W=4 z;;{zF{WFXU_)PWF_R4&O))dWB_u(`6d&-^rd!1_QIvb&uJf8&~%*K^J+ox(TT#f*4 zY6O0ygYLf6mE}Nl+XykIp{|{_hGi4qewd%jfk5k`2x1P`v2Ew9(5J)L(Ml(pI&Z76 zqYPHPkFo|@Hlu9Hug$h*?5;O{&Em~j*z9wf)Y+8?k@^EG$-YU}W2>5YiA^2^4*(-wpR=ZT;$h+m=H$QuqnKDC??vSZc@KWkjLC2|8jyWVDvAeDiI|_&BS~@qO%P zw)GyW)Q6zM-P8F)!RGPqtdYMq@~&*#{^6y;+x75*FNlNXVCVpqt#eMrN(>VXlc5Dl z!i_2^Y?`k195Q}_axD79iZSt>u`S~9%EmFUk*tqip#>I)s{c~|Lr6$n=K3iu5uIfJ z&qLSCM6viEhcbo?Ay0Y_J~o=4yg{3Ge;PMU7&*Fk@UJ~5e|@lUWOEtuGK&$Y9`~2% z(3!E5BZYdIYX+4!D}>gE`|dtiXc7DwpqQWj!M>uc zk3(4b753+bP6ZxBFJMU&>fb7iB}!NCv^yxDL|l7+%~?j<|4#{8L3b@ytL^VC50cpv z>{#VWoR+P#+^_rap{6amMf$EU>2@!b^*Zy}3HpqRt6Fi*A#rPIUDunWGNyap`Jb~> z69y>;r{l2M_JgiP3pKB!o^LE}Yq#B9z^!Ak7diJ7*5xW#>?0GXoC^7Jk)@8(lN7IZ z`nX@3w}eDi6j2*%9=~f3hNYq1+d zzAJCn$mD6yKv6S#N;l5em)gL^GKVmX4+zwAU3Pe#H-u!~4rIXS?N>3l(>U)0Oj&=`t@T{%Sc+aSg7yrgGd%#r!v(%-(HR^FQGHRpL3YF@B>BE&t z;n{o-2S=(R16^M_zVH3m`@FeWWI+o0S0ccX@3O$qsDbHIDlfsPkI$YSdn++lYCx%_ zY0m6MZR6vs2>~`I!Cz{arpbRQ-I%kckgcLbPO{#COANey3lzT9@z+N^Q0%DRhy9fe zMSo{H5S$dP^GTi+&;4gf<+H{}uC1&bTZ@xwz@$}RFy)NLcK^rV>E-11==uL|fRlz6E(`#DT@#&3Ep+&Q0JZpn&Hw-a literal 0 HcmV?d00001 diff --git a/icons/light/16/audio-volume-high.png b/icons/light/16/audio-volume-high.png new file mode 100644 index 0000000000000000000000000000000000000000..96f746e91c2d56872a597a6972b16173f2829de7 GIT binary patch literal 577 zcmV-H0>1r;P)Z&nj8u$#%#6)C8L{_3G zde>(LARdqJGREBWeScC!7Jx-#%y|*{2`mDs7_hB7n+E$R7K`tJDsZk=t8GPJO(Ux=lkGcrBc}s6o6YI5*TBS0G^2a2KEBWYb92z)g7HqXAVep zQNyyV%k6eM<2cT&@B3-sF>uAQtVS4ypMdGLClm^WueNQcf%zWG=kq_xTn8E=;wd(e>$-(b}E<4wKfbGl!%M~FZr|oQzG&O{^K_$lgYPAsd3;$&kV2%7P7wNm|1OK P00000NkvXXu0mjf?n3^x literal 0 HcmV?d00001 diff --git a/icons/light/16/audio-volume-high@2x.png b/icons/light/16/audio-volume-high@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3893e4d0f36558ab72b85996d1be8c284424ba2f GIT binary patch literal 1164 zcmV;71atd|P)hDIwKTEPrI8hAiDlcuj_Y>3ztfBJ zJG+Oqdw0jU^}vPaexL92=XdV&JI{0Bf0j@=f?AffOeys$FamUl$ao>HQH}zXQrm(1 zfM8V}26gu!yYG z2e<}24*aFL?XeWC*v6%iQ*Rs!E>s1o>4L~68s1bD;eE2WY}3lNd# zUDw?Q#Aiw#@cABC3;dv=`M@i_JrarRAb&j{5|NvyYr>F-bOYT*ay5y_A^kh=0)7IX z5|Jyqkb8lnKn%!*!{K{6J3G&5+X3JK9p{ONh@77iJOTp<9GmH zKBrf)h+GVZ!_DM>*Vc48T?tTES9cWnQ{#0a5(7|5oeP9mFy~p&+1Ys>IHJE+PE1VP z4U_l(qQ)aYwJ!P^`RiC!u3_ZMM zS=Mr;)Fw^Kg~Q?90k=ndB@_yMR`8S}azF4saMLvAe~n{bdV71Tvf1pHz?~Y}BO?8`y6=+jcn5e=MD|YQM#@ou>$;l*$u9s~L?kmEw=4zVZOFF)XDTWxQtj>S ef0QZD+~prm`h%M6=_z>t00002o$>j literal 0 HcmV?d00001 diff --git a/icons/light/16/audio-volume-muted.png b/icons/light/16/audio-volume-muted.png new file mode 100644 index 0000000000000000000000000000000000000000..37be31de95758b8b75b29e2339b727c56f44dad0 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt^TJY5_^ zEPBsQ*vQosAkwyfcG828CVl}nTc_DE3pXfczCT;lwTy#h^X@B`jJKFyQQ~tenB8*X zXtcoJ%MT0w_4hN^&y4Z1o?dkA*|yL7EL1zS6Lec5q?q{%-%NF#$n-gERYNSl77zRP zRkxTHZ%bvU)_=VSS2u8$n&4%p07K2?BGYI`&sCWx%&EuR=* zl-ft+23RaYJ~qGv!7*?F_(jQ611w0a^$FIVB9duK{(@_idip*{L}mn5iWYwbAhK4L zERDjSxDKcTCgCE|`7yE92iky?5}@Pw@b}Otb%4kjFjsUy>KEuF>4@XBfwxKm08|u3 z78<~FB>@0|F%PciZ2|X{1OPO8y%!5wAgLq(ATZ|j-S>A9InO)5o%Y~fxBFq?$Y}jo k%6^U`*1GWZtoc8D0vsDfQHeIp)Bpeg07*qoM6N<$fqUp;%dHz%Heo5UaVu zCPly>AP6>cKS1pK3u1A#MI@bVSR@$>H(+vmS2(b6``(+kyKfF^D$nyI&-0{C)2t(l zqSzA=1781S0Gydwt$|b#s@eoTfkv99?Ow0<9n&=`okb3T4Pbpd9v@WdRCU!k*IFjL z_jf=yY#*I-R}sym39NYU?}5{*Q-H4bzPp4yJLfLu8Az68*JgGL)Q$}>U;x~x>TDsz zEX(%I?1|sE$Lb3>SJjs}P@00OZUF_boXZ60sOraGFgAs?NHahyNs9oX^49x5}e0rcCmJe9TU?BrlZ383V9k^80w}5wm2Vfi6RMk<{4UT|8nx;GR=3r0& myCTvLe`vgh!(lAETFnnXu3_>h^@#BR0000B1&8^O4MdQ*=ONGmdy-1J1a4sWSV*Ro^$7( zH}5|9pCPGDQd=t=fmV`@F;{^r|EvIO?Eo;)3WxEVI5RWTp3CJ{0TYcz55?p0pMH3J zcOaL`T?cjp(L$ln>zAh$3pQI{wZa#CSuiy!DES=;-L^YSRTwPEH;#m&?06 zB^WbctxdbEC9JgzUU-7R;QH9u*isOnQmLE(7J!3|60Nn{t)&6S;_C3NTrS@PmNqSr z)B!94=RFX#f;YeoNgttZ56sQYMb_5VW`VmqB>p%Siw%v8jN~?6IBr5ow}1@L(xj_F z9=IcEzJ9^=FOYN=cmkYkQT$UN5a=HtAAjw%=z0!FdJA*|&sq?F0d%{GgST_Qq*AGS z)gA8j;IhChkdRc_mfRyDlHLQI9>YIKI^`wM(-%!lOq^*#9H3Lu>AzNhwbsv|@s#7W zfNMSq@RZYV4@f!!EPIKqg9VOBTHY?mO9y&g#4Yfs`Zx^;gNGZlmjax;%mRIq1|4%#}{iR|H^7(vE zjrfW&W;mHlK6i?h^a~hDrBY9=wevs+5HiMG;?JmeL?a6}TDTYvhr8UwYmrPQUqz$Q zF5n?F{)1dC$Ye61Y&QE2h)eP{4jtz+e|E%b*vDNAMQjz%I3A z(8|sm1SF)5Da2UpMm944{=o+W%gnqt!`ofz=;Ua`Zi_%4m$iny7J(sFcy8%y6#u{s z`~MNR&eK;I)TSJu8`N%#^BrD-o=-T$Q!#DZ7~^QIw1W}8wjh4vLBkDRNyasi=PG`z zafU@eUgX*aqBVMAQ@c?<61b^sSF8PEBCWi{eU4?$P1?vOlFM(nDDvlc&9&2#@b{+; r@h&~0X_24dQ8My44+s&NSMB)$Rd_+c@#dXK00000NkvXXu0mjf#tDGL literal 0 HcmV?d00001 diff --git a/icons/light/16/bookmark-new_active@2x.png b/icons/light/16/bookmark-new_active@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9d924a1a3f2e093998f17523e268376569250266 GIT binary patch literal 495 zcmVv6+sln@gE|-C2|E~OpJ-t($HAj35B2}CRkV*zk^@F z*3!a;LW2p=_!eVtFtM|szyeLc#9E1{ti|39yKuqTnZ1|rBqy86?mYi9GdnYfBd#;; zh}N;h1g3B!(IZKVSU4>BS^UH%TCpZ6OE81eIECp1pJ=^A){Ymwh7aLaDS<7F#2Cdc z!EKxy3(5Yb3DRBpAkOz>ByNVjMa}F&9mvm(vIJXJEN0 z_(l0oRX2fc`RRI~LY81!z7uvU!;^Mo9p3k8sA7I&@95T5>%a+o>G0j_RCr6Z^vdvD z=fhDkXz=hN`{R5E12iU6`z}Wx*002ovPDHLkV1kvF>Ei$Z literal 0 HcmV?d00001 diff --git a/icons/light/16/bookmark-remove.png b/icons/light/16/bookmark-remove.png new file mode 100644 index 0000000000000000000000000000000000000000..906a7c173922904a092e2691a7531582a514bad6 GIT binary patch literal 383 zcmV-_0f7FAP){qd=bkwODP;{Z)*zFH3Dbtmv5H|p z^e=%p;NUj_`34pd+K?w8P@aNH43lty_xk}AU;&utm31F1w1_F*$vO7L;n-drb7WWC zIqVsFvL>?@QU~_DZj!610Vjc~ys5&z;0zf3foKNjYO}2^UM51c7Hbx8$=B{l^{jwn zm1&I|&K2yMCCktOCzqZ^6t&6Y&!&5YEYk^OfE=OQcLhf5dh7%4%n>lL^Zo2g)Ff&^ zAHd8Nm<;iTfeQ_j`agt38Vi+(QRYQ^C<7~1P76D1@P=@bObfUMQ zl`}vK(2UU^RVK0lGX~3J{*1#$q!!pDZPvsB(6sVtSqXM3&6luR(JHWkfeJW4iW2!K zQjrW)mcRT>(b^EY1vbT2OY(ugk8nu|b}P+d7`-HbJz^h*=af-mum&8!qr@efW5h{E zlys7}0BG2qC$qQx^6Ij3IUK*c6hj(HV1Zg6Y8 z*ar8sk)x~d-+pQ>EO61_HpKhWRm2a$HD$S5JyG`*Sb@U^_ksN};qR1SKxv-Tnyh;d zD9dvd=|Vh+0e>i;u6po50|3ggq%>y**8x8S*bHvyF@5UcYaqi_(VyWJqQec(>qIvP zT$Var#Wu|$XE=c9usII?GXr=Lel)&Yj#~wc(_#6BTTa70UL<&Y%mO1jd_u5{9y^8v@H}vurWr6Kh{T zWAeFLeZFmq&3=lTGA>u>E-S@0o#0zNMsNjuddqNQK_Z{Xa=zB(q8v+JhAS)8SnYd? zpL7Qy0uA^omvBrToyssCXc$n28Hr94cd9hPg*_$Z&+^h{qCGYNY>pnkzzZ8)(?ZAO qY1(a)wy5y3&LnL%O?zCOb&Fqcq_TOi!R-S80000^Y#VVrr0p@K0*SOJ0&|@-{+DBxP!1H?ra%AiMc1Slh_X zMyV+)7B=<tu@5}#4bvVFrimRBjh3b&d>u3`*3;@S`n;UZ=t<^i@; zA>J=N?gDRcutod`rXv0kR(5OPM3eZoHaWErFL0q7@pa0YALx*~6JMp0jN}?P8sb+n z`QyPqlWX8cBpA!&j|Trmu7Q`4V0$Kip;UqyEXqmnB@EOu4Z!=zG1w|_!C%h3jWxHG z`mi+i-{lyn$2y#8V0Yx5#>X53mtsAVY2a|I*E0FGDtmcPhujVLB-MX3*8r)93p~R> zo1DeC9lVKd#9QEB|HV(zs(k2D S*`#a$0000>WSV2pxWbOKlM2oyN3;P(m;w_HERZLM%ke_XF z;6?l7?ipL8=ACD-aOUmRRAlYj*wJ0Mc~0j!<-Z0zD}SVr$*^XzberP`k?l85 z9j!dTyY7R;^_zU_9&uhjn4j^8(YoT>*Q@THcMs=f%znv|f8&5{*rpFbLHdvNw$#q( q?%mdZev3)w9?lBKbho$h$E`zd8GnwLE2jnYCWEJ|pUXO@geCwU?R<6s literal 0 HcmV?d00001 diff --git a/icons/light/16/email@2x.png b/icons/light/16/email@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5f7d4951db8278b7a1dba7530c46256fe964f966 GIT binary patch literal 463 zcmV;=0WkiFP)B`q(f^B=kg#Xu-yUk9bCyXe-+!yCk(NRt2G{VmEcY6UyH_e&G&ILWYPFPLzDG-R$|5=eii{3LpMHW3~zRQiCLpX z0G6Y%UGp@=QZoRXCurJeZDavVML{RcZ)5?KDpl&g>kl#Mejwu2G&KMK002ovPDHLk FV1maO%PIf> literal 0 HcmV?d00001 diff --git a/icons/light/16/facebook.png b/icons/light/16/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..7df7fc632ea1443dee2c816a9d7492a0954c3742 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^d_XL~!3HGNrubO_DYhhUcNd2LAh=-f^2tDvS)MMA zAsQ2t6BaPUq$MQ$@K@S*gd=qV&y0?M&9fRC4+62{%*IAVmPn@glg}9%7#Jw8yxM$H za8n^aGxOp26>aT2JUs1*LOLua``Rx>0Xao4Cf%4*F|V<4;T)iTrd4jjr{=dWVOxD{ zsYT3$BMt}pwSbB;ftW2|pV5@}{wG!%#BhcR_vyL&*d(zsFaBsG`Rl|*W=00pS@y+O TO_o0cx{|@u)z4*}Q$iB}1u{@F literal 0 HcmV?d00001 diff --git a/icons/light/16/facebook@2x.png b/icons/light/16/facebook@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d51f50ebd0405af393645f7e6c1ec01adff4dd0 GIT binary patch literal 340 zcmV-a0jvIrP)IG>#s^b{)6nJz;nZ2fxT9_n9p)GidKo9 zP1-rZVD6jpTw#CT=jm0i!;L=YihA z5qhP*91Hp?cjHK|(`j*aO8OYFtxgO2^>aA%+9Y=o{|Gk~eNkj&Qh3`+Jm7Jdqv?|N zLPOJl-Q0kk(2)8(UE{u753wHjPq10Z{idX;U0R*9+lYS!Yab=ZK8G{F8Q|Xle8`Ab mv~g(i_uaINagYJ$hvfy7sdWH&mEQCK0000#GE<+ literal 0 HcmV?d00001 diff --git a/icons/light/16/go-next.png b/icons/light/16/go-next.png new file mode 100644 index 0000000000000000000000000000000000000000..eb900d877229de48fb5911eab4f1fe1384fad3f0 GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_^o-U3d z7QJUL81gwdNVq)I=aM>iVCIgsK|4D4ws)({}(auI4o4y@?(iP(0B$>gjVhP6Z`hoDDJ|{ettmcgGrsoh zm5vEm7`r5`Nk~>sLy75KtwV%Mqm@mdKI;Vst06IlgVgLXD literal 0 HcmV?d00001 diff --git a/icons/light/16/go-previous.png b/icons/light/16/go-previous.png new file mode 100644 index 0000000000000000000000000000000000000000..7922ae8c964851b5b59c600c2c51299235e50201 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt^Jo-U3d z7QI(5?B;B65OBTN+}Jcw&CZ>Xk<-B_y8ocNG{Da%_)`fyz$!L!q zhgz5q*B>%Sa{s?=hV7a26IPddP1K*Tpgm<08~3#UMv)guEcXi;D-Q~nr*|bB1lq&k M>FVdQ&MBb@04ohUjQ{`u literal 0 HcmV?d00001 diff --git a/icons/light/16/go-previous@2x.png b/icons/light/16/go-previous@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..78e528b7d72089e1feeca70c2cd554a568fdad78 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v}|>o-U3d z5v^~p`EngH5NLbIZrk-P`|r;F@DDq<<{h|o@4&7VtSh-Sx6JLTEaz0b&plJpNlEZb z(SzS%7LVGp9_-#fX|Y4Ecgl|^Om{#0&dwJyoBM&QeLwI0S->P)jK~z|U?bbVJR8bJd@n2BmBdb`7jYX6sCSs|TjY49ywYIUcRS+ep zog@TeFJj{&_(ZHVLO`$*6cHN>SvJuk_^4EpwK%ivx_Yy@`#_X@aM1hdGj$l3U|E<&yRZWE5^SZmi26&-e zcvUar{&L;jM!b{i+|w?+uL0gT=i^e%moOD@*ddMDm{hKo;Ba>ZcvWX2i3&d95pLj> zTC+ZG1wO9!RU2+%dsKQCG>+vWE@DGoI6R0T=W_V$$4Q*RVZSzKZY@Lt{W*LFa4iyP zTh+y3wY3{kOZq}55-3Hmti_@cJ|Fh8a=v7;RE&UAe*;chmnzcwG@4O72thb_+r*ZY=T)T5AyyjjfeWVP~26 z11T)*e1{Zf;i@28iEkiUWs6P7vAG*UFmI44=P>8Y%z+iONVHn5&8n)-B^{aBZlpgY zJts+WHy8|lmn~4U**rC~YhViqlDcLlGrN$q5Bx~F8V-jKfXxj7;5Gkcv* zrw1W~Nt`Fnxfh@jkM&2RQKu-1Y7M|S_f(4kB=tfF6Yu?p_x`;oim{p9&be(z4Qr7Y zFX_l?tN&mOunv%>>2X<>b%707*qoM6N<$f_h4jUH||9 literal 0 HcmV?d00001 diff --git a/images/mark-watched@2x.png b/icons/light/16/mark-watched@2x.png similarity index 81% rename from images/mark-watched@2x.png rename to icons/light/16/mark-watched@2x.png index 73f8fb47c78022ec40c5ea95684cf18e958aea05..5d232ac1332b75984cc4f4f70c4a7bc9529c4ae0 100644 GIT binary patch delta 26 fcmbQvcAs^E3O`$tx4R3&e-K=-clqRv*=v{pfzS%! delta 82 zcmcc5I-PBTiaSfOlV=DA5Y%v_bTBY5a29w(7BetNuLohqIH`#;Ktah8*NBqf{Irtt h#G+J&^73-M%)IR4hnx||7+vynxT)eyun3^@Vt}y9q`Xl;&&UMxJPeOGoB}2Iqj<@l- ziWF2iHtTR%*e>v4d=qgouKTn2>{L0oowMz@+&rw3^nV=VEM4+(u@yta73H|lgU@?` PRxxnrNx@9Q2x)& zO`j9m=PnHRQO?GD?#hdY4OiBl%f4jneny#9DDOs+i0XvmV@5#=>HURE91gK+e_6~@ zq~!yYTX!Qt0w{M#DM;aA-`(Y>t+VHt{amTek$Z@1vl+L!D97K9Z!=0lf6R5bVZbGO lJdxY8cWc_~XrM*2*j0bdvD>Vf`yc3N22WQ%mvv4FO#o5;Uh4n= literal 0 HcmV?d00001 diff --git a/icons/light/16/media-playback-start_checked.png b/icons/light/16/media-playback-start_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..0671d08b5ec9710f1083a00a18752bb32c00825a GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt`fo-U3d z7QJ^bZWMJ$;Ay>hnx||7+vynxT)eyun3^@Vt}y9q`Xl;&&UMxJPeOGoB}2Iqj<@l- ziWF2iHtTR%*e>v4d=qgouKTn2>{L0oowMz@+&rw3^nV=VEM4+(u@yta73H|lgU@?` PRxxnrNx@9Q2x)& zO`j9m=PnHRQO?GD?#hdY4OiBl%f4jneny#9DDOs+i0XvmV@5#=>HURE91gK+e_6~@ zq~!yYTX!Qt0w{M#DM;aA-`(Y>t+VHt{amTek$Z@1vl+L!D97K9Z!=0lf6R5bVZbGO lJdxY8cWc_~XrM*2*j0bdvD>Vf`yc3N22WQ%mvv4FO#o5;Uh4n= literal 0 HcmV?d00001 diff --git a/icons/light/16/safesearch.png b/icons/light/16/safesearch.png new file mode 100644 index 0000000000000000000000000000000000000000..8af718d1130ab1f25892de8e9f5c7f7f9fdc0cb0 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt`ZJY5_^ zEPA(2wBz5u1HSE2CnEEkvEPhHLpodlNFWc`F!~H z_WbmR{TdCET@oi&aW+aCOqx;m`T>WG%M#(pkn@cTga7cX`BK1ca_+{y-HOboUO240 zW0#=2k@3}v^{LOq8lE5BIcdX1e=Y~OyL u#q_tOnIUEg|9q~PMy*%c57G+i56cE*Yh+B&iem-3oWax8&t;ucLK6T#vtic& literal 0 HcmV?d00001 diff --git a/icons/light/16/safesearch@2x.png b/icons/light/16/safesearch@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b77f57627658b99e8868e39a60ed80569a715d5b GIT binary patch literal 402 zcmV;D0d4+?P)0$h38qA6oi^FDvngi)0P1wWf?(NO>=J^;fA$*1FeKAEWr&w-H-sZt~0 zY|$VY(8a#OE(w~k^po1DFE6I-*F0n&N2Z)B(LI2 z;6gkHUCA14iev?_22`0nx@($%+7ih6-vDn+j@N6;J4*+UHNF$`o|qrS1dyw{w*W-l z0U5k!nMeB)D$HKh8^X5^e3kYO!hiLgYKY`72LH40H++L?NNw9cmFx-cKF|Pc{{nC; w<~xeG-r5BofG6P4;BNwtL!isQn)f)Y-no?;MJ z>H%B`(L@qP+eQ7+wx=Pnl3G;P|G%rMtD$avUhc4)`I z7qFcW$;G(@xL4pJ(234>5&~`IicMe_SOCun;3|6(XRjfp4Y^N%&w(Y@^A+$C?um@Y2rbq%mH(8y4B%`-$hy{(gGfY9fpYv zTTib_a_p%p>!|FxFgL(fte#=(S@QQYErNKdn``9Idf0{)z?;0l2G)(i-{3-CKx3+l Q`Tzg`07*qoM6N<$f?1xM2LJ#7 literal 0 HcmV?d00001 diff --git a/icons/light/16/search-duration@2x.png b/icons/light/16/search-duration@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cc38ef6d39a6492be938400873f206cf74850c25 GIT binary patch literal 721 zcmV;?0xtcDP)WvG9V@NrIKP5^XfL8jaXlczFOj zRHD%*(5UZVVj%@y;w9P|eE=0=A+a$55p44WIEE&oNsecU$bjL(F5fWShqm4E_)C7K~%A0&&`wa37nhk`Llb ziR3>TZK*@-2lSUEKZKcrBrBN273@VV`llwWiK}?gh)w^W#!#7_7m>fg;j%rz5xgtN zGo{T)Y{n<~Uf)N(0(piSZeTgd`B95CFp$qen*Gc42LyO#T>T6g`~2{63Hf_ z_?;ySfR_o^xw@e3F`iaPSQTmR6LsNWi80Z|0@B>Wbqqu={s_|AEllHY^gK`(7GZ{; z!b0g5RvH```g)Rpc3ZC%`h<~-u_WxQBIG!JX+Gh~$Zos~irO+R7yP%OQEV1BQ^(gR zpclJK6wt}-)N!)l{T{@l{ID7|1Gj z#93jV=eAmkDq^!$(DD)C`ZgecJ^v(J&nL7#j+^ig{NzFJvfb4-00000NkvXXu0mjf D_jORK literal 0 HcmV?d00001 diff --git a/icons/light/16/search-quality.png b/icons/light/16/search-quality.png new file mode 100644 index 0000000000000000000000000000000000000000..50cc7b437e73a823eec41827404bc3b9321189a0 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt`FJY5_^ zEP9V#TIkmlC~@rL{qiG{H+L{_J93K6HBVOFA(-LTd@^IP#=+YKcNh<@VSKhBfTKo` zNup-1pKMJ!+x&`ef98MQ^Zp*wcDvHv@Y!4YPp{_}o|DutS$D$+o~D@xxb!1ZSag~q zMFW2_#o9co^gfW-@}*#MfHQM-M0myqzI^3~(?52ekyyx+Hq*eB{V>;u_O^pQ!mljc zjg=Qj`q-vA?ANNCzr4SY&75!9lYZ|tiEY+f3d0niF!=8{e)YSoh^W+`4}advzFE+k keCDIT%h_AH7bm#oC%v@k4$WDz1?WQtPgg&ebxsLQ0A22OQ2+n{ literal 0 HcmV?d00001 diff --git a/images/search-quality@2x.png b/icons/light/16/search-quality@2x.png similarity index 64% rename from images/search-quality@2x.png rename to icons/light/16/search-quality@2x.png index 303515352a434224748fea144712e9759f99d29b..d4127f3c42f1402aeb8c98577153290db6e83b2e 100644 GIT binary patch delta 26 fcmZ3?@|t;q3O`$tx4R3&e-K=-clqRv**h2kgWn4P delta 82 zcmaFOyqINziaSfOlV=DA5Y%v_bTBY5a29w(7BeuglpinR(g8$%zH2dih1^v)|cBZ8YA&2mqVp88QF> diff --git a/icons/light/16/search-sortBy.png b/icons/light/16/search-sortBy.png new file mode 100644 index 0000000000000000000000000000000000000000..8fb835629b8f367e52e6cbd34996db53e293ae4a GIT binary patch literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_aJzX3_ zEP9g@99Vm-tgJTjq%gTC>m_lh;bNc^a}f8*8>1XlZMg+t>fI`E`E2 zz0Cf)zd|jZ1qB6rQj?N?sj91AKYaGAZF6U*r=Z&LBS*gccye;``~aUz*RJ_FZ;OqO zzt5}2o_hG{f-J>0!PovV30D}{*xV!z-?=mA{r&y*n+*Q#umAt9{_ii-#}^m7tN#D@ zH~II+$L$6_eZ9SJZMnHm^Lu)pT*{Ig5Ek|>>A<8DOO`CT*4ox)B>dE;#M$FgPaa3) htVD)cS0yJNWoR`CIUcXRaU#%r44$rjF6*2UngHxCdZhpW literal 0 HcmV?d00001 diff --git a/icons/light/16/search-sortBy@2x.png b/icons/light/16/search-sortBy@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7e42d9023b52c6f8ec72c87562e12b7cf7bbc239 GIT binary patch literal 374 zcmV-+0g3*JP)e@qtuMut*~#(tx1D5YKy1dnkX-yJLt z02=_<7)AV!8$}GojUdM2S`n&oEr@;Nvm&O(=Rz!!B)P5Y`cgb>n&zzU`(xRH=Xvk0 z>wb!7Q53Dpvb-mREIH@rvOR>*2V?A6Nfn0S0}Q-zmSs0Xd)qtd-u9V)=157TaV)?B zEWiRRU?Dpsgj{gWPi1!qp%=#3Yo6yQqE`+hT3}HMjA`TY+8I@1_Z#uNu U9Bk&kfB*mh07*qoM6N<$f{@Xm1^@s6 literal 0 HcmV?d00001 diff --git a/icons/light/16/search-time.png b/icons/light/16/search-time.png new file mode 100644 index 0000000000000000000000000000000000000000..a7aaa62bd725fe6ab51c0572349fd44d4432da9a GIT binary patch literal 394 zcmV;50d@X~P)rKZ#J z06c+bAT-{9f>Mnv5%HB+#m>DZi^e3AJO48?C+E%#KkX2Uzu=2F=mz^zT#xSrrqa8x zDNH83m6K;^;~iJHjCb}6Tt`hCr$zH%D!Rocc77q)j@(_s1}o^tR#J11Aym75;T=yH zP1t@V6kPhB%|R_>)x-BNAMr+bJ+Qr0cZXWBg|_dhzTh$5&w;ft9JyBNDc3lFX2i2t z)fwnQV7(ZNTr>5Q1fPld^$!Kr$hA^WN$`P_h?j7Vn~*JXGoj!l^^^p?XhdAcbYSCw zHGE%ZBlVO7)vg(=;6bb@l#F!EpuCfs&cF)W4Y{xuF0kEHcZ*t2F^qrOXC;CoETg#0 ow;=A1{0Dk;R`;TF1IJkZ0_KWQw_xWrO#lD@07*qoM6N<$g0$1HCjbBd literal 0 HcmV?d00001 diff --git a/icons/light/16/search-time@2x.png b/icons/light/16/search-time@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba32ee9750f68c2bde5007c673d84b89cc6019c GIT binary patch literal 617 zcmV-v0+#)WP)aIiC-*DO!RT1;(FnAw6Hl-3Ef+Nwy2Pi0J~GXL-=P21@8J zNa_jRrRN!p7iAtWo^0VQ9v9f`wKz?nT<2{n*oRTr{t z26}Nb$nukM%;FZCV<2(#=}LUv(vz=uxddkMFv!+p1kYthX)Sgpjy^p&f=Borbc5Iw zWYv9OA}VC7U;)Dgx+|$g3@4w?0^Nj~ePGRNpp`%@!Yw!xWE&EXU`NpHP8@yqCq9K= z1u?3~Ps6h8`ZbSl!J$pItSa~uba^#@5AqJ#Jzz#wa4~^)*@bmF7q(VGD}h-{i=~-Z zNHMFcfb6GZ$m*cef>jip)lz&YGf^S}5POYYdKPdQ?a00000NkvXXu0mjf DfI|}H literal 0 HcmV?d00001 diff --git a/icons/light/16/show-updated.png b/icons/light/16/show-updated.png new file mode 100644 index 0000000000000000000000000000000000000000..8b8fb39406c8092d36e594083e6c42c7fe44249b GIT binary patch literal 423 zcmV;Y0a*TtP)LbPEaE~HJmitoT+;?jvQ7<4hl zI5-7P@1MaPp8v|;c)oA4j@d^JQ6~j0hv>)eK#OOd*#@9quhZ#t zs8*|4PlQtH+_vp}0?;(AnV#8oT^z?@G#X_B06h%D-87)Kta62F2_T9hwYoM_DgglF zKLEyQ2YtTwJl{esHTEK literal 0 HcmV?d00001 diff --git a/icons/light/16/show-updated@2x.png b/icons/light/16/show-updated@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c33adae87adbdd983e8037201e60c6ec5b8519aa GIT binary patch literal 725 zcmV;`0xJE9P)CI!X5+ z2&CBh4;l%CNct1t$4{#XD*Lqlf`!*~YZZj?7bA|fxe)-#^x?Ii{n9UVQlzrX*AWIkcAUn{0* z-rU&O_!b+We&z1~Ts>y^q1J1)+Su0C)=z)I2y&;>>3IOR<8+>wm_R0zK`NEP?(S}w z?UHGlJ=JRUt-sK62W;E6NxDHbzxw<8v9PcJr4&|IS5YVw+S!Gq?YgelIH+2P$l0iH z!!VG|X5lyvlxk@aCIWE$gJ&8Kvf1nvt##Yy?l=ze`8>#^lL8QtG0UIiyl406;F6!}RoYA^;H?2k2P$)>NB@)awc1`v^^4CN~pI}Jc` zDHMR@>rMlR$eU0A*LA-DSngCf$zrKg`Wy-X03z}LzBe^YBEKh%){&$fnIMpSf&IEn|Koo@9Hkd~^ovTjnZN9q zI5k*PyM2bo(%mvrJ}^~l?pPaEV)LQuM(-fEY47O(8n+%!HobyTjaneL(56iUJgzRO> zvnH%NFyk3tNZ<_VsRqWoT0|F1`SeapSpDK`!`VgY>}I}`o98oZV7{UHfsyA@b$}Uz z|M9=kPNo-<;~&adJbiGxv2*@e?FX8-?{iP9os(~C`1;|7h?gIidQ~3~+xm^$ptSRd k%9UQmdKI;Vst0FEMGhX4Qo literal 0 HcmV?d00001 diff --git a/icons/light/16/twitter.png b/icons/light/16/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..2d597d991d96e13480fcdcdfe3f1df85a88a5222 GIT binary patch literal 322 zcmV-I0lof-P)Vq(f zl?1tlN3=t;-OPGMp2r8Z8Xu)#`Zug}`ocoPKQLt)f6J61&3ritDhGpRkR? zvSuB73Fk8meMihNja9sqkoSEqyaV$%O1Pe5+1D3$I7dui7UOuw4Nh@c_TGQN2RMgr UGQ-5XWdHyG07*qoM6N<$f(IRp`~Uy| literal 0 HcmV?d00001 diff --git a/icons/light/16/twitter@2x.png b/icons/light/16/twitter@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6a74c2adbe791300ab553c197a612f96401a1408 GIT binary patch literal 553 zcmV+^0@nSBP)0$*gbO7Iix2QMqA2^92oWT>^&n7sY1YRlu zAHsACc{m|<0Y@U?sZU@ao9IM~;7OreYjXsgD*=Cn)s2ZNm}&E14UcdcdoYM&B?OE& zE9W79|E00}8RHML(DXxa1sjDEFxO0Bj#VOJQ~R772g(Fz@2vG*^W1hC}4j(0Rz~En?g(NCj4bPIBXLCZ7=GFiwKas zeYfFbMd5_uN;A4mIc^r&|HwxJ)^zK;rFP!cK-YZNl?@GXb;#3*v z8tlV0ye|Q+;{$fbKo1DLV74SM^_tMo`joH=&I+T_LL|6~>q0w^w!Fr$8N=9%9m3c> rn24Ok7h!u|3-kRmVUSt2f7AW|{Sc(0Z2$aG00000NkvXXu0mjfSzzvz literal 0 HcmV?d00001 diff --git a/icons/light/16/unwatched.png b/icons/light/16/unwatched.png new file mode 100644 index 0000000000000000000000000000000000000000..e3fe02260faa25f2ea8c5b16efea07f2c74e80dc GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt^-JeT8E@6KtCB75Dw$W7bll>55T<@U~=dnyI$c|qbzcOLC};ZpSU z)-7gvp5k-=rJv1Cd|w){xxOztX3-J$dxk&PKje-3D7(R9>W@PPGj&ZrG=&~2E-9F@ z>$uWAuJef=2@;>TTYbEhk$QJ+3R_~YmYd+>UP-&cuE{aU#@1OZHFX=}@{fXO3-lkiWp0_w~RW0EA#B&Ah-3OBQBt}iX bpT3qcrOY*uVZL<|&|?gqu6{1-oD!MDLfIfg`qCs7Fg9w>0F1iNFcB(HlxavO?G}QUdcc?mjik6m^W*or& zCJFY{h&_n2Si~93p%?Z(V;gIDjurf9QdrD!+`}&nE3Th-h|^7s=dg^ugvl=YVeKYj z_Ck)sb((M zP(~M91$o}!XkK(mcCqV?tU3(I22Edar2~I=e+Mt4{T10*9#Q2A z`oSA4R4C$BuKErx;9Sle?p82g$REcMte1$odC?epvIa}#Sr1-gnZ)A)@oGsCJ=uel z(mzr#h+?kcYa&Tsb{+9=IfL8M1LzB`Rxys5#RKVP8iZoD!gng>zT)v+%Dno|@ozja zrqP-(g)6e>bptz*;f`#^FXM8E{Vz=T_Z?_yX{p5@tN#s#0syH+00000NkvXXu0mjf D_m=9K literal 0 HcmV?d00001 diff --git a/icons/light/16/video-display.png b/icons/light/16/video-display.png new file mode 100644 index 0000000000000000000000000000000000000000..f5021bd62345db1e52ca247e9c76bb2674bf0537 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_Io-U3d z7QM*{5*Y_LmWmr07}%%>u2*4ZcDCuc?9uj=mj?)ZQZif^7qcnwC>&Q%V`Fo%`qA%v zkzeC%qw_q$w9lqSF$KIgS#LD(GBY=uPN{8aY<#GpxvbsLz~D}DL&aNrX6D1OY;0`- elkR$mGcZiq#;gAR+9#mh3=E#GelF{r5}E)Cx;fYY literal 0 HcmV?d00001 diff --git a/icons/light/16/video-display@2x.png b/icons/light/16/video-display@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6daa132145f0c952086424f20920c6f0e85f8e4e GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v}~%o-U3d z5v^~hd2=-z2sq5&mATRVV^r%s=kp2tAyrLsuI8Fs3WZqa@lO(x#p1d1dqOy)G3>mSTN~+J|$JYy=#Jh1!K|qpPUx#d%l~7Kj7OF`R+aU h4#rX+!KeFQFcjAX#LRnrUKr?L22WQ%mvv4FO#rs>SpEP2 literal 0 HcmV?d00001 diff --git a/icons/light/16/worldwide.png b/icons/light/16/worldwide.png new file mode 100644 index 0000000000000000000000000000000000000000..97218d0d9f264119f06190ddc71151fdd90db7ae GIT binary patch literal 377 zcmV-<0fzpGP)DwLDESu( z#i;xMjg|_boz|nGktl_P1jX#jbtf~Cu#%JPv+v#OtaZ-fkH}T%=P``|eBc(xc>WFH z7OwGzs~CQb-bVJ&T&6z2I}Wjg^Y}J_0v2Px63W!32sSZ}LCoS5J?OwgDC3LZ?!Qno(4G z@0REl@rYf#Wc_A?w4Aas85S2}zZ%44$c5)5%GbW5e1$btqMW^xDWz0?1kp(%`(N=1 XvcE^Ns*K#A00000NkvXXu0mjfNi(Y7 literal 0 HcmV?d00001 diff --git a/icons/light/16/worldwide@2x.png b/icons/light/16/worldwide@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..41d22d847c2f470f7de631a3122d8ed17018c14b GIT binary patch literal 821 zcmV-51Iqk~P)Ly_|3^Exv z9dVNZBt{37vJn{3m`DPCByH)agR&SDA$*F(0fj(NG`4lP=lWc3U*5hiA?dR+>HFv-*n;C>tQ<2fWV0B@=KEo| z2cuzV24CQ27I8#~ZQU7dq!-z=3%78rI`PtS5r2 zEA(J82#%o_i*OFV_^x+16lwzwl_GTw~x^ZCCo$vWL_Qd-ALa%fH_J__paey*L zKJL``me}|WhVd1q3!v9j@e$#)&?Ub#LhY7GnrYKh;h z%7WpvCeTW}E;+WXlGlj{UJc#tEqG2!x#EA+H*g7S@Ngy1@AZjRURPLA?aAoHv+jId za~v28r0S+HHPL`$Tjk*2}O?lZe)Zj4l&VD~y|)iu*OG_=>xy z;{26NN$ERdC^*}mKH~*VF?vzDjeiOwr=-HOS5u7MjT6?z=u7G9zm4^#P8=YW&bl1$ zj`atH-rdUaE_2KAhn*THC8LShc+}j6BzHKa11qJ(^OL3_IjZRqyKc>v7fyUHwOP3( z8*Smh!FaX<8)8p3o;#?nu~>Q?t(wxeG)?K-A-k?=N~fi3zvrg3#AYdiJ8jFyu(1>3 zZhuVHN&OQ9A0<~fqCwRgOa@a&) zrNxJEk3251i&bPJ=pBowEw}g^t}yt`c{>gwd_8UP7QuNAZ6)GruoWR=X#i6(={x7e zs>d}h(g2QyZ=O^$(NNb<0mV(~!C4Bx>8HFzfJVHg02E@Ik^=6~odQsBew#F5Bf9EH z7@x%>>XHI_g`en97{yYW5E?v}w0J=aF>xCWC5k@_E2KFspjKE5dE8(Y!x+FMj^f!v zyzeV>WsC|Vn=iT^FfUBba(uQ|E(AI;f^qcw+R#jd?D)NJWos!yc2)2urBs8p2wABH hpa99>yjp-ist*aQMfyM-M&1Ab002ovPDHLkV1f;2qyGQ^ literal 0 HcmV?d00001 diff --git a/icons/light/24/edit-find@2x.png b/icons/light/24/edit-find@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bdf40718fb53ce6a17ebaa3e5ab05be3e014197c GIT binary patch literal 822 zcmV-61Ihe}P)$|AQf?CMml4t(TGrlnjk{bCNPTBMInRAgied&pYz_i_nmvsJ@*a+ z_X`)E`@ZM=p65C5x#vCSxpe8$rORAWX|exmyo*r`qaQEhHvYs-oW^&UY|&R%MsNYs z4SpVAFILV)urYl&j>j4gKK|Up&W=a@gx7JSMo^dW2{vLa2C)vCu^+c;^f`(poeX|h znAdR-6L_nEzP;FHW+LaYunn=!pbyt0$B*%8g3eN$QcPlBo)F797CC%`tu1Kv;E-Yx z@3k6W8E*(dKXvwT2F(TdKGOUhi!^hfsH6oIk?;>Wu>MZ5t9 zBl}L>Rwa}r@M~m`)@_weMRDc1oZG<`&)4Wbge^c$Mk4+H@KR=dFXDdWNAW4-8l?@2JJJoAbZ4*}|3;3+g#$_}4C3!d^M@AoW+mPeP2)=}O4J%uJRD78 zO@S3;DvV&mv*^#Ln+ym*QGpO$Be_+p50RXXE4~nkj7c zVmCgjaqk*_6h`wn}{cwE%;4ZU1&m>^A&tvb?>YW zmRc6#1L2@ETajxDXRy9Xqk;ofZO1fuK5n%(N;uz+;x%DQyiC}b-NjX5)a&hLD>#Vl z(X}%|h&e62&}$6WN_h++~a&9fwWg%W<8 z#Nj!OD1ly_Q2bwE-myR^1W!t&OQ9A0<~fqCwRgOa@a&) zrNxJEk3251i&bPJ=pBowEw}g^t}yt`c{>gwd_8UP7QuNAZ6)GruoWR=X#i6(={x7e zs>d}h(g2QyZ=O^$(NNb<0mV(~!C4Bx>8HFzfJVHg02E@Ik^=6~odQsBew#F5Bf9EH z7@x%>>XHI_g`en97{yYW5E?v}w0J=aF>xCWC5k@_E2KFspjKE5dE8(Y!x+FMj^f!v zyzeV>WsC|Vn=iT^FfUBba(uQ|E(AI;f^qcw+R#jd?D)NJWos!yc2)2urBs8p2wABH hpa99>yjp-ist*aQMfyM-M&1Ab002ovPDHLkV1f;2qyGQ^ literal 0 HcmV?d00001 diff --git a/icons/light/24/refine-search@2x.png b/icons/light/24/refine-search@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bdf40718fb53ce6a17ebaa3e5ab05be3e014197c GIT binary patch literal 822 zcmV-61Ihe}P)$|AQf?CMml4t(TGrlnjk{bCNPTBMInRAgied&pYz_i_nmvsJ@*a+ z_X`)E`@ZM=p65C5x#vCSxpe8$rORAWX|exmyo*r`qaQEhHvYs-oW^&UY|&R%MsNYs z4SpVAFILV)urYl&j>j4gKK|Up&W=a@gx7JSMo^dW2{vLa2C)vCu^+c;^f`(poeX|h znAdR-6L_nEzP;FHW+LaYunn=!pbyt0$B*%8g3eN$QcPlBo)F797CC%`tu1Kv;E-Yx z@3k6W8E*(dKXvwT2F(TdKGOUhi!^hfsH6oIk?;>Wu>MZ5t9 zBl}L>Rwa}r@M~m`)@_weMRDc1oZG<`&)4Wbge^c$Mk4+H@KR=dFXDdWNAW4-8l?@2JJJoAbZ4*}|3;3+g#$_}4C3!d^M@AoW+mPeP2)=}O4J%uJRD78 zO@S3;DvV&mv*^#Ln+ym*QGpO$Be_+p50RXXE4~nkj7c zVmCgjaqk*_6h`wn}{cwE%;4ZU1&m>^A&tvb?>YW zmRc6#1L2@ETajxDXRy9Xqk;ofZO1fuK5n%(N;uz+;x%DQyiC}b-NjX5)a&hLD>#Vl z(X}%|h&e62&}$6WN_h++~a&9fwWg%W<8 z#Nj!OD1ly_Q2bwE-myR^1W!tEujWra7d2bd^`l@g-+|8e2P~&ooMKqlo_NFR`J{*bpMco`8T*Y-TX;U$EiQOfmTBp5fZ^DT z=aQ|n!CEevKQi0cKQn#p*;T7<9iL=u^;MQ(OYZNzaqH(d_&<=b rVE%SM`vZr~l+>T1(S03AKxT6Me$PFg?fU{j;mhFZ>gTe~DWM4f9p;{- literal 0 HcmV?d00001 diff --git a/icons/light/32/document-save.png b/icons/light/32/document-save.png new file mode 100644 index 0000000000000000000000000000000000000000..222b2de01d6c560e7c4e7d1710c8f4d92904c8b6 GIT binary patch literal 344 zcmV-e0jK_nP)+g@Y>&?h&lq!s6hrS(`0FNY>sD}HpoYgfyuQx4jIAoSzP4dO_Oey0VmPU?qL1B` zQ6RSIS4Dsr!$lPrNq#|=hFvWgj}<2|1WXnvwQ&()DE98KlT+euNZ|B5=FG%+Ap=NzBo(Fr0ZO z9M|By;M}G+J}&EYcg+ZzmbzW%^tw|1!u()s3SRJrI{9u9e!8n%vT)QtFacKE$7%+KmMxuD$A?D;W=S9kwE@8}Q zu9{>bznkwx^IfIPp1csrgKR6B*Reed?kkfP;F==Yczt67t4;zVcfP z_^++L^WpO)vYbFO89w|!@**)U&a0}UWA(Yq*E46uU9-zrd)I5%%by!pRZadfW9Iw0 zvXhpaJuy+m^+;{#+tsVSf0;7#`b^9Ex*0Qfud0&E?CO|Q@?!7gCEXJjAIDLG delta 82 zcmdnV^pR~}U&8;#i*0b{2ZKL7v# diff --git a/icons/light/32/media-playback-start@2x.png b/icons/light/32/media-playback-start@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..88b679aac6c50357d7b0fcd29af716e28837b056 GIT binary patch literal 454 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Y)RhkE)4%caKYZ?lNlHot36#D zLn`LHy|vqy#ZaX6p|SF=1xzX&4Qv9253eMzvw5nc4 zpPvnQjz>RVHsw-#M@~{t`qTN>+`<~{&&}rBXm;S6ZLrtNU>7-;CyOrZy*ic4fw@7N zK|joC3ZFxo(-kgt&sOy$~;5TZzcgdW!uPVAG3XB*APgg&ebxsLQ0E+&vX8-^I literal 0 HcmV?d00001 diff --git a/icons/light/32/media-playback-start_checked.png b/icons/light/32/media-playback-start_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..d799121cdbe4c87438e76a272f434ebedeced619 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v}~Jo-U3d z5v^~pS$8oxinu*2_C9z-)Mv#4>jHyIZ#9;w?dtisQG)l%fiyLF0m1KzE3PkCE*$vg z_*DQhTw`5ToVmze?Pol`D==p=-+B{-46{ClYbr81p1u8)78&q Iol`;+05&9h761SM literal 0 HcmV?d00001 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 index 0000000000000000000000000000000000000000..6fb34cd34469865baadb150dbfb9e0bd8641c8e6 GIT binary patch literal 475 zcmV<10VMv3P)VK~#90?b{ADzNF~%5Uj4|e4>SiBT@0+EM5sM3Yx^55P zaR*=m=f?QNIZoH#(i|<1LD=Jd?dLu3kFerjP2cTpjv;?5&A+!acXd$!)AcnX{-HlMUTcID&9)PC* zM|eDA*zSeBxa1Zkzy!`+!(j)<)nmwutEo`*v;~MZ zoU{apKAfZnNH(0L21q`fqy@+}oTLQEKAcJiC^no*1t>n8at9DIoN@&aH=I%j5IdYw z1rR@+Vh2z&oMHu-xinR&x->O!0QI-U0t2{@?;q-i_qSjdC5ssA94hPIFzhbGUWpZmAU>^Bk;{5xlUo8U(8Ta(Ps z1)aJmeXjZG)~(x8*4^HxQpy9U My85}Sb4q9e03f$J-~a#s literal 0 HcmV?d00001 diff --git a/icons/light/32/media-playback-stop@2x.png b/icons/light/32/media-playback-stop@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d944741cf36382eb8887435f1a6a56396c765b1e GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Y)RhkE)4%caKYZ?lYt_uJzX3_ zD(1Ysb&!|IP=Lj8c7TGi!U9f?6C6Q1PbXdv^4{@!e^K^5Ip;|#o|CrBziH|BX-@II zu%jU|$3OahT6J$ayGN0H@}AY-yKN`cXths2w)pz7w^>i8onxp!$oPU`7V`y06ZQqn z8GH`x7i1dv7FaUKIutWlH9TV|VK|FV!8OST+t(~we`nKQaY;|0<(K;WxUPx2sW9cd Q06Lq&)78&qol`;+02;4bp#T5? literal 0 HcmV?d00001 diff --git a/images/media-skip-forward.png b/icons/light/32/media-skip-forward.png similarity index 70% rename from images/media-skip-forward.png rename to icons/light/32/media-skip-forward.png index e59e645a55e7b277f33f9310c9b4e59ac47547af..6283be9669e7d2244f64f2f8b2a9c7cb0eb73c76 100644 GIT binary patch delta 26 fcmeBRy}&X-g`X|S+ueoXKL{?^yL|G-Y*{7%eUu6^ delta 82 zcmcb>(!n}G#hsS|Iv5xjI14-?iy0V1lt7rVpV$2aP*AeOHKHUqKdq!Z gu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$J?jmEM}088^13IG5A diff --git a/icons/light/32/media-skip-forward@2x.png b/icons/light/32/media-skip-forward@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e79b0bf36d6c377c4b0179e7c4fa3b6ce42cc077 GIT binary patch literal 1001 zcmVhG{k+=^10p&aSSmV?#qj ze-vdv(xX6sM@PqWxm>%gnP zAxYO<>olX3^<|o-+qfCd-PYPSf%9=3KV5)_-j&a-KpFT>(hD;)GeH4b{~|MhCiGcr zPe}T#QmH&pm?PK@9G#n+J142XKph}o0INKirs=Gt_a$w2?&u~D0;eRMDl+hP0$82* zEbzS~O-)U0b?B*){xnU`Njfqz%fZ_9s`nWj-Own zwopz=TFRr-jCMPqk$Zt7z&DZ}cT_WrJAeqdB1#=kt*4OF0IdC|n|CKkGB4?{q`O>dG_VUeBWb2ut?q26fYShsxuqjJfxW;& z9LEPGZE>kpFAMBRlH{VK!;_PfcjlSw`WC3~f>pLzYbSvVaUAb)tyjkuVDHk>(n1`^ zFG}iYWs>s%8oHpK`>nMzl1@u{*p*&J;Jwz`*MSR?cK_FWSC7HGq`)+_SKliI>HwS! zf?zNV!}+X1=N-@@CE$7Bi`$^}RFWiTCnhF(vj#URfd35}6+jBS13YmXv`z*=uxogD zcrk0RrIpe#p9A|O%>qbTS21gCvxWB}a6r=WwdOkyU=2f;j4|aXiavCym!Y+2jHy~{ z-)Ln6$9Vu5hOQc8Ubfc07Ddqw*Lro_0N(EI?jFsvh37PYhJXJr;4L7EqUd**I*qcx zM?nxA48!n;ypx>fM@L3ResNj2-2jpmL!oO! z8GI51!GUJ)^6RCY08W=mrG2BLqvxHwxs`cfzoZjIisT7k&KR>lilR>ob5rAIAnfbw zn;sY#ND9`<4xm&jT}{)}(r`2uMUnHPF<_AmJQ~~dhoc^k$K&yMJRXn7%Q-|z!h)_d;)I~ai4wjl63N{!`TC^!`9(x)?w?gb=W$bvCE!yICB_Shb8H#SckI* zT8FK})2zeRVe7DUIAfPR>u}~UuntR-BuP3BU)myrjML18zyJUM07*qoM6N<$f{ojq ARR910 literal 0 HcmV?d00001 diff --git a/icons/light/32/view-fullscreen.png b/icons/light/32/view-fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..2725ed2d7ab28cebdc8e01d98bb16aa3d7d51d60 GIT binary patch literal 561 zcmV-10?z%3P)|WK~z|U?Upfb(@+$J&$(u(q3lSoqDsjC3m|pDYKf58$pF8A&PZm7 z{p3tmXVhO%2D2armeMFw2P&b8Aq>osy3;gW{4R@8!Pv$oE?x31mVE7ZKIz?eZD7fg zONM2I;c&R7s;fmcYwgq+bKHi&m6Gt=X_|hm5c~q*&V>o6>eZ5P3*ZdEi#Ebb1u77d zFT*fAXiGqvJWMiG)$J&X9$bomsyZZBthFDFG57z6fX%LHPQnGS--g8B1kOlq0hs3{ zvPRvDPx12__qM`0N5h{#4>BIASa`zMPCln$aiQ54;?){aQJ)>@K$+v#+k z_xt_4R3mCPVGsm|BC?^XGm=R@+!!POvK@_1K@bFouIt_c@LPD2Bo2Vx`FuWY+L*Ps zpo}C*oXKRetEx{)0>CMNO=HZbI`2CR=Cj-FzRAJ`07*Iko_U_PKOT>##+Y@_^WOJ* zy?3M0XqI1~(H7*1<9HRolV9)U=EN3&JpgypG`&%@#zF$V?|&DOO_HZ59#*dbtW>SB zkN|LgI#4jI>SQb|@KXZ7LjW`UIjkyaMF0SdF`r4kLc_*xQ(zsyHqJj_wP^Z7QpJv{ zUMs0-)~FXM@K{7{78OXI3m z&W1oB5C{YUfk5EDq+nX0l)41Wr|9)nM7Hbox=vARjZ-EkZ)|K_PLkw+6ygB1)?2{c zG_|%Unw*>@$yri{A6_Cutrd&}WTaCnmF_$2HxaG`yakrl*4Dl_>Nhbi1ayEKN~zBd zdKu$FKrABP9rQ_s3jr0a^=`A-oORI47*}(^LLA3CK1y);FW{vFmjYlQVY}68iQ~S< z;#vR<=7hE{67moL9!Z#nfDW*qqOPK~exj5z+K)_|tO7c~qKH&UC*fMLSUjI`44E($ z&>#8S0Tz`~hxK}0rgu;?_cvwg0^mdm-h>BB0^nE)UWEsX0^qlVZQwfa zDvF}&B;hwVH)p!t?zwWg{K57dVO0Q_hiNvOvvC}^fvdm^5m~YMAl1466OO_^1+I*I zy)PnFTN1nxa7_3i)+Bf#;Dqo)tVqaRz=Yw4jFr%6H0JvK{zIT3BKJ%r<+U3G1`}i) z$2&|G9>(kV@+pfKsXltl&_BR|3Gj1n&gEK!S+e#=)H6r2v4VMb2s4eY_OVXf)=4UEqT4Ub!xX z$1$?Dw;mq+yaOKKC_MNnAPYZ%k0u4){RBj$*6Z~ilVWUzLgCZ;`uZCS!wLiffj}S- d2n7B$egVLVAC0o#Z^r-t002ovPDHLkV1j=#Xp{f| literal 0 HcmV?d00001 diff --git a/icons/light/32/view-list.png b/icons/light/32/view-list.png new file mode 100644 index 0000000000000000000000000000000000000000..0587ecfe85066961b72f99caf62ec1cfa2067e9b GIT binary patch literal 696 zcmV;p0!RIcP)e2ix5l`11eZ4 zmVy=mvyZzINr+YoK`aynwMb*5pcbN7Td4#L!51R&5l|F&b-c&ISyG-=YL$v;Nb7r9(+TNs8XfG%J;@Bx^LqG-IMqhn%VVBq^)gPd~#a1A)^ zoV(MIK-zo>*k3DEMG3ebhT-vCE;m&Zz8bgzYz79Mb0-%n5CKTKY-W4sscixJlO*YP z&OHb20k46rz;+;5Xy|VOX11YnL(&&B`;rd)2xNdYRqFulz=6LBtfkiay`t|ybpJQx}pnrT=deZ)6_wmNlX zNjD|s^7;J3dV`9^;xRKjC26owD4bfTK$M0b^xmJN&ZlH%NLplOeP-6@oO>l{JPgCD zNs@dEf}q!Xf7r}6r}cCFN9Gpr-XE$Cmo#H$WuR5k%9@_FW;T)}$w=DUNM3z`DrSKh zGrK71HL%{ydVqC6E3jmqd9#w<|CNB5C6bEHxid92#o^)MF7N#j@BJQ0iKI3&Q~G)g zc+1cIoC_+P`gq-T4pdvI(v;lQ@opYWp=8PQH znVmViXUB`)9}IKm{rJD{`=9sgIpArpqcGf)#EeKxqLqV7;x10{i8FX50e<6s_i23ikYngx*F)51wQvYPkrA% zl10bz`TSE+6m19oRMkaUXx5^p0fj>0;c~hBzKC3wM$F6*k>kKSz)Pw+I+acZK`;OW zW+uQ%Rh>T-oo2Ym0973V`ZCZCMdW~)ZBo^ro2j6x*8n5H9f{=D84v`)J~LZARk3&m z)XMtZ^Steyot>|4-n{wD#LRerZvtxoZ$fr!3o(cRtMqpH2CI;W$fW3Gri2)qWI0VLi7&S%jv5h=%y$Fk6? zM|++05NHEF1=iKN==AsZp91y*dxwXI`%9(L<07)b%$CPR&I4(eNR@FaOY$)A$P_i2 zBDo2!HnV++yu7L^DW|roE(4wg)&g@xB#h7VhRe;&&WcDE74wFGV5)bCW~pg_h`f-? z<%Xssymjl=dE?{bPXil(#j&nMQFO-hyf9wlWh(!4Z(J<|c2P0EIGy&J<$?i)LSbH+rw_W&z^+koT^vjltx>;Xnp^^dIDZiY*81`Q7nca}<}D}j=# zp1u@)aKtp60fj>0<}eJOr!tMN0D7owj3E8ej`>V~x74i3UaFE)r4N zbeS+KJm7ZVU0{c*o)Qr;v-V=K*uy0{j-h@mFcAaJnORqLP?i4Mn3>HMk&CXmlPn>$yrKX#AF6k2Y(278Q7z$gW1z$R6FgE|pmyqWC=cB|@jzZhe%IQ*~Yc@O!%|4mv!RhH;`w}jX%s~pfhVZEPD@gK>5=3putimOlJP6H zdR#DIaB%SQ(b3T%&-32)egANljl|L4auK=P%x(d$1?B_)0Dp~}U&8;y@L0sv+57ykeN diff --git a/images/view-restore@2x.png b/icons/light/32/view-restore@2x.png similarity index 81% rename from images/view-restore@2x.png rename to icons/light/32/view-restore@2x.png index ecee8be08458f7df620b6bd7b14ddbc705151b40..c93a128aabe94c81826df26422334f0fefbcecb5 100644 GIT binary patch delta 26 fcmdnZ-poEhg`X|S+ueoXKL{?^yL|G->;Pr}d}s<$ delta 82 zcmZo>-_1Tj#hsS|Iv5xjI14-?iy0U+TtJvnVe6I}prB-lYeY$Kep*R+ gVo@qXd3m{BW?pu2a$-TMUVc&f>~}U&8;t{)0cklG?*IS* diff --git a/icons/light/88/channels.png b/icons/light/88/channels.png new file mode 100644 index 0000000000000000000000000000000000000000..27fbc142ec637b0b622a145eb2eb148b579a7636 GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^5g^RL1|$oo8khhnwj^(N7l!{JxM1({$qWpPTAnVB zAr*7p-a44u>>%QL(f+dAoUTjkxeB{Hv^*WlM#G1H{2v~;+bq2yc@G$l44$rjF6*2U FngA^lt9JkZ literal 0 HcmV?d00001 diff --git a/icons/light/88/channels@2x.png b/icons/light/88/channels@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c83867d0fc6dd777a8aa91f49f688591a3959f0b GIT binary patch literal 1168 zcmeAS@N?(olHy`uVBq!ia0vp^8$g(Y4M?uv{v-}au_bxCyDI;2Yqqv4i%w_T+<(&oH@9Yv-MQ$?ZW@QArWW@e)jak|; z<)g*3i1(Wt^8G$G1*vL#zS{L}-}~SHRc*a~`CtAubxW^dUeUSTQtUVDC%V3k=k2fe z*fsmd!{FajF73*kb>7hZMtqLo@53La7r#BO`f}U*-_tijgqr*Qe|!EZLGq z&-!P4+R|J!Z^QmMfBzXgetaN-==TA zFQ3#S?fF|$?*FteHs3gYck3qApRjx%)VdotK9Yd^I>JJSLR)l zv(INJztP{;@i+0u>7CymPb#_n{qN};5Fy9o^}mZNR~0n>efs7*yYK&*Pv^|$GQSaj xuFn4c8D>fY02qFwVK5p7pkzd)bkKe4KYyHQ&*^`E;#PpNgr}>Y%Q~loCIC0r{=NVJ literal 0 HcmV?d00001 diff --git a/icons/light/88/unwatched.png b/icons/light/88/unwatched.png new file mode 100644 index 0000000000000000000000000000000000000000..abda7b22732eebd8626ed058366b21d8406dcd15 GIT binary patch literal 2163 zcmV-(2#oiMP)A0onaTZAB@`4wImWkzbsUWDTY@oT1}(z6JGYQ%t{2UtWt z4zvUJ0#4ZD`J@c&avY~Sl}i0N%z;7+a5kI$Y^74!0DL~IF$}Q}$ce}cVTc7o@#AvJ!eADM1Ylu zd;{nLt~a1J!g=7JQtGhQ`Y3QJ7K@z&D3{AqE0xMkzyhuHZNQztWP@W5D5cuc>GaR* z6{;)1`FwsxsZ`nm++D9Dqc{RQ2J94(cl=!7Jw3~SM}b=m`mzW3wut<@R)LxWEF#N+ zCos2;9Z*WGOQ+M@0bQd{j#6r+*18AykRiWb2OboWowZyXIKW+9UDpf`4?hlkwUz;e#V5Dh+GeB2bP%7^LQF~L`2>Sa#5fFXS3Obl}hDV+!}!?wgRg}}X;5!CoPdbjXG?hvn@q2;q0B16pFKMl}10Sr(wd(YW$Qpxsm!d@EDd1ZMH3yYa z52VxSXZ#pX8qYvP93ZQ;ZpZApc3&cqc-Wv`H8ijW_la9R%XiUZou!u}67K;agFB)`g zgddoCq4}YsqoZFb)gFTMJ;h>ie^*!6HDei64X}tz2YwBFDg>vF;lAM;N^^7b7U1O& zq;DM_9)2O0%gw6x zQB+E8YHn`c=yxs`k!iqdzz2dj9e~$>J4NL9$Ojjv2(JpZm+*oMa42AK_W-R_FCH~% zt+RzfVMkwIU&N25h`b5>-oLwnnF$>1?CkvL$On!$@J!q}_ah+=4sh77VSj&r9)FqG z(n6uI-mk7w>NkFEYh+frTt48%6sNnpdkP`Wxua1^9ra_E&*yIdz865cO+8=)Q-16&T)1#I!RE=yN-1A;2gh;#9zeGqZW$UH$~wTZ z1`SBLTt4r|PHR0csJ7PnW_39FCB`**B zr;5oSZW(p?cs1D(yj1zqK7Fl)vp`EKmAbgrzO2~eZ3`X5>=yh*YAN@DQmNFU zwVvx0dyZ?Z_XXV_%VaVSYps78$YK8s0H5=2)QeOtqIY(7&MlYAdw{tiI8p!>`n~H+ z;dON$C!CD+1n>nB`Rk=$RILXmlgXE3vDhNuw;?!_0KOiA?iEo=ts=pyOpwTs5}xztebf%yFD$qnBah5%}&Ak*k0oW44*&0Ifcyv2%ieK;f zy3xFM;M1-=HVWKY1mejAr3l@ zvo^>I!4ILWK}F;TFO(2(u>h94C4GOR_obM#jTsqQ?A zv%qsosU6d&Pv7em@*Bp4p2YO}&lV}A7Hh2+C>lYS1Of+*GO_R?lG~ey}!}_kmGIKwelNEspDGfzu*@0Uzx~&S}Yce#bU8o pEEbE!VzF2(7K_DVu~;mZ@Lx|~!5(kHASnO<002ovPDHLkV1lKU6`lY9 literal 0 HcmV?d00001 diff --git a/images/unwatched@2x.png b/icons/light/88/unwatched@2x.png similarity index 89% rename from images/unwatched@2x.png rename to icons/light/88/unwatched@2x.png index 573c25e78398f512cb42ee214fddf85023ed8642..b04db9b61609eab5778474af2cd6dbb25818280f 100644 GIT binary patch delta 25 ecmaE@v{`9_3O`$tx4R3&e-K=-clqRn*@6Inf(n%Y delta 81 zcmdn2^jc|xiaSfOlV=DA5Y%v_bTBY5a29w(7Beu+Z~$ROtK_D8Ktah8*NBqf{Irtt g#G+J&^73-M%)IR45kTg;H1$7ccx(Y3Hs{IR2l{yrh9E1-20d5bty7o`#;_OfgB3-(QARUW~ zbQ0&+p=n|?E}rRf&%?RgBON0ynNZqapD-qj<_h1m({{iN%aw#j8yBAmwO#uUFm(CE zA=rQOg)^2CMaEgcDN6~9OO90*P-TRJ#c;uo_)P;C zrWClNFLBSCa0v)#BncAdEGSC6bCWz;k57cGc#wGFBVE;FoZv}3Gsjn2lrfS-$QOhE g+B1a1^+9u28$9xlC60Oym;e9(07*qoM6N<$f{~hV#{d8T diff --git a/images/audio-volume-high@2x.png b/images/audio-volume-high@2x.png deleted file mode 100644 index a4bc0de07dd05eb42fdb4ca02388634d6702c20c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 831 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0U^EKw32_B-8%6;sA+S{^Xf`m^ zQcHsTf*BZ@m|0la*f}`4xOw;mMMTAAWaSi;lvPyKwRQCj%q(o}9Gu)dy}W$_0z<>X zBO;>{lT*^tGqdvx3X4lCD(f4XnmcCAp0{+_s@0peZr^qA@X_NZ&RxCv;L(#8uit+B z{Pp|K-+yH?P8kBDE8Nq?F~s8Z-pe<`gdHVZAKtynD5cJ(EbHFX!6kK&u~W+3A)tA4 zM}s2I@4xW}yTih6&z)-hS3m3Os?V{ZbDQdozn-1BLOFUaD)!X$ zFW=_>oq4`$?VQ^T5;J-f*SxOKJbysDT;KV}gZ>>e(jOdZJa@2j9e-CPo5!sx!MsWf zj*mC9wno+F$O+u~*TWgR{&4u`%MCxz)g1ZISpHz;`~yl6*RNkF;XaqG(fq;ZbgB$gk7vIa$mX2x_64M?E&ul!r5L+G9_KSt&Xmkr*!e6QN&eq!R|cgRfhMw z9{hWlq;T`Qp3O&Yo+VqO)_(i*es<5jn=1vZ&dKd})#hir|4MYm*Nl#)42C7Ku?=2?kGBKbLh*2~7Y7(r{Y< diff --git a/images/audio-volume-muted@2x.png b/images/audio-volume-muted@2x.png deleted file mode 100644 index 0f8fb55bf0b29c9fba980fc1879a96e14e8266f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 412 zcmV;N0b~A&P)#fr{g$oy-&^bh_WgHFLLfhKD@|&4H82p_yzE^zMeVN zmt4?I-%tn+uyU%;vCn_cb0kb5cyh`Y6DR>sA(2EN7kpc^vDUO+WDht9)WZqzSQ2#z zES3ceyaLCyg(sGcO}|+RULrozH>MYOjF71Xm=baKS&)*6ofELV3T~(4c&-TFT7adt zpNoHncz^3fzJU8y8ZcV}PW1t}ZaIu*i!hf&)p5LpMCwA4 zyAxhbYNGE>I3aNf+yO^U^)sjEL;j(c=ro;%Ev5S3n%fVeF_fR0#bjsz0000Pf)~Gu@XNT!QY^0 zFNnXw(qABU)>_%b+Ezl`*`zqGNS=iW97#5U1IsQm^M-x94;X-|j#PCt<48m*@E&fHeTCd7kfvd`4B5y!Yd_P*v5jh&1OD zlCN!#wRS35maRll^Z?*+u%|Ab0GtT`sH%AHF9F;Fi2f*40Qc6~8xg5wUUAOth{z*; zd+)=yh@4t$pBqBj4(HsahWdLnX#l`X@puRM7PeZg?NJmh0iZn3 zcQ+ah>x1L1Kp2J>0Bi%WRR}TZi{sq@5v@s|=ef5HUKSW*G=N0_TZ&i9K@jYz*Xzl! zSiTBOcaQ@>l;`=B50GyI-6Z$2VQ34oEZgCnYhvRJabXzl8yOi{8U`-}fHX}H5|JoV z;GCQ3LJk08Wo6~6F=nm|4l_Hg^%p7{I7viv0CtsZylVUea9Jt!dd-3)N%jjN<^dev zWb(Jnd{Jxt8R*!AaU3s5DNix;{mo)#<|kQ}omeFYdTc^#?Gm`5#(R+iCFTG#leP91 zfIBEZxER1)DdlY<%KIgj9z@2Nj{rtXrhigO9WIkt>Wh*jIXVP60E}i?cI2NmAcPqD zpmENXswwN~`C%I8y9&fFwP z>OzQn0Iqf5E;I8^t+naJ(N_T>#6$=AV)50N){_H39LK+uQr8PLR(+3%=yX4hz6~(* zs)X}F5R7T9U-Zid04e3GJkQ4fJS<-M{6PRSN5+^BX_|UnHUYp|d&XM(o|(4{&fZwu nD6Zd?!Wg5iwT-Gcn-+fn8SUN)&j{~*00000NkvXXu0mjfgaT7( diff --git a/images/bookmark-new_active.png b/images/bookmark-new_active.png deleted file mode 100644 index ea26fa55d96ba093fb6630a8c44560473e578686..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4-- zKJT}Sh&{IBN5PfdVm1D7If2%%Cm(jh-Z>V|4F|8nYUq))_ n+3@O?*Jl#4d)5D4sA2qf+v2dbZ|iBGCmB3l{an^LB{Ts5EQ5k}@P)?|x|5d{^jH5g5P7EW|FJFqkNZdQF@i(&71{?9%4GIJvS zXJHCcIUaj5GhB#>^O+v$!b3dxTkr$;igiq<`s5tJrHGh}i2V`K&G1PRp7^83qlS0z ztrB1ZvngJwM{qeJCMwV45wVcy)m~W9uNR|&AIA5Z06%b~t>50#^k_tkHLO|OuG^up zh*-kChCZ7`QLIH7;50tf>}t348E1OpU>+|=0DpyJwLQT;+}uU@ZA=b499+ehA%fR& zt>NAH9pWV34gtP~Q?1~Y5C`!n1O5~X(usSqw2|v*S zzOCa;yqmTt+a^cf@M=!Xq=H=qoOKW+@ac$sGPJk;FvyT{fvC3||uhcE(1h`w- zb>pG4?A&r$dnXk>jyFgiHgOKGF)?^`fAI`Dsql(Vr|!=DGx`Ze{bRM9yaFu%0000< KMNUMnLSTZR)&AK4 diff --git a/images/bookmark-remove.png b/images/bookmark-remove.png deleted file mode 100644 index 48d46a52a5bb860da04456a1bdbe417a5530326b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)i&62Tx#(XYHnU9(%Z3SBY;wID-p4S-k9*$<9(dv9JRdyg92ihi!;N(SlL z@>fo+NGCU0c_lXKaF}kf1uS~g2?= zn)mKK=gv9r3;yR&Eo4*+nMN=Y4Rzq_3Hg2SDdXb|Z;otO`HT16oNAki;#W z*J6G$7LJA`Nne;=0CEz0P?<#85~H%#+z=u|F(5u7au%5WZGrx{vymZuA5A1D_{0$Oziy3^f+GN#pK+NDXOwbPUpaJqn7`MF|Im9>zR8{atuYe2@|KL)P z>zLWABG(2bR5lWD4WtM0!6j&mG=?XKo;xEyE2k z#myp@zf|L>t$-hPl#$PZbV*b0OeHyh$fa+R%JnU(vE;jppFUcPBFchA31tdXYLlki z!-#w&)&CfiXhY@p!iZYzAV9U?syF&Vy}Z?96F`e&$J85pu6oZzXRl~ryhSzNY)k$J iC*Q0(-qI+}n#C_8huq8LQ>aP+0000~}U&3=E9^o-U3d z6?5L++8D@`DB^bUc%P}{O;J_`)(cYweG?TLCq8;LSMd9s8UMNSHZXoJJO21%YT7yD z&o=jqXU_clu%P1a8~5dxGcW%>ed>5|b@9H(<+Ja_huQtJ_$0mm=l0*H`Q?h)8QJ?{{~1{A5G5vRv?;l3X>|0BKZ*z+83) zYB1dgQu&X%Q~lo FCIH0IzD585 diff --git a/images/channels@2x.png b/images/channels@2x.png deleted file mode 100644 index abba86bf58f49915b68b7613006d71bb3caa9cee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1200 zcmeAS@N?(olHy`uVBq!ia0vp^8$g(Y4M?uv{v-}au@pObhHwBu4M$1`kk47*5n0T@ zAiW-h8RMiT$^Zo=OI#yLg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kpdc|>*?Yc zQZeW4T}8iYUy0U_mn*-z@8VI}G;4#h&smklC$@|6NClpidbLqy>5JKaj=Cy(Se56s z8&}qGS3GaA5eV5maY@jrwM+i1-u3?F|N7UYl8%nrb3bo9QuwWJbmiaK?~n5*ZPT{@ zb6HcV%)7qh&3dyV|CanTe}1dq`%>AgWy{&hZ^Wl}{Pp}i|8Ja})SZNS+uwgZU;jV< zY4&V3^BeK!>*`Jn z>M#A7UHtaAYNoaC<=|%PH|vc-L2|xMpC9U=C13xi|2*s4Ed6GE`u_R;&l*jc+9*U@ zm;BtktmE;8r->+}J_n*Wk%f1Ji6oOsesLr*r`KS1@@TDL)dQMr&D<`SHbiMSG zi+{eVt@keNrF-;kkNxZ3D39j-1M@e4Q~$Gk+5Ojl-LL(v`cgpf_nglgk2-#fPg(JI z_WR@aRdU1Yek=#tGVPzh&G=kUQqMoPRet)CwdhY4h6u z`A^NwA!(t`ZYKLR4hc%pq-W=6y?VO&An&*9oA1w8k+tHh$b}?@ow32YI%J{0Dzv9wa|Lvc<|M?NYxBM0;AUs|DT-G@y GGywp%AQg!K diff --git a/images/content-loading.png b/images/content-loading.png deleted file mode 100644 index e88c4f0aee0d3b051b7a81cbb7ec3517c7ae938a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8wRq zq}PKmW1Q4P8K9tKiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0xWAVndW zB{``K?s@sSsS3fBxk>psnaK*C$@zH-AsMN;seMZ{R|Az6c)B=-M6|xWWXRQEAmDOw zeaH5mdroUhE-rpDttlf}aZ{_S*z8K*69$c%OiD^h_N(tT&fI!{P2~ZLk-_Ynal4o= zSS$`psJL_d_Z%jxE9V-{o&2?+&-y{hCgl~6`A*1h3X=WKf9Cs4x&CPj`Z_vJ{MVag Wsk5_^Z(|eCZU#?RKbLh*2~7a8bY`Rg diff --git a/images/content-loading@2x.png b/images/content-loading@2x.png deleted file mode 100644 index d68c67bb00d4c2eeaf4d6201210a730bd8244a0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 471 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zn8$)Jqhx~{kij5X;u=vBoS#-wo>-L1P+nfHmzkGcoSayYs+V7sKKq@G6i|^oNKr^; zNlq$*dtQESszPvOZc=_uX0n23a(Ar*7pUNZDL;vjMK zM-B1k)!FVo@0(#S(;jG zTkU?MpB4Oa%t}b0NXF;I!eYIbzyHbYU<_+NpMPwI@2q9M^PcaPC~jg~@%mSiM4R}7 zSMw*Ii<&L!`cW{L??C7K1D4Y(PBE-&PrPCEd{RV_+dW3U^Es>Dtl#qf_iiw^;daC9 z3AdL%Wk_U>Ij0fh`zJbzPr&SejQz%^Ej%CW78krK%d~Vjz;JBFbII1(U@ezSavSs( ztYMx7=6$lcADM0JpP9b)?5b6_j!!bS`YOw?CHMEFs{ei<~O6pJ1 f=)MjlATv3Bzvmv$_I-h%C}i+-^>bP0l+XkK_6WL( diff --git a/images/document-save.png b/images/document-save.png deleted file mode 100644 index 4ac54e008abcdbbbc5ef08c2fc5c5fc88f8645a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 400 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8wRq z#8g3;(KATp15i-1#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDFy~cUQZXt zkcif|S2lJXau8s9P`_9sBI1eerC%(W{BPEMkxfcgmdRnu$zAkz>a_2CD_fhj`Q8bx zxpU~zgLRR!b)7t}%{-x5@jE>6d-^lw2@Hyg|82XXb-I!=dLkJm;#bUjWKnl=)0=kV z?dMq~l7$$!)5_T&G+*xB(V)?FBj5YVQ?dVAi~|5&eT)yP2jScH_V-R|FN1x&T zyPx5jTdzg!Hx;e5U&PqZ$^X(*H0$n~V_$CRo!a!r=kgbZgjan_1h^TzR_?s^E0m|8 z<9lDnt53b_U$BgTe~DWM4fda|C` diff --git a/images/document-save@2x.png b/images/document-save@2x.png deleted file mode 100644 index 360b8f0b3e1ae2554dfa411af90e400c10ea5fb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 591 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ z=y-!L<5JscD}jQNC9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NHH)lp7C^X z45^s&c80YVbD&7u{y-n?h?!p82U&M8q_1H;D!SsP%c;veDYABA57<&-8hR51Ki-Vw zEEAI0aAL8*@)@&>-zz?;$t|k?_y6>g8Qgu3Ex<@eIdaW|g9#h#qn>}9J~$#pG~U^;%o-qo}~PieYh zd-MC7t5#L%bUosX=Dhq-d?90TtcjHOjB1-Rnha-DR`js5GERtTx>XS%#Bfq&MIXB> zqd;uauZjRMhKnjLlKg@!4ZB)09xF~_2$(ETYU3ipQ0(1dC#S^SkihAA%$bStLX^{{ zj}tr?dQ_iO{$rE5sI!pG&pAHF$wQ6dSpLMydHX-@Z+ZD+^_01jxEkCz)gB*oU^w$m zIIh8Y!MROud|cM)?wS!aEp@xj>2;<2#|ve^Xj*@~*!&&ar=^|F14cfBr>mdKI;Vst E0P~aQ>;M1& diff --git a/images/edit-clear.png b/images/edit-clear.png deleted file mode 100644 index 42901d89d94fb63ca348c856e0ef8b88f501e862..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 431 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4)PauR+4R?c^<;+WC3E6mF&~_VSkKPH_r+C45Ei zt)IH(U5yN%e2n}1q9pItmNQ0b6nk>9C*#ttH`TQ@!bz3;cOLqgNVSG8Xy+7h#$ zOD4~XyE~grO=9ZftN#RQ@0ai0WzM=z;MujP_-tLT)tpZ3GfdA(T`!bbug$poHdoj0 z+(S$P3wjN%M}42HQu%jF(_FUZgFB0^FkiSA#l3suT@&Bw_s=mXoG)fwVe7r++Eo5$ z90F~Lr%Uhh>|lu7mb+zYm~gVsXK#>W_MA}H&-L;uc91!q zEnR{^R+Xv-GxJt4(NoWOmx9I-sdTW^Ua2=hOB+EQna$wf4@T{4f9KzhwS! XX_~&58^bbS;4*l+`njxgN@xNA(9E*l diff --git a/images/edit-find.png b/images/edit-find.png deleted file mode 100644 index aabed51957c3eb1e30d705db06fc9e7474b8c789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 693 zcmV;m0!safP)*DgvnE)_*LLYE4HE(&3ilH#nSMSNAFZbVyg)lwe_mW0VYE;@~EHPb=s zS=~AJpKrc9P3N>Aoxq%9oQv&p)oS&diVgvwbj9QGZh*F6peG1>rBdljNRDxL{1LDK zK#XIdU={5ShOfh*E9w=1QPUM}Z21g;uqPro381R8;b6lX0F;x(VliyX$mMcLU|(?g zJQQrPqk(a1a&mGw46LGe0*S939UV_0j4K=tML}$G0I2a5JE4ob7^7npjOao7vvF5{Ssrr7cU~4PFpswqF zT6^PqWdQp6`Vt!nZGj!f8QMr_3oIgY8wrKr@aE^|?A?z b`kVR*=auYw$TmWb00000NkvXXu0mjf1F$Dd diff --git a/images/email.png b/images/email.png deleted file mode 100644 index 11e34d72ef076f2bb2d248e635c27a54254dc2be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+3?vf;>QaGJOMp*^E07*=@Ml%t2B0IfOM?7@ z85o(Exwv@*1Vu!}lvPYEY<&H~Bco$07OYykY0tsK$4{I)fAR9O7jNJH{_FHl<}FZL znWu|mh=gSC!Rtaz1`MtTxivJ*)3d~%!lxx)J(^2<8@z>{nS3&^WL^}w-emW*VBPyBF0U=7 sT@u#4xKhjNQNNDd>NAfv%&hv!pwFt3dNFJ2Qc&o5y85}Sb4q9e0L$iGIRF3v diff --git a/images/email@2x.png b/images/email@2x.png deleted file mode 100644 index ed0f958fba1fa6193c6e1eeecc851656f385ff1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 629 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k3?#4J%UA`Z9tQY?xB}_n4UD&m?+5zOt0c%T zn1PXrg^itqiWo=) z=dE75eb>GNhmT&qdj00J*Kgl{{QUL%@4xHY-$(#W{psoA7$PBg_F$$pQzAp##p-2e zg*JH$rfTvksd~4G`JVs(-!aN$YU;h8Pm1{J-+qX6{M9RaW7Idnlg%Y4!ah|mlz*lyT>T7|8jp;p~7p$skskMTsFUFm=N#b zQ51aO(&Bv;QEb;@JTqRfY&d7~{0;L zSp_)?cP|~dEtg&FdETxMQaG}M}SX=E0CrNSlzN_HqapPk|4ie z21X`UHcl>X9zK3K9rw(+>#yE=^z!rfH1V3lKqY>jE{-7*QaoykYh(MPeaMZnnDJfLCb%+3($OuZwA djvr@FW@f0E$e!XU<@z1u3r|-+mvv4FO#s8MFKhq+ diff --git a/images/facebook@2x.png b/images/facebook@2x.png deleted file mode 100644 index f3eb5de312f9da43efb77cc2f3dded82a0d08b99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k3?#4J%UA`Z@&kNAT!Az_LGG))Zb17~N`m}? z85o(E**JIvgheE!6jgL|4XteKlG7SGCe50=e*4Z-XU={9{r707LkCcOwWo_?h{fr* z7f%W`8SpebR7lY5?ueS`EB5){`HlR#NcW;%9>Gc2d-F;hm`**}m7cJ2O2=)U5FysEl8GEPj>@lGS+)e|*=oLUbTsGMRp;2V pU-(sd^uzu=Py7G47H(YLA0Aw;Tk>dM2^%PwJYD@<);T3K0RW=OT!{bx diff --git a/images/go-next.png b/images/go-next.png deleted file mode 100644 index b927522a653ab0c05e6c3d4865d33a72581ad9f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4vz~eq$T~Dro)lQ_na#%8Ll(BcFdE0BH>Z;gyW8au2;JYzv`r?+r1PtPG)7q zxJ)!GJ2d6}^UKniav$U;xfuWMF!WQI`{iaTZ-L=4-G+PyZqMzCEz9>c@G^v7xA)v< z@jUO7O?@u&j`&yUlk|eUij8;cpDwy04s>TjuxEJWw7sFBrMjK5x+a;PpJWf*sQ!QF zX;Uq)YOKesnKutBHn{OtoiU60A*}GS`$^QLx!>!&f_DEk%Pzau?(Pz02U~>gTe~DWM4fl?R1U diff --git a/images/go-next@2x.png b/images/go-next@2x.png deleted file mode 100644 index 1e617cd7a662f3e620a013b905fe081b6f160fb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 583 zcmV-N0=WH&P)m6%|I1qL>L43Q8>Kp2Y{5K{D>vD5|ZZx<|Cl@%v;oDA^U?#_4b<9EL2 z6Zy|M%Vf9mo zkdu7e@At=p!Qkhj^IHr2ed`H;UGM$UKclt+KSbofIrj)a-eiHM0>4Qf<$1mz$MLU* zYcv*E>6L65fle-^i&Vl>00(KBURiZ;k@^fgB{}xqS5~o3FtfL2_R-94tS#t2%^5ob V)Y*wQ{2%}T002ovPDHLkV1hY^`nv!C diff --git a/images/go-next_active.png b/images/go-next_active.png deleted file mode 100644 index 95213415d66718ac8d45a7c0d80b8487140ac504..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 253 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR45;2Ap}i^ZK>3037KN6eH?+_|{ewTr{ZJZ3{TR|Tu>qbb?1LltWu q-&byN)`?1fF`>(E=bM>c6-?i@PJL)q?GOrd1%s!npUXO@geCy|h*iP> diff --git a/images/go-next_active@2x.png b/images/go-next_active@2x.png deleted file mode 100644 index 3e3e7dc661019b3e145baeda5ea0a45a8088053b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8wRq zq}PKmW1Q4P8K9tKiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0w|JY5_^ zB3j>GGt_Gd6lr_-dGUfkt;mL`MHeSTv;}3ewy54{niRBFmX)`c)k7n6vA`#dCu%+d z!p5IP_dGr8dv5mZ>V3tl{z)>}#w6MX*4%RM+c-m{!LH13wot~MByG(L7Ke+180Hmp z{G8(OnD6!sFHRG^4YNcV#AOl{RWDQ=;7bc)>Xpt>W0hJS;l=e~+Opqvo|p3i7Oa}r zUbXGvjyJpVp8h?U@$0*|kJp2ZtN2!By}ccs{EK;`zeM)o2m#l{7G^!~T8|Z$D0Z$* z*|>B|zwMdctv|$>)azy?DNnIDZfd3$_2c{}u5*(-1g)l+m;rsv;OXk;vd$@?2>?_M BiY)*D diff --git a/images/go-previous.png b/images/go-previous.png deleted file mode 100644 index c62d60f4ab147788217d256ea43a1cfcb6d5b20b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 372 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4n}50guk4ty`}f@4=7Ne1H$yhhy!vh*OJAdmfWTsga(iD*TL$q1k5ZhO2?oaJnbYuDyw(u3JUR7?5n#=HA z@S~XEdu!hd;r1Srt~tI^lVB||+8lMs>Xd8X8I`4m_uJa+F3<|`HcaD5H3sF1#owsx`hD-gTd)er&BCL zJu~Z*yj9n@7C;E0o9Fo!$)^BT7B#O7ASp7lO-VZ>&q@NxCj83*y!UG%gg(jDBG#1o zu>iy2@LU|nuSsqKXwI<=K+*{_dqVORfHQ3w9|;gb*duwPZR0}GvWzMNBuR2p(lY?# zzXCvV>YRHsoldVxdJjNt0+jA<@BJFdK7iG-+$u_vBtHBeDBT0jxlc)w^dvn7@Eri8 z<`$*WCM20V=iZULBI)hnKUh??4jAu!4?wNUXzmgGNzS?bG)->2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4YJYb3Sz%w7alei`b?kk@s(1S@`bIul^g3 z$)#WMK5QLRaF~1bod;~k1^!$(&voHJ1N*!-A8x0evhR*biYIgnuj@>RYB1Y#<9rKy z(;m-fpF+9qC6iP%{~z-R+2+x?ZQA-G6;1nPBKeuT{mz*iHeLogh{4m<&t;ucLK6Vp C7F_%Q diff --git a/images/go-previous_active@2x.png b/images/go-previous_active@2x.png deleted file mode 100644 index faf42a598d775a8cd0b455d41d119c2d6a19b622..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8wRq zq}PKmW1Q4P8K9tKiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$Qb0xjJzX3_ zB3j>Gv)6NV6glwl;{<`ZL5``6i!N!bm>LzGz)7^n9-oQ*2av7pZx&ay6Cpw%o}=)_muaPGbgk#+kE8!($e3VTW&LMXezGZ zk@zp$P$~1M@z>ue|Np6B Y(&}-lP`-E05f~Hzopr0NlHgR{#J2 diff --git a/images/go-top.png b/images/go-top.png deleted file mode 100644 index b2ea402789e80b3090c3b3ce9a284126893e68f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 406 zcmV;H0crk;P)zvnJSyb%H-&Da>vl5VW?^%EH!Pun|lD zK&&jUze*JB?nsdh3jTptWSi@3<#uechsH}tcdwdx@6Fp8SYX?rP%!hQQmPm;v>+^&#_S_##qr9^OS}F##jk}=jRsENHh)R0>)}UDb-_)UBo=U zH2i+WaaKz8M!B&rhzlV^CZEsW0a(oCa(k^->oZB5q`!a)g~BC(27ujuzkii9mX+jC zrBXQuaO|T5=R7N=yiZ*~xm-RZqO$*f^6 z0bo^Y-K^DWD>DfYLS!tFxxfg2MM zm0Z{Lf^1><^L=10V5|tEpp@zkx7$G5?2_jAZ}QaH!nE;;e&!G=v+wr=0K z>)`Pd=dNDAb?@ctw;#WL|M|N!?^+AcxMiL$jv*3~tOq-lm>3yY58Pbq(QaJiqWIl+ zXXx$U|LcvKuRVUViI3sdXO+uR3}OF^LSmk;WO<;rT!eoo(*t$YSMixl73bu%r5~3t z^*-o-pBQnY!i07iNrYo|BHQ@Y3UQ_@8`9TIK4`@^{$`Z`4AUf1I^lsv+~6 z`s-7-dl+k$v8JXm@O{30%rTC^;&jbsiDg;L4=g;>Vi_(}uYY%qfuW&ZSYF(A#S=Ad PP;7d-`njxgN@xNAK!SV5 diff --git a/images/link@2x.png b/images/link@2x.png deleted file mode 100644 index bb31be1d6a13c928c1a8973b2ec3ced533ddb18d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 903 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k3?#4J%UH$0z~~a-6XFWwjsglpfOk)YJkW^j zk|4ie21X`k7FITP4o)s^9zK2nAz=|QaS2H&8Cf|+C1o`&ZC!ms3o8dl7k5uDU%#M` zu<*#}nE1q`iVYEwvO(;{)v;O&zQSt@v`;X_Z&ZU?(+4Ux9;3~^yJyg z*Kgl{{QUL%&)=0Qe`G!8idI_V=>E6q5Eo0>e8Foo zLL$-*KmPG}%at{|pH6tyo@TFL8z1tF@W3z*NX7;U~B$x1S znMf#e*ZGALC+%!ssj-T)C-d9C9k;v=G%niKDE-Pa!A~(&r_jo8PWlFZ%DcS9XI?tbLHw3%$mC nPu_`ZnXJA0+KTTi+4YYx_R1RH6<06Z10^O;S3j3^P62?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR41D>5?3GEk=mDgTdtO^P(vyFz&UY&`JrD; zPH+0jc4XTjgEybP0l+XkKAd*k) diff --git a/images/media-playback-pause@2x.png b/images/media-playback-pause@2x.png deleted file mode 100644 index c78ae2d99e443c96ef083d85aa1eafd085998923..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zXt;ndqr%oLH9$eh64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<_&MLb;` zLn`LHy=Cad94ONIuvoQqNk^-7A-`FkQ@PRiwMt7a``*~W{{4~2L8XG+Wdbr6zB9>P z_^++L^WpO)vYbF`7(V4nJ zXt;ndqr%oLH9$eh64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<_&t36#D zLn`LHy|vqy#ZaX6p|SF=1xzX&4Qv9253eMzvw5nc4 zpPvnQjz>RVHsw-#M@~{t`qTN>+`<~{&&}rBXm;S6ZLrtNU>7-;CyOrZy*ic4fw@7N zK|joC3ZFxo(-kgt&sOy$~;5TZzcgdW!uPVAG3XCfTPgg&ebxsLQ0CZ!$1poj5 diff --git a/images/media-playback-stop.png b/images/media-playback-stop.png deleted file mode 100644 index 6c4e66d762879c578892dabda4f3775ff350736d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8wRq zM3g|7v7gud1W-`2#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWIYRPZ!6K zh}O5)Hu5$Y@VH#GK9bhcFr(2>ssA94hPIFzhbGUWpZmAU>^Bk;{5xlUo8U(8Ta(Ps z1)aJmeXjZG)~(x8*4^HxQpy9U My85}Sb4q9e0B$-+xBvhE diff --git a/images/media-playback-stop@2x.png b/images/media-playback-stop@2x.png deleted file mode 100644 index b3a9971f62b6bac04b5e5a69e19eaff44457f513..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zXt;ndqr%oLH9$eh64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1T6d%8G= zRLpsM>mV;MI2g$0}(Cpdz3o=&_T?(Q2Q5Z1MGDZ?m3GJI7Feknsh>EanT0ChQBC zGx!|XFUU0TEwE&ebtq=AYIw#_!f+O!f@_iwwy#;V{?4Yq;*y>~%P;l$aa|L4Q(?+^ Q0dzKlr>mdKI;Vst0P_H8KL7v# diff --git a/images/media-skip-forward@2x.png b/images/media-skip-forward@2x.png deleted file mode 100644 index 5300dc804209ced336d6368467cd5398f93c38cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1057 zcmV++1m63JP)hG{k+=^10p&aSSm zV?#qje-vdv(xX6sM@PqWxm>%gnPAxYO<>olX3^<|o-+qfCd-PYPSf%9=3KV5)_-j&a-KpFT>(hD;)GeH4b{~|Mh zCiGcrPe}T#QmH&pm?PK@9G#n+J142XKph}o0INKirs=Gt_a$w2?&u~D0;eRMDl+hP z0$82*EbzS~O-)U0b?B*){xnU`Njfqz%fZ_9s`nW zj-Ownwopz=TFRr-jCMPqk$Zt7z&DZ}cT_WrJAeqdB1#=kt*4OF0IdC|n|CKkGB4?{q`O>dG_VUeBWb2ut?q26fYShsxuqjJ zfxW;&9LEPGZE>kpFAMBRlH{VK!;_PfcjlSw`WC3~f>pLzYbSvVaUAb)tyjkuVDHk> z(n1`^FG}iYWs>s%8oHpK`>nMzl1@u{*p*&J;Jwz`*MSR?cK_FWSC7HGq`)+_SKliI z>HwS!f?zNV!}+X1=N-@@CE$7Bi`$^}RFWiTCnhF(vj#URfd35}6+jBS13YmXv`z*= zuxogDcrk0RrIpe#p9A|O%>qbTS21gCvxWB}a6r=WwdOkyU=2f;j4|aXiavCym!Y+2 zjHy~{-)Ln6$9Vu5hOQc8Ubfc07Ddqw*Lro_0N(EI?jFsvh37PYhJXJr;4L7EqUd** zI*qcxM?nxA48!n;ypx>fM@L3ResNj2-2jpm zL!oO!8GI51!GUJ)^6RCY08W=mrG2BLqvxHwxs`cfzoZjIisT7k&KR>lilR>ob5rAI zAnfbwn;sY#ND9`<4xm&jT}{)}(r`2uMUnHPF<_AmJQ~~dhoc^k$K&yMJRXn7u4^P)L@S)Tn3H-`+#*|1z1Qay{YQUO5LN*so!nZqI!PN@KJR+6CAanUeD#GB785f1Y8Jf zpMiN`75D?31}1@X!LtdBr<7jicK^JzyJrQo>`Uq`^=I&YRS!0WpHhE^h9|kP)vIbR z^vpJyr-Jc=x-$nT`d);-k7}wjE(PPgHsM9zSm{{LRQX1LPN^BLVchP@FmnI2LPM^c7(cH2UrXB!~lS6;kgIAt8}S1!i(yu7jwz#QMDWTUNxC_ z7oXX-x;ET1>JjyI=?iL}T%9a6Y^V>^PVgME{}+EMd3);J+|;O#t=2bnUEQi#-_}$c z$Zd5q6V#+TZ6IgW1^>ODqTZ`F)kih!_hVl5{m};<1}*`|fnD_fmz9*#+u(T!RGt5H zN@*rLO&h7RHIvK@1yDfhj_b-WAv2}$r$Y-5km>&x9w6hf{tJYb%4XkQgUSE^002ov JPDHLkV1fB82({#g10ZT$x+RcN(4K`DZ}EOcRo&@N)7KekXR4G|9Q>{q8-JAukLJd~=`geP`yJGxr;(0s@|s)FY`&(n?8-vd3ul_(jqWlCC@FCW`ck zE?^2COgZb=JZHc1ZeGQj+8PF-f;14QJ0=CACTF$k~5fQm=DvAm{7D z0=h|ej*uP$UjrL~CJ(0#*aQ5Qa z@KizeYT(Zr{Nh_Iu$b_U^J!uDe?49Vh66nYDSFre2Lj;BK%NDir_U?ERG`<^B2Wk1 z_5de?t98D7FHr@(o?J3Z}x1bS>!33Zb1{J_psIUf(2yMvOvw}r>YW3 zzk6yu7LZXPwrS@}Bn^6M?Le0WW>Dyd+f5S*=nb62r_1@Q+OHjW=;=2NEU^L?0{1+? zM?h!f0Otd5*tMe2Qvv98pgEHH*MNTly>=A^cMu*{eM8B?6tIf$J;yNMJCkxXI2V9J zI?pj3GBenlfYBQKHw&B3nzZIPm0h1lt+`K>+4J7b^A+)$bxi}9%$B2M7Y?^yyq zHin2kF)S_Mqaq=qOr2xMIk!*JXNF6b(y0HQLjn%xc=yi`Y2;rQ0Uyj;^A!Q_&s*~q r0q@N)e|BD(uL#&XZ_QT}+HL*?RoUH>^#@iw00000NkvXXu0mjfM4!b? diff --git a/images/safesearch.png b/images/safesearch.png deleted file mode 100644 index eef8f60a40031ce0a91bd0a869054c8a4af168f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 338 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}MSxF;E0As&2=FM@?FGGT(+2Z{*$=3+b0;JQ zxh!(6aN#;rbw@(As?2VwfYgnKu6+lO9zSvF_2;kO zfBybk6tGJXsQ;j+i(`nz>9>=^g_;}$T-dKMG+A~Fif&#~{(tk&g{-R&vFiLT{(bda zNqBFE-tqgt`=(^G9oTYZ{pXOiJP~^s-rwHzyRGAgeuA=++x=^Q6j`3#=1*fdEEB=R z9(>zTwjq@-LbD(wlB<*@$b0rh?}o`j<>EQAE9W#W`MoXGno)1@@^ub(ex113b5V46 zyr=ZUXYOT=zF{+dE)0!$qQJ~{)VJJd!Dsd-&l}YWrVA8wGnO__UGs36SKj7VzC4D< eKCk-sgCQc??)ejuhJK)D7(8A5T-G@yGywn~GSlt= diff --git a/images/search-duration.png b/images/search-duration.png deleted file mode 100644 index fb816df035f5ab6c7fbf76361b1888c15b9bc1e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ zFqwle<99ZfLqI{v64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<_&Sv*}F zLo9mNUN-b%4isU%@LBbRj??Xsmm)J=XB_g2aV=Zmn{%gKn&W13(>A4RlWu7l2v&;i zyO=T~K~O)YZD#%Z+Wdd@`(0W}rv$a_I8rDyZ&JD7$6bjX9Fjo>2ZbiasNVjbnsur_ z|E!IFV6$dc$VsdHrYmP|SIj-2B0kSB>+hgQPN zWglAFZMZLN9 zbHRX44eDkYSJ>!-W(wi2DmSq70jlt8^&t;ucLK6V+#hS(d diff --git a/images/search-duration@2x.png b/images/search-duration@2x.png deleted file mode 100644 index daef48d7c1aaecc7a1162e6d38641eb40e17cfec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 zcmV+k1NQuhP)WvG9V@NrIKP5^XfL8jaXl zczFOjRHD%*(5UZVVj%@y;w9P|eE=0=A+a$55p44WIEE&oNsecU$bjL(F5fWShqm4E_)C7K~%A0&&`wa37nh zk`LlbiR3>TZK*@-2lSUEKZKcrBrBN273@VV`llwWiK}?gh)w^W#!#7_7m>fg;j%rz z5xgtNGo{T)Y{n<~Uf)N(0(piSZeTgd`B95CFp$qen*Gc42LyO#T>T6g`~2{ z63Hf__?;ySfR_o^xw@e3F`iaPSQTmR6LsNWi80Z|0@B>Wbqqu={s_|AEllHY^gK`( z7GZ{;!b0g5RvH```g)Rpc3ZC%`h<~-u_WxQBIG!JX+Gh~$Zos~irO+R7yP%OQEV1B zQ^(gRpclJK6wt}-)N!)l{T{@l{ID z7|1Gj#93jV=eAmkDq^!$(DD)C`ZgecJ^v(J&nL7#j+^ig{NzFJvfb4-00000NkvXX Hu0mjfp;BJm diff --git a/images/search-quality.png b/images/search-quality.png deleted file mode 100644 index ae97229773ea9f48087acda2161ce4c55936afcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ zFqwle<99ZfLqI{v64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1U9c)B=- zSo9vfZ0ptRDAM{c{g{d7rRW<=CpEM*ycX2fj%`$E(!Sv4J^dk9P=u;Nm}ZN}%E?*+ zlHBWELiVM2JZ#Kt`#kUWVV^^Lx8`|i2J?Dp%0Bq?`>qJH&BA!EizOHM-z!A0=0}9F z=rn6;2AwNx4DXrkc0iD?uzPop*td(vezQeX+&Z`=p;hpisPdWS$ka0hT@S^s?l<8{ zn|AS_N(%HZ5jWk iKd-;4nJ z@c4l+V|{s#GEh*m#5JNMI6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWIZvo-U3d z7QJsL+xs0hkT^QOP{^?HkjPBMiZ2N#ll3krN34m}4(mJEq{VmeTJjT#vjLjIf<7Ac zUm^^O0+*^KJ)OMoVBN33du*Cj4pqHhy0uL9{@mNj5t>_>9oBv=o2bxs_}tVvEz><( zuKn7Xd;52xjDCCKj$_|bBEP9El{vMk=G5mutxsZ4c*wol^^b#nLW^OV@cwsmqFc{c zZtpTqKBJZ*{^Ri1I^CIg}^PKMP;B?8@Bc6pu zjSiEGEhoF3zUX`_`d9DU+CTL^;tXzWL0!k{7Uya#jT2z$FO<12tHZ}2&2U4)^-rI) zE6>qhg=3GR1XmU6cGN!JthDUdb0?iiDm&M{{uN}CGwa!!_rH^GHa)L=zwUKiS9+kx z+EbhMsJG}?K7YQ+w&enQ@|#Fc(YA-oJbMbKrJ6fe-?n~v@7v7!XC9tQ3Uv-Jflz{? zwMt7&!8!lba*l=K$8KcLdmbJd8rxkSdwuSfT z`uveTE@3a~`tU!ufv9104Oh~)A9ftMg`zFre(1Gq+`*G*qo>3t_Fr;MZir!&?ZnQf_qo4v+x1x zL-+vQ6{M>?L*JoWAH$vCCbk8|rcgtY=^~SI4c+L#f%Bhx=KS;D`(K%<1x|7JfPRS6 zb$v@!MC@S;1N0+e?CGV=;n>1W8o_d>2=T|cW{+gOx%jgL5fAmF&xuIqV$ z?Z75JHKYENRw5fo@@hx5bYN-O@&l?*g6GDJ(K`3=wgI z%gh`{#4jk`!A;{^;QknQBI2m{0X{^;72cHj8TN3{_;%WWIacRB!6km+XK7>0ou4$m zc^fdz%=3ENJABE^Z#A~eJdcPT`i*be25d&ee(_zrtmYSG-Y>q}_%_>s4u-{F;eCxi zDD4TJ*V?e@xhLc1(wv}Ie28K39!?tH7@ZBvKWy&bYmUV#ZDhH_x0vBi@oC|aIiC-*DO!RT1;(FnAw6Hl-3Ef+Nwy2Pi0J~GXL-=P z21@8JNa_jRrRN!p7iAtWo^0VQ9v9f`wKz?nT<2{n*oRTr{t26}Nb$nukM%;FZCV<2(#=}LUv(vz=uxddkMFv!+p1kYthX)Sgpjy^p&f=Bor zbc5IwWYv9OA}VC7U;)Dgx+|$g3@4w?0^Nj~ePGRNpp`%@!Yw!xWE&EXU`NpHP8@yq zCq9K=1u?3~Ps6h8`ZbSl!J$pItSa~uba^#@5AqJ#Jzz#wa4~^)*@bmF7q(VGD}h-{ zi=~-ZNHMFcfb6GZ$m*cef>jip)lz&YGf^S}5POYYdKPdQ?a00000NkvXX Hu0mjfWp*JK diff --git a/images/show-updated.png b/images/show-updated.png deleted file mode 100644 index 9af84f2609fa2bc50c04971acdf01bd3f6458121..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 486 zcmV@P)U0+fA}d6GiBQ zg`Jtl_kR1$?lQoc{;?RuY?|h%Qfd~!G|3SWNdN%JeG#buSkqdsmP)0seFd;>+XS!# z;D*n-pS9N6QmORmQ~(nb6X%D9hF$=;*URf65s_C~>&0@pd>9yDYHBKabaeEV!%EFYO7YOW%l;=e!CZxm%Gt+U*XYc6k}s!7#<$(X1f{= zhnGFG+!K~%StMV>=bqzJpjxeBcXzj&)r!aCA1al~LF?p+4v<{(6CQ~~kj-Xc z+qNH0G-^I`Po8TXOioT-(OP$H{oLFfY}-aMnS@~&e!~F(B9bvpbF}q8(bj=_y*|^c z$oTj;QmItn@Z&(GZncivIzTeht3WQ7Lnssi0Ho7t%+JpU0uYgD0M9)E09@)-z;T@8 z`$QrUC>#Jtwq>|q!i#|`2G>Y80Qn05x*Na&1}u_4Is%Y%Fkp#DTZVEi<_8Txa;+l( z$#;VW5RvyC0d{tFz5`evR5;0eu~_`p5dZ*0n2f#)j!q*MMnDIId0UigErX0ul6^q5* zD5Wj{un_!EssXHoLZQ2xo0~s7nS2#BO*5sGdJ5pCA3w=F$;aE<+h2Nm{S<5}olajC wky#O$R!SvAB;HusZ(MnjtBue3O#g@e04IUL$)AW?VE_OC07*qoM6N<$f(G?U^8f$< diff --git a/images/sort.png b/images/sort.png deleted file mode 100644 index 95fcb55a86dc56114afbb48ecb51c85447f2c7d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 396 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4P0iBH+0D|EWfU8)>}Zx$Ba0YDwm-Zi&`qom*Z2OHnh;c_wCME| zp^g{Ju7s4{J3qI$|L>oKgHgL#E(i%SW~|+MT8QDnLiyBKyO+UfanqTM*N2u`r8#zV z6rB@Ji{oMFp7S|{=Rm6T>s@v#_p0^8>z4n`mG*Dl`Yp^w@ARxy=ZeqeE4EC(DBW>F z?!dM+j5oSY%zMEuHmBH{VKrmI(>b?mHy$|ieYt{SOYf>U#*CRiuU7AWxmDTW=b^U5 no|#7_Zb}$iTYb4-|CqhKv3QBx6uZm7kYn(4^>bP0l+XkKUId<~ diff --git a/images/sort@2x.png b/images/sort@2x.png deleted file mode 100644 index 74753259086e316c17da142633c402ba932c8d28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 602 zcmV-g0;T>@ea9sxN%F$c9H0$62OeY@ z0O~ohW0V#Jt#uc;ms4|JYkhN-CQBPI##~WKz2$`AR7$<^egA5q2HNem2lN4NiMq4a z+J3XyJXe^3$z<{jI0KAlPZsldsa!5UJ*|PvPi>5`E?q=&f6uCQ$A)mzYz=G;I9Ks1 z7-L=odrRaUy%GB&au*8t0@Z5uF>uJA8<2Mg=J7O5(kr4{ z@ipN3Lg>Bcd5>H*xqJO@Xti4B(=-i%OEc-qbUO7%qtRDae&KsyFc^GON_Bx>z@bv= oMppB+qP6a6t$Q19yyc(cH=4YN|Lj=e0RR9107*qoM6N<$f{Dod^Z)<= diff --git a/images/system-search.png b/images/system-search.png deleted file mode 100644 index 4cf77ded91797f0e9b499f08308ff9ac9a399153..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 548 zcmV+<0^9wGP)Px$-bqA3R5;6}lQC=CKorOSZmA$Ob`LT{N;SC1n>B=lpurx3AWI7-V0S3iS}pd{ zp&)QRfIfh?toI4@0W_3&F*IE~Mz|Mf?WJ&%Xl*Hm>!O6K-FERG9tV%(ckkZ&KVV0d z{*tQz*atuWl#AR?035&%0O>ygmSxrI^?F0s^#el49_M@+MNv8)kNJE)zb`=F0DRwi z*0QYHU@&;c7z?v3i-iy(%d(g;7P_wMXqt8o;0!@v~$;Woy{qvX?iDw z5OEye+O~ZmHS$RIK@C7n)3j&JW|J6(QF-uz5TZ034u{oh^%dv*w$tf+48xEE_*%rH z`%WeLA7R_}MI6V`dcD3MjYc2jF+ME-d2zmw`|_aKY_^(ACZATT)t7$1KLK!8*m`Rh m4gtKBc3@LmR^EA;cGho9U)VJLrvUZ<0000Z7qZKwBbE zgG3raEeI8{?|b~do)!dkaNN7^zx(e0@9s&n+067GSzS(+BuNJ5hHgPO#d#n01^SGF zuL9GIWV}Hn64?y|0wIUPv2C;2)`r93sNHVA*X#ASUavPn#sK<&oJXC61k`Xiyqiv^ zGp$zZdNdln0h`g%Po+|4PN(w(NypGbsBfOPLMC3Dm9yFGu`J7;N~LnCD9R}<{d_)u z7>!0Z7z;5&{o<)6jR1g~*LJyFO0`*K1|A5IZdUq!VoM%@&Kr zS}YdtMUZCb6X&@R$2Xg-6Kt|!>=Hlg_wOzd{I+ik>Rv7>Ui z90~@5>%#ik%%$Wnm)(S@>@vH(JOxzlaw7eHzoV*Z3bC97Rb{TW2k58*AP5pOyUm8p z_W-!5P$;DBcKgm?Fz6%_iHlmTHU!)RK||XCINZJi=$w7QvNDpNrqk)9R4Sc|SXsXT z0PcbhW*^Eqe-n_{;_! diff --git a/images/system-search_selected.png b/images/system-search_selected.png deleted file mode 100644 index 0db8a8f2fe878ff7f3dcb603f49a7b5ce5882718..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 457 zcmV;)0XF`LP)qFUQCD3K*j z$Zgk1b{wY?hT(Oi(Kupc4t$Sw(pC^_l4A$~0PMNIm7^%S?Q}XfaU6dzGAAy{DzHUz zG65c#!EN{Z{dfhb_ltyZg|6&cKl>zEu1juH?U56t)doo=_gYuolJ zqvv^B@33;Y%--ir+yV~d5HlN`<$>w9K=O1t{YsML6=!lj8jW}hv`5^;u@dkB>QaH!x&WULS0FuvK-`Jor9kIemIV0) zGcYnSv#_#r3JM8}h)T(*X=rKd8(TQ}`UM8X#>J;)=N1;1R8LyEZ1tusdybwsb?)AS zC$HbW|M>Oiqqz(GfQHn0x;TbNNKQQ%D%KRh;Ck`%n*)AF!rmO)^?Cjbo5&qMO7s7v zM*WqNP`Lh3yLQr+nUxN)v1@j&xvjw+ni*DE175gP6ymH6C=xbX&&q%(1-~V`1;*aC(cbCeXZ1q~t P0SbLjS3j3^P6|k3?#4J%UH$0z$g;n6XFWwjsmn0@DTdq1r&-d z3GxeOU}R!uWn<^yFA>wsCNDa(40b^6?J{jfhFi$jr_u zC@d~1t!rrR=B>?xGRePsFrKt zjYFrD{<+e zZgaf#`o;Gne)Tq;^5dANAed5r`Od!=>K6z{d<1%^yGUEposBw^>bP0l+XkK Dx9Gh3 diff --git a/images/unwatched.png b/images/unwatched.png deleted file mode 100644 index 63da99685f3d5411bada89800e96a9cf3a28aece..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1904 zcmai#dpy&N8^^!jZMHBpY;3NL=yiw`a!oObklQv+Q$s{!=8_@H<`g=j?2!0yfX!x`MKKu(VIiz=v)K64W%CXM06@yzNrwWG%Vp&$*;KN6kJG|wuMxwT zx?j{IGO_BI7*gnMxv0XsK0$~}RS`a3J6*Mv{Yql4zW?EUsGMKh-)?6@v?3HS*b8{I zT8fRV;3vz!zD|B<2)fB0U#J+r9PhOFWmCc)x1HvsB#gIDm)i$bOdHPKS!M(6_U`ns zYmms0m+KDGDnM8p$wzyt4&N{oSp2WW>dXRQcZ-je1WLBkFF0kg*I)7~xs~>5ZRy{2 zztBh{XLQrdPi+Pl_={b*ts53{HdP)4UJyazCGoSn$Yc~rkxYE!n-2fdxQ(S)R zZk-bOCVl}|i`Q^4LV7!OggeA-cm2pwM7@=xh~uEg(DI#3SqdohB@x01xkdv1JbYAv zhML26N6MnWdHB3~en+V0Wuk!p3c!H|jn=D9cOiS|voPx&j&8#I6+gwvMb&|7yh9jH ztQ=D{e@aZ?d(g=`paZh}c#OVIBw`+*{Cvc}(?%I#1~5U<;mcEGdU5tfz~I3s9(jc* z384oDO$SH9n5g3H-n?rTVlW}jU(Cywjv!y+@-<$RYnyJJrQ)!SMiGUWM+0>^y@Q&f zfU|~+ds%+ya~0EE2I=F_)q)HJMZ6@$zQu&J$O=#MN{IccZ;kkNnt=*5-M4dx>HO?8 z|K$r$VU4p5kV#jWDo}yV$75t?UC7tfs2^jX6yl$U@;fi~7oC3+e3TZqAJ}Iw@;SHU z1JyJWWIQ(F^w&fD5ugb`Qt^RqrK|+zKvcEyqbq+v=9u4CB9K6*4 zIrwUY7~jDgJK-zV;+{i$I-y^xn{3H*$OQkyVZA6-(Oq=)j)4_&yQ;)dyubV&zcd-;@G9wEEwZaF;^py z>^5=YnMl(Pm0UA(kbG0I{3LybeBgaRAsGzo+k;#q>{qC|#dqO}A)Bxs;3DOqqK4V1 zp3nrE5ABFId0GdwhvQLA6md0{j$|b&m1i8aU45RXWA6>DDht~6ShSa#M3&{A8s`{_ zzfAqHP?8m09BzC#ZwGRp|LH3hSemGTa`^`vvoG$ITc5}e%kJz~5NHpT`zk5~(}IJ? zdZ2MIDIID{#6N%;z$giKW=(0h zrq5^Q*^(bIDR$6cWVGr5Su|*rG>4mA#!aN(xXeI3pl@&fdR!Be65V@sM@_4#JzQwp zcOOaj<8U@-w#PXhdIDd^v!|2JbkADo&8gvH>HAJv!PH(*^7^FIRIL_wR5YyaQtwc3 z`plB!u!;jd`L+Iy7GZzt-IAPK;R0y9W@xtTyS&Un9_5)3QDTI)TBGN<#(|zqIq`lr zdkRl;eKgb|awdn9A1nDtliZnrQP9xT#(*GC=%{L|PrP4sk9V>7#fLZDLSwe6_buzj z-r6qg8e1YIN}ZD@p9e}(B!zqPSDrQxB;VH+THfxq+Vy@?=@TW BSMdM< diff --git a/images/video-display.png b/images/video-display.png deleted file mode 100644 index a32f4a6242cb2dc6e6043ba049168cd05adc2d1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPGa2=EDU1=0=JK!%bOz>Ak_OC&6-x{wB(!*wI~r4j g8hc6v7WXhR2u+ZA@ZrVHvp|gup00i_>zopr0NQ0Sf&c&j diff --git a/images/video-display@2x.png b/images/video-display@2x.png deleted file mode 100644 index adacbe8156671e620b9e6c3355e2bb2e50a1f44f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv-2k5uS0LR$G`OX_cQ#N1UrCT( zFaxWYxN>>rxvRJCJbUq8>+`P{Kp78D7sn8diOC;o+nKr~eg&}3Gv4FOe1k<|!hsbG z4+T~`^QRPtG$!pxT+udJ>wpBWap7u)2nVjj<1QN<|WK~z|U?Upfb(@+$J&$(u(q3lSoqDsjC3m|pDYKf58$pF8A z&PZm7{p3tmXVhO%2D2armeMFw2P&b8Aq>osy3;gW{4R@8!Pv$oE?x31mVE7ZKIz?e zZD7fgONM2I;c&R7s;fmcYwgq+bKHi&m6Gt=X_|hm5c~q*&V>o6>eZ5P3*ZdEi#Ebb z1u77dFT*fAXiGqvJWMiG)$J&X9$bomsyZZBthFDFG57z6fX%LHPQnGS--g8B1kOlq z0hs3{vPRvDPx12__qM`0N5h{#4>BIASa`zMPCln$aiQ54;?){aQJ)>@K$ z+v#+k_xt_4R3mCPVGsm|BC?^XGm=R@+!!POvK@_1K@bFouIt_c@LPD2Bo2Vx`FuWY z+L*Pspo}C*oXKRetEx{)0>CMNO=HZbI`2CR=Cj-FzRAJ`07*Iko_U_PKOT>##+Y@_ z^WOJ*y?3M0XqI1~(H7*1<9HRolV9)U=EN3&JpgypG`&%@#zF$V?|&DOO_HZ59#*db ztW>SBkN|LgI#4jI>SQb|@KXZ7LjW`UIjkyaMF0SdF`r4kLc_*xQ(zsyHqJj_wP^Z7 zQpJv{UMs0-)~FXM@K{7{78OXI3m&W1oB5C{YUfk5EDq+nX0l)41Wr|9)nM7Hbox=vARjZ-EkZ)|K_PLkw+6ygB1 z)?2{cG_|%Unw*>@$yri{A6_Cutrd&}WTaCnmF_$2HxaG`yakrl*4Dl_>Nhbi1ayEK zN~zBddKu$FKrABP9rQ_s3jr0a^=`A-oORI47*}(^LLA3CK1y);FW{vFmjYlQVY}68 ziQ~S<;#vR<=7hE{67moL9!Z#nfDW*qqOPK~exj5z+K)_|tO7c~qKH&UC*fMLSUjI` z44E($&>#8S0Tz`~hxK}0rgu;?_cvwg0^mdm-h>BB0^nE)UWEsX0^qlV zZQwfaDvF}&B;hwVH)p!t?zwWg{K57dVO0Q_hiNvOvvC}^fvdm^5m~YMAl1466OO_^ z1+I*Iy)PnFTN1nxa7_3i)+Bf#;Dqo)tVqaRz=Yw4jFr%6H0JvK{zIT3BKJ%r<+U3G z1`}i)$2&|G9>(kV@+pfKsXltl&_BR|3Gj1n&gEK!S+e#=)H6r2v4VMb2s4eY_OVXf)=4UEqT4 zUb!xX$1$?Dw;mq+yaOKKC_MNnAPYZ%k0u4){RBj$*6Z~ilVWUzLgCZ;`uZCS!wLif hfj}S-2n7B$egVLVAC0o#Z^r-t002ovPDHLkV1mPwb}|3} diff --git a/images/view-list.png b/images/view-list.png deleted file mode 100644 index 9727d29b6b54dab836cc450d047ebfca6d6105d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 866 zcmV-o1D*VdP)5^iny^0cY;dA zg_ht#E4VP?A{6Pu&Ahn@V-W0WbYWbHbRkm|QHm|Nstb2kY{&@3A|eeUr8XFCCX@TS zm^T<+rpfHOnGYWCaqhk6|IdB*To^KB$dEgXcABuTuuxfEUOoyOk~9WflXP`rV&aFX zsi}=YM4HWJsnu$|3zTcM+NpsI)a&(wola*KcnRnLcS{vdw}wG)k(TxI{{xy`pWi|5Q0-mhk@56 zJqhf!E#O?yz?S5#R_lWh!mGLXW^U#0-J659$MVnDY`+Msr<4rr%7uILI+9k4dRq+G zey}Gl=_c@x?X$ooAV@l5`xtN^a2GJr+YDfDpG`v2O-UCkmC9Q?8p!ELx-MxZ%d)e8 z3*&Fs>-B>h8yl~rl->v-?6Wj1Px{bl& znnS>|z@xxJ!1WNqk13_|F~*;IT8&0y*UZe!+O`H7jmF64NioJFlAg2u7;pgi6}Vg~ zmAVmrF167_*w;0BoL8{|d>2Ca zMAFYGCE0!icp-lT+*?>*6z2XO90}t4~ s0bCs)AOC)OdU}1397Bc-8FI(?4_7}khz$N=+5i9m07*qoM6N<$f|2@;3IG5A diff --git a/images/view-list@2x.png b/images/view-list@2x.png deleted file mode 100644 index 20ea4b16650eb5d78b8d7a3ec0500f4b43e62c04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1433 zcmV;K1!nq*P)+sP z85_slxp(f&;*Z`3=FgpT-gDk_-j8!$a5x+ehr{7;I2;a#!{KoJzmN;+6?ybaeEWldY?T!LF{Z8NTn&2W|zf2Ce`m0RI4IfS-V`frAYV4g1!vU3DmDurtgTRjZ`ugo*8H$Rk&Nj2R zMC7WX&kI$xwgFJpi-1*T_N<7sm>D9m{1YpC4 z4UL0?gBjo{GxMs*csb4n)Xe@6k^Lg_C2-Wt`pKPFO~7@)Jm5}p884TJ&;W!Uf~q=Y zU|`^L5t%L`7Iii;I6Dauxfob%W{XD>EZKHdWy6agGh0Na=MBd(c23aDBE_(*ov;;{ zPn3C{cQVXS)z@Ij-a82#1^UTNVv?C%DgCho!9{=Ox(62Sh)vCjB2Rr}%^i;AjxiA3T` z-}k2h6GS9oW`n@5z%f-F2+^-gBoZ?^J3GG(vlVIIsp@p#M^z0%4&dhw^ zOd^pu;rsp%VfwY8ySsbZ@bK^s;5N_m<|dQL-g5F)AHnH#`t~3Q9s(AStI|fREe3&J z5!ovud%_%xgQ_k8wvg*YVJw=Vydh6S9x$^G@||Fm!>dH*dth5#UES7o>(-r!<42J{ zO+YvBWd2jZ$e474i-8{C`YP}7LP6vMABxB}GkZr>|E$251`$~ebeh@35w6qP1t62j zOd1**+6XL*suP1j;Jy6sBUSxnB%yRVeS?`T4T4~mh_o6cZ2!+d1v0(m`xra2m&b?e=>0QX!3EZ$6`Xe!5|1G z)xubmRys;>BX)uS$APcH#;d9uELwtD;8PL##LSM6dk7sCk;%X{W;Vyn<^h+M=+%^b zt8KtWU`><*HEsY5kRS+t^E~f4Gux`Fr^+d)Y8`L~@L&)G%SGg}g3~qFR)s_G81R^? z9*C+KryE2>{sOwp?0HrFyFzCRFUt3s*(x);UqoUhqG%=kEF$a8Y;&dl#4&ux%vx3T zSPWeN@~^+Uf!+C6-DSYTz%3NL>=tj8kyeVC9Tt%n%xr5al^TlUOQkE|72xSOS&xFM zHi^i5Gn)(C2($nbfd=4ra?Q|pBJzcq?W-mU1wsSR)6;W#Hk*A@L|#j!Qg2ji(BW`6 n91e%W;cz${4u`|xxUl>SN{?2Paj{3(00000NkvXXu0mjf;OLWk diff --git a/images/view-more.png b/images/view-more.png deleted file mode 100644 index 6143f4bd4bfdae18d6dd96b2f76d4a574d80ab8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^S|H5A3?$9pUdaGb`~f~8u0Xm036zDL)WY3+67E^m(K7G2B$wf0& zlbwlu(UeuqQ^W+9UXfX{WyUQp&jJR{drYihGa0rnZkW;`>$`sO%TklQeao(2{9-jL g&i$Z<#2N;M_p9t95_4Dn2HL~m>FVdQ&MBb@0OmePRsaA1 diff --git a/images/view-refresh.png b/images/view-refresh.png deleted file mode 100644 index c4603d928040809712e9bbfd6955e18ec56615d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 528 zcmV+r0`L8aP)Jo`Ws$Q?_p-^ZI zF;=%fCF0xx4~~Ho;M6dT%SNMdYno6@K@R6`5yHqOO^AfY!tj1PIprF_5^%sl9 z8~Tos<5D;Z%#~y017crd61+^_2YeT+qHr>qe1!WA@at7@yFvgCMfeK&kd^?prT~-1 zJ0N)gJb~Qg;aEU8Bat2KIp=xyiQuy&Ghs?V1g3u`IbN2`o@aih83Bs?#eM*YQ@mYO S>cjm20000XP)bWA0+H=&_xl48rr0mpruJj3{j$> zK_sY#z!oJ7>h*osd*!Ju(Sh&Xd(J)Ix#!1yx@nr)f5bLS)^&Xjwne}R%*yi?)+gW* z7rsiHSjMah$ZqIiAPCF@_Dm+Tigf{40XC!$wmsZVA>vF5*SORQcLxEeXreaA3@iAKHqw!QaMZ{5-e*1@JNoPGGNWqfFMVs z(akO-m&={Nrgb`Yw!(c*G z2As2yMx${T3WeN$zn=}|1vc!b%H^_w^7eaP;wv~R12%^}FpI_F4V#$FX17=hT^Z2f za5xKv!XO9K-ZWR11FQo2dL}=Z@=QJ6ak=c5`2YxNa8Kf z1XzVVAeqg*23cg+4?s^Dgb#{y{!QLsyj5g63d!o*gKv)78y(7^{#Q1Jo$040QmP~it$Nkw8~l)|%+ zMiU|yh9d@|obT2%)V^9o+`I-_nV-(p$jS3Lr$;08$tyC%xz{D_&c(GXQjYgvx z_JbqSL;ycP%3qKcW%qi$Hv~^%^YZ!pErLHGe^HlySVfGiAYflBvZ*A&B#T6{2b{Bj z<2VD;G~+0J69(>wAV+JpTDo4ZANyY7D`I2?eV87AQb;8pF-xG z*MTQ@WiTC<5(vZc&m_mohV{XfpJ^olll%qWpAoUv*XNjH00000NkvXXu0mjfF7f9q diff --git a/images/window-close.png b/images/window-close.png deleted file mode 100644 index 7a23bfd5504b703968860af277ae1994fd926721..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320 zcmV-G0l)rPx#`bk7VR5;76ld)<7K@f&N%^8GnDA>hD((V;0w6gIbe2cWciZJhRzJ_lw+oljq z7YN8z3Tv}_42zoLoND=J{@H(K|AmgxyMdL!1Q-Kv5o>@y;1@VOYVZzB4Ka@fI08Qj zc*)2g!0>_MS6~EOl9IlWJp~E?5t-*qgB7v1wD}pZ0#+ij2_XbktrMoIbqFDd$R=Tt z)5fPsv;8 zRWcRLQ`HSdJ~yGxly6srYB?3-F!XXq|Y9$zgQdJu4rql{P#lyHz2Mn?Ld6Aj|JjrfjyH(a!?S{4cn6$_@ Z_6aAku->ovfD8Zt002ovPDHLkV1gRAhi(7> diff --git a/images/window-close_selected.png b/images/window-close_selected.png deleted file mode 100644 index 41585423f8bf251883e56f4a1fb454cce9924073..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 zcmV-c0jd6pP)10b=$Qg()ZfEn?I_ABjM+D9<=0}Sl#!c+-1CYEw%;>uKFD7BQvoP8HN zF}~|MH^t^dviE)sY=^bQT7~!>`wgqcfo;HK^R@zz_dsK;rA;#a1O5e^t75%CARiD| zXz<7W0$!=ej7gzvo-E`vP1(km8H_n`;5ZQgz@DXSCT3Z7%JZC<7=2ymt|QorWkhNsDR$K#BpLxM@2|hOcFjH;b;p6ALnWXeCLf)*|U~S zOe+~wL5@=a4v7fxqLge2#G0mgntLF}$sB|yIpTN!5+;#(j=~1xK%F_2lLx66;3;7m o{~Kir&_X*LlJS2gQ5gAo1Gq_wSzQ@oaR2}S07*qoM6N<$f=nWf#sB~S diff --git a/images/worldwide.png b/images/worldwide.png deleted file mode 100644 index d676740ad2d812d033646451e9e12ed09e8a8d34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 630 zcmV-+0*U>JP)jK~y-6rBlCaQ(+W6=lhZd3=VG*k=jn7Lox;4OUR%u{sjtd zmV%3j1;H-TvDLwDI=D$)v{3&7!Cym2UP?fSL&#L?FI=8NKx+H$ad|14#HHTxeD{3! z-gD2TB>Ye0rh#D?E_m-7lBxhevYV!9v(xGH$8-5-2c=S}5Cp**$*UxPn%TqbgBt)& zNP3ba$?9M**dKA+7`aJu&N;U%=|ddH8*vL>=iFQGef8kr;FNRj1<5T*X8=IbXOdUU>|#Ej z|CXj{*UZj!I-PzF0KE4NGke)=Hb0U45=GI;;c$4X*X#WPfJ&t@Q!Ey*?d>jtjEi_= zq$rB+lUxMw5x{9lZ=7?_0l>^IkvtFJD}Y%^n{gbk{AHvhNmeE80T_^6Cs}BoXeTnT>v0?N75mH&2cMp+BF{gn2r;_0di;>)&>71 Q!vFvP07*qoM6N<$f>t>Z)Bpeg diff --git a/images/worldwide@2x.png b/images/worldwide@2x.png deleted file mode 100644 index 2393b389579d60b8fb4437588b7d336679650680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1424 zcmV;B1#kL^P)AGB0M+ZmoHrXZA-((vUs$d@p0 z=D2wGk!kx1g}_SBX6`%Z{?Gq^&)m89JovAhTnWe+Gs$(`DPb5^1EkaGLs1kR@qPb~ zYbGF@&DQ4g`DVv)mZ<97lF3ANsp@A@6s^tWa=lkYKtn^rWMHMLJ_)1~p&p<|L_}3< zfx1L2RMj= z!?14W&Yiy{F{u)HR$6kkz&EWw1P~F7F{^>&s(Q})%2ait#gNbEeGy4+SOqH6>GTR< z`G^(BX0x?X6zv8ij^n#UF^7(wT zh@@5Z{T(}Y_<#?r1IT1D2SsGM<2cLWIF5lT5h(+(l{hXUDOG)2Rr`V5GbAb?B6^KThgp&~l>yv=_^XD&&p=JWruw z-N07h3{X7j$|+Q25pcVR*(Bev+c1?%y`0bIn}JGI9ROZY)o)$b zoujHB0mppbUp^FP#cu-;k)@8~?1|&}o;Z%57m?|}@i>lG4aE#=NNf++1e~+LdR5(< zPN%onDl~eYS2+~7V8MckKo$tYFx*mIUAR<+|?X zFbr=Jkv3qA>$)vNF<}@!L7{1BbsVRc6D`qIug$%+j7tIZb+q4D^&GqAbCLT7m*$S zRjmVR6S4EE`c53jt4fvqvIG<@lgZ4q&BRhh?v8pyWSy$6O@3-uML^Q*eLgk$*9setMaxSize(maxSize); } -QObject *CachedHttp::request(const HttpRequest &req) { +HttpReply *CachedHttp::request(const HttpRequest &req) { bool cacheable = req.operation == QNetworkAccessManager::GetOperation || (cachePostRequests && req.operation == QNetworkAccessManager::PostOperation); if (!cacheable) { diff --git a/src/http/src/cachedhttp.h b/lib/http/src/cachedhttp.h similarity index 69% rename from src/http/src/cachedhttp.h rename to lib/http/src/cachedhttp.h index 8758f24..e487a17 100644 --- a/src/http/src/cachedhttp.h +++ b/lib/http/src/cachedhttp.h @@ -11,7 +11,7 @@ public: void setMaxSeconds(uint seconds); void setMaxSize(uint maxSize); void setCachePostRequests(bool value) { cachePostRequests = value; } - QObject *request(const HttpRequest &req); + HttpReply *request(const HttpRequest &req); private: Http &http; @@ -33,19 +33,17 @@ private slots: private: const QByteArray bytes; - const HttpRequest &req; + const HttpRequest req; }; -class WrappedHttpReply : public QObject { +class WrappedHttpReply : public HttpReply { 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); + 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); @@ -53,7 +51,7 @@ private slots: private: LocalCache *cache; QByteArray key; - QObject *httpReply; + HttpReply *httpReply; }; #endif // CACHEDHTTP_H diff --git a/src/http/src/http.cpp b/lib/http/src/http.cpp similarity index 93% rename from src/http/src/http.cpp rename to lib/http/src/http.cpp index 2326e66..7f58d39 100644 --- a/src/http/src/http.cpp +++ b/lib/http/src/http.cpp @@ -17,8 +17,8 @@ QNetworkAccessManager *networkAccessManager() { return nam; } -static int defaultReadTimeout = 10000; -} +int defaultReadTimeout = 10000; +} // namespace Http::Http() : requestHeaders(getDefaultRequestHeaders()), readTimeout(defaultReadTimeout) {} @@ -39,8 +39,8 @@ void Http::setReadTimeout(int timeout) { } Http &Http::instance() { - static Http *i = new Http(); - return *i; + static Http i; + return i; } const QMap &Http::getDefaultRequestHeaders() { @@ -72,7 +72,7 @@ QNetworkReply *Http::networkReply(const HttpRequest &req) { QNetworkAccessManager *manager = networkAccessManager(); - QNetworkReply *networkReply = 0; + QNetworkReply *networkReply = nullptr; switch (req.operation) { case QNetworkAccessManager::GetOperation: networkReply = manager->get(request); @@ -93,14 +93,14 @@ QNetworkReply *Http::networkReply(const HttpRequest &req) { return networkReply; } -QObject *Http::request(const HttpRequest &req) { +HttpReply *Http::request(const HttpRequest &req) { return new NetworkHttpReply(req, *this); } -QObject *Http::request(const QUrl &url, - QNetworkAccessManager::Operation operation, - const QByteArray &body, - uint offset) { +HttpReply *Http::request(const QUrl &url, + QNetworkAccessManager::Operation operation, + const QByteArray &body, + uint offset) { HttpRequest req; req.url = url; req.operation = operation; @@ -109,15 +109,15 @@ QObject *Http::request(const QUrl &url, return request(req); } -QObject *Http::get(const QUrl &url) { +HttpReply *Http::get(const QUrl &url) { return request(url, QNetworkAccessManager::GetOperation); } -QObject *Http::head(const QUrl &url) { +HttpReply *Http::head(const QUrl &url) { return request(url, QNetworkAccessManager::HeadOperation); } -QObject *Http::post(const QUrl &url, const QMap ¶ms) { +HttpReply *Http::post(const QUrl &url, const QMap ¶ms) { QByteArray body; QMapIterator i(params); while (i.hasNext()) { @@ -133,7 +133,7 @@ QObject *Http::post(const QUrl &url, const QMap ¶ms) { return request(req); } -QObject *Http::post(const QUrl &url, const QByteArray &body, const QByteArray &contentType) { +HttpReply *Http::post(const QUrl &url, const QByteArray &body, const QByteArray &contentType) { HttpRequest req; req.url = url; req.operation = QNetworkAccessManager::PostOperation; diff --git a/src/http/src/http.h b/lib/http/src/http.h similarity index 86% rename from src/http/src/http.h rename to lib/http/src/http.h index 02c9695..23f3754 100644 --- a/src/http/src/http.h +++ b/lib/http/src/http.h @@ -13,6 +13,31 @@ public: QMap 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 headers() const { + return QList(); + } + 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(); @@ -29,46 +54,22 @@ public: int getReadTimeout() { return readTimeout; } QNetworkReply *networkReply(const HttpRequest &req); - virtual QObject *request(const HttpRequest &req); - QObject *request(const QUrl &url, + virtual HttpReply *request(const HttpRequest &req); + HttpReply * + 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 ¶ms); - QObject *post(const QUrl &url, const QByteArray &body, const QByteArray &contentType); + HttpReply *get(const QUrl &url); + HttpReply *head(const QUrl &url); + HttpReply *post(const QUrl &url, const QMap ¶ms); + HttpReply *post(const QUrl &url, const QByteArray &body, const QByteArray &contentType); private: QMap 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 headers() const { - return QList(); - } - 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 diff --git a/src/http/src/localcache.cpp b/lib/http/src/localcache.cpp similarity index 95% rename from src/http/src/localcache.cpp rename to lib/http/src/localcache.cpp index 1d38262..8bee99e 100644 --- a/src/http/src/localcache.cpp +++ b/lib/http/src/localcache.cpp @@ -43,9 +43,9 @@ QByteArray LocalCache::hash(const QByteArray &s) { bool LocalCache::isCached(const QString &path) { bool cached = (QFile::exists(path) && - (maxSeconds == 0 || - QDateTime::currentDateTime().toTime_t() - QFileInfo(path).created().toTime_t() < - maxSeconds)); + (maxSeconds == 0 || QDateTime::currentDateTimeUtc().toTime_t() - + QFileInfo(path).created().toTime_t() < + maxSeconds)); #ifndef QT_NO_DEBUG_OUTPUT if (!cached) misses++; #endif diff --git a/src/http/src/localcache.h b/lib/http/src/localcache.h similarity index 100% rename from src/http/src/localcache.h rename to lib/http/src/localcache.h diff --git a/src/http/src/throttledhttp.cpp b/lib/http/src/throttledhttp.cpp similarity index 84% rename from src/http/src/throttledhttp.cpp rename to lib/http/src/throttledhttp.cpp index 1438a3a..7cfb3fd 100644 --- a/src/http/src/throttledhttp.cpp +++ b/lib/http/src/throttledhttp.cpp @@ -1,17 +1,10 @@ #include "throttledhttp.h" -namespace { - -QElapsedTimer initElapsedTimer() { - QElapsedTimer timer; - timer.start(); - return timer; -} +ThrottledHttp::ThrottledHttp(Http &http) : http(http), milliseconds(1000) { + elapsedTimer.start(); } -ThrottledHttp::ThrottledHttp(Http &http) : http(http), elapsedTimer(initElapsedTimer()) {} - -QObject *ThrottledHttp::request(const HttpRequest &req) { +HttpReply *ThrottledHttp::request(const HttpRequest &req) { return new ThrottledHttpReply(http, req, milliseconds, elapsedTimer); } @@ -19,7 +12,7 @@ ThrottledHttpReply::ThrottledHttpReply(Http &http, const HttpRequest &req, int milliseconds, QElapsedTimer &elapsedTimer) - : http(http), req(req), milliseconds(milliseconds), elapsedTimer(elapsedTimer), timer(0) { + : http(http), req(req), milliseconds(milliseconds), elapsedTimer(elapsedTimer), timer(nullptr) { checkElapsed(); } diff --git a/src/http/src/throttledhttp.h b/lib/http/src/throttledhttp.h similarity index 95% rename from src/http/src/throttledhttp.h rename to lib/http/src/throttledhttp.h index 4173b37..bd78d28 100644 --- a/src/http/src/throttledhttp.h +++ b/lib/http/src/throttledhttp.h @@ -9,7 +9,7 @@ class ThrottledHttp : public Http { public: ThrottledHttp(Http &http = Http::instance()); void setMilliseconds(int milliseconds) { this->milliseconds = milliseconds; } - QObject *request(const HttpRequest &req); + HttpReply *request(const HttpRequest &req); private: Http &http; diff --git a/lib/idle/README.md b/lib/idle/README.md new file mode 100644 index 0000000..a10d818 --- /dev/null +++ b/lib/idle/README.md @@ -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/src/idle/idle.pri b/lib/idle/idle.pri similarity index 100% rename from src/idle/idle.pri rename to lib/idle/idle.pri diff --git a/src/idle/src/idle.h b/lib/idle/src/idle.h similarity index 99% rename from src/idle/src/idle.h rename to lib/idle/src/idle.h index 6a004af..4f77d0a 100644 --- a/src/idle/src/idle.h +++ b/lib/idle/src/idle.h @@ -4,7 +4,6 @@ #include class Idle { - public: static bool preventDisplaySleep(const QString &reason); static bool allowDisplaySleep(); @@ -13,7 +12,6 @@ public: 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/lib/idle/src/idle_linux.cpp similarity index 59% rename from src/idle/src/idle_linux.cpp rename to lib/idle/src/idle_linux.cpp index 80c9e8c..1992a66 100644 --- a/src/idle/src/idle_linux.cpp +++ b/lib/idle/src/idle_linux.cpp @@ -1,19 +1,22 @@ #include "idle.h" -#include +#include #include #include -#include +#include namespace { -const char *fdDisplayService = "org.freedesktop.ScreenSaver"; -const char *fdDisplayPath = "/org/freedesktop/ScreenSaver"; -const char *fdDisplayInterface = fdDisplayService; +const QString fdDisplayService = "org.freedesktop.ScreenSaver"; +const QString fdDisplayPath = "/org/freedesktop/ScreenSaver"; +const QString fdDisplayInterface = fdDisplayService; -const char *gnomeSystemService = "org.gnome.SessionManager"; -const char *gnomeSystemPath = "/org/gnome/SessionManager"; -const char *gnomeSystemInterface = gnomeSystemService; +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; @@ -21,7 +24,6 @@ QString errorMessage; bool handleReply(const QDBusReply &reply) { if (reply.isValid()) { cookie = reply.value(); - qDebug() << "Success!" << cookie; errorMessage.clear(); return true; } @@ -29,17 +31,18 @@ bool handleReply(const QDBusReply &reply) { return false; } -} +} // namespace bool Idle::preventDisplaySleep(const QString &reason) { QDBusInterface dbus(fdDisplayService, fdDisplayPath, fdDisplayInterface); - QDBusReply reply = dbus.call("Inhibit", QCoreApplication::applicationName(), reason); + QDBusReply reply = + dbus.call(inhibitMethod, QCoreApplication::applicationName(), reason); return handleReply(reply); } bool Idle::allowDisplaySleep() { QDBusInterface dbus(fdDisplayService, fdDisplayPath, fdDisplayInterface); - dbus.call("UnInhibit", cookie); + dbus.call(uninhibitMethod, cookie); return true; } @@ -49,17 +52,17 @@ QString Idle::displayErrorMessage() { bool Idle::preventSystemSleep(const QString &reason) { QDBusInterface dbus(gnomeSystemService, gnomeSystemPath, gnomeSystemInterface); - QDBusReply reply = dbus.call("Inhibit", QCoreApplication::applicationName(), reason); + QDBusReply reply = + dbus.call(inhibitMethod, QCoreApplication::applicationName(), reason); return handleReply(reply); } bool Idle::allowSystemSleep() { QDBusInterface dbus(gnomeSystemService, gnomeSystemPath, gnomeSystemInterface); - dbus.call("UnInhibit", cookie); + dbus.call(uninhibitMethod, cookie); return true; } QString Idle::systemErrorMessage() { return errorMessage; } - diff --git a/src/idle/src/idle_mac.cpp b/lib/idle/src/idle_mac.cpp similarity index 77% rename from src/idle/src/idle_mac.cpp rename to lib/idle/src/idle_mac.cpp index 315cdef..ff62bff 100644 --- a/src/idle/src/idle_mac.cpp +++ b/lib/idle/src/idle_mac.cpp @@ -10,11 +10,12 @@ IOReturn displayRes = 0; IOPMAssertionID systemAssertionID = 0; IOReturn systemRes = 0; -} +} // namespace bool Idle::preventDisplaySleep(const QString &reason) { displayRes = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, - kIOPMAssertionLevelOn, reason.toCFString(), &displayAssertionID); + kIOPMAssertionLevelOn, reason.toCFString(), + &displayAssertionID); return displayRes == kIOReturnSuccess; } @@ -30,7 +31,8 @@ QString Idle::displayErrorMessage() { bool Idle::preventSystemSleep(const QString &reason) { systemRes = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleSystemSleep, - kIOPMAssertionLevelOn, reason.toCFString(), &systemAssertionID); + kIOPMAssertionLevelOn, reason.toCFString(), + &systemAssertionID); return systemRes == kIOReturnSuccess; } diff --git a/src/idle/src/idle_win.cpp b/lib/idle/src/idle_win.cpp similarity index 100% rename from src/idle/src/idle_win.cpp rename to lib/idle/src/idle_win.cpp diff --git a/lib/media/.gitignore b/lib/media/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/lib/media/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/lib/media/README.md b/lib/media/README.md new file mode 100644 index 0000000..4aeec30 --- /dev/null +++ b/lib/media/README.md @@ -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 index 0000000..f6b64fa --- /dev/null +++ b/lib/media/media.pri @@ -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 index 0000000..a14327f --- /dev/null +++ b/lib/media/src/media.h @@ -0,0 +1,82 @@ +#ifndef MEDIA_H +#define MEDIA_H + +#include +#ifndef MEDIA_AUDIOONLY +#include +#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"); + } + 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 index 0000000..b077530 --- /dev/null +++ b/lib/media/src/mpv/mediampv.cpp @@ -0,0 +1,410 @@ +#include "mediampv.h" + +#include +#include + +#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(list->values[i].u.int64); + } else if (strcmp(key, "h") == 0) { + height = static_cast(list->values[i].u.int64); + } else if (strcmp(key, "stride") == 0) { + stride = static_cast(list->values[i].u.int64); + } else if (strcmp(key, "data") == 0) { + data = static_cast(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 index 0000000..1621148 --- /dev/null +++ b/lib/media/src/mpv/mediampv.h @@ -0,0 +1,64 @@ +#ifndef MEDIAMPV_H +#define MEDIAMPV_H + +#include + +#include "media.h" +#include + +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 index 0000000..335012d --- /dev/null +++ b/lib/media/src/mpv/mpvwidget.cpp @@ -0,0 +1,102 @@ +#include "mpvwidget.h" +#include + +#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) +#include +#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(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(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 index 0000000..c647311 --- /dev/null +++ b/lib/media/src/mpv/mpvwidget.h @@ -0,0 +1,36 @@ +#ifndef PLAYERWINDOW_H +#define PLAYERWINDOW_H + +#include +#include +#include +#include + +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 index 0000000..c5a095b --- /dev/null +++ b/lib/media/src/qtav/mediaqtav.cpp @@ -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 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 index 0000000..d343790 --- /dev/null +++ b/lib/media/src/qtav/mediaqtav.h @@ -0,0 +1,79 @@ +#ifndef MEDIAQTAV_H +#define MEDIAQTAV_H + +#include "media.h" + +#include +#ifndef MEDIA_AUDIOONLY +#include +#include +#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 queue; + bool aboutToFinishEmitted = false; + QString lastErrorString; + +#ifndef MEDIA_AUDIOONLY + QtAV::VideoRendererId rendererId; +#endif + bool audioOnly = false; +}; + +#endif // MEDIAQTAV_H diff --git a/locale/ar.ts b/locale/ar.ts index 7964978..fc2a950 100644 --- a/locale/ar.ts +++ b/locale/ar.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 ترجم %1 إلى لغتك الأم بإستعمال %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. صمم الايقونة %1. @@ -155,6 +163,10 @@ Show Updated أظهر التّحديثات + + You have no subscriptions. Use the star symbol to subscribe to channels. + لا يوجد لديك اشتراكات. استخدم رمز النجمة للإشتراك في القنوات. + All Videos جميع الفيديوهات @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. لا يوجد تحديثات لقوائم اشتراكاتك حاليًا - - You have no subscriptions. Use the star symbol to subscribe to channels. - لا يوجد لديك اشتراكات. استخدم رمز النجمة للإشتراك في القنوات. - - - - ClearButton - - Clear - مسح - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 مشاهدة + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - هذه ليست سوى النسخة التجريبية من %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - يمكن تحميل الفيديو في أقل من %1 دقيقة بحيث يمكنك اختبار وظيفة التحميل. - - - Continue - متابعة - - - Get the full version - احصل على النسخة الكاملة - %1 downloaded in %2 %1 حمل في %2 @@ -632,10 +636,6 @@ &Float on Top &اطفو علي القمة - - &Adjust Window Size - &ضبط حجم النافذة - &Stop After This Video &أوقف العرض التلقائي بعد الفيديو الحالي @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! أت&حبّ %1؟ قيّمه! @@ -796,6 +804,10 @@ Update تحديث + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. الرابط سيكون صالحا لمدة محدودة. - - This is just the demo version of %1. - هذه ليست سوى النسخة التجريبية من %1. - - - It allows you to test the application and see if it works for you. - انها تتيح لك تجربة البرنامج. - - - Get the full version - احصل على النسخة الكاملة - - - Continue - متابعة - Downloading %1 جاري تحميل %1 @@ -858,6 +854,10 @@ Subscribe to %1 اشترك في %1 + + Switched to %1 + + Unsubscribed from %1 تم إلغاء متابعتك ل %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 مشاهدة + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 1% من 2% (3%) — 4% @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - جاري البحث... - Show %1 More اظهر %1 المزيد @@ -1071,25 +1070,16 @@ مرحبا بك في <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - أدخل + to start watching videos. + لبدء مشاهدة أشرطة الفيديو a keyword كلمة مفتاح - a channel - قناة - - - to start watching videos. - لبدء مشاهدة أشرطة الفيديو - - - Watch - شاهد + Enter + أدخل Recent keywords @@ -1110,6 +1100,10 @@ &Back &عودة + + &Forward + + Forward to %1 تقدّم إلى %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - لا يمكن الحصول على دفق الفيديو %1 - - YTRegions @@ -1365,4 +1352,11 @@ العالم + + YTVideo + + Cannot get video stream for %1 + لا يمكن الحصول على دفق الفيديو %1 + + \ No newline at end of file diff --git a/locale/ast.ts b/locale/ast.ts index 2a18063..bf7f858 100644 --- a/locale/ast.ts +++ b/locale/ast.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traduz %1 a la to llingua nativa usando %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Iconu diseñáu por %1. @@ -155,6 +163,10 @@ Show Updated + + You have no subscriptions. Use the star symbol to subscribe to channels. + + All Videos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. - - You have no subscriptions. Use the star symbol to subscribe to channels. - - - - - ClearButton - - Clear - Llimpiar - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 reproducciones + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Esto ye namái la versión demo de %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Namái pues descargar vídeos de duración menor que %1 minutos pa que puedas probar la función de descarga. - - - Continue - Siguir - - - Get the full version - Consigui la versión completa - %1 downloaded in %2 %1 descargáu en %2 @@ -632,10 +636,6 @@ &Float on Top &Flotar na parte superior - - &Adjust Window Size - - &Stop After This Video &Detener tres d'esti videu @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! ¿&Préstate %1? ¡Puntúalo! @@ -796,6 +804,10 @@ Update Anovar + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Esto ye namái la versión de prueba de %1. - - This is just the demo version of %1. - Esto ye namái la versión demo de %1. - - - It allows you to test the application and see if it works for you. - Déxate probar l'aplicación y ver si te funciona. - - - Get the full version - Consigui la versión completa - - - Continue - Siguir - Downloading %1 Descargando %1 @@ -858,6 +854,10 @@ Subscribe to %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 reproducciones + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Guetando... - Show %1 More Amosar %1 más @@ -1071,25 +1070,16 @@ Bienveníu a <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Escribi + to start watching videos. + pa entamar a ver vídeos. a keyword una pallabra clave - a channel - una canal - - - to start watching videos. - pa entamar a ver vídeos. - - - Watch - ver + Enter + Escribi Recent keywords @@ -1110,6 +1100,10 @@ &Back &Atrás + + &Forward + + Forward to %1 Dir a %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Nun pue obtenese'l fluxu de videu pa %1 - - YTRegions @@ -1365,4 +1352,11 @@ Tol mundu + + YTVideo + + Cannot get video stream for %1 + Nun pue obtenese'l fluxu de videu pa %1 + + \ No newline at end of file diff --git a/locale/be.ts b/locale/be.ts index 64ed283..a887a34 100644 --- a/locale/be.ts +++ b/locale/be.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Перакладайце %1 на родную мову праз %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Дызайн іконкі выканаў %1. @@ -155,6 +163,10 @@ Show Updated Паказаць абноўленыя + + You have no subscriptions. Use the star symbol to subscribe to channels. + Няма жаднай падпіскі. Ужывайце зорачку, каб падпісвацца на каналы. + All Videos Усе відэа @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Падпіскі пакуль не абнаўляліся. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Няма жаднай падпіскі. Ужывайце зорачку, каб падпісвацца на каналы. - - - - ClearButton - - Clear - Ачысціць - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 праглядаў + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Гэта дэма-версія %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Можна пампаваць толькі відэа, каротшыя за %1 хв., што дастаткова для тэсціравання. - - - Continue - Працягнуць - - - Get the full version - Атрымаць поўную версію - %1 downloaded in %2 %1 сцягнута ў %2 @@ -632,10 +636,6 @@ &Float on Top &Заставацца па-над усімі вокнамі - - &Adjust Window Size - - &Stop After This Video &Спыніцца пасля гэтага відэа @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Падабаецца %1? Ацані яго! @@ -796,6 +804,10 @@ Update Абнаўленне + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Спасылка будзе заставацца слушнай абмежаваны час. - - This is just the demo version of %1. - Гэта дэма-версія %1. - - - It allows you to test the application and see if it works for you. - Яна дазваляе пратэсціраваць праграму і праверыць на адпаведнасць вашым задачам. - - - Get the full version - Атрымаць поўную версію - - - Continue - Працягнуць - Downloading %1 Сцягваецца %1 @@ -858,6 +854,10 @@ Subscribe to %1 Падпісацца на %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 праглядаў + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 з %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Пошук... - Show %1 More Паказаць яшчэ %1 @@ -1071,25 +1070,16 @@ Ласкава запрашаем у <a href='%1'>%2</a> - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Задайце + to start watching videos. + , каб пачаць глядзець відэа. a keyword ключавое слова - a channel - канал - - - to start watching videos. - , каб пачаць глядзець відэа. - - - Watch - Глядзець + Enter + Задайце Recent keywords @@ -1110,6 +1100,10 @@ &Back &Назад + + &Forward + + Forward to %1 Наперад да %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Не ўдалося атрымаць відэапаток для %1 - - YTRegions @@ -1365,4 +1352,11 @@ Увесь свет + + YTVideo + + Cannot get video stream for %1 + Не ўдалося атрымаць відэа-паток для %1 + + \ No newline at end of file diff --git a/locale/bg_BG.ts b/locale/bg_BG.ts index 7fe65e0..1a9264f 100644 --- a/locale/bg_BG.ts +++ b/locale/bg_BG.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Преведи &1 на твоя роден език използвайки &2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Иконите са изработени от %1. @@ -155,6 +163,10 @@ Show Updated Покажи обновени + + You have no subscriptions. Use the star symbol to subscribe to channels. + Нямате абонаменти. Изполвай звезда за абониране към канали + All Videos Всички видео клипове @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Няма обновени абонаменти - - You have no subscriptions. Use the star symbol to subscribe to channels. - Нямате абонаменти. Изполвай звезда за абониране към канали - - - - ClearButton - - Clear - Изчисти - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 гледания + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Това е демо верция на %1 - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Може да изтегляте видео клипове по-къси от &1 минута, за да изпробвате функцията за изтегляне. - - - Continue - Продължи - - - Get the full version - Пълна версия - %1 downloaded in %2 %1 изтеглено за %2 @@ -632,10 +636,6 @@ &Float on Top &Залепи най отгоре - - &Adjust Window Size - - &Stop After This Video &Спри след този видео клип @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! @@ -796,6 +804,10 @@ Update Обнови + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Линка ще е валиден само за определено време. - - This is just the demo version of %1. - Това е демо верция на %1 - - - It allows you to test the application and see if it works for you. - Позволява ви да изпробвате програмата, за да проверите дали работи добре при вас. - - - Get the full version - Пълна версия - - - Continue - Продължи - Downloading %1 изтеглане %1 @@ -858,6 +854,10 @@ Subscribe to %1 Абонирай се за %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 гледания + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 от %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Търся... - Show %1 More Покажи %1 повече @@ -1071,25 +1070,16 @@ Добре дошли в <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Напишете + to start watching videos. + за гледане на видео клипове a keyword ключова дума - a channel - канал - - - to start watching videos. - за гледане на видео клипове - - - Watch - Гледай + Enter + Напишете Recent keywords @@ -1110,6 +1100,10 @@ &Back &Назад + + &Forward + + Forward to %1 Напред до %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - - - YTRegions @@ -1365,4 +1352,11 @@ На световно ниво + + YTVideo + + Cannot get video stream for %1 + + + \ No newline at end of file diff --git a/locale/ca.ts b/locale/ca.ts index 1f8fade..f3d56f3 100644 --- a/locale/ca.ts +++ b/locale/ca.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traduïu el %1 al vostre idioma natal utilitzant %2 + + Powered by %1 + Creat per %1 + + + Open-source software + Programari de codi obert + Icon designed by %1. Icona dissenyada per %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + Teniu %n vídeo(s) nou(s)Teniu %n vídeos nous @@ -155,6 +163,10 @@ Show Updated Mostra actualitzats + + You have no subscriptions. Use the star symbol to subscribe to channels. + No teniu subscripcions. Feu servir el símbol de l'estrella per subscríure-us als canals. + All Videos Tots els vídeos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. No hi han subscripcions actualitzades. - - You have no subscriptions. Use the star symbol to subscribe to channels. - No teniu subscripcions. Feu servir el símbol de l'estrella per subscríure-us als canals. - - - - ClearButton - - Clear - Neteja - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + fa %n horesfa %n hores %n day(s) ago - + fa %n diesfa %n dies - %n weeks(s) ago - + %n month(s) ago + fa %n mesosfa %n mesos + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visualitzacions - %n month(s) ago - + %n week(s) ago + fa %n setmanesfa %n setmanes @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Aquesta només és la versió de demostració del %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Només pot baixar vídeos de menys de %1 minuts per tal que en pugui provar aquesta funció. - - - Continue - Continua - - - Get the full version - Aconsegueix la versió completa - %1 downloaded in %2 %1 descarregat en %2 @@ -277,7 +281,7 @@ %n Download(s) - + %n baixades%n baixades @@ -632,10 +636,6 @@ &Float on Top Manté a &sobre - - &Adjust Window Size - &Ajusta la mida de la finestra - &Stop After This Video &Atura després d'aquest vídeo @@ -666,11 +666,19 @@ Restricted Mode - + Mode restringit Hide videos that may contain inappropriate content - + Amaga vídeos amb contingut no apropiat + + + Toggle &Menu Bar + A&maga la barra de menú + + + Menu + Menú &Love %1? Rate it! @@ -796,6 +804,10 @@ Update Actualitza + + You can still access the menu bar by pressing the ALT key + Encara podreu accedir a les opcions del menú mitjançant la tecla ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. L'enllaç només serà vàlid durant un temps limitat. - - This is just the demo version of %1. - Aquesta només és la versió de demostració del %1. - - - It allows you to test the application and see if it works for you. - Us permet probar l'aplicació i veure si us va bé. - - - Get the full version - Aconseguiu la versió completa - - - Continue - Continua - Downloading %1 Baixant %1 @@ -858,6 +854,10 @@ Subscribe to %1 Subscriu-me a %1 + + Switched to %1 + S'ha canviat a %1 + Unsubscribed from %1 De-subscrit de %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visualitzacions + Pick a video + Trieu un vídeo + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Cercant... - Show %1 More Mostra %1 Més @@ -1071,25 +1070,16 @@ Benvinguts al <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Introdueix + to start watching videos. + per comencar a veure vídeos. a keyword una paraula - a channel - un canal - - - to start watching videos. - per comencar a veure vídeos. - - - Watch - Veure + Enter + Introdueix Recent keywords @@ -1110,6 +1100,10 @@ &Back &Enrere + + &Forward + E&ndavant + Forward to %1 Avança a %1 @@ -1163,13 +1157,6 @@ Descarregant %1... - - Video - - Cannot get video stream for %1 - No es pot obtenir flux de vídeo per %1 - - YTRegions @@ -1365,4 +1352,11 @@ Global + + YTVideo + + Cannot get video stream for %1 + No es pot obtenir flux de vídeo per %1 + + \ No newline at end of file diff --git a/locale/ca_ES.ts b/locale/ca_ES.ts index 11f3677..300c0f8 100644 --- a/locale/ca_ES.ts +++ b/locale/ca_ES.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traduïu el %1 al vostre idioma natal utilitzant %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Icona dissenyada per %1. @@ -155,6 +163,10 @@ Show Updated Mostra actualitzats + + You have no subscriptions. Use the star symbol to subscribe to channels. + No teniu subscripcions. Feu servir el símbol de l'estrella per subscríure-us als canals. + All Videos Tots els vídeos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. No hi han subscripcions actualitzades. - - You have no subscriptions. Use the star symbol to subscribe to channels. - No teniu subscripcions. Feu servir el símbol de l'estrella per subscríure-us als canals. - - - - ClearButton - - Clear - Neteja - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visualitzacions + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Aquesta només és la versió de demostració del %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Només pot baixar vídeos de menys de %1 minuts per tal que en pugui provar aquesta funció. - - - Continue - Continua - - - Get the full version - Aconsegueix la versió completa - %1 downloaded in %2 %1 descarregat en %2 @@ -632,10 +636,6 @@ &Float on Top Manté a &sobre - - &Adjust Window Size - - &Stop After This Video &Atura després d'aquest vídeo @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! T'&Agrada %1? Puntua'l! @@ -796,6 +804,10 @@ Update Actualitza + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. L'enllaç només serà vàlid durant un temps limitat. - - This is just the demo version of %1. - Aquesta només és la versió de demostració del %1. - - - It allows you to test the application and see if it works for you. - Us permet probar l'aplicació i veure si us va bé. - - - Get the full version - Aconseguiu la versió completa - - - Continue - Continua - Downloading %1 Baixant %1 @@ -858,6 +854,10 @@ Subscribe to %1 Subscriu-me a %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visualitzacions + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Cercant... - Show %1 More Mostra %1 Més @@ -1071,25 +1070,16 @@ Benvinguts al <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Introdueix + to start watching videos. + per comencar a veure vídeos. a keyword una paraula - a channel - un canal - - - to start watching videos. - per comencar a veure vídeos. - - - Watch - Veure + Enter + Introdueix Recent keywords @@ -1110,6 +1100,10 @@ &Back &Enrere + + &Forward + + Forward to %1 Avança a %1 @@ -1163,13 +1157,6 @@ Descarregant %1... - - Video - - Cannot get video stream for %1 - No es pot obtenir flux de vídeo per %1 - - YTRegions @@ -1365,4 +1352,11 @@ Global + + YTVideo + + Cannot get video stream for %1 + No es pot obtenir flux de vídeo per %1 + + \ No newline at end of file diff --git a/locale/cs_CZ.ts b/locale/cs_CZ.ts index 3af0d90..816e0d2 100644 --- a/locale/cs_CZ.ts +++ b/locale/cs_CZ.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Přeložte %1 do vaÅ¡eho mateřského jazyka pomocí %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Autor ikony: %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + @@ -155,6 +163,10 @@ Show Updated Zobrazit aktualizace + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nemáte žádné odběry. Použijte hvězdičku k přihlásení odběru kanálů. + All Videos VÅ¡echna videa @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Nyní nejsou k dispozici žádné aktualizace odběrů. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nemáte žádné odběry. Použijte hvězdičku k přihlásení odběru kanálů. - - - - ClearButton - - Clear - Odstranit vÅ¡e - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + %n day(s) ago - + - %n weeks(s) ago - + %n month(s) ago + + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 zobrazení - %n month(s) ago - + %n week(s) ago + @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Toto je pouze demoverze %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Umí stahovat pouze videa délky do %1 minut, abyste mohli funkci stahování vyzkouÅ¡et - - - Continue - Pokračovat - - - Get the full version - Získat plnou verzi - %1 downloaded in %2 %1 staženo v %2 @@ -277,7 +281,7 @@ %n Download(s) - + @@ -632,10 +636,6 @@ &Float on Top &Plovoucí navrchu - - &Adjust Window Size - &Přizpůsobit velikost okna - &Stop After This Video &Zastavit po tomto videu @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Skrýt videa, která by mohla obsahovat nevhodný obsah + + Toggle &Menu Bar + Přepnout pruh s &nabídkou + + + Menu + Nabídka + &Love %1? Rate it! &Líbí se %1? Ohodnotit! @@ -796,6 +804,10 @@ Update Aktualizovat + + You can still access the menu bar by pressing the ALT key + Stále jeÅ¡tě můžete přistupovat k pruhu s nabídkou stisknutím klávesy Alt + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Tento odkaz platí jen po omezenou dobu. - - This is just the demo version of %1. - Toto je %1 -- demoverze. - - - It allows you to test the application and see if it works for you. - Umožňuje vyzkouÅ¡et aplikaci, abyste ověřili, jestli pro vás funguje. - - - Get the full version - Stáhnout plnou verzi - - - Continue - Pokračovat - Downloading %1 Je stahováno %1 @@ -858,6 +854,10 @@ Subscribe to %1 Přihlásit k %1 + + Switched to %1 + + Unsubscribed from %1 Odhlášen z odběru %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 zobrazení + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 z %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Hledá se... - Show %1 More Zobrazit dalších %1 @@ -1071,25 +1070,16 @@ Vítejte v <a href='%1'>%2</a> - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Vložit + to start watching videos. + pro sledování videí. a keyword klíčové slovo - a channel - kanál - - - to start watching videos. - pro sledování videí. - - - Watch - Sledovat + Enter + Vložit Recent keywords @@ -1110,6 +1100,10 @@ &Back &Zpět + + &Forward + + Forward to %1 Předat k %1 @@ -1163,13 +1157,6 @@ Stahování %1... - - Video - - Cannot get video stream for %1 - Nelze získat video stream pro %1 - - YTRegions @@ -1365,4 +1352,11 @@ Celosvětově + + YTVideo + + Cannot get video stream for %1 + Nelze získat video stream pro %1 + + \ No newline at end of file diff --git a/locale/da.ts b/locale/da.ts index e336189..db5efdc 100644 --- a/locale/da.ts +++ b/locale/da.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Oversæt %1 til din modersmÃ¥l ved hjælp af %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikon designet af %1. @@ -96,7 +104,7 @@ AppWidget Download - + Hent @@ -155,6 +163,10 @@ Show Updated Show opdateret + + You have no subscriptions. Use the star symbol to subscribe to channels. + Du har ingen abonnementer. Brug stjernetegnet til at abonnere pÃ¥ kanaler. + All Videos Alle videoer @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Der er ingen opdateringer i de abonnerede. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Du har ingen abonnementer. Brug stjernetegnet til at abonnere pÃ¥ kanaler. - - - - ClearButton - - Clear - Fjern - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visninger + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Dette er kun demoversionen af %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Det kan kun hente videoer kortere end %1 minut, sÃ¥ du kan teste downloadfunktionaliteten. - - - Continue - Forsæt - - - Get the full version - Hent den fulde version - %1 downloaded in %2 %1 downloaded pÃ¥ %2 @@ -633,10 +637,6 @@ Kopiér &URL'en til videostrømmen &Float on Top &Behold øverst - - &Adjust Window Size - &Juster vinduesstørrelse - &Stop After This Video &Stop efter denne video @@ -667,10 +667,18 @@ Kopiér &URL'en til videostrømmen Restricted Mode - + Begrænset-MÃ¥de Hide videos that may contain inappropriate content + Gem videoer der muligvis indeholder stødene indhold. + + + Toggle &Menu Bar + + + + Menu @@ -797,6 +805,10 @@ Kopiér &URL'en til videostrømmen Update Opdatér + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -812,22 +824,6 @@ Kopiér &URL'en til videostrømmen The link will be valid only for a limited time. Linket vil kun være gyldigt i en begrænset periode. - - This is just the demo version of %1. - Dette er kun demoversionen af %1. - - - It allows you to test the application and see if it works for you. - Det giver dig mulighed for at teste programmet og se om det virker for dig. - - - Get the full version - Hent den fulde version - - - Continue - Forsæt - Downloading %1 Downloader %1 @@ -859,6 +855,10 @@ Kopiér &URL'en til videostrømmen Subscribe to %1 Abonner pÃ¥ %1 + + Switched to %1 + + Unsubscribed from %1 Abonnerer ikke længere pÃ¥ %1 @@ -903,11 +903,14 @@ Kopiér &URL'en til videostrømmen - PlaylistItemDelegate + PickMessage - %1 views - %1 visninger + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 af %2 (%3) — %4 @@ -947,10 +950,6 @@ Kopiér &URL'en til videostrømmen PlaylistModel - - Searching... - Søger... - Show %1 More Vis %1 mere @@ -1072,25 +1071,16 @@ Kopiér &URL'en til videostrømmen Velkommen til <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Indtast + to start watching videos. + for at begynde at se video. a keyword et nøgleord - a channel - en kanal - - - to start watching videos. - for at begynde at se video. - - - Watch - Afspil + Enter + Indtast Recent keywords @@ -1111,6 +1101,10 @@ Kopiér &URL'en til videostrømmen &Back &Tilbage + + &Forward + + Forward to %1 Frem til %1 @@ -1161,14 +1155,7 @@ Kopiér &URL'en til videostrømmen Downloading %1... - - - - - Video - - Cannot get video stream for %1 - Kan ikke hente videostrøm for %1 + Henter %1... @@ -1366,4 +1353,11 @@ Kopiér &URL'en til videostrømmen Hele verden + + YTVideo + + Cannot get video stream for %1 + Kan ikke hente videostrøm for %1 + + \ No newline at end of file diff --git a/locale/de_DE.ts b/locale/de_DE.ts index 6802af7..ec338db 100644 --- a/locale/de_DE.ts +++ b/locale/de_DE.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Übersetzen Sie %1 in Ihre Muttersprache mit %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Icons wurden gestaltet von %1. @@ -155,6 +163,10 @@ Show Updated Zeige aktualisierte + + You have no subscriptions. Use the star symbol to subscribe to channels. + Du hast keine Abonnements. Benutze das Stern-Symbol, um einen Kanal zu abonnieren. + All Videos Alle Videos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Zurzeit gibt es nichts Neues bei den Abonnements. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Du hast keine Abonnements. Benutze das Stern-Symbol, um einen Kanal zu abonnieren. - - - - ClearButton - - Clear - Säubern - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 mal betrachtet + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Dies ist nur die Demoversion von %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Sie kann nur Videos herunterladen, die kürzer als %1 Minuten sind, um die Funktion zum Herunterladen zu testen. - - - Continue - Fortfahren - - - Get the full version - Die Vollversion kaufen - %1 downloaded in %2 %1 heruntergeladen nach %2 @@ -632,10 +636,6 @@ &Float on Top Im Vordergrund &bleiben - - &Adjust Window Size - &Fenstergröße anpassen - &Stop After This Video Nach diesem Video &anhalten @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Verstecke Videos die unpassende Inhalte enthalten können + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Gefällt Ihnen %1? Bewertung abgeben! @@ -796,6 +804,10 @@ Update Aktualisierung + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Der Link wird nur eine beschränkte Zeit gültig sein. - - This is just the demo version of %1. - Dies ist nur die Demoversion von %1. - - - It allows you to test the application and see if it works for you. - Sie erlaubt es Ihnen, die Anwendung zu testen und zu schauen, ob sie bei Ihnen läuft. - - - Get the full version - Die Vollversion kaufen - - - Continue - Fortfahren - Downloading %1 %1 herunterladen @@ -858,6 +854,10 @@ Subscribe to %1 Abonnieren von %1 + + Switched to %1 + + Unsubscribed from %1 Abonnement von %1 wurde aufgehoben. @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 mal betrachtet + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 von %2 (%3) – %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Suche... - Show %1 More Weitere %1 zeigen @@ -1071,25 +1070,16 @@ Willkommen bei <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Eingeben + to start watching videos. + um die Wiedergabe zu starten. a keyword ein Suchbegriff - a channel - ein Kanal - - - to start watching videos. - um die Wiedergabe zu starten. - - - Watch - Anschauen + Enter + Eingeben Recent keywords @@ -1110,6 +1100,10 @@ &Back &Zurück + + &Forward + + Forward to %1 Weiter zu %1 @@ -1163,13 +1157,6 @@ heruntergeladen %1 - - Video - - Cannot get video stream for %1 - Videostream für %1 konnte nicht geöffnet werden - - YTRegions @@ -1365,4 +1352,11 @@ Weltweit + + YTVideo + + Cannot get video stream for %1 + Videostream für %1 konnte nicht geöffnet werden + + \ No newline at end of file diff --git a/locale/el.ts b/locale/el.ts index 8ecd850..330b6c0 100644 --- a/locale/el.ts +++ b/locale/el.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Μεταφράστε το %1 στη γλώσσα σας με χρήση του %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Σχεδιασμός εικονιδίου από %1. @@ -155,6 +163,10 @@ Show Updated Εμφάνιση ενημερωμένων + + You have no subscriptions. Use the star symbol to subscribe to channels. + Δεν έχετε συνδρομές. Χρησιμοποιήστε το αστέρι για να κάνετε συνδρομή σε κανάλια. + All Videos Όλα τα βίντεο @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Δεν υπάρχουν ενημερωμένες συδρομές αυτήν την στιγμή. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Δεν έχετε συνδρομές. Χρησιμοποιήστε το αστέρι για να κάνετε συνδρομή σε κανάλια. - - - - ClearButton - - Clear - Εκκαθάριση - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 προβολές + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Αυτή είναι απλά η δοκιμαστική έκδοση του %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Μπορεί να κάνει λήψη βίντεο μικρότερα από %1 λεπτά ώστε να δοκιμάσετε τη λειτουργία λήψης. - - - Continue - Συνέχεια - - - Get the full version - Αποκτήστε την πλήρη έκδοση - %1 downloaded in %2 %1 λήφθηκε σε %2 @@ -632,10 +636,6 @@ &Float on Top &Διατήρηση στην κορυφή - - &Adjust Window Size - &Προσαρμογή του μεγέθους του παραθύρου - &Stop After This Video &Διακοπή μετά από αυτό το βίντεο @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Λατρεύετε το %1; Βαθμολογήστε το! @@ -796,6 +804,10 @@ Update Ενημέρωση + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Ο σύνδεσμος θα είναι έγκυρος για περιορισμένο χρονικό διάστημα. - - This is just the demo version of %1. - Αυτή είναι απλά μια δοκιμαστική έκδοση του %1. - - - It allows you to test the application and see if it works for you. - Σαε επιτρέπει να δοκιμάσετε την εφαρμογή και να δείτε αν σας κάνει. - - - Get the full version - Αποκτήστε τη πλήρη έκδοση - - - Continue - Συνέχεια - Downloading %1 Λήψη %1 @@ -858,6 +854,10 @@ Subscribe to %1 Εγγραφή στο %1 + + Switched to %1 + + Unsubscribed from %1 Διεγράφη από το %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 προβολές + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 από %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Αναζήτηση... - Show %1 More Εμφάνιση %1 ακόμα @@ -1071,25 +1070,16 @@ Καλωσορίσατε στο <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Εισάγετε + to start watching videos. + για να αρχίσετε να βλέπετε βίντεο. a keyword μια λέξη-κλειδί - a channel - ένα κανάλι - - - to start watching videos. - για να αρχίσετε να βλέπετε βίντεο. - - - Watch - Παρακολουθήστε + Enter + Εισάγετε Recent keywords @@ -1110,6 +1100,10 @@ &Back &Επιστροφή + + &Forward + + Forward to %1 Προώθηση σε %1 @@ -1163,13 +1157,6 @@ Λήψη %1... - - Video - - Cannot get video stream for %1 - Αδυναμία λήψης της ροής βίντεο για το %1 - - YTRegions @@ -1365,4 +1352,11 @@ Παγκοσμίως + + YTVideo + + Cannot get video stream for %1 + Αδυναμία λήψης της ροής βίντεο για το %1 + + \ No newline at end of file diff --git a/locale/en.ts b/locale/en.ts index 9a66576..9e662e3 100644 --- a/locale/en.ts +++ b/locale/en.ts @@ -7,7 +7,7 @@ You have %n new video(s) - You have one new video + You have a new video You have %n new videos @@ -61,7 +61,7 @@ %n Download(s) - One Download + 1 Download %n Downloads diff --git a/locale/en_GB.ts b/locale/en_GB.ts new file mode 100644 index 0000000..f029b05 --- /dev/null +++ b/locale/en_GB.ts @@ -0,0 +1,1362 @@ + + + AboutView + + There's life outside the browser! + There's life outside the browser! + + + Version %1 + Version %1 + + + Licensed to: %1 + Licensed to: %1 + + + %1 is Free Software but its development takes precious time. + %1 is Free Software but its development takes precious time. + + + Please <a href='%1'>donate</a> to support the continued development of %2. + Please <a href='%1'>donate</a> to support the continued development of %2. + + + Translate %1 to your native language using %2 + Translate %1 to your native language using %2 + + + Powered by %1 + Powered by %1 + + + Open-source software + Open-source software + + + Icon designed by %1. + Icon designed by %1. + + + Released under the <a href='%1'>GNU General Public License</a> + Released under the <a href='%1'>GNU General Public License</a> + + + &Close + &Close + + + About + About + + + + ActivationDialog + + Enter your License Details + Enter your Licence Details + + + &Email: + &Email: + + + &Code: + &Code: + + + + ActivationView + + Please license %1 + Please license %1 + + + This demo has expired. + This demo has expired. + + + The full version allows you to watch videos without interruptions. + The full version allows you to watch videos without interruptions. + + + Without a license, the application will expire in %1 days. + Without a licence, the application will expire in %1 days. + + + By purchasing the full version, you will also support the hard work I put into creating %1. + By purchasing the full version, you will also support the hard work I put into creating %1. + + + Use Demo + Use Demo + + + Enter License + Enter Licence + + + Buy License + Buy Licence + + + + AppWidget + + Download + Download + + + + ChannelAggregator + + By %1 + By %1 + + + You have %n new video(s) + You have a new videoYou have %n new videos + + + + ChannelItemDelegate + + All Videos + All Videos + + + Unwatched Videos + Unwatched Videos + + + + ChannelView + + Name + Name + + + Last Updated + Last Updated + + + Last Added + Last Added + + + Last Watched + Last Watched + + + Most Watched + Most Watched + + + Sort by + Sort by + + + Mark all as watched + Mark all as watched + + + Show Updated + Show Updated + + + You have no subscriptions. Use the star symbol to subscribe to channels. + You have no subscriptions. Use the star symbol to subscribe to channels. + + + All Videos + All Videos + + + Unwatched Videos + Unwatched Videos + + + Mark as Watched + Mark as Watched + + + Unsubscribe + Unsubscribe + + + There are no updated subscriptions at this time. + There are no updated subscriptions at this time. + + + + DataUtils + + Just now + Just now + + + %n hour(s) ago + An hour ago%n hours ago + + + %n day(s) ago + Yesterday%n days ago + + + %n month(s) ago + A month ago%n month(s) ago + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + %1 views + + + %n week(s) ago + A week ago%n weeks ago + + + + DownloadItem + + bytes + bytes + + + KB + KB + + + MB + MB + + + bytes/sec + bytes/sec + + + KB/sec + KB/sec + + + MB/sec + MB/sec + + + seconds + seconds + + + minutes + minutes + + + %4 %5 remaining + %4 %5 remaining + + + + DownloadManager + + %1 downloaded in %2 + %1 downloaded in %2 + + + Download finished + Download finished + + + %n Download(s) + 1 Download%n Downloads + + + + DownloadSettings + + Change location... + Change location... + + + Choose the download location + Choose the download location + + + Download location changed. + Download location changed. + + + Current downloads will still go in the previous location. + Current downloads will still go into the previous location. + + + Downloading to: %1 + Downloading to: %1 + + + + DownloadView + + Downloads + Downloads + + + + Extra + + The executable file has been tempered with, maybe by a virus. + The executable file has been tampered with, maybe by a virus. + + + %1 will not run. Try installing again. + %1 will not run. Try installing again. + + + Quit + Quit + + + Reinstall + Reinstall + + + + GlobalShortcuts + + Play + Play + + + Pause + Pause + + + Play/Pause + Play/Pause + + + Stop + Stop + + + Stop playing after current track + Stop playing after current track + + + Next track + Next track + + + Previous track + Previous track + + + Increase volume + Increase volume + + + Decrease volume + Decrease volume + + + Mute + Mute + + + Seek forward + Seek forward + + + Seek backward + Seek backward + + + + HomeView + + Search + Search + + + Find videos and channels by keyword + Find videos and channels by keyword + + + Browse + Browse + + + Browse videos by category + Browse videos by category + + + Subscriptions + Subscriptions + + + Channel subscriptions + Channel subscriptions + + + Make yourself comfortable + Make yourself comfortable + + + + LoadingWidget + + Error + Error + + + + MainWindow + + &Window + &Window + + + &Minimize + &Minimise + + + &Stop + &Stop + + + Stop playback and go back to the search view + Stop playback and go back to the search view + + + P&revious + &Previous + + + Go back to the previous track + Go back to the previous track + + + S&kip + &Skip + + + Skip to the next video + Skip to the next video + + + &Play + &Play + + + Resume playback + Resume playback + + + &Full Screen + &Full Screen + + + Go full screen + Go full screen + + + &Compact Mode + &Compact Mode + + + Hide the playlist and the toolbar + Hide the playlist and the toolbar + + + Open the &YouTube Page + Open the &YouTube Page + + + Go to the YouTube video page and pause playback + Go to the YouTube video page and pause playback + + + Copy the YouTube &Link + Copy the YouTube &Link + + + Copy the current video YouTube link to the clipboard + Copy the current video YouTube link to the clipboard + + + Copy the Video Stream &URL + Copy the Video Stream &URL + + + Copy the current video stream URL to the clipboard + Copy the current video stream URL to the clipboard + + + Find Video &Parts + Find Video &Parts + + + Find other video parts hopefully in the right order + Find other video parts hopefully in the right order + + + &Remove + &Remove + + + Remove the selected videos from the playlist + Remove the selected videos from the playlist + + + Move &Up + Move &Up + + + Move up the selected videos in the playlist + Move up the selected videos in the playlist + + + Move &Down + Move &Down + + + Move down the selected videos in the playlist + Move down the selected videos in the playlist + + + &Clear Recent Searches + &Clear Recent Searches + + + Clear the search history. Cannot be undone. + Clear the search history. Cannot be undone. + + + &Quit + &Quit + + + Bye + Bye + + + &Website + &Website + + + %1 on the Web + %1 on the Web + + + Make a &Donation + Make a &Donation + + + Please support the continued development of %1 + Please support the continued development of %1 + + + &About + &About + + + Info about %1 + Info about %1 + + + Search + Search + + + Mute volume + Mute volume + + + &Manually Start Playing + &Manually Start Playing + + + Manually start playing videos + Manually start playing videos + + + &Downloads + &Downloads + + + Show details about video downloads + Show details about video downloads + + + &Download + &Download + + + Download the current video + Download the current video + + + Take &Snapshot + Take &Snapshot + + + &Subscribe to Channel + &Subscribe to Channel + + + Share the current video using %1 + Share the current video using %1 + + + &Email + &Email + + + Email + Email + + + &Close + &Close + + + &Float on Top + &Float on Top + + + &Stop After This Video + &Stop After This Video + + + &Report an Issue... + &Report an Issue... + + + &Refine Search... + &Refine Search... + + + More... + More... + + + &Related Videos + &Related Videos + + + Watch videos related to the current one + Watch videos related to the current one + + + Open in &Browser... + Open in &Browser... + + + Restricted Mode + Restricted Mode + + + Hide videos that may contain inappropriate content + Hide videos that may contain inappropriate content + + + Toggle &Menu Bar + Toggle &Menu Bar + + + Menu + Menu + + + &Love %1? Rate it! + &Love %1? Rate it! + + + Buy %1... + Buy %1... + + + &Application + &Application + + + &Playback + &Playback + + + &Playlist + &Playlist + + + &Video + &Video + + + &Share + &Share + + + &View + &View + + + &Help + &Help + + + Press %1 to raise the volume, %2 to lower it + Press %1 to raise the volume, %2 to lower it + + + Choose your content location + Choose your content location + + + Opening %1 + Opening %1 + + + Do you want to exit %1 with a download in progress? + Do you want to exit %1 with a download in progress? + + + If you close %1 now, this download will be cancelled. + If you close %1 now, this download will be cancelled. + + + Close and cancel download + Close and cancel download + + + Wait for download to finish + Wait for download to finish + + + Error: %1 + Error: %1 + + + &Pause + &Pause + + + Pause playback + Pause playback + + + &Loading... + &Loading... + + + Leave &Full Screen + Leave &Full Screen + + + Remaining time: %1 + Remaining time: %1 + + + Volume at %1% + Volume at %1% + + + Volume is muted + Volume is muted + + + Volume is unmuted + Volume is unmuted + + + Maximum video definition set to %1 + Maximum video definition set to %1 + + + Your privacy is now safe + Your privacy is now safe + + + Downloads complete + Downloads complete + + + %1 version %2 is now available. + %1 version %2 is now available. + + + Remind me later + Remind me later + + + Update + Update + + + You can still access the menu bar by pressing the ALT key + You can still access the menu bar by pressing the ALT key + + + + MediaView + + You can now paste the YouTube link into another application + You can now paste the YouTube link into another application + + + You can now paste the video stream URL into another application + You can now paste the video stream URL into another application + + + The link will be valid only for a limited time. + The link will be valid for a limited time only. + + + Downloading %1 + Downloading %1 + + + of + Used in video parts, as in '2 of 3' + of + + + part + This is for video parts, as in 'Cool video - part 1' + part + + + episode + This is for video parts, as in 'Cool series - episode 1' + episode + + + Sent from %1 + Sent from %1 + + + Unsubscribe from %1 + Unsubscribe from %1 + + + Subscribe to %1 + Subscribe to %1 + + + Switched to %1 + Switched to %1 + + + Unsubscribed from %1 + Unsubscribed from %1 + + + Subscribed to %1 + Subscribed to %1 + + + + MessageWidget + + A new version of %1 is available! + A new version of %1 is available! + + + %1 %2 is now available. You have %3. + %1 %2 is now available. You have %3. + + + Would you like to download it now? + Would you like to download it now? + + + Skip This Version + Skip This Version + + + Remind Me Later + Remind Me Later + + + Install Update + Install Update + + + + PasteLineEdit + + Paste + Paste + + + + PickMessage + + Pick a video + Pick a video + + + + PlaylistItemDelegate + + %1 of %2 (%3) — %4 + %1 of %2 (%3) — %4 + + + Preparing + Preparing + + + Failed + Failed + + + Completed + Completed + + + Stopped + Stopped + + + Stop downloading + Stop downloading + + + Show in %1 + Show in %1 + + + Open parent folder + Open parent folder + + + Restart downloading + Restart downloading + + + + PlaylistModel + + Show %1 More + Show %1 More + + + No videos + No videos + + + No more videos + No more videos + + + + RefineSearchWidget + + Sort by + Sort by + + + Relevance + Relevance + + + Date + Date + + + View Count + View Count + + + Rating + Rating + + + Anytime + Any time + + + Today + Today + + + 7 Days + 7 Days + + + 30 Days + 30 Days + + + Duration + Duration + + + All + All + + + Short + Short + + + Medium + Medium + + + Long + Long + + + Less than 4 minutes + Less than 4 minutes + + + Between 4 and 20 minutes + Between 4 and 20 minutes + + + Longer than 20 minutes + Longer than 20 minutes + + + Quality + Quality + + + High Definition + High Definition + + + 720p or higher + 720p or higher + + + Done + Done + + + + RegionsView + + Done + Done + + + + SearchLineEdit + + Search + Search + + + + SearchView + + Welcome to <a href='%1'>%2</a>, + Welcome to <a href='%1'>%2</a>, + + + to start watching videos. + to start watching videos. + + + a keyword + a keyword + + + Enter + Enter + + + Recent keywords + Recent keywords + + + Recent channels + Recent channels + + + Get the full version + Get the full version + + + + SidebarHeader + + &Back + &Back + + + &Forward + &Forward + + + Forward to %1 + Forward to %1 + + + Back to %1 + Back to %1 + + + + SidebarWidget + + Refine Search + Refine Search + + + Did you mean: %1 + Did you mean: %1? + + + + SnapshotSettings + + Change location... + Change location... + + + Snapshot saved to %1 + Snapshot saved to %1 + + + Snapshots location changed. + Snapshot location changed. + + + + StandardFeedsView + + Most Popular + Most Popular + + + + UpdateDialog + + Downloading update... + Downloading update... + + + Downloading %1... + Downloading %1... + + + + YTRegions + + Algeria + Algeria + + + Argentina + Argentina + + + Australia + Australia + + + Belgium + Belgium + + + Brazil + Brazil + + + Canada + Canada + + + Chile + Chile + + + Colombia + Colombia + + + Czech Republic + Czech Republic + + + Egypt + Egypt + + + France + France + + + Germany + Germany + + + Ghana + Ghana + + + Greece + Greece + + + Hong Kong + Hong Kong + + + Hungary + Hungary + + + India + India + + + Indonesia + Indonesia + + + Ireland + Ireland + + + Israel + Israel + + + Italy + Italy + + + Japan + Japan + + + Jordan + Jordan + + + Kenya + Kenya + + + Malaysia + Malaysia + + + Mexico + Mexico + + + Morocco + Morocco + + + Netherlands + Netherlands + + + New Zealand + New Zealand + + + Nigeria + Nigeria + + + Peru + Peru + + + Philippines + Philippines + + + Poland + Poland + + + Russia + Russia + + + Saudi Arabia + Saudi Arabia + + + Singapore + Singapore + + + South Africa + South Africa + + + South Korea + South Korea + + + Spain + Spain + + + Sweden + Sweden + + + Taiwan + Taiwan + + + Tunisia + Tunisia + + + Turkey + Turkey + + + Uganda + Uganda + + + United Arab Emirates + United Arab Emirates + + + United Kingdom + United Kingdom + + + Yemen + Yemen + + + Worldwide + World Wide + + + + YTVideo + + Cannot get video stream for %1 + Cannot retrieve video stream for %1 + + + \ No newline at end of file diff --git a/locale/es.ts b/locale/es.ts index c0d34c4..0018053 100644 --- a/locale/es.ts +++ b/locale/es.ts @@ -23,7 +23,15 @@ Translate %1 to your native language using %2 - Traduzca %1 a su idioma natal usando %2 + Traduzca %1 a su idioma usando %2 + + + Powered by %1 + + + + Open-source software + Icon designed by %1. @@ -155,6 +163,10 @@ Show Updated Mostrar actualizados + + You have no subscriptions. Use the star symbol to subscribe to channels. + No se ha suscrito a ningún canal. Use el símbolo de la estrella para suscribirse a los canales. + All Videos Todos los vídeos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. No hay suscripciones actualizadas en este momento. - - You have no subscriptions. Use the star symbol to subscribe to channels. - No se ha suscrito a ningún canal. Use el símbolo de la estrella para suscribirse a los canales. - - - - ClearButton - - Clear - Vaciar - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 reproducciones + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Esta es solo la versión de prueba de %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Solo puede descargar vídeos de duración menor que %1 minutos para que pueda probar la función de descarga. - - - Continue - Continuar - - - Get the full version - Obtener la versión completa - %1 downloaded in %2 %1 descargados en %2 @@ -632,10 +636,6 @@ &Float on Top &Flotar en la parte superior - - &Adjust Window Size - &Ajustar tamaño de la ventana - &Stop After This Video &Detener tras este vídeo @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Ocultar videos que puedan contener contenido inapropiado + + Toggle &Menu Bar + Apagar &Barra de menú + + + Menu + Menú + &Love %1? Rate it! ¿&Le gusta %1? ¡Valórelo! @@ -797,6 +805,10 @@ Update Actualizar + + You can still access the menu bar by pressing the ALT key + Por abrir la barra de menú puede presionar la tecla Alt. + MediaView @@ -812,22 +824,6 @@ The link will be valid only for a limited time. El enlace es válido solo por un tiempo limitado. - - This is just the demo version of %1. - Esto es solo la versión de prueba de %1. - - - It allows you to test the application and see if it works for you. - Le permite probar la aplicación y ver si le funciona. - - - Get the full version - Obtener la versión completa - - - Continue - Continuar - Downloading %1 Descargando %1 @@ -859,6 +855,10 @@ Subscribe to %1 Suscribirse a %1 + + Switched to %1 + + Unsubscribed from %1 Desuscrito de %1 @@ -903,11 +903,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 reproducciones + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -947,10 +950,6 @@ PlaylistModel - - Searching... - Buscando… - Show %1 More Mostrar %1 más @@ -1072,25 +1071,16 @@ Bienvenido a <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Escriba + to start watching videos. + para empezar a ver vídeos. a keyword una palabra clave - a channel - un canal - - - to start watching videos. - para empezar a ver vídeos. - - - Watch - Ver + Enter + Escriba Recent keywords @@ -1111,6 +1101,10 @@ &Back &Atrás + + &Forward + + Forward to %1 Reenviar a %1 @@ -1164,13 +1158,6 @@ Descargando %1... - - Video - - Cannot get video stream for %1 - No se puede obtener el flujo de vídeo para %1 - - YTRegions @@ -1366,4 +1353,11 @@ Todo el mundo + + YTVideo + + Cannot get video stream for %1 + No se puede obtener el stream de vídeo para %1 + + \ No newline at end of file diff --git a/locale/es_AR.ts b/locale/es_AR.ts index 7945c35..5f9e59c 100644 --- a/locale/es_AR.ts +++ b/locale/es_AR.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traduce %1 a tu idioma natal usando %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Iconos diseñados por %1. @@ -155,6 +163,10 @@ Show Updated Show actualizado + + You have no subscriptions. Use the star symbol to subscribe to channels. + No tiene suscripciones. Haga click en el ícono de la estrella para suscribirse a un canal + All Videos Todos los videos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. No hay actualizaciones a sus suscripciones en este momento - - You have no subscriptions. Use the star symbol to subscribe to channels. - No tiene suscripciones. Haga click en el ícono de la estrella para suscribirse a un canal - - - - ClearButton - - Clear - Limpiar - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visitas + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Esta es sólo una versión de demostración de %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Sólo se pueden bajar videos de menos de %1 minutos, para probar la funcionalidad de descarga. - - - Continue - Continuar - - - Get the full version - Consigue la versión completa - %1 downloaded in %2 %1 descargado en %2 @@ -632,10 +636,6 @@ &Float on Top &Siempre Visible - - &Adjust Window Size - Ajustar el tamaño de la ventana - &Stop After This Video &Finalizar Después de este Video @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Ocultar videos que puedan tener contenido inapropiado + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! ¿Amas %1? ¡Califícalo! @@ -796,6 +804,10 @@ Update Actualizar + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. El enlace va a ser válido sólo por un tiempo limitado. - - This is just the demo version of %1. - Esta es sólo la versión de demostración de %1. - - - It allows you to test the application and see if it works for you. - Te permite probar la aplicación y ver si te funciona. - - - Get the full version - Conseguir la versión completa - - - Continue - Continuar - Downloading %1 Descargando %1 @@ -858,6 +854,10 @@ Subscribe to %1 Suscribirse a %1 + + Switched to %1 + + Unsubscribed from %1 Desubscripto de %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visitas + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 of %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Buscando... - Show %1 More Mostrar %1 más @@ -1071,25 +1070,16 @@ Bienvenido a <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Escribir + to start watching videos. + para empezar a ver videos. a keyword una palabra clave - a channel - un canal - - - to start watching videos. - para empezar a ver videos. - - - Watch - Ver + Enter + Escribir Recent keywords @@ -1110,6 +1100,10 @@ &Back &Atrás + + &Forward + + Forward to %1 Avanzar a %1 @@ -1163,13 +1157,6 @@ Descargando %1... - - Video - - Cannot get video stream for %1 - No puedo obtener el stream de video de %1 - - YTRegions @@ -1365,4 +1352,11 @@ Todo el mundo + + YTVideo + + Cannot get video stream for %1 + No puedo obtener el stream de video de %1 + + \ No newline at end of file diff --git a/locale/es_ES.ts b/locale/es_ES.ts index a06f5ca..5dea213 100644 --- a/locale/es_ES.ts +++ b/locale/es_ES.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traducir %1 a tu idioma utilizando %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Icono diseñado por %1. @@ -155,6 +163,10 @@ Show Updated Mostrar actualizados + + You have no subscriptions. Use the star symbol to subscribe to channels. + No tienes ninguna subscripción. Usa la estrella para subscribirte a los canales. + All Videos Todos los videos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. No existen subscripciones actualizadas en este momento. - - You have no subscriptions. Use the star symbol to subscribe to channels. - No tienes ninguna subscripción. Usa la estrella para subscribirte a los canales. - - - - ClearButton - - Clear - Limpiar - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 vistas + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Esta es la versión de prueba de %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Sólo se pueden descargar vídeos inferiores a %1 minutos. Así podrá probar la funcionalidad de descarga. - - - Continue - Continuar - - - Get the full version - Obtener la versión completa - %1 downloaded in %2 %1 descargado en %2 @@ -632,10 +636,6 @@ &Float on Top &Siempre Visible - - &Adjust Window Size - &Ajustar el tamaños de la ventana - &Stop After This Video &Detener después de este vídeo @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Ocultar video que puedan poseer contenido inapropiado + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &¿Te encanta %1? ¡Puntúalo! @@ -796,6 +804,10 @@ Update Actualizar + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. El enlace será válido sólo por un plazo de tiempo limitado. - - This is just the demo version of %1. - Esta es la versión de prueba de %1. - - - It allows you to test the application and see if it works for you. - Esta versión le permite probar la aplicación y ver si le sirve. - - - Get the full version - Obtener la versión completa - - - Continue - Continuar - Downloading %1 Descargando %1 @@ -858,6 +854,10 @@ Subscribe to %1 Subscribirse a %1 + + Switched to %1 + + Unsubscribed from %1 Desubscripto de %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 vistas + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Buscando... - Show %1 More Mostrar %1 más @@ -1071,25 +1070,16 @@ Bienvenidos a <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Introducir + to start watching videos. + para empezar a ver vídeos a keyword una palabra clave - a channel - un canal - - - to start watching videos. - para empezar a ver vídeos - - - Watch - Ver + Enter + Introducir Recent keywords @@ -1110,6 +1100,10 @@ &Back &Volver + + &Forward + + Forward to %1 Hacia adelante %1 @@ -1163,13 +1157,6 @@ Descargando %1... - - Video - - Cannot get video stream for %1 - No se puede obtener el flujo de vídeo para %1 - - YTRegions @@ -1365,4 +1352,11 @@ Mundial + + YTVideo + + Cannot get video stream for %1 + No se puede obtener el flujo de vídeo para %1 + + \ No newline at end of file diff --git a/locale/es_MX.ts b/locale/es_MX.ts index 337e4ac..80e6566 100644 --- a/locale/es_MX.ts +++ b/locale/es_MX.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traducir %1 a tu idioma nativo usando %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ícono diseñado por %1 @@ -54,7 +62,7 @@ &Code: - &Códico + &Código @@ -155,6 +163,10 @@ Show Updated Mostrar actualizados + + You have no subscriptions. Use the star symbol to subscribe to channels. + No tienes suscripciones. Usa el símbolo de estrella para suscribirte a los canales. + All Videos Todos los vídeos @@ -169,23 +181,12 @@ Unsubscribe - Quitar suscripción + Cancelar suscripción There are no updated subscriptions at this time. No hay suscripciones actualizadas - - You have no subscriptions. Use the star symbol to subscribe to channels. - No tienes suscripciones. Usa el símbolo de estrella para suscribirte a los canales. - - - - ClearButton - - Clear - Limpiar - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 vistas + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Esta es la versión demostrativa de %1 - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Sólo puede descargar videos con duración menor a %1 minutos asi que puedes probar la funcionalidad de descarga - - - Continue - Continuar - - - Get the full version - Obtener la versión completa - %1 downloaded in %2 Descargados %1 en %2 @@ -630,11 +634,7 @@ &Float on Top - %Mantener arriba - - - &Adjust Window Size - &Ajustar tamaño de ventana + &Mantener arriba &Stop After This Video @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Ocultar vídeos que pueden tener contenido inapropiado + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Amas %1? Calíficalo! @@ -796,6 +804,10 @@ Update Actualizar + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. El vínculo será válido sólo por un tiempo limitado - - This is just the demo version of %1. - Esta es la versión demostrativa de %1 - - - It allows you to test the application and see if it works for you. - Le permite probar la aplicación y ver si funciona para ti. - - - Get the full version - Obtener la versión completa - - - Continue - Continuar - Downloading %1 Descargando %1 @@ -858,6 +854,10 @@ Subscribe to %1 Suscribir a %1 + + Switched to %1 + + Unsubscribed from %1 Desuscribir de %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 vistas + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) - %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Buscando... - Show %1 More Mostrar %1 más @@ -1071,25 +1070,16 @@ Bienvenido a <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Introduzca + to start watching videos. + para ver vídeos a keyword una palabra clave - a channel - un canal - - - to start watching videos. - para ver vídeos - - - Watch - Ver + Enter + Introduzca Recent keywords @@ -1110,6 +1100,10 @@ &Back Retroceder + + &Forward + + Forward to %1 Avanzar a %1 @@ -1163,13 +1157,6 @@ Descargando %1... - - Video - - Cannot get video stream for %1 - No se puede obtener el vídeo para %1 - - YTRegions @@ -1365,4 +1352,11 @@ Mundial + + YTVideo + + Cannot get video stream for %1 + No se puede obtener el vídeo para %1 + + \ No newline at end of file diff --git a/locale/fi.ts b/locale/fi.ts index 2a33153..37b42f9 100644 --- a/locale/fi.ts +++ b/locale/fi.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Käännä %1 äidinkielellesi käyttämällä %2 + + Powered by %1 + + + + Open-source software + Avoimen lähdekoodin ohjelmisto + Icon designed by %1. Kuvakkeen suunnitteli %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + Sinulle on %n uusi videoSinulle on %n uutta videota @@ -155,6 +163,10 @@ Show Updated Näytä päivitetyt + + You have no subscriptions. Use the star symbol to subscribe to channels. + Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia. + All Videos Kaikki videot @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Päivitettyjä tilauksia ei ole tällä hetkellä. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia. - - - - ClearButton - - Clear - Tyhjennä - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + %n tunti sitten%n tuntia sitten %n day(s) ago - + %n päivä sitten%n päivää sitten - %n weeks(s) ago - + %n month(s) ago + %n kuukausi sitten%n kuukautta sitten + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + Katsottu %1 kertaa - %n month(s) ago - + %n week(s) ago + %n viikko sitten%n viikkoa sitten @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Tämä on vain %1-kokeiluversio. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Voit ladata vain videoita jotka ovat lyhyempiä kuin %1 minuuttia, jotta voit testata latausominaisuutta. - - - Continue - Jatka - - - Get the full version - Hanki täysi versio - %1 downloaded in %2 %1 ladattu ajassa %2 @@ -277,7 +281,7 @@ %n Download(s) - + %n lataus%n latausta @@ -632,10 +636,6 @@ &Float on Top &Pysy päällimmäisenä - - &Adjust Window Size - &Muuta ikkunan kokoa - &Stop After This Video Py&säytä toisto tämän videon jälkeen @@ -666,11 +666,19 @@ Restricted Mode - + Rajoitettu tila Hide videos that may contain inappropriate content - + Piilota videot, jotka saattavat sisältää soveltumatonta sisältöä + + + Toggle &Menu Bar + Näytä/piilota &valikkopalkki + + + Menu + Valikko &Love %1? Rate it! @@ -796,6 +804,10 @@ Update Päivitä + + You can still access the menu bar by pressing the ALT key + Saat valikkopalkin näkyviin painamalla ALT-näppäintä + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Osoite on käytössä vain rajoitetun ajan. - - This is just the demo version of %1. - Tämä on vain %1n kokeiluversio. - - - It allows you to test the application and see if it works for you. - Voit kokeilla ohjelmaa nähdäksesi, toimiiko se. - - - Get the full version - Hanki täysi versio - - - Continue - Jatka - Downloading %1 Ladataan %1ta/tä @@ -858,6 +854,10 @@ Subscribe to %1 Tilaa kanava %1 + + Switched to %1 + + Unsubscribed from %1 Kohteen %1 tilaus lopetettu @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - Katsottu %1 kertaa + Pick a video + Valitse video + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 / %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Etsitään... - Show %1 More Näytä %1 lisää @@ -1071,25 +1070,16 @@ Tervetuloa <a href='%1'>%2en</a> - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Syötä + to start watching videos. + aloittaaksesi videoiden katselu. a keyword hakusana - a channel - kanava - - - to start watching videos. - aloittaaksesi videoiden katselu. - - - Watch - Katso + Enter + Syötä Recent keywords @@ -1110,6 +1100,10 @@ &Back &Takaisin + + &Forward + &Eteenpäin + Forward to %1 Eteenpäin kohteeseen %1 @@ -1163,13 +1157,6 @@ Ladataan %1... - - Video - - Cannot get video stream for %1 - Videostriimiä ei saada kohteelle %1 - - YTRegions @@ -1365,4 +1352,11 @@ Maailmanlaajuinen + + YTVideo + + Cannot get video stream for %1 + Videostriimiä ei saada kohteelle %1 + + \ No newline at end of file diff --git a/locale/fi_FI.ts b/locale/fi_FI.ts index 9af8f00..7dd0a99 100644 --- a/locale/fi_FI.ts +++ b/locale/fi_FI.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Käännä %1 äidinkielellesi käyttämällä %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Kuvakkeen suunnitteli %1. @@ -96,7 +104,7 @@ AppWidget Download - + Lataa @@ -155,6 +163,10 @@ Show Updated Näytä päivitetyt + + You have no subscriptions. Use the star symbol to subscribe to channels. + Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia. + All Videos Kaikki videot @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Päivitettyjä tilauksia ei ole tällä hetkellä. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Sinulla ei ole tilauksia. Käytä tähtisymbolia tilataksesi kanavia. - - - - ClearButton - - Clear - Tyhjennä - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + Katsottu %1 kertaa + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Tämä on vain %1-kokeiluversio. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Voit ladata vain videoita jotka ovat lyhyempiä kuin %1 minuuttia, jotta voit testata latausominaisuutta. - - - Continue - Jatka - - - Get the full version - Hanki täysi versio - %1 downloaded in %2 %1 ladattu ajassa %2 @@ -632,10 +636,6 @@ &Float on Top &Pysy päällimmäisenä - - &Adjust Window Size - &Muuta ikkunan kokoa - &Stop After This Video Py&säytä toisto tämän videon jälkeen @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + Näytä/piilota &valikkopalkki + + + Menu + Valikko + &Love %1? Rate it! &Pidätkö %1sta? Arvostele se! @@ -796,6 +804,10 @@ Update Päivitä + + You can still access the menu bar by pressing the ALT key + Saat valikkopalkin näkyviin painamalla ALT-näppäintä + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Osoite on käytössä vain rajoitetun ajan. - - This is just the demo version of %1. - Tämä on vain %1n kokeiluversio. - - - It allows you to test the application and see if it works for you. - Voit kokeilla ohjelmaa nähdäksesi, toimiiko se. - - - Get the full version - Hanki täysi versio - - - Continue - Jatka - Downloading %1 Ladataan %1ta/tä @@ -858,6 +854,10 @@ Subscribe to %1 Tilaa kanava %1 + + Switched to %1 + + Unsubscribed from %1 Lopeta kohteen %1 tilaaminen @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - Katsottu %1 kertaa + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 / %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Etsitään... - Show %1 More Näytä %1 lisää @@ -1071,25 +1070,16 @@ Tervetuloa <a href='%1'>%2en</a> - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Syötä + to start watching videos. + aloittaaksesi videoiden katselu. a keyword hakusana - a channel - kanava - - - to start watching videos. - aloittaaksesi videoiden katselu. - - - Watch - Katso + Enter + Syötä Recent keywords @@ -1110,6 +1100,10 @@ &Back &Takaisin + + &Forward + + Forward to %1 Eteenpäin kohteeseen %1 @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - Videostriimiä ei saada kohteelle %1 + Ladataan %1... @@ -1365,4 +1352,11 @@ Maailmanlaajuinen + + YTVideo + + Cannot get video stream for %1 + Videostriimiä ei saada kohteelle %1 + + \ No newline at end of file diff --git a/locale/fr.ts b/locale/fr.ts index 3392dd0..978bb23 100644 --- a/locale/fr.ts +++ b/locale/fr.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traduisez %1 dans votre langue native en utilisant %2 + + Powered by %1 + + + + Open-source software + Logiciel open-source + Icon designed by %1. Icône dessinée par %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + Vous avez %n nouvelle vidéoVous avez %n nouvelles vidéos @@ -155,6 +163,10 @@ Show Updated Afficher les mises à jours + + You have no subscriptions. Use the star symbol to subscribe to channels. + Vous n'avez pas d'abonnements. Utilisez le symbole en forme d'étoile pour vous abonner à des chaines, + All Videos Toutes les vidéos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Il n'y a pas d'abonnements mis à jour en ce moment. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Vous n'avez pas d'abonnements. Utilisez le symbole en forme d'étoile pour vous abonner à des chaines, - - - - ClearButton - - Clear - Nettoyer - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + Il y a une heureIl y a %n heures %n day(s) ago - + Il y a %n jourIl y a %n jours - %n weeks(s) ago - + %n month(s) ago + Il y a %n moisIl y a %n mois + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + %1 vues - %n month(s) ago - + %n week(s) ago + Il y a %n semaineIl y a %n semaines @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Il s'agit seulement de la version de démonstration de %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - 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. - - - Continue - Continuer - - - Get the full version - Obtenir la version complète - %1 downloaded in %2 %1 téléchargé sur %2 @@ -277,7 +281,7 @@ %n Download(s) - + %n téléchargement%n téléchargements @@ -632,10 +636,6 @@ &Float on Top &Laisser au dessus - - &Adjust Window Size - &Ajuster la taille de la fenètre - &Stop After This Video &Arrêter après cette vidéo @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Masquer les vidéos qui peuvent contenir un contenu inapproprié + + Toggle &Menu Bar + Bascule la barre de &Menu + + + Menu + Menu + &Love %1? Rate it! &Aimer %1? Notez-le ! @@ -796,6 +804,10 @@ Update Mettre à jour + + You can still access the menu bar by pressing the ALT key + Vous pouvez continuer d'accéder à la barre de menu en appuyant sur la touche ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Le lien ne sera valide que pour un temps limité. - - This is just the demo version of %1. - C'est juste la version démo de %1. - - - It allows you to test the application and see if it works for you. - Cela vous permet de tester l'application et voir si cela fonctionne pour vous. - - - Get the full version - Obtenir la version complète - - - Continue - Continuer - Downloading %1 %1 Téléchargement @@ -858,6 +854,10 @@ Subscribe to %1 S'abonner à %1 + + Switched to %1 + + Unsubscribed from %1 Se désabonner de %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 vues + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Recherche en cours… - Show %1 More Afficher %1 de plus @@ -1071,25 +1070,16 @@ Bienvenue sur <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Entrer + to start watching videos. + pour commencer à regarder des vidéos. a keyword un mot-clé - a channel - une chaîne - - - to start watching videos. - pour commencer à regarder des vidéos. - - - Watch - Regarder + Enter + Entrer Recent keywords @@ -1110,6 +1100,10 @@ &Back &Retour + + &Forward + + Forward to %1 Continuer à %1 @@ -1163,13 +1157,6 @@ Téléchargement de %1... - - Video - - Cannot get video stream for %1 - Impossible d'obtenir le flux vidéo de %1 - - YTRegions @@ -1365,4 +1352,11 @@ Monde entier + + YTVideo + + Cannot get video stream for %1 + Impossible d'obtenir le flux vidéo de %1 + + \ No newline at end of file diff --git a/locale/gl.ts b/locale/gl.ts index ae4941f..9bd8a11 100644 --- a/locale/gl.ts +++ b/locale/gl.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traducir %1 ao seu idioma empregando %2 + + Powered by %1 + Coa tecnoloxía de %1 + + + Open-source software + Software de fontes abertas + Icon designed by %1. Icona deseñada por %1. @@ -96,7 +104,7 @@ AppWidget Download - + Descarga @@ -107,7 +115,7 @@ You have %n new video(s) - + Dispón dun novo vídeoDispón de %n novos vídeos @@ -155,6 +163,10 @@ Show Updated Amosar a actualización + + You have no subscriptions. Use the star symbol to subscribe to channels. + Non ten subscricións. Utilice o símbolo da estrela para subscribirse ás canles. + All Videos Todos os vídeos @@ -175,39 +187,47 @@ There are no updated subscriptions at this time. Neste momento non ten subscricións de actualización. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Non ten subscricións. Utilice o símbolo da estrela para subscribirse ás canles. - - - - ClearButton - - Clear - Limpar - DataUtils Just now - + Agora mesmo %n hour(s) ago - + Hai %n horaHai %n horas %n day(s) ago - + Hai %n diaHai %n dias - %n weeks(s) ago - + %n month(s) ago + Hai %n mesHai %n meses + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + %1 vistas - %n month(s) ago - + %n week(s) ago + Hai %n semanaHai %n semanas @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Isto é só a versión demo de %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Só se poden descargar vídeos curtos de menos de %1 minutos para que poida probar a utilidade de descargas. - - - Continue - Continuar - - - Get the full version - Obter a versión completa - %1 downloaded in %2 %1 descargado en %2 @@ -277,7 +281,7 @@ %n Download(s) - + %n descarga%n descargas @@ -314,19 +318,19 @@ Extra The executable file has been tempered with, maybe by a virus. - + O ficheiro executable foi manipulado, quizais por un virus. %1 will not run. Try installing again. - + %1 non se executará. Probe a instalalo de novo. Quit - + Saír Reinstall - + Volver instalar @@ -594,7 +598,7 @@ Show details about video downloads - Mostrar os detalles sobre as descargas de vídeo + Amosar os detalles sobre as descargas de vídeo &Download @@ -632,10 +636,6 @@ &Float on Top &Flotante e arriba - - &Adjust Window Size - - &Stop After This Video &Deter despois deste vídeo @@ -666,11 +666,19 @@ Restricted Mode - + Modo restrinxido Hide videos that may contain inappropriate content - + Agochar os vídeos que poidan conter contido inadecuado + + + Toggle &Menu Bar + Alternar a barra do &menú + + + Menu + Menú &Love %1? Rate it! @@ -750,7 +758,7 @@ &Loading... - + &Cargando... Leave &Full Screen @@ -796,6 +804,10 @@ Update Actualizar + + You can still access the menu bar by pressing the ALT key + Aínda pode acceder á barra de menús premendo a tecla ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. A ligazón ten validez só por un tempo limitado. - - This is just the demo version of %1. - Isto é só a versión demo de %1. - - - It allows you to test the application and see if it works for you. - Permítelle probar o aplicativo e comprobar se vai ao seu xeito. - - - Get the full version - Obter a versión completa - - - Continue - Continuar - Downloading %1 Descargando %1 @@ -839,7 +835,7 @@ part This is for video parts, as in 'Cool video - part 1' - peza + parte episode @@ -848,7 +844,7 @@ Sent from %1 - Enviado desde %1 + Enviado dende %1 Unsubscribe from %1 @@ -858,13 +854,17 @@ Subscribe to %1 Subscribirse a %1 + + Switched to %1 + Cambiado a %1 + Unsubscribed from %1 - + Non está subscrito a %1 Subscribed to %1 - + Subscrito a %1 @@ -879,15 +879,15 @@ Would you like to download it now? - Queres descargala agora? + Quere descargala agora? Skip This Version - Saltar esta versión + Omitir esta versión Remind Me Later - Acórdamo máis adiante + Lembremo máis adiante Install Update @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 vistas + Pick a video + Deleccione un vídeo + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -933,7 +936,7 @@ Show in %1 - Mostrar en %1 + Amosar en %1 Open parent folder @@ -946,13 +949,9 @@ PlaylistModel - - Searching... - Buscando... - Show %1 More - Mostrar %1 máis + Amosar %1 máis No videos @@ -1071,25 +1070,16 @@ Benvido a <a href='%1'>%2</a, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Introduza + to start watching videos. + para comezar a ver vídeos. a keyword unha palabra clave - a channel - unha canle - - - to start watching videos. - para comezar a ver vídeos. - - - Watch - Ver + Enter + Introduza Recent keywords @@ -1110,6 +1100,10 @@ &Back &Atrás + + &Forward + A&diante + Forward to %1 Ir a %1 @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - Non é posíbel obter o fluxo de vídeo de %1 + Descargando %1... @@ -1365,4 +1352,11 @@ Todo o mundo + + YTVideo + + Cannot get video stream for %1 + Non é posíbel obter o fluxo de vídeo de %1 + + \ No newline at end of file diff --git a/locale/he_IL.ts b/locale/he_IL.ts index 9dfd611..fffa966 100644 --- a/locale/he_IL.ts +++ b/locale/he_IL.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 ניתן לתרגם את %1 לשפת אמך באמצעות %2 + + Powered by %1 + מופעל על ידי %1 + + + Open-source software + תכנה בקוד פתוח + Icon designed by %1. הסמל עוצב על ידי %1. @@ -96,7 +104,7 @@ AppWidget Download - + הורד @@ -107,7 +115,7 @@ You have %n new video(s) - + יש לך סרטון חדשיש לך שני סרטונים חדשיםיש לך %n סרטונים חדשיםיש לך %n סרטונים חדשים @@ -155,6 +163,10 @@ Show Updated הצג עדכונים + + You have no subscriptions. Use the star symbol to subscribe to channels. + אין לך מנויים. השתמש בסמל הכוכב על מנת להיות מנוי לערוצים. + All Videos כל הסרטונים @@ -175,39 +187,47 @@ There are no updated subscriptions at this time. אין עדכונים בערוצים שאתה מנוי עליהם כרגע. - - You have no subscriptions. Use the star symbol to subscribe to channels. - אין לך מנויים. השתמש בסמל הכוכב על מנת להיות מנוי לערוצים. - - - - ClearButton - - Clear - מחיקה - DataUtils Just now - + זה עתה %n hour(s) ago - + לפני שעהלפני שעתייםלפני %n שעותלפני %n שעות %n day(s) ago - + אתמולשלשוםלפני %n ימיםלפני %n ימים - %n weeks(s) ago - + %n month(s) ago + לפני חודשלפני חודשייםלפני %n חודשיםלפני %n חודשים + + + K + K as in Kilo, i.e. thousands + ק׳ + + + M + M stands for Millions + מ׳ + + + B + B stands for Billions + ב׳ + + + %1 views + %1 צפיות - %n month(s) ago - + %n week(s) ago + לפני שבועלפני שבועייםלפני %n שבועותלפני %n שבועות @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - זוהי רק גרסת ההדגמה של %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - באמצעות גרסה זו ניתן להוריד קטעי וידאו שאורכם אינו עולה על %1 דקות כדי שתהיה באפשרותך לבחור את אפשרות ההורדה. - - - Continue - המשך - - - Get the full version - קבלת הגרסה המלאה - %1 downloaded in %2 %1 התקבל במהירות של %2 @@ -277,7 +281,7 @@ %n Download(s) - + הורדה אחתשתי הורדות%n הורדות%n הורדות @@ -314,19 +318,19 @@ Extra The executable file has been tempered with, maybe by a virus. - + הקובץ שונה,אולי על ידי וירוס. %1 will not run. Try installing again. - + %1 לא ניתן להפעלה,נסה להתקין מחדש. Quit - + יציאה Reinstall - + התקן מחדש @@ -632,10 +636,6 @@ &Float on Top &ציפה מלמעלה - - &Adjust Window Size - - &Stop After This Video ל&עצור לאחר וידאו זה @@ -666,11 +666,19 @@ Restricted Mode - + מצב מוגבל Hide videos that may contain inappropriate content - + הסתר סרטונים שעשויים להכיל תוכן לא הולם + + + Toggle &Menu Bar + החלפת מ&צב סרגל תפריט + + + Menu + תפריט &Love %1? Rate it! @@ -750,7 +758,7 @@ &Loading... - + %טוען... Leave &Full Screen @@ -796,6 +804,10 @@ Update עדכון + + You can still access the menu bar by pressing the ALT key + עדיין ניתן לגשת לסרגל התפריט עם לחיצה על ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. הקישור יהיה תקף לזמן מוגבל בלבד. - - This is just the demo version of %1. - זוהי רק גרסת ההדגמה של %1. - - - It allows you to test the application and see if it works for you. - גרסה זו מאפשרת לך לבחון את היישום ולראות האם הוא מתאים לצרכיך. - - - Get the full version - קבלת הגרסה המלאה - - - Continue - המשך - Downloading %1 %1 מתקבל @@ -858,13 +854,17 @@ Subscribe to %1 מנוי ל %1 + + Switched to %1 + הוחלף אל %1 + Unsubscribed from %1 - + בוטלה הרשמה מ%1 Subscribed to %1 - + בוצע רישום ל%1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 צפיות + Pick a video + נא לבחור סרטון + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 מתוך %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - בהליכי חיפוש... - Show %1 More הצגת %1 נוספים @@ -1071,25 +1070,16 @@ ברוך בואך אל <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - הזנה + to start watching videos. + כדי להתחיל לצפות בסרטונים. a keyword מילת מפתח - a channel - ערוץ - - - to start watching videos. - כדי להתחיל לצפות בסרטונים. - - - Watch - צפייה + Enter + הזנה Recent keywords @@ -1110,6 +1100,10 @@ &Back הקודם + + &Forward + ה&עברה + Forward to %1 העבר אל %1 @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - לא ניתן לקבל את תזרים הווידאו עבור %1 + מוריד %1... @@ -1365,4 +1352,11 @@ כל העולם + + YTVideo + + Cannot get video stream for %1 + לא ניתן לקבל את תזרים הווידאו עבור %1 + + \ No newline at end of file diff --git a/locale/hr.ts b/locale/hr.ts index 0ef5015..ce41669 100644 --- a/locale/hr.ts +++ b/locale/hr.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Prevedite %1 na svoj jezik koristeći %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Dizajn ikone %1. @@ -155,6 +163,10 @@ Show Updated Prikaži ažurirano + + You have no subscriptions. Use the star symbol to subscribe to channels. + Niste pretplaćeni na ni jedan kanal. Za pretplatu kliknite na zvijezdicu. + All Videos Svi videozapisi @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Trenutno nema dostupnih ažuriranja pretplata - - You have no subscriptions. Use the star symbol to subscribe to channels. - Niste pretplaćeni na ni jedan kanal. Za pretplatu kliknite na zvijezdicu. - - - - ClearButton - - Clear - ObriÅ¡i - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 pregleda + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Ovo je samo probna verzija %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Može preuzeti samo video kraći od %1 minuta tako da možete testirati mogućnost preuzimanja. - - - Continue - Nastavi - - - Get the full version - Preuzmi punu verziju - %1 downloaded in %2 %1 preuzet u %2 @@ -632,10 +636,6 @@ &Float on Top &Budi na vrhu - - &Adjust Window Size - - &Stop After This Video &Stani nakon ovog videa @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! @@ -796,6 +804,10 @@ Update Ažuriraj + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Link će biti valjan samo ograničeno vrijeme. - - This is just the demo version of %1. - Ovo je samo demo verzija %1. - - - It allows you to test the application and see if it works for you. - Omogućava Vam da testirate program i vidite da li Vam odgovara. - - - Get the full version - Preuzmi punu verziju - - - Continue - Nastavi - Downloading %1 Preuzimam %1 @@ -858,6 +854,10 @@ Subscribe to %1 Pretplata na %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 pregleda + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 od %2 (%3) - %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Pretražujem... - Show %1 More Prikaži %1 viÅ¡e @@ -1071,25 +1070,16 @@ DobrodoÅ¡li u <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Unesi + to start watching videos. + da počnete gledati video. a keyword ključna riječ - a channel - kanal - - - to start watching videos. - da počnete gledati video. - - - Watch - Gledaj + Enter + Unesi Recent keywords @@ -1110,6 +1100,10 @@ &Back &Nazad + + &Forward + + Forward to %1 Naprijed na %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Ne mogu naći video stream za %1 - - YTRegions @@ -1365,4 +1352,11 @@ Å irom svijeta + + YTVideo + + Cannot get video stream for %1 + Ne mogu naći video stream za %1 + + \ No newline at end of file diff --git a/locale/hu.ts b/locale/hu.ts index 46e53e9..9d60117 100644 --- a/locale/hu.ts +++ b/locale/hu.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Fordítsa le a %1 programot az anyanyelvére a következővel: %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikon tervezője: %1 @@ -96,7 +104,7 @@ AppWidget Download - + Letöltés @@ -155,6 +163,10 @@ Show Updated Frissítések mutatása + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nincsenek feliratkozások. A csillag szimbólumot kell használni a csatornákra való feliratkozáshoz. + All Videos Összes videó @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Nincs frissítés a feliratkozott csatornákon. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nincsenek feliratkozások. A csillag szimbólumot kell használni a csatornákra való feliratkozáshoz. - - - - ClearButton - - Clear - Törlés - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 néző + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Ez csak a demó verziója a %1 programnak. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Csak %1 percnél rövidebb videók tölthetők le vele a letöltési funkciók teszteléséhez. - - - Continue - Folytatás - - - Get the full version - Teljes verzió beszerzése - %1 downloaded in %2 %1 letöltve ide: %2 @@ -632,10 +636,6 @@ &Float on Top &Többi ablak fölött - - &Adjust Window Size - &Ablak méretének beállítása - &Stop After This Video &Videó után leállítás @@ -666,10 +666,18 @@ Restricted Mode - + Korlátozott mód Hide videos that may contain inappropriate content + Esetleg nem megfelelő tartalmú videók elrejtése + + + Toggle &Menu Bar + + + + Menu @@ -796,6 +804,10 @@ Update Frissítés + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. A hivatkozás csak korlátozott ideig lesz érvényben. - - This is just the demo version of %1. - Ez csak a demó verziója a %1 programnak. - - - It allows you to test the application and see if it works for you. - Kipróbálhatja az alkalmazást, hogy megfelel-e az igényeinek. - - - Get the full version - Teljes verzió beszerzése - - - Continue - Folytatás - Downloading %1 Letöltés: %1 @@ -858,6 +854,10 @@ Subscribe to %1 Feliratkozás az alábbi csatornára: %1 + + Switched to %1 + + Unsubscribed from %1 Leiratkozva %1-ról/ről. @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 néző + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 %2 közül (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Keresés... - Show %1 More További %1 elem megjelenítése @@ -1071,25 +1070,16 @@ Üdvözli a <a href='%1'>%2</a> program, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Írjon be + to start watching videos. + a videók megtekintéséhez. a keyword egy kulcsszót - a channel - egy csatornát - - - to start watching videos. - a videók megtekintéséhez. - - - Watch - Megtekintés + Enter + Írjon be Recent keywords @@ -1110,6 +1100,10 @@ &Back &Vissza + + &Forward + + Forward to %1 Tovább a %1-re @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - Nem található videó adatfolyam a következőhöz: %1 + Letöltés %1... @@ -1365,4 +1352,11 @@ Világszerte + + YTVideo + + Cannot get video stream for %1 + Nem található videó adatfolyam a következőhöz: %1 + + \ No newline at end of file diff --git a/locale/id.ts b/locale/id.ts index 9daf0b8..2672b63 100644 --- a/locale/id.ts +++ b/locale/id.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Terjemahkan %1 ke bahasa aslimu dengan menggunakan %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Desain ikon oleh %1. @@ -155,6 +163,10 @@ Show Updated Acara Terbarui + + You have no subscriptions. Use the star symbol to subscribe to channels. + Anda tidak punya langganan. Gunakan simbol bintang untuk berlangganan ke saluran. + All Videos Semua video @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Tidak ada langganan yang diperbarui saat ini - - You have no subscriptions. Use the star symbol to subscribe to channels. - Anda tidak punya langganan. Gunakan simbol bintang untuk berlangganan ke saluran. - - - - ClearButton - - Clear - Bersih - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 tampilan + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Ini adalah hanya versi demo dari %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Itu bisanya hanya download video yang pendek daripada %1 menit sehingga kamu bisa menguji fungsinya downloadnya. - - - Continue - Teruskan - - - Get the full version - Dapatkan versi komplitnya - %1 downloaded in %2 %1 diunduh dalam %2 @@ -632,10 +636,6 @@ &Float on Top &Mengambang ke puncak - - &Adjust Window Size - - &Stop After This Video &berhenti setelah video ini @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Love %1? Beri Nilai! @@ -796,6 +804,10 @@ Update Pembaruan data + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Link akan berlaku hanya untuk waktu yang terbatas. - - This is just the demo version of %1. - This is just the demo version of %1. - - - It allows you to test the application and see if it works for you. - Ini memungkinkan Anda untuk menguji aplikasi dan melihat apakah ia bekerja untuk Anda. - - - Get the full version - Get the full version - - - Continue - Continue - Downloading %1 Downloading %1 @@ -858,6 +854,10 @@ Subscribe to %1 Berlangganan ke %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 tampilan + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 dari %2 (%3) --- %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Sedang mencari... - Show %1 More Tampilkan lebih banyak %1 @@ -1071,25 +1070,16 @@ Selamat datang ke <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Masukkan + to start watching videos. + to start watching videos. a keyword sebuah kata kunci - a channel - a channel - - - to start watching videos. - to start watching videos. - - - Watch - Watch + Enter + Masukkan Recent keywords @@ -1110,6 +1100,10 @@ &Back &Kembali + + &Forward + + Forward to %1 Maju sampai %1 @@ -1163,13 +1157,6 @@ Mengunduh %1... - - Video - - Cannot get video stream for %1 - Tidak bisa mendapatkan video stream untuk %1 - - YTRegions @@ -1365,4 +1352,11 @@ Dunia + + YTVideo + + Cannot get video stream for %1 + Tidak bisa mendapatkan video stream untuk %1 + + \ No newline at end of file diff --git a/locale/it.ts b/locale/it.ts index 8625647..3445da6 100644 --- a/locale/it.ts +++ b/locale/it.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traduci %1 nella tua lingua usando %2 + + Powered by %1 + Utilizza %1 + + + Open-source software + Software open-source + Icon designed by %1. Icona disegnata da %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + C'è un nuovo videoCi sono %n nuovi video @@ -155,6 +163,10 @@ Show Updated Mostra aggiornati + + You have no subscriptions. Use the star symbol to subscribe to channels. + Non hai iscrizioni. Usa il simbolo della stella per sottoscrivere i canali. + All Videos Tutti i video @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Non ci sono iscrizioni aggiornate in questo momento. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Non hai iscrizioni. Usa il simbolo della stella per sottoscrivere i canali. - - - - ClearButton - - Clear - Cancella - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + Un'ora fa%n ore fa %n day(s) ago - + Ieri%n giorni fa - %n weeks(s) ago - + %n month(s) ago + Un mese fa%n mesi fa + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + %1 visualizzazioni - %n month(s) ago - + %n week(s) ago + Una settimana fa%n settimane fa @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Questa è solo la versione demo di %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Puoi scaricare solo video più corti di %1 minuti, così puoi testare la funzionalità dei download. - - - Continue - Continua - - - Get the full version - Compra la versione completa - %1 downloaded in %2 %1 scaricato in %2 @@ -277,7 +281,7 @@ %n Download(s) - + Un download%n download @@ -632,10 +636,6 @@ &Float on Top &Fluttua in alto - - &Adjust Window Size - &Adatta le dimensioni della finestra - &Stop After This Video &Ferma dopo questo video @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Nascondi contenuti inappropriati + + Toggle &Menu Bar + Mostra/Nascondi &Menu + + + Menu + Menu + &Love %1? Rate it! Ti piace %1? @@ -796,6 +804,10 @@ Update Aggiorna + + You can still access the menu bar by pressing the ALT key + Puoi accedere di nuovo al menu premendo ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Il link rimarrà valido per un periodo di tempo limitato. - - This is just the demo version of %1. - Questa è solo la versione demo di %1. - - - It allows you to test the application and see if it works for you. - Ti permette di testare l'applicazione e verificare che funzioni sul tuo computer. - - - Get the full version - Compra la versione completa - - - Continue - Continua - Downloading %1 Scarica in: %1 @@ -858,6 +854,10 @@ Subscribe to %1 Iscriviti a %1 + + Switched to %1 + Passato a %1 + Unsubscribed from %1 Iscrizione a %1 annullata @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visualizzazioni + Pick a video + Scegli un video + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 di %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Ricerca... - Show %1 More Mostra altri %1 @@ -1071,25 +1070,16 @@ Benvenuto su <a href="%1">%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Scrivi + to start watching videos. + per iniziare a guardare i video. a keyword una parola chiave - a channel - un canale - - - to start watching videos. - per iniziare a guardare i video. - - - Watch - Guarda + Enter + Scrivi Recent keywords @@ -1110,6 +1100,10 @@ &Back &Indietro + + &Forward + &Avanti + Forward to %1 Avanza a %1 @@ -1163,13 +1157,6 @@ Download di %1 - - Video - - Cannot get video stream for %1 - Impossibile ottenere il flusso video per %1 - - YTRegions @@ -1365,4 +1352,11 @@ Tutto il mondo + + YTVideo + + Cannot get video stream for %1 + Impossibile ottenere il flusso video per %1 + + \ No newline at end of file diff --git a/locale/ja_JP.ts b/locale/ja_JP.ts index 40a6fc7..ed2e914 100644 --- a/locale/ja_JP.ts +++ b/locale/ja_JP.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 %2を使って、%1をあなたの言語に翻訳してください + + Powered by %1 + + + + Open-source software + + Icon designed by %1. アイコンは%1さんのデザインです。 @@ -96,7 +104,7 @@ AppWidget Download - + ダウンロード @@ -155,6 +163,10 @@ Show Updated 動画の更新を確認 + + You have no subscriptions. Use the star symbol to subscribe to channels. + 購読済みのものがありません。チャンネルを購読するには星マークをクリックします。 + All Videos 全ての動画 @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. 購読したチャンネルに更新はありません。 - - You have no subscriptions. Use the star symbol to subscribe to channels. - 購読済みのものがありません。チャンネルを購読するには星マークをクリックします。 - - - - ClearButton - - Clear - クリア - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1回 閲覧 + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - これは%1 の試用版です。 - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - 動画を%1分程度でダウンロードできる機能を試すことができます。 - - - Continue - 続ける - - - Get the full version - 製品版を入手する - %1 downloaded in %2 %1を%2にダウンロード中 @@ -632,10 +636,6 @@ &Float on Top 最前面に表示(&F) - - &Adjust Window Size - &ウィンドウの大きさを調節 - &Stop After This Video 再生終了後に停止(&S) @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! %1を評価(&L) @@ -796,6 +804,10 @@ Update 更新 + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. リンクは制限時間内のみ有効です。 - - This is just the demo version of %1. - これは%1 の試用版です。 - - - It allows you to test the application and see if it works for you. - アプリケーションのテストや動作確認にご利用いただけます。 - - - Get the full version - 製品版を入手する - - - Continue - 続ける - Downloading %1 %1をダウンロード中 @@ -858,6 +854,10 @@ Subscribe to %1 %1を購読する + + Switched to %1 + + Unsubscribed from %1 %1の購読を解除しました @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1回 閲覧 + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1は%2(%3)%4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - 検索中... - Show %1 More さらに%1件表示 @@ -1071,25 +1070,16 @@ ようこそ<a href='%1'>%2</a>へ - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - キーワードを入力して + to start watching videos. + を検索する。 a keyword 動画 - a channel - チャンネル - - - to start watching videos. - を検索する。 - - - Watch - 検索 + Enter + キーワードを入力して Recent keywords @@ -1110,6 +1100,10 @@ &Back 戻る(&B) + + &Forward + + Forward to %1 %1 に進む @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - %1の動画を取得できませんでした - - YTRegions @@ -1365,4 +1352,11 @@ 全世界 + + YTVideo + + Cannot get video stream for %1 + %1の動画を取得できませんでした + + \ No newline at end of file diff --git a/locale/ko_KR.ts b/locale/ko_KR.ts index 87ec5f9..1240d49 100644 --- a/locale/ko_KR.ts +++ b/locale/ko_KR.ts @@ -19,12 +19,20 @@ Please <a href='%1'>donate</a> to support the continued development of %2. - %2의 계속된 개발을 위해 <a href='%1'>기부</a>룰 해주요... + %2의 계속된 개발을 위해 <a href='%1'>기부</a>룰 해주세요. Translate %1 to your native language using %2 %2을(를) 사용해서 %1를 사용자의 언어로 번역 하세요. + + Powered by %1 + + + + Open-source software + + Icon designed by %1. 아이콘 디자인: %1 @@ -65,7 +73,7 @@ This demo has expired. - 데모 만료! + 데모버전이 만료되었습니다! The full version allows you to watch videos without interruptions. @@ -77,7 +85,7 @@ By purchasing the full version, you will also support the hard work I put into creating %1. - 구입하면, 제가 %1를 만드는데 드는 노력을 지원 합니다. + 구입하면, 개발자가 %1를 만드는데 드는 소중한 노력을 지원 합니다. Use Demo @@ -96,7 +104,7 @@ AppWidget Download - + 다운로드 @@ -118,7 +126,7 @@ Unwatched Videos - 안 본 비디오 + 시청하지 않은 비디오 @@ -149,23 +157,27 @@ Mark all as watched - 모두 본 걸로 표시 + 모두 시청함으로 표시 Show Updated 업데이트 표시 + + You have no subscriptions. Use the star symbol to subscribe to channels. + 구독 항목이 없습니다. 채널을 구독 하려면 별 아이콘을 사용 하세요. + All Videos 모든 비디오 Unwatched Videos - 안 본 비디오 + 시청하지 않은 비디오 Mark as Watched - 본 비디오로 마크 + 모두 시청함으로 표시 Unsubscribe @@ -175,23 +187,12 @@ There are no updated subscriptions at this time. 현재 업데이트된 구독이 없습니다. - - You have no subscriptions. Use the star symbol to subscribe to channels. - 구독 항목이 없습니다. 채널을 구독 하려면 별 아이콘을 사용 하세요. - - - - ClearButton - - Clear - 지우기 - DataUtils Just now - + 지금 %n hour(s) ago @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 조회수 + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - %1의 데모 버전 입니다. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - 사용자가 다운로드 기능을 테스트 할수 있도록 %1보다 짧은 비디오만 다운로드 됩니다. - - - Continue - 계속 - - - Get the full version - 풀 버전 구입 - %1 downloaded in %2 %1 downloaded in %2 @@ -314,19 +318,19 @@ Extra The executable file has been tempered with, maybe by a virus. - + 실행 파일이 바이러스 또는 다른 외부 원인에 의해 손상되었습니다. %1 will not run. Try installing again. - + %1을 실행할 수 없습니다. 다시 설치하시기 바랍니다. Quit - + 종료 Reinstall - + 재설치 @@ -369,15 +373,15 @@ Mute - 무음 + 음소거 Seek forward - 앞으로찾기 + 되감기 Seek backward - 뒤로찾기 + 빨리감기 @@ -422,11 +426,11 @@ MainWindow &Window - + ì°½ (&W) &Minimize - + 최소화 (&M) &Stop @@ -434,7 +438,7 @@ Stop playback and go back to the search view - 재생을 멈추고 검색 보기로 이동 + 재생을 멈추고 검색 창로 이동 P&revious @@ -442,7 +446,7 @@ Go back to the previous track - 이전트랙으로 이동 + 이전 트랙으로 이동 S&kip @@ -526,7 +530,7 @@ Move &Down - 아래로이동(&D) + 아래로 이동(&D) Move down the selected videos in the playlist @@ -546,7 +550,7 @@ Bye - 안녕... + 안녕히 가세요. &Website @@ -632,10 +636,6 @@ &Float on Top 위에 떠있기(&F) - - &Adjust Window Size - - &Stop After This Video 이 비디오 재생 후 정지(&S) @@ -666,10 +666,18 @@ Restricted Mode - + 제한된 보기 Hide videos that may contain inappropriate content + 부적절한 내용을 포함한 동영상 숨기기 + + + Toggle &Menu Bar + + + + Menu @@ -750,7 +758,7 @@ &Loading... - + 로딩중 (&L) Leave &Full Screen @@ -766,11 +774,11 @@ Volume is muted - 볼륨 소거됨 + 볼륨 음소거됨 Volume is unmuted - 볼륨 소거 해제됨 + 볼륨 응소거 해제됨 Maximum video definition set to %1 @@ -796,6 +804,10 @@ Update 업데이트 + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. 해당 링크는 제한된 시간 동안만 유효 합니다. - - This is just the demo version of %1. - %1의 데모 버전 입니다.%1의 데모 버전 입니다. - - - It allows you to test the application and see if it works for you. - 이 버전으로 프로그램이 사용자 필요에 맞는지 테스트 할수 있습니다. - - - Get the full version - 풀 버전 구입 - - - Continue - 계속 - Downloading %1 %1 다운로드 @@ -834,7 +830,7 @@ of Used in video parts, as in '2 of 3' - / + of part @@ -859,12 +855,16 @@ %1 구독 - Unsubscribed from %1 + Switched to %1 + + Unsubscribed from %1 + %1 구독 해지됨 + Subscribed to %1 - + %1 구독됨 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 views + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 of %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - 검색중... - Show %1 More %1 추가 보기 @@ -1071,25 +1070,16 @@ <a href='%1'>%2</a>에 오신걸 환영 합니다! - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - 언터 + to start watching videos. + 비디오 보기 시작 a keyword 키워드 - a channel - 채널 - - - to start watching videos. - 비디오 보기 시작 - - - Watch - 감상 + Enter + 언터 Recent keywords @@ -1110,6 +1100,10 @@ &Back 뒤로(&B) + + &Forward + + Forward to %1 %1(으)로 이동 @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - %1의 비디오 가져올수 없음 + %1 다운로드중 @@ -1362,7 +1349,14 @@ Worldwide - 전세계적 + 전세계 + + + + YTVideo + + Cannot get video stream for %1 + %1의 비디오 가져올수 없음 \ No newline at end of file diff --git a/locale/ky.ts b/locale/ky.ts index e379eef..be95ecc 100644 --- a/locale/ky.ts +++ b/locale/ky.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 %2 аркылуу %1'ду өз эне тилиңизге которуңуз + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Иконканын автору %1. @@ -155,6 +163,10 @@ Show Updated Жаңыланганын көрсөтүү + + You have no subscriptions. Use the star symbol to subscribe to channels. + Сизде жазылуулар жок. Каналдарга жазылуу үчүн жылдызча символун колдонуңуз. + All Videos Бардык видеолор @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Учурда жаңыланган жазылуулар жок. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Сизде жазылуулар жок. Каналдарга жазылуу үчүн жылдызча символун колдонуңуз. - - - - ClearButton - - Clear - Тазалоо - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 көрүү + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Бул жөн эле %1'дун демо-версиясы. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Жүктөө функционалдуулугун текшерүү үчүн, бул %1 минутадан кыскараак видеолорду гана жүктөп бере алат. - - - Continue - Улантуу - - - Get the full version - Толук жоромолун алуу - %1 downloaded in %2 %2 жерине %1 файлы жүктөлдү @@ -632,10 +636,6 @@ &Float on Top Үстүнөн &калкытуу - - &Adjust Window Size - - &Stop After This Video Бул видеодон кийин &токтотуу @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! @@ -796,6 +804,10 @@ Update Жаңылоо + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Чакан убакытка чейин гана шилтеме анык болот. - - This is just the demo version of %1. - Бул жөн эле %1'дун демо-версиясы. - - - It allows you to test the application and see if it works for you. - Бул тиркемени сынап көргөнгө мүмкүндүк берет. - - - Get the full version - Толук версиясын алуу - - - Continue - Улантуу - Downloading %1 %1 жүктөп алынууда @@ -858,6 +854,10 @@ Subscribe to %1 %1 каналына жазылуу + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 көрүү + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 / %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Изделүүдө... - Show %1 More Дагы %1 видеону көрсөтүү @@ -1071,25 +1070,16 @@ <a href='%1'>%2</a>'га кош келиңиз, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Видеолорду + to start watching videos. + менен табып көрүү. a keyword ачкыч сөз - a channel - канал - - - to start watching videos. - менен табып көрүү. - - - Watch - Көрүү + Enter + Видеолорду Recent keywords @@ -1110,6 +1100,10 @@ &Back &Артка + + &Forward + + Forward to %1 %1 алга @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - %1 үчүн видео агымын алуу мүмкүн эмес - - YTRegions @@ -1365,4 +1352,11 @@ Бүткүл дүйнө + + YTVideo + + Cannot get video stream for %1 + %1 үчүн видео агымын алуу мүмкүн эмес + + \ No newline at end of file diff --git a/locale/locale.pri b/locale/locale.pri index edf9282..a5f367c 100644 --- a/locale/locale.pri +++ b/locale/locale.pri @@ -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 diff --git a/locale/ms_MY.ts b/locale/ms_MY.ts index 0aa3a61..9d6756b 100644 --- a/locale/ms_MY.ts +++ b/locale/ms_MY.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Terjemah %1 kepada bahasa ibunda anda menggunakan %2 + + Powered by %1 + Diperkasakan oleh %1 + + + Open-source software + Perisian sumber-terbuka + Icon designed by %1. Ikon direka oleh %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + Anda mempunyai %n video baharu @@ -155,6 +163,10 @@ Show Updated Papar Dikemaskini + + You have no subscriptions. Use the star symbol to subscribe to channels. + Anda tidak mempunyai langganan. Gunakan simbol bintang untuk melanggan saluran. + All Videos Semua Video @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Tiada langganan dikemaskini buat masa ini. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Anda tidak mempunyai langganan. Gunakan simbol bintang untuk melanggan saluran. - - - - ClearButton - - Clear - Kosongkan - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + %n jam yang lalu %n day(s) ago - + %n hari yang lalu - %n weeks(s) ago - + %n month(s) ago + %n bulan yang lalu + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + ditonton %1 kali - %n month(s) ago - + %n week(s) ago + %n minggu yang lalu @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Ini hanyalah versi demo %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Ia hanya boleh muat turun video kurang daripada %1 minit supaya anda boleh menguji kefungsian muat turunnya. - - - Continue - Teruskan - - - Get the full version - Dapatkan versi penuh - %1 downloaded in %2 %1 dimuat turun dalam %2 @@ -277,7 +281,7 @@ %n Download(s) - + %n Muat Turun @@ -632,10 +636,6 @@ &Float on Top Te&rapung Diatas - - &Adjust Window Size - &Laras Saiz Tetingkap - &Stop After This Video &Henti Selepas Video Ini @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Sembunyi video yang mengandungi kandungan tidak senonoh + + Toggle &Menu Bar + Togol Palang &Menu + + + Menu + Menu + &Love %1? Rate it! &Suka %1? Beri penarafan! @@ -796,6 +804,10 @@ Update Kemaskini + + You can still access the menu bar by pressing the ALT key + Anda masih boleh mencapai palang menu dengan menekan kekunci ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Pautan hanya sah untuk masa yang terhad. - - This is just the demo version of %1. - Ini hanyalah versi demo %1. - - - It allows you to test the application and see if it works for you. - Ia membolehkan anda uji aplikasi dan lihat jika ia berfungsi untuk anda. - - - Get the full version - Dapatkan versi penuh - - - Continue - Teruskan - Downloading %1 Memuat turun %1 @@ -858,6 +854,10 @@ Subscribe to %1 Langgan ke %1 + + Switched to %1 + Beralih ke %1 + Unsubscribed from %1 Nyahlanggan daripada %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - ditonton %1 kali + Pick a video + Pilih satu video + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 daripada %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Menggelintar... - Show %1 More Papar %1 Lagi @@ -1071,25 +1070,16 @@ Selamat datang ke <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Masukkan + to start watching videos. + untuk menonton video. a keyword kata kunci - a channel - saluran - - - to start watching videos. - untuk menonton video. - - - Watch - Tonton + Enter + Masukkan Recent keywords @@ -1110,6 +1100,10 @@ &Back &Undur + + &Forward + &Maju + Forward to %1 Maju ke %1 @@ -1163,13 +1157,6 @@ Memuat turun %1... - - Video - - Cannot get video stream for %1 - Tidak dapat strim video untuk %1 - - YTRegions @@ -1365,4 +1352,11 @@ Seluruh Dunia + + YTVideo + + Cannot get video stream for %1 + Tidak dapat strim video untuk %1 + + \ No newline at end of file diff --git a/locale/nb.ts b/locale/nb.ts index 8e6ae67..c0ebc30 100644 --- a/locale/nb.ts +++ b/locale/nb.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Oversett %1 til ditt morsmÃ¥l ved hjelp av %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikonet er designet av %1. @@ -155,6 +163,10 @@ Show Updated Program Oppdatert + + You have no subscriptions. Use the star symbol to subscribe to channels. + Du har ingen abonnement. Bruk stjernesymbolet for Ã¥ abonnemere pÃ¥ kanaler. + All Videos Alle Videoer @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Det er ingen oppdaterte abonnement for øyeblikket - - You have no subscriptions. Use the star symbol to subscribe to channels. - Du har ingen abonnement. Bruk stjernesymbolet for Ã¥ abonnemere pÃ¥ kanaler. - - - - ClearButton - - Clear - Nullstill - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visninger + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Dette er kun demo-versjonen av %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Den kan kun laste ned videoer pÃ¥ under %1 minutter, for at du skal kunne prøve ut nedlastingsfunksjonen. - - - Continue - Fortsett - - - Get the full version - Kjøp fullversjonen - %1 downloaded in %2 %1 nedlastet pÃ¥ %2 @@ -632,10 +636,6 @@ &Float on Top &Vis over andre - - &Adjust Window Size - &Tilpass Vindusstørrelse - &Stop After This Video &Stopp etter denne videoen @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Skjul videoer som kan inneholde upassende innhold + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Liker du %1? Ranger den! @@ -796,6 +804,10 @@ Update Oppdater + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Denne linken vil kun være gyldig i en begrenset tid. - - This is just the demo version of %1. - Dette er kun demoversjonen av %1. - - - It allows you to test the application and see if it works for you. - Dette gir deg muligheten til Ã¥ prøve ut applikasjonen og se om du den er noe for deg. - - - Get the full version - Kjøp fullversjonen - - - Continue - Fortsett - Downloading %1 Nedlasting %1 @@ -858,6 +854,10 @@ Subscribe to %1 Abonnér pÃ¥ %1 + + Switched to %1 + + Unsubscribed from %1 Du er utmeldt fra %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visninger + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 av %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Søker... - Show %1 More Vis %1 Mer @@ -1071,25 +1070,16 @@ Velkommen til <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Skriv + to start watching videos. + for Ã¥ begynne avspilling av video a keyword ett nøkkelord - a channel - en kanal - - - to start watching videos. - for Ã¥ begynne avspilling av video - - - Watch - Se + Enter + Skriv Recent keywords @@ -1110,6 +1100,10 @@ &Back &Tilbake + + &Forward + + Forward to %1 Fremover til %1 @@ -1163,13 +1157,6 @@ Laster ned %1... - - Video - - Cannot get video stream for %1 - Kan ikke hente mediastrøm for %1 - - YTRegions @@ -1365,4 +1352,11 @@ Over hele verden + + YTVideo + + Cannot get video stream for %1 + Kan ikke hente mediastrøm for %1 + + \ No newline at end of file diff --git a/locale/nl.ts b/locale/nl.ts index f4666af..3f2cbeb 100644 --- a/locale/nl.ts +++ b/locale/nl.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Vertaal %1 naar uw moedertaal met behulp van %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Pictogram ontworpen door %1. @@ -96,7 +104,7 @@ AppWidget Download - + Downloaden @@ -155,6 +163,10 @@ Show Updated Toon bijgewerkte + + You have no subscriptions. Use the star symbol to subscribe to channels. + U heeft geen abonnementen. Gebruik het ster-symbool om te abonneren op kanalen. + All Videos Alle video's @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Er zijn op dit moment geen bijgewerkte abonnementen. - - You have no subscriptions. Use the star symbol to subscribe to channels. - U heeft geen abonnementen. Gebruik het ster-symbool om te abonneren op kanalen. - - - - ClearButton - - Clear - Wis - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 bekeken + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Dit is slechts de demoversie van %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Het kan alleen maar videos downloaden korter dan %1 minuten zodat u de downloadfunctionaliteit kunt testen. - - - Continue - Ga door - - - Get the full version - Verkrijg de volledige versie - %1 downloaded in %2 %1 gedownload in %2 @@ -314,11 +318,11 @@ Extra The executable file has been tempered with, maybe by a virus. - + Het uitvoerbare bestand is aangepast, misschien door een virus. %1 will not run. Try installing again. - + %1 kan niet starten. Probeer opnieuw te installeren. Quit @@ -632,10 +636,6 @@ &Float on Top &Zweef erboven - - &Adjust Window Size - - &Stop After This Video &Stop na deze video @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + &Menubalk weergeven/verbergen + + + Menu + Menu + &Love %1? Rate it! Vindt u %1 te &gek? Waardeer het! @@ -796,6 +804,10 @@ Update Werk bij + + You can still access the menu bar by pressing the ALT key + U kunt de menubalk nog steeds benaderen door op ALT te drukken + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. De link zal maar een beperkte tijd geldig zijn. - - This is just the demo version of %1. - Dit is slechts de demoversie van %1. - - - It allows you to test the application and see if it works for you. - Het biedt de mogelijkheid de applicatie te testen en te beoordelen. - - - Get the full version - Verkrijg de volledige versie - - - Continue - Ga door - Downloading %1 Bezig met downloaden van %1 @@ -858,6 +854,10 @@ Subscribe to %1 Abonneer op %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 bekeken + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 van %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Bezig met zoeken... - Show %1 More Toon %1 meer @@ -1071,25 +1070,16 @@ Welkom bij <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Typ + to start watching videos. + om te beginnen met het bekijken van video's. a keyword een zoekwoord - a channel - een kanaal - - - to start watching videos. - om te beginnen met het bekijken van video's. - - - Watch - Bekijk + Enter + Typ Recent keywords @@ -1110,6 +1100,10 @@ &Back &Terug + + &Forward + + Forward to %1 Spoel vooruit naar %1 @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - Kan de videostream niet verkrijgen voor %1 + Downloaden van %1... @@ -1365,4 +1352,11 @@ Wereldwijd + + YTVideo + + Cannot get video stream for %1 + Kan de videostream niet verkrijgen voor %1 + + \ No newline at end of file diff --git a/locale/nn.ts b/locale/nn.ts index 94c5c64..40af51a 100644 --- a/locale/nn.ts +++ b/locale/nn.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Omset %1 til morsmÃ¥let ditt med %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikonet er utforma av %1. @@ -155,6 +163,10 @@ Show Updated Vis oppdaterte + + You have no subscriptions. Use the star symbol to subscribe to channels. + Du har ingen tingingar. Bruk stjernesymbolet for Ã¥ tinga kanalar. + All Videos Alle videoar @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Ingen oppdaterte tingingar enno. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Du har ingen tingingar. Bruk stjernesymbolet for Ã¥ tinga kanalar. - - - - ClearButton - - Clear - Nullstill - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visingar + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Dette er berre demoutgÃ¥va av %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Han kan berre lasta ned videoar pÃ¥ under %1 minutt, for at du skal kunna prøva ut nedlastingsfunksjonen. - - - Continue - Hald fram - - - Get the full version - Kjøp fullversjonen - %1 downloaded in %2 %1 lasta ned pÃ¥ %2 @@ -632,10 +636,6 @@ &Float on Top &Vis over andre - - &Adjust Window Size - - &Stop After This Video &Stopp etter denne videoen @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! @@ -796,6 +804,10 @@ Update Oppdater + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Denne lenkja vil berre vera gyldig i ei avgrensa tid. - - This is just the demo version of %1. - Dette er berre demoutgÃ¥va av %1. - - - It allows you to test the application and see if it works for you. - Dette lèt prøva ut programmet og sjÃ¥ om det er noko for deg. - - - Get the full version - Kjøp fullversjonen - - - Continue - Hald fram - Downloading %1 Lastar ned %1 @@ -858,6 +854,10 @@ Subscribe to %1 Ting %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visingar + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 av %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Søkjer … - Show %1 More Vis %1 til @@ -1071,25 +1070,16 @@ Velkomen til <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Skriv + to start watching videos. + for Ã¥ Ã¥ sjÃ¥ videoar. a keyword eit nøkkelord - a channel - ein kanal - - - to start watching videos. - for Ã¥ Ã¥ sjÃ¥ videoar. - - - Watch - Snurr film + Enter + Skriv Recent keywords @@ -1110,6 +1100,10 @@ &Back &Tilbake + + &Forward + + Forward to %1 Fram til %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Kan ikkje henta videostraumen til %1 - - YTRegions @@ -1365,4 +1352,11 @@ Heile verda + + YTVideo + + Cannot get video stream for %1 + Kan ikkje henta videostraumen til %1 + + \ No newline at end of file diff --git a/locale/pl.ts b/locale/pl.ts index 6b1aac8..760f2d1 100644 --- a/locale/pl.ts +++ b/locale/pl.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Przetłumacz %1 na swój język używając %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikony zaprojektowane przez %1. @@ -155,6 +163,10 @@ Show Updated Pokaż nowości + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nie masz żadnych subskrypcji. Użyj symbolu gwiazdy do subskrybowania kanałów. + All Videos Wszystkie filmy @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Obecnie nie ma żadnych aktualizacji subskrypcji. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nie masz żadnych subskrypcji. Użyj symbolu gwiazdy do subskrybowania kanałów. - - - - ClearButton - - Clear - Wyczyść - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + Wyświetleń: %1 + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - To jest jedynie wersja demonstracyjna %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Funkcja testowa - można pobierać filmy krótsze niż %1 minut. - - - Continue - Kontynuuj - - - Get the full version - Pobierz pełną wersję - %1 downloaded in %2 %1 pobrane w %2 @@ -632,10 +636,6 @@ &Float on Top &Zawsze na wierzchu - - &Adjust Window Size - Dostosuj wielkość okn&a - &Stop After This Video Zatrzymaj odtwarzanie, po obejrzeniu tego filmu @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Ukryj filmy mogące zawierać nieodpowiednie treści + + Toggle &Menu Bar + Przełącz pasek &menu + + + Menu + + &Love %1? Rate it! &Kochasz %1? Oceń to! @@ -796,6 +804,10 @@ Update Aktualizuj + + You can still access the menu bar by pressing the ALT key + Wciąz masz dostęp do paska menu poprzez przyciśnięcie klawisza ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Link będzie ważny tylko przez ograniczony czas. - - This is just the demo version of %1. - To jest jedynie wersja demonstracyjna %1. - - - It allows you to test the application and see if it works for you. - Pozwala ci to na testowanie i sprawdzenie działania aplikacji. - - - Get the full version - Uzyskaj pełną wersję - - - Continue - Kontynuuj - Downloading %1 Pobieranie %1 @@ -858,6 +854,10 @@ Subscribe to %1 Subskrybuj %1 + + Switched to %1 + + Unsubscribed from %1 Zakończono subskrypcję %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - Wyświetleń: %1 + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 z %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Wyszukiwanie ... - Show %1 More Pokaż kolejne %1 @@ -1071,25 +1070,16 @@ Witaj w <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Zatwierdź + to start watching videos. + aby rozpocząć oglądanie a keyword słowo kluczowe - a channel - kanał - - - to start watching videos. - aby rozpocząć oglądanie - - - Watch - Oglądaj + Enter + Zatwierdź Recent keywords @@ -1110,6 +1100,10 @@ &Back &Wstecz + + &Forward + + Forward to %1 Przewiń do %1 @@ -1163,13 +1157,6 @@ Pobieranie %1... - - Video - - Cannot get video stream for %1 - Nie można uzyskać dostępu do %1 - - YTRegions @@ -1365,4 +1352,11 @@ Ogólnoświatowy + + YTVideo + + Cannot get video stream for %1 + Nie można uzyskać dostępu do %1 + + \ No newline at end of file diff --git a/locale/pl_PL.ts b/locale/pl_PL.ts index f541c97..826dfb7 100644 --- a/locale/pl_PL.ts +++ b/locale/pl_PL.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Przetłumacz %1 na swój język używając %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikony zaprojektowane przez %1. @@ -155,6 +163,10 @@ Show Updated Pokaż zaktualizowane + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nie masz żadnej subskrypcji. Użyj symbolu gwiazdki, aby subskrybować kanały. + All Videos Wszystkie filmy @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Brak zaktualizowanych subskrypcji. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nie masz żadnej subskrypcji. Użyj symbolu gwiazdki, aby subskrybować kanały. - - - - ClearButton - - Clear - Wyczyść - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1widziany + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - To jest tylko wersja demo %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Może pobierać jedynie filmy krótsze niż %1 minut, dla przetestowania funkcji pobierania. - - - Continue - Dalej - - - Get the full version - Pobierz pełną wersję - %1 downloaded in %2 %1 ściągnięte w %2 @@ -632,10 +636,6 @@ &Float on Top &Ustaw na wierzchu - - &Adjust Window Size - &Dopasuj rozmiar okna - &Stop After This Video &Zatrzymaj po tym filmie @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Ukryj filmy mogące zawierać nieodpowiednie treści + + Toggle &Menu Bar + Przełącz pasek &menu + + + Menu + + &Love %1? Rate it! Uwie&lbiasz %1? Oceń to! @@ -796,6 +804,10 @@ Update Zaktualizuj + + You can still access the menu bar by pressing the ALT key + Dostęp do paska menu można uzyskać naciskając klawisz ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Link będzie ważny tylko przez ograniczony czas. - - This is just the demo version of %1. - To jest tylko wersja demo %1. - - - It allows you to test the application and see if it works for you. - Pozwala przetestować aplikację, i zobaczyć czy Ci odpowiada. - - - Get the full version - Zdobądź pełną wersję - - - Continue - Dalej - Downloading %1 Pobieranie %1 @@ -858,6 +854,10 @@ Subscribe to %1 Subskrybuj %1 + + Switched to %1 + + Unsubscribed from %1 Zaprzestano subskrybować %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1widziany + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 z %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Szukanie... - Show %1 More Pokaż kolejne %1 @@ -1071,25 +1070,16 @@ Witaj w <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Zatwierdź + to start watching videos. + aby rozpocząć oglądanie a keyword słowo kluczowe - a channel - kanał - - - to start watching videos. - aby rozpocząć oglądanie - - - Watch - Oglądaj + Enter + Zatwierdź Recent keywords @@ -1110,6 +1100,10 @@ &Back &Wstecz + + &Forward + + Forward to %1 Idź do %1 @@ -1163,13 +1157,6 @@ Pobieranie %1... - - Video - - Cannot get video stream for %1 - Strumieniowanie %1 nie powiodło się - - YTRegions @@ -1365,4 +1352,11 @@ Ogólnoświatowy + + YTVideo + + Cannot get video stream for %1 + Nie można uzyskać dostępu do %1 + + \ No newline at end of file diff --git a/locale/pt.ts b/locale/pt.ts index 8cd19c1..eb05ba1 100644 --- a/locale/pt.ts +++ b/locale/pt.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Ajude a traduzir o %1 através do %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ícone criado por %1. @@ -155,6 +163,10 @@ Show Updated Mostrar atualizados + + You have no subscriptions. Use the star symbol to subscribe to channels. + Ainda não possui subscrições. Utilize a estrela para subscrever os canais. + All Videos Todos os vídeos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Não existem atualizações de subscrições. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Ainda não possui subscrições. Utilize a estrela para subscrever os canais. - - - - ClearButton - - Clear - Limpar - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visualizações + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Esta é uma versão de demonstração do %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Apenas pode transferir vídeos mais curtos que %1 minuto(s) de forma a testar a funcionalidade de transferência. - - - Continue - Continuar - - - Get the full version - Obter a versão completa - %1 downloaded in %2 %1 transferência em %2 @@ -632,10 +636,6 @@ &Float on Top &Flutuante na frente - - &Adjust Window Size - &Ajuste o tamanho da janela - &Stop After This Video Parar após es&te vídeo @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Gosta? %1? Avalie! @@ -796,6 +804,10 @@ Update Atualizar + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. A ligação será válida por tempo limitado. - - This is just the demo version of %1. - Esta é uma versão de demonstração do %1. - - - It allows you to test the application and see if it works for you. - Permite-lhe testar ea aplicação e verificar se é do seu agrado. - - - Get the full version - Obter a versão completa - - - Continue - Continuar - Downloading %1 Transferência: %1 @@ -858,6 +854,10 @@ Subscribe to %1 Subscrever %1 + + Switched to %1 + + Unsubscribed from %1 Não subscrito de %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visualizações + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Procura... - Show %1 More Mostrar mais %1 @@ -1071,25 +1070,16 @@ Bem-vindo ao <a href='%1'>%2</a> - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Introduza + to start watching videos. + para começar a visualizar os vídeos. a keyword uma palavra-chave - a channel - um canal - - - to start watching videos. - para começar a visualizar os vídeos. - - - Watch - Ver + Enter + Introduza Recent keywords @@ -1110,6 +1100,10 @@ &Back &Recuar + + &Forward + + Forward to %1 Avançar para %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Não é possível obter a emissão de %1 - - YTRegions @@ -1365,4 +1352,11 @@ Global + + YTVideo + + Cannot get video stream for %1 + Não é possível obter a emissão de %1 + + \ No newline at end of file diff --git a/locale/pt_BR.ts b/locale/pt_BR.ts index 589af55..2330982 100644 --- a/locale/pt_BR.ts +++ b/locale/pt_BR.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Traduza %1 para seu idioma nativo usando %2 + + Powered by %1 + Produzido por %1 + + + Open-source software + Software open-source + Icon designed by %1. Ícone desenhado por %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + Você tem %n vídeo(s) novo(s)Você tem %n vídeo(s) novo(s) @@ -155,6 +163,10 @@ Show Updated Mostrar Atualização + + You have no subscriptions. Use the star symbol to subscribe to channels. + Você não tem assinaturas. Use o símbolo da estrela para assinar canais. + All Videos Todos Os Vídeos @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Não há assinaturas atualizadas neste momento. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Você não tem assinaturas. Use o símbolo da estrela para assinar canais. - - - - ClearButton - - Clear - Limpar - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + %n hora(s) atrás%n hora(s) atrás %n day(s) ago - + %n dia(s) atrás%n dia(s) atrás - %n weeks(s) ago - + %n month(s) ago + %n mes(es) atrás%n mes(es) atrás + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + %1 visualizações - %n month(s) ago - + %n week(s) ago + %n semana(s) atrás%n semana(s) atrás @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Esta é apenas a versão demonstração de %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Só pode fazer download de vídeos menores que %1 minutos para que você possa testar a funcionalidade de download. - - - Continue - Continuar - - - Get the full version - Obter a versão completa - %1 downloaded in %2 %1 baixados em %2 @@ -277,7 +281,7 @@ %n Download(s) - + %n Downloads%n Download(s) @@ -326,7 +330,7 @@ Reinstall - Reinstale + Reinstalar @@ -632,10 +636,6 @@ &Float on Top &Sempre Acima - - &Adjust Window Size - &Ajuste o tamanho da janela - &Stop After This Video &Parar Após Este Vídeo @@ -670,7 +670,15 @@ Hide videos that may contain inappropriate content - Ocultar vídeos com conteúdo inapropriado + Ocultar vídeos que podem conter conteúdo inapropriado + + + Toggle &Menu Bar + Mostrar Barra De &Menu + + + Menu + Menu &Love %1? Rate it! @@ -796,6 +804,10 @@ Update Atualizar + + You can still access the menu bar by pressing the ALT key + Você ainda pode acessar a barra de menu pressionando a tecla ALT + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. O link só será válido por um tempo limitado. - - This is just the demo version of %1. - Esta é apenas a versão demonstração de %1. - - - It allows you to test the application and see if it works for you. - Ele permite que você teste o aplicativo e veja se ele funciona para você. - - - Get the full version - Obter a versão completa - - - Continue - Continuar - Downloading %1 Baixando %1 @@ -858,9 +854,13 @@ Subscribe to %1 Assinar %1 + + Switched to %1 + Mudar para %1 + Unsubscribed from %1 - Desinscrito de %1 + Não subscrito de %1 Subscribed to %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visualizações + Pick a video + Escolher um vídeo + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 de %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Pesquisando... - Show %1 More Mostrar Mais %1 @@ -1071,25 +1070,16 @@ Bem-vindo ao <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Digite + to start watching videos. + para começar a assistir vídeos. a keyword uma palavra-chave - a channel - um canal - - - to start watching videos. - para começar a assistir vídeos. - - - Watch - Assistir + Enter + Digite Recent keywords @@ -1110,6 +1100,10 @@ &Back &Voltar + + &Forward + &Avançar + Forward to %1 Avançar para %1 @@ -1163,13 +1157,6 @@ Baixando %1... - - Video - - Cannot get video stream for %1 - Não foi possível obter stream de vídeo de %1 - - YTRegions @@ -1365,4 +1352,11 @@ Mundial + + YTVideo + + Cannot get video stream for %1 + Não foi possível obter stream de vídeo de %1 + + \ No newline at end of file diff --git a/locale/pt_PT.ts b/locale/pt_PT.ts new file mode 100644 index 0000000..6caf996 --- /dev/null +++ b/locale/pt_PT.ts @@ -0,0 +1,1362 @@ + + + AboutView + + There's life outside the browser! + Existe vida para além do browser! + + + Version %1 + Versão %1 + + + Licensed to: %1 + Licenciado a: + + + %1 is Free Software but its development takes precious time. + %1 é Software Gratuito mas o seu desenvolvimento leva tempo precioso. + + + Please <a href='%1'>donate</a> to support the continued development of %2. + Por favor <a href='%1'>doe</a> para suportar o desenvolvimento continuado do %2. + + + Translate %1 to your native language using %2 + Traduza %1 para a sua língua utilizando %2 + + + Powered by %1 + + + + Open-source software + + + + Icon designed by %1. + Ícone desenhado por %1. + + + Released under the <a href='%1'>GNU General Public License</a> + Lançado nos termos da <a href='"%1'>GNU General Public License</a> + + + &Close + &Fechar + + + About + Sobre + + + + ActivationDialog + + Enter your License Details + Introduza os detalhes da sua Licença + + + &Email: + &Email: + + + &Code: + &Código: + + + + ActivationView + + Please license %1 + Por favor licencie %1 + + + This demo has expired. + Esta demonstração expirou. + + + The full version allows you to watch videos without interruptions. + A versão completa permite-lhe ver vídeos sem interrupções. + + + Without a license, the application will expire in %1 days. + Sem uma licença, a aplicação irá expirar em %1 dias. + + + By purchasing the full version, you will also support the hard work I put into creating %1. + Ao comprar a versão completa, também irá apoiar o trabalho árduo que pus %1. + + + Use Demo + Usar Demo + + + Enter License + Introduzir Licença + + + Buy License + Comprar Licença + + + + AppWidget + + Download + Descarregar + + + + ChannelAggregator + + By %1 + Por %1 + + + You have %n new video(s) + + + + + ChannelItemDelegate + + All Videos + Todos os Vídeos + + + Unwatched Videos + Vídeos não visualizados + + + + ChannelView + + Name + Nome + + + Last Updated + Actualizado ultimamente + + + Last Added + Adicionado ultimamente + + + Last Watched + Visualizado ultimamente + + + Most Watched + Mais visualizados + + + Sort by + Ordenar por + + + Mark all as watched + Marcar todos com vistos + + + Show Updated + Mostrar actualizados + + + You have no subscriptions. Use the star symbol to subscribe to channels. + Você não tem subscrições. Use o símbolo estrela para subscrever canais. + + + All Videos + Todos os Vídeos + + + Unwatched Videos + Vídeos não visualizados + + + Mark as Watched + Marcar como visto + + + Unsubscribe + Anular subscrição + + + There are no updated subscriptions at this time. + Não há subscrições actualizadas de momento. + + + + DataUtils + + Just now + Agora mesmo + + + %n hour(s) ago + + + + %n day(s) ago + + + + %n month(s) ago + + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visualizações + + + %n week(s) ago + + + + + DownloadItem + + bytes + bytes + + + KB + KB + + + MB + MB + + + bytes/sec + bytes/seg + + + KB/sec + KB/seg + + + MB/sec + MB/seg + + + seconds + segundos + + + minutes + minutos + + + %4 %5 remaining + %4 %5 restante + + + + DownloadManager + + %1 downloaded in %2 + %1 transferido em %2 + + + Download finished + Transferência concluída + + + %n Download(s) + + + + + DownloadSettings + + Change location... + Mudar local... + + + Choose the download location + Escolha o local de downloads + + + Download location changed. + Local de downloads mudado + + + Current downloads will still go in the previous location. + Transferências actuais ainda irão para o local prévio + + + Downloading to: %1 + A transferir para: %1 + + + + DownloadView + + Downloads + Transferências + + + + Extra + + The executable file has been tempered with, maybe by a virus. + O ficheiro executável foi alterado ilegalmente, provavelmente por um vírus. + + + %1 will not run. Try installing again. + %1 não irá correr. Tente instalar outra vez. + + + Quit + Sair + + + Reinstall + Reinstalar + + + + GlobalShortcuts + + Play + Reproduzir + + + Pause + Pausa + + + Play/Pause + Reproduzir/Pausa + + + Stop + Parar + + + Stop playing after current track + Parar reprodução após faixa actual + + + Next track + Próxima faixa + + + Previous track + Faixa anterior + + + Increase volume + Aumentar volume + + + Decrease volume + Baixar volume + + + Mute + Desactivar som + + + Seek forward + Avançar + + + Seek backward + Retroceder + + + + HomeView + + Search + Pesquisar + + + Find videos and channels by keyword + Encontrar vídeos e canais por palavra-chave + + + Browse + Navegar + + + Browse videos by category + Navegar vídeos por categoria + + + Subscriptions + Subscrições + + + Channel subscriptions + Subscrições de canais + + + Make yourself comfortable + Sinta-se confortável + + + + LoadingWidget + + Error + Erro + + + + MainWindow + + &Window + &Janela + + + &Minimize + &Minimizar + + + &Stop + &Parar + + + Stop playback and go back to the search view + Parar reprodução e voltar para à vista de pesquisa + + + P&revious + A&nterior + + + Go back to the previous track + Retroceder para a faixa anterior + + + S&kip + S&altar + + + Skip to the next video + Saltar para o próximo vídeo + + + &Play + &Reproduzir + + + Resume playback + Retomar reprodução + + + &Full Screen + &Ecrã Inteiro + + + Go full screen + Ir para ecrã inteiro + + + &Compact Mode + &Modo compacto + + + Hide the playlist and the toolbar + Esconder a lista de reprodução e a barra de ferramentas + + + Open the &YouTube Page + Abrir a &Página do YouTube + + + Go to the YouTube video page and pause playback + Ir para a página de vídeos do YouTube e pausar reprodução + + + Copy the YouTube &Link + Copiar a &ligação do YouTube + + + Copy the current video YouTube link to the clipboard + Copiar o URL do video actual do YouTube para a área de transferência + + + Copy the Video Stream &URL + Copiar o &URL da Stream do Vídeo + + + Copy the current video stream URL to the clipboard + Copiar o URL da emissão de vídeo actual para a área de transferência + + + Find Video &Parts + Encontrar &Partes do Vídeo + + + Find other video parts hopefully in the right order + Procurar outras partes do video, com esperança de estarem na ordenação correcta + + + &Remove + &Remover + + + Remove the selected videos from the playlist + Remover os vídeos selecionados da lista de reprodução + + + Move &Up + Mover &Para cima + + + Move up the selected videos in the playlist + Mover para cima os vídeos selecionados na lista de reprodução + + + Move &Down + Mover &Para baixo + + + Move down the selected videos in the playlist + Mover para baixo os vídeos selecionados na lista de reprodução + + + &Clear Recent Searches + &Limpar as Pesquisas Recentes + + + Clear the search history. Cannot be undone. + Limpar o histórico de pesquisa. Não poderá ser desfeito. + + + &Quit + &Sair + + + Bye + Adeus + + + &Website + &Website + + + %1 on the Web + %1 na Web + + + Make a &Donation + Faça um &Donativo + + + Please support the continued development of %1 + Por favor apoie o desenvolvimento contínuo do %1 + + + &About + &Sobre + + + Info about %1 + Info sobre %1 + + + Search + Procurar + + + Mute volume + Desactivar som + + + &Manually Start Playing + &Começar a Reproduzir Manualmente + + + Manually start playing videos + Manualmente começar a reproduzir vídeos + + + &Downloads + &Transferências + + + Show details about video downloads + Mostrar detalhes sobre transferências de vídeos + + + &Download + &Download + + + Download the current video + Transferir o vídeo actual + + + Take &Snapshot + &Captura de ecrã + + + &Subscribe to Channel + &Subscrever ao Canal + + + Share the current video using %1 + Partilhar o video actual usando %1 + + + &Email + &Email + + + Email + Email + + + &Close + &Fechar + + + &Float on Top + &Flutuar no Topo + + + &Stop After This Video + &Parar Após Este Vídeo + + + &Report an Issue... + &Comunicar um Problema... + + + &Refine Search... + &Redefinir Pesquisa... + + + More... + Mais... + + + &Related Videos + &Vídeos Relacionados + + + Watch videos related to the current one + Ver vídeos relacionados com o actual + + + Open in &Browser... + Abrir no &Browser + + + Restricted Mode + Modo Restrito + + + Hide videos that may contain inappropriate content + Ocultar vídeos que possam conter conteúdo inapropriado + + + Toggle &Menu Bar + + + + Menu + + + + &Love %1? Rate it! + &Gosta do %1? Classifique-o! + + + Buy %1... + Comprar %1... + + + &Application + &Aplicação + + + &Playback + &Reprodução + + + &Playlist + &Lista de reprodução + + + &Video + &Vídeo + + + &Share + &Partilhar + + + &View + &Visualizar + + + &Help + &Ajuda + + + Press %1 to raise the volume, %2 to lower it + Pressione %1 para aumentar o volume, %2 para baixá-lo + + + Choose your content location + Escolha o local do seu conteúdo + + + Opening %1 + A abrir %1 + + + Do you want to exit %1 with a download in progress? + Deseja sair %1 com uma transferência activa? + + + If you close %1 now, this download will be cancelled. + Se fechar %1 agora, a transferência será cancelada. + + + Close and cancel download + Fechar e cancelar transferência + + + Wait for download to finish + Aguarde para que a transferência termine + + + Error: %1 + Erro: %1 + + + &Pause + &Pausar + + + Pause playback + Pausar reprodução + + + &Loading... + &A carregar... + + + Leave &Full Screen + Sair do &Ecrã Completo + + + Remaining time: %1 + Tempo restante: %1 + + + Volume at %1% + Volume a %1% + + + Volume is muted + O volume está silenciado + + + Volume is unmuted + O volume não está silenciado + + + Maximum video definition set to %1 + Definição de vídeo máxima definida para %1 + + + Your privacy is now safe + A sua privacidade está agora segura + + + Downloads complete + Transferência completa + + + %1 version %2 is now available. + %1 versão %2 está agora disponível. + + + Remind me later + Relembrar-me mais tarde + + + Update + Actualizar + + + You can still access the menu bar by pressing the ALT key + + + + + MediaView + + You can now paste the YouTube link into another application + Pode agora colar a ligação do Youtube para outra aplicação + + + You can now paste the video stream URL into another application + Agora você pode colar o URL do vídeo noutra aplicação + + + The link will be valid only for a limited time. + A ligação será válida apenas por um tempo limitado. + + + Downloading %1 + A transferir %1 + + + of + Used in video parts, as in '2 of 3' + de + + + part + This is for video parts, as in 'Cool video - part 1' + parte + + + episode + This is for video parts, as in 'Cool series - episode 1' + episódio + + + Sent from %1 + Enviado de %1 + + + Unsubscribe from %1 + Anular subscrição de %1 + + + Subscribe to %1 + Subscrever %1 + + + Switched to %1 + + + + Unsubscribed from %1 + Anular subscrição de %1 + + + Subscribed to %1 + Subscrito a %1 + + + + MessageWidget + + A new version of %1 is available! + Uma nova versão do %1 está disponível! + + + %1 %2 is now available. You have %3. + %1 %2 está agora disponível. Você tem %3. + + + Would you like to download it now? + Gostaria de descarregar agora? + + + Skip This Version + Saltar Esta Versão + + + Remind Me Later + Relembrar-me Mais Tarde + + + Install Update + Instalar Actualização + + + + PasteLineEdit + + Paste + Colar + + + + PickMessage + + Pick a video + + + + + PlaylistItemDelegate + + %1 of %2 (%3) — %4 + %1 de %2 (%3) — %4 + + + Preparing + A preparar + + + Failed + Falhou + + + Completed + Completado + + + Stopped + Parado + + + Stop downloading + Parar de transferir + + + Show in %1 + Mostrar em %1 + + + Open parent folder + Abrir a pasta-mãe + + + Restart downloading + Recomeçar a transferir + + + + PlaylistModel + + Show %1 More + Mostrar Mais %1 + + + No videos + Sem vídeos + + + No more videos + Sem mais vídeos + + + + RefineSearchWidget + + Sort by + Ordenar por + + + Relevance + Relevância + + + Date + Data + + + View Count + Contador de visualizações + + + Rating + Classificação + + + Anytime + Qualquer altura + + + Today + Hoje + + + 7 Days + 7 Dias + + + 30 Days + 30 Dias + + + Duration + Duração + + + All + Todos + + + Short + Curto + + + Medium + Médio + + + Long + Longo + + + Less than 4 minutes + Menos de 4 minutos + + + Between 4 and 20 minutes + Entre 4 a 20 minutos + + + Longer than 20 minutes + Mais de 20 minutos + + + Quality + Qualidade + + + High Definition + Alta-Definição + + + 720p or higher + 720p ou mais alto + + + Done + Feito + + + + RegionsView + + Done + Feito + + + + SearchLineEdit + + Search + Pesquisar + + + + SearchView + + Welcome to <a href='%1'>%2</a>, + Bem-vindo ao <a href='%1'>%2</a>, + + + to start watching videos. + para começar a ver vídeos. + + + a keyword + uma palava-chave + + + Enter + Insira + + + Recent keywords + Palavras-chave recentes + + + Recent channels + Canais recentes + + + Get the full version + Obter a versão completa + + + + SidebarHeader + + &Back + &Atrás + + + &Forward + + + + Forward to %1 + Avançar até %1 + + + Back to %1 + Retroceder até %1 + + + + SidebarWidget + + Refine Search + Redefinir Pesquisa + + + Did you mean: %1 + Queria dizer: %1 + + + + SnapshotSettings + + Change location... + Mudar local... + + + Snapshot saved to %1 + Captura de ecrã guardada em %1 + + + Snapshots location changed. + Local de capturas de ecrã mudado. + + + + StandardFeedsView + + Most Popular + Mais Popular + + + + UpdateDialog + + Downloading update... + A transferir actualização... + + + Downloading %1... + A transferir %1... + + + + YTRegions + + Algeria + Argélia + + + Argentina + Argentina + + + Australia + Austrália + + + Belgium + Bélgica + + + Brazil + Brasil + + + Canada + Canadá + + + Chile + Chile + + + Colombia + Colômbia + + + Czech Republic + República Checa + + + Egypt + Egipto + + + France + França + + + Germany + Alemanha + + + Ghana + Gana + + + Greece + Grécia + + + Hong Kong + Hong Kong + + + Hungary + Hungria + + + India + Índia + + + Indonesia + Indonésia + + + Ireland + Irlanda + + + Israel + Israel + + + Italy + Itália + + + Japan + Japão + + + Jordan + Jordânia + + + Kenya + Quénia + + + Malaysia + Malásia + + + Mexico + México + + + Morocco + Marrocos + + + Netherlands + Holanda + + + New Zealand + Nova Zelândia + + + Nigeria + Nigéria + + + Peru + Perú + + + Philippines + Filipinas + + + Poland + Polónia + + + Russia + Rússia + + + Saudi Arabia + Arábia Saudita + + + Singapore + Singapura + + + South Africa + África do Sul + + + South Korea + Coreia do Sul + + + Spain + Espanha + + + Sweden + Suécia + + + Taiwan + Ilha Formosa + + + Tunisia + Tunísia + + + Turkey + Turquia + + + Uganda + Uganda + + + United Arab Emirates + Emirados Árabes Unidos + + + United Kingdom + Reino Unido + + + Yemen + Iémen + + + Worldwide + Mundial + + + + YTVideo + + Cannot get video stream for %1 + Não é possível obter a stream do video para %1 + + + \ No newline at end of file diff --git a/locale/ro.ts b/locale/ro.ts index fd0155c..6f62bb5 100644 --- a/locale/ro.ts +++ b/locale/ro.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Tradu %1 în limba proprie folosind %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Iconița a fost concepută de %1. @@ -96,7 +104,7 @@ AppWidget Download - + Descarcă @@ -155,6 +163,10 @@ Show Updated Arata actualizeazările + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nu esti abonat la nimic. Ca sa te abonezi la canale, foloseste simbolul stea. + All Videos Toate videoclipurile @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Nici unul din canalele la care esti abonat nu are actualizari momentan. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nu esti abonat la nimic. Ca sa te abonezi la canale, foloseste simbolul stea. - - - - ClearButton - - Clear - Șterge - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 vizionări + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Aceasta este doar o versiune demo a %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Poate să descarce doar videoclipurile mai mici de %1 minute astfel încât să puteți testa funcționalitatea de descărcare. - - - Continue - Continuă - - - Get the full version - ObțineIa versiunea integrală - %1 downloaded in %2 %1 descărcat în %2 @@ -632,10 +636,6 @@ &Float on Top &Detașează - - &Adjust Window Size - &Ajustați dimensiunea ferestrei - &Stop After This Video &Oprește După Acest Videoclip @@ -666,10 +666,18 @@ Restricted Mode - + Mod restricționat Hide videos that may contain inappropriate content + Ascundeți videoclipuri care pot conține conținut neadecvat + + + Toggle &Menu Bar + + + + Menu @@ -796,6 +804,10 @@ Update Actualizează + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Adresa va fi validă doar pentru o perioadă limitată de timp. - - This is just the demo version of %1. - Aceasta este doar o versiune demo a %1. - - - It allows you to test the application and see if it works for you. - Vă permite să testați aplicația și să vedeți dacă funcționează. - - - Get the full version - Obține versiunea integrală - - - Continue - Continuă - Downloading %1 Descărcare %1 @@ -858,6 +854,10 @@ Subscribe to %1 Aboneaza-te de la %1 + + Switched to %1 + + Unsubscribed from %1 Dezabonează-te de la %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 vizionări + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 din %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Căutare... - Show %1 More Afișează încă %1 @@ -1071,25 +1070,16 @@ Bine ați venit la <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Introduceți + to start watching videos. + pentru a începe să vizionați videoclipuri. a keyword un cuvânt cheie - a channel - un canal - - - to start watching videos. - pentru a începe să vizionați videoclipuri. - - - Watch - Urmărește + Enter + Introduceți Recent keywords @@ -1110,6 +1100,10 @@ &Back Î&napoi + + &Forward + + Forward to %1 Avanseaza la %1 @@ -1142,7 +1136,7 @@ Snapshots location changed. - + Locația instantaneelor a fost modificată. @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - Nu poate fi accesat fluxul video pentru %1 + Se descarcă %1 @@ -1365,4 +1352,11 @@ Global + + YTVideo + + Cannot get video stream for %1 + Nu poate fi accesat fluxul video pentru %1 + + \ No newline at end of file diff --git a/locale/ru.ts b/locale/ru.ts index a810188..179bbb5 100644 --- a/locale/ru.ts +++ b/locale/ru.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Перевести %1 на ваш родной язык с помощью %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Автор значка %1. @@ -156,6 +164,10 @@ Show Updated Показать обновленные + + You have no subscriptions. Use the star symbol to subscribe to channels. + У вас нет подписок. Используйте символ звездочки чтобы подписаться на каналы. + All Videos Все видео @@ -176,17 +188,6 @@ There are no updated subscriptions at this time. В настоящее время нет обновлений подписок. - - You have no subscriptions. Use the star symbol to subscribe to channels. - У вас нет подписок. Используйте символ звездочки чтобы подписаться на каналы. - - - - ClearButton - - Clear - Очистить - DataUtils @@ -203,11 +204,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 просмотров + - %n month(s) ago + %n week(s) ago @@ -252,22 +272,6 @@ DownloadManager - - This is just the demo version of %1. - Это всего лишь демо версия %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Можно загружать только видео не длиннее %1 минут, для проверки функциональности загрузчика. - - - Continue - Продолжить - - - Get the full version - Получить полную версию - %1 downloaded in %2 %1 загружен в %2 @@ -633,10 +637,6 @@ &Float on Top &Поверх всех окон - - &Adjust Window Size - &Подстраивать размер окна - &Stop After This Video Ост&ановить после этого видео @@ -673,6 +673,14 @@ Hide videos that may contain inappropriate content Скрыть видео, содержащие недопустимый контент + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Нравится %1? Оцени! @@ -797,6 +805,10 @@ Update Обновление + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -812,22 +824,6 @@ The link will be valid only for a limited time. Адрес будет существовать ограниченное время. - - This is just the demo version of %1. - Данная программа является демо-версией %1. - - - It allows you to test the application and see if it works for you. - Она позволяет вам оценить приложение. - - - Get the full version - Купить полную версию - - - Continue - Продолжить - Downloading %1 Загружаю %1 @@ -859,6 +855,10 @@ Subscribe to %1 Подписаться на %1 + + Switched to %1 + + Unsubscribed from %1 Отписаны от %1 @@ -903,11 +903,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 просмотров + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 из %2 (%3) — %4 @@ -947,10 +950,6 @@ PlaylistModel - - Searching... - Идет поиск... - Show %1 More Показать ещё %1 @@ -1072,25 +1071,16 @@ Добро пожаловать в <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Введите + to start watching videos. + чтобы начать просмотр. a keyword запрос - a channel - канал - - - to start watching videos. - чтобы начать просмотр. - - - Watch - Смотреть + Enter + Введите Recent keywords @@ -1111,6 +1101,10 @@ &Back Н&азад + + &Forward + + Forward to %1 Вперед к %1 @@ -1164,13 +1158,6 @@ Загрузка %1... - - Video - - Cannot get video stream for %1 - Не удалось получить видео поток для %1 - - YTRegions @@ -1366,4 +1353,11 @@ Во всем мире + + YTVideo + + Cannot get video stream for %1 + Не удалось получить видео поток для %1 + + \ No newline at end of file diff --git a/locale/sk.ts b/locale/sk.ts index d393d1b..61d4fd2 100644 --- a/locale/sk.ts +++ b/locale/sk.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Prelož %1 do svojho materinského jazyka cez %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikonu nadizajnoval %1. @@ -107,7 +115,7 @@ You have %n new video(s) - + @@ -155,6 +163,10 @@ Show Updated ZobraziÅ¥ aktualizované + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nemáš žiadne subskripcie. Použi symbol hviezdy pre odoberanie kanálov. + All Videos VÅ¡etky videá @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Nie sú k dispozícii žiadne aktualizované subskripcie. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nemáš žiadne subskripcie. Použi symbol hviezdy pre odoberanie kanálov. - - - - ClearButton - - Clear - VyčisiÅ¥ - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + %n day(s) ago - + - %n weeks(s) ago - + %n month(s) ago + + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 prezretí - %n month(s) ago - + %n week(s) ago + @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Ide o demoverziu %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Umožní ti stiahnuÅ¥ iba videý kratÅ¡ie ako %1 minút, takže aspoň môžeÅ¡ otestovaÅ¥ túto funkcionalitu. - - - Continue - Pokračuj - - - Get the full version - ZískaÅ¥ plnú verziu - %1 downloaded in %2 %1 stiahnuté za %2 @@ -277,7 +281,7 @@ %n Download(s) - + @@ -632,10 +636,6 @@ &Float on Top &Vždy na vrchu - - &Adjust Window Size - &UpraviÅ¥ veľkosÅ¥ okna - &Stop After This Video &Zastav po tomto videu @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! %Páči sa vám %1? ohodnoÅ¥te ho! @@ -796,6 +804,10 @@ Update Aktualizácia + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Odkaz bude platný len obmedzenú dobu. - - This is just the demo version of %1. - Ide o demoverziu %1. - - - It allows you to test the application and see if it works for you. - Umožní ti aplikáciu vyskúšaÅ¥ a pohodlne otestovaÅ¥. - - - Get the full version - ZískaÅ¥ plnú verziu - - - Continue - Pokračuj - Downloading %1 SÅ¥ahujem %1. @@ -858,6 +854,10 @@ Subscribe to %1 OdoberaÅ¥ z %1 + + Switched to %1 + + Unsubscribed from %1 Bol zruÅ¡ený odber kanála %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 prezretí + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 z %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Hľadám... - Show %1 More Zobraz %1 viac @@ -1071,25 +1070,16 @@ Vitaj v aplikácii <a href='%1>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Vlož + to start watching videos. + pre spustenie sledovania. a keyword kľúčové slovo - a channel - názov kanálu - - - to start watching videos. - pre spustenie sledovania. - - - Watch - PozeraÅ¥ + Enter + Vlož Recent keywords @@ -1110,6 +1100,10 @@ &Back &Späť + + &Forward + + Forward to %1 Vpred k %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Nedostupný video stream pre %1 - - YTRegions @@ -1365,4 +1352,11 @@ Celosvetovo + + YTVideo + + Cannot get video stream for %1 + Nedostupný video stream pre %1 + + \ No newline at end of file diff --git a/locale/sl.ts b/locale/sl.ts index bfa3343..3f5ea59 100644 --- a/locale/sl.ts +++ b/locale/sl.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Prevedite %1 v vaÅ¡ jezik z uporabo programa %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikone je izrisal %1. @@ -96,7 +104,7 @@ AppWidget Download - + Prenesi @@ -155,6 +163,10 @@ Show Updated Pokaži posodobitve + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nimate naročnin. Uporabite zvezdo da se naročite na kanale. + All Videos Vsi videoposnetki @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Za zdaj ni nobenih novih posnetkov, na katere ste prijavljeni - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nimate naročnin. Uporabite zvezdo da se naročite na kanale. - - - - ClearButton - - Clear - Počisti - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 predvajanj + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - To je samo preizkusna različica programa %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Prenese lahko samo posnetke krajÅ¡e od %1 minut, da lahko preverite delovanje funkcije prenosa. - - - Continue - Nadaljuj - - - Get the full version - Pridobi popolno različico - %1 downloaded in %2 %1 preneseno v %2 @@ -632,10 +636,6 @@ &Float on Top Lebdeče na vrhu - - &Adjust Window Size - &Prilagodi velikost okna - &Stop After This Video U&stavi za tem posnetkom @@ -666,10 +666,18 @@ Restricted Mode - + Omejeni način Hide videos that may contain inappropriate content + Skriti videoposnetki lahko vsebujejo neprimerno vsebino. + + + Toggle &Menu Bar + + + + Menu @@ -796,6 +804,10 @@ Update Posodobitev + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Povezava bo delovala le za omejen čas. - - This is just the demo version of %1. - To je samo demo različica programa %1. - - - It allows you to test the application and see if it works for you. - Dovoli vam testiranje aplikacije in preverjanje delovanja, - - - Get the full version - Pridobi celotno različico - - - Continue - Nadaljuj - Downloading %1 PrenaÅ¡anje %1 @@ -858,6 +854,10 @@ Subscribe to %1 Naroči se na %1 + + Switched to %1 + + Unsubscribed from %1 Odjavljeni od %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 predvajanj + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 od %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Iskanje ... - Show %1 More Pokaži %1 več @@ -1071,25 +1070,16 @@ Pozdravljeni v <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Vnesite + to start watching videos. + in začnite gledati posnetke. a keyword ključno besedo - a channel - kanal - - - to start watching videos. - in začnite gledati posnetke. - - - Watch - Glejte + Enter + Vnesite Recent keywords @@ -1110,6 +1100,10 @@ &Back Na&zaj + + &Forward + + Forward to %1 Naprej na %1 @@ -1160,14 +1154,7 @@ Downloading %1... - - - - - Video - - Cannot get video stream for %1 - Za %1 ni mogoče pridobiti video pretoka + PrenaÅ¡anje %1 ... @@ -1365,4 +1352,11 @@ Svetovno + + YTVideo + + Cannot get video stream for %1 + Za %1 ni mogoče pridobiti video pretoka + + \ No newline at end of file diff --git a/locale/sq.ts b/locale/sq.ts index 02f56b3..bebe518 100644 --- a/locale/sq.ts +++ b/locale/sq.ts @@ -26,6 +26,14 @@ Translate %1 to your native language using %2 Perkthe %1 ne gjuhen e tuaj ame duke perdorur %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Dizajnimi i ikones u be nga %1 @@ -156,6 +164,10 @@ Show Updated Shfaq arrnimin + + You have no subscriptions. Use the star symbol to subscribe to channels. + Nuk keni abonime . Perdor simbolin yll për tu abonuar te kanalet + All Videos T'gjitha videot @@ -176,17 +188,6 @@ There are no updated subscriptions at this time. Nuk ka abonime të freskuar për momentin - - You have no subscriptions. Use the star symbol to subscribe to channels. - Nuk keni abonime . Perdor simbolin yll për tu abonuar te kanalet - - - - ClearButton - - Clear - Paster - DataUtils @@ -203,11 +204,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 e shikimeve + - %n month(s) ago + %n week(s) ago @@ -252,22 +272,6 @@ DownloadManager - - This is just the demo version of %1. - Ky eshte vetem version per demonstrim i %1 - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Mund te shkarkoj vetem video me te shkurta se %1 minut ne menyr qe te testoni funksionimin e shkarkuesit. - - - Continue - Vazhdon - - - Get the full version - Merrni versionin e plote - %1 downloaded in %2 %1 shkarkuar në %2 @@ -633,10 +637,6 @@ &Float on Top &Nxjerr ne Krye - - &Adjust Window Size - - &Stop After This Video &Ndalo pas kesaj video @@ -673,6 +673,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! @@ -797,6 +805,10 @@ Update Arrnim + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -812,22 +824,6 @@ The link will be valid only for a limited time. Linku do te jet i vlefshem per nje kohe te kufizuar - - This is just the demo version of %1. - Ky eshte version vetem per demonstrim i %1. - - - It allows you to test the application and see if it works for you. - Ju lejon qe te provoni programin dhe te shifni se a funksionon per ju . - - - Get the full version - Merrni versionin e plote - - - Continue - Vazhdim - Downloading %1 Duke shkarkuar %1 @@ -859,6 +855,10 @@ Subscribe to %1 Abonohu në %1 + + Switched to %1 + + Unsubscribed from %1 @@ -903,11 +903,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 e shikimeve + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 e %2 (%3)--%4 @@ -947,10 +950,6 @@ PlaylistModel - - Searching... - Duke kerkuar - Show %1 More Shfaq %1 më shumë @@ -1072,25 +1071,16 @@ Mire se erdhet ne <h href="%1">%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Hyr + to start watching videos. + Per te filluar shikimin e videove. a keyword Nje fjal kyqe - a channel - Nje kanal - - - to start watching videos. - Per te filluar shikimin e videove. - - - Watch - Shiko + Enter + Hyr Recent keywords @@ -1111,6 +1101,10 @@ &Back Prapa + + &Forward + + Forward to %1 Përpara në %1 @@ -1164,13 +1158,6 @@ - - Video - - Cannot get video stream for %1 - Nuk mund te merr rrjedhen e videos per %1 - - YTRegions @@ -1366,4 +1353,11 @@ Mbar Bota + + YTVideo + + Cannot get video stream for %1 + Nuk mund te merr rrjedhen e videos per %1 + + \ No newline at end of file diff --git a/locale/sr.ts b/locale/sr.ts index 8663b60..15e77de 100644 --- a/locale/sr.ts +++ b/locale/sr.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Преведите %1 на ваш матерњи језик помоћу %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Иконицу дизајниро %1. @@ -155,6 +163,10 @@ Show Updated Прикажи ажурирано + + You have no subscriptions. Use the star symbol to subscribe to channels. + Немате претплате. Користи звезду да се претплатиш. + All Videos Сви видеи @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Нема ажурираних субскрипција тренутно - - You have no subscriptions. Use the star symbol to subscribe to channels. - Немате претплате. Користи звезду да се претплатиш. - - - - ClearButton - - Clear - Очисти - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 прегледа + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Ово је тек демо верзија %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Може преузети само снимке краће од %1 минута тако да можете испробати употребљивост преузимања. - - - Continue - Настави - - - Get the full version - Преузмите пуну верзију - %1 downloaded in %2 %1 преузето у %2 @@ -632,10 +636,6 @@ &Float on Top &Флутај на врху - - &Adjust Window Size - &Прилагоди величину прозора - &Stop After This Video Зау&стави након овог видеа @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Обожавате %1? Оцените! @@ -796,6 +804,10 @@ Update Ажурирај + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Веза је исправна само одређено време. - - This is just the demo version of %1. - Ово је тек демо верзија %1. - - - It allows you to test the application and see if it works for you. - Омогућава вам да испробате програм и увидите да ли вам одговара. - - - Get the full version - Преузмите пуну верзију - - - Continue - Настави - Downloading %1 преузимам %1 @@ -858,6 +854,10 @@ Subscribe to %1 Претплати се на %1 + + Switched to %1 + + Unsubscribed from %1 Ун-претплаћен од %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 прегледа + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 од %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Тражим... - Show %1 More Прикажи још %1 @@ -1071,25 +1070,16 @@ Добродошли у <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - унесите + to start watching videos. + да би започели преглед видео снимака. a keyword кључну реч - a channel - канал - - - to start watching videos. - да би започели преглед видео снимака. - - - Watch - Гледај + Enter + унесите Recent keywords @@ -1110,6 +1100,10 @@ &Back &Назад + + &Forward + + Forward to %1 Напред до %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - не могу да добавим видео ток за %1 - - YTRegions @@ -1365,4 +1352,11 @@ Широм света + + YTVideo + + Cannot get video stream for %1 + не могу да добавим видео ток за %1 + + \ No newline at end of file diff --git a/locale/sv_SE.ts b/locale/sv_SE.ts index b581eb3..88d809e 100644 --- a/locale/sv_SE.ts +++ b/locale/sv_SE.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Översätt %1 till ditt modersmÃ¥l med %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Ikon designad av %1 @@ -155,6 +163,10 @@ Show Updated Visa Uppdaterat + + You have no subscriptions. Use the star symbol to subscribe to channels. + Du har inga prenumerationer. Använd stjärnsymbolen för att prenumerera pÃ¥ kanaler. + All Videos Alla Videor @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Det finns inga uppdaterade prenumerationer just nu. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Du har inga prenumerationer. Använd stjärnsymbolen för att prenumerera pÃ¥ kanaler. - - - - ClearButton - - Clear - Rensa - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 visningar + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Detta är bara en demoversion av %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Den kan bara ladda ner filmer kortare än %1 minuter sÃ¥ att du kan testa ladda-ned funktionen. - - - Continue - Fortsätt - - - Get the full version - Skaffa den fullständiga versionen - %1 downloaded in %2 %1 nedladdad i %2 @@ -632,10 +636,6 @@ &Float on Top &Flyt ovanpÃ¥ - - &Adjust Window Size - &Justera Fönster Storleken - &Stop After This Video &Stoppa efter denna video @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Älskar du %1? Betygsätt den! @@ -796,6 +804,10 @@ Update Uppdatera + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Länken kommer att gälla endast under en begränsad tid. - - This is just the demo version of %1. - Detta är bara en demoversion av %1. - - - It allows you to test the application and see if it works for you. - Den tillÃ¥ter dig att testa programmet och se om det fungerar för dig. - - - Get the full version - Skaffa den fullständiga versionen - - - Continue - Fortsätt - Downloading %1 Hämtar %1 @@ -858,6 +854,10 @@ Subscribe to %1 Prenumerera pÃ¥ %1 + + Switched to %1 + + Unsubscribed from %1 Prenumeration avbruten frÃ¥n %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 visningar + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 av %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Söker... - Show %1 More Visa %1 Till @@ -1071,25 +1070,16 @@ Välkommen till <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Ange + to start watching videos. + för att börja titta pÃ¥ video. a keyword ett sökord - a channel - en kanal - - - to start watching videos. - för att börja titta pÃ¥ video. - - - Watch - Titta + Enter + Ange Recent keywords @@ -1110,6 +1100,10 @@ &Back &Tillbaka + + &Forward + + Forward to %1 FramÃ¥t till %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Kan inte fÃ¥ videoström för %1 - - YTRegions @@ -1365,4 +1352,11 @@ FrÃ¥n Hela Världen + + YTVideo + + Cannot get video stream for %1 + Kan inte fÃ¥ videoström för %1 + + \ No newline at end of file diff --git a/locale/th.ts b/locale/th.ts index 99814e2..0992c98 100644 --- a/locale/th.ts +++ b/locale/th.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 แปล %1 สุ่ภาษาของคุณโดยใช้ %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. ออกแบบไอคอนโดย %1 @@ -155,6 +163,10 @@ Show Updated แสดงอัพเดต + + You have no subscriptions. Use the star symbol to subscribe to channels. + คุณไม่มีการสมัครติดตาม ใช้สัญลักษณ์ดาวเพื่อสมัครติดตามช่อง + All Videos วิดีโอทั้งหมด @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. ไม่มีการสมัครติดตามที่อัพเดต ณ เวลานี้ - - You have no subscriptions. Use the star symbol to subscribe to channels. - คุณไม่มีการสมัครติดตาม ใช้สัญลักษณ์ดาวเพื่อสมัครติดตามช่อง - - - - ClearButton - - Clear - ล้าง - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + ดู %1 ครั้ง + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - นี้เป็นแค่เวอร์ชั่นทดลองใช้ของ %1 - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - สามารถดาวน์โหลดวิดึโอส้ันๆกว่า %1 นาที เพื่อทดสอบฟังชั่นดาวน์โหลด - - - Continue - ต่อไป - - - Get the full version - รับเวอร์ชั่นเต็ม - %1 downloaded in %2 %1 ถูกดาวน์โหลดใน %2 @@ -632,10 +636,6 @@ &Float on Top &วางอยู่บนสุด - - &Adjust Window Size - - &Stop After This Video &หยุดหลังจากวิดีโอนี้ @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &ชอบ %1 มั้ย? ให้คะแนนมันหน่อย! @@ -796,6 +804,10 @@ Update อัพเดต + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. ลิงค์จะใช้ได้ในระยะเวลาที่จำกัดเท่านั้น - - This is just the demo version of %1. - นี้เป็นแค่เวอร์ชั่นทดลองใช้ %1. - - - It allows you to test the application and see if it works for you. - มันช่วยให้คุณทดสอบโปรแกรมและดูว่ามันใช้ได้ผลกับคุณหรือเปล่า - - - Get the full version - ทำให้เป็นเวอร์ชั่นเต็ม - - - Continue - ต่อไป - Downloading %1 กำลังดาวน์โหลด %1 @@ -858,6 +854,10 @@ Subscribe to %1 สมัครติดตามสู่ %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - ดู %1 ครั้ง + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 ของ %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - กำลังค้นหา... - Show %1 More แสดง %1 เพิ่มเติม @@ -1071,25 +1070,16 @@ ยินดีต้อนรับสู่ <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - ตกลง + to start watching videos. + เพื่อเริ่มรับชมวิดีโอ a keyword คำสำคัญ - a channel - ช่อง - - - to start watching videos. - เพื่อเริ่มรับชมวิดีโอ - - - Watch - รับชม + Enter + ตกลง Recent keywords @@ -1110,6 +1100,10 @@ &Back &กลับ + + &Forward + + Forward to %1 เดินหน้าสู่ %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - ไม่สามารถรับกระแสวิดีโอของ %1 - - YTRegions @@ -1365,4 +1352,11 @@ ทั่วโลก + + YTVideo + + Cannot get video stream for %1 + ไม่สามารถรับกระแสวิดีโอของ %1 + + \ No newline at end of file diff --git a/locale/tr.ts b/locale/tr.ts index d2f4462..73a4a73 100644 --- a/locale/tr.ts +++ b/locale/tr.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 %1'i, %2 kullanarak kendi dilinize çevirin + + Powered by %1 + % 1 tarafından desteklenmektedir + + + Open-source software + Açık kaynaklı yazılım + Icon designed by %1. Simge %1 tarafından tasarlandı. @@ -107,7 +115,7 @@ You have %n new video(s) - + %n yeni videonuz var%n yeni videonuz var @@ -155,6 +163,10 @@ Show Updated Güncellenenleri Göster + + You have no subscriptions. Use the star symbol to subscribe to channels. + Hiç aboneliğiniz yok. Kanallara abone olmak için yıldız simgesini kullanın. + All Videos Tüm Videolar @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. Şu anda güncellenen abonelik yok. - - You have no subscriptions. Use the star symbol to subscribe to channels. - Hiç aboneliğiniz yok. Kanallara abone olmak için yıldız simgesini kullanın. - - - - ClearButton - - Clear - Temizle - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + &n saat önce&n saat önce %n day(s) ago - + &n gün önce&n gün önce - %n weeks(s) ago - + %n month(s) ago + &n ay önce&n ay önce + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + %1 görüntülenme - %n month(s) ago - + %n week(s) ago + &n hafta önce&n hafta önce @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Bu sadece %'in demo sürümüdür. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Bu sadece %1 dakikadan kısa videoları indirebilir, indirme özelliğini böylece test edebilirsiniz. - - - Continue - Devam - - - Get the full version - Tam sürüme geç - %1 downloaded in %2 %2 de %1 indirildi @@ -277,7 +281,7 @@ %n Download(s) - + &n Ä°ndirme&n Ä°ndirme @@ -422,11 +426,11 @@ MainWindow &Window - Pencere + &Pencere &Minimize - Küçült + &Küçült &Stop @@ -632,10 +636,6 @@ &Float on Top Üstte Sabitle - - &Adjust Window Size - &Pencere Boyutunu Ayarla - &Stop After This Video Bu Videodan &Sonra Durdur @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content Uygunsuz içerik içerebilecek videoları gizle + + Toggle &Menu Bar + &Menü Çubuğunu Aç/Kapat + + + Menu + Menü + &Love %1? Rate it! %1 &seviyor musunuz? Değerlendirin! @@ -796,6 +804,10 @@ Update Güncelle + + You can still access the menu bar by pressing the ALT key + ALT tuşuna basarak hala menü çubuğuna erişebilirsiniz + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Bağlantı kısıtlı bir süre için geçerli olacak. - - This is just the demo version of %1. - Bu sadece %1'in demo sürümüdür. - - - It allows you to test the application and see if it works for you. - Bu, uygulamayı test etmenizi ve çalışıp çalışmadığını görmenizi sağlar. - - - Get the full version - Tam sürüme geç - - - Continue - Devam - Downloading %1 Ä°ndiriliyor %1 @@ -858,6 +854,10 @@ Subscribe to %1 %1 Abone Ol + + Switched to %1 + % 1 olarak değiştirildi + Unsubscribed from %1 %1 aboneliğinden çık @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 görüntülenme + Pick a video + Bir video seç + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 of %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Aranıyor... - Show %1 More %1 Tane Daha Göster @@ -1071,25 +1070,16 @@ <a href='%1'>%2</a>'a Hoşgeldiniz - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Giriş yapın + to start watching videos. + ve videoları izlemeye başlayın. a keyword bir anahtar kelime - a channel - bir kanal - - - to start watching videos. - ve videoları izlemeye başlayın. - - - Watch - Ä°zle + Enter + Giriş yapın Recent keywords @@ -1110,6 +1100,10 @@ &Back Geri + + &Forward + &Ä°leri + Forward to %1 %1 Yönlendir @@ -1163,13 +1157,6 @@ %1 indiriliyor... - - Video - - Cannot get video stream for %1 - %1 için video akışı alınamıyor. - - YTRegions @@ -1365,4 +1352,11 @@ Dünya Çapında + + YTVideo + + Cannot get video stream for %1 + %1 için video akışı alınamıyor. + + \ No newline at end of file diff --git a/locale/uk.ts b/locale/uk.ts index 10dae88..fccc370 100644 --- a/locale/uk.ts +++ b/locale/uk.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Перекласти %1 Вашою рідною мовою за допомогою %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Розробник піктоґрам %1. @@ -108,7 +116,7 @@ You have %n new video(s) - + @@ -156,6 +164,10 @@ Show Updated Показати оновлені + + You have no subscriptions. Use the star symbol to subscribe to channels. + У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали. + All Videos Усі видива @@ -176,17 +188,6 @@ There are no updated subscriptions at this time. Наразі оновлень підписок немає. - - You have no subscriptions. Use the star symbol to subscribe to channels. - У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали. - - - - ClearButton - - Clear - Очистити - DataUtils @@ -196,19 +197,38 @@ %n hour(s) ago - + %n day(s) ago - + - %n weeks(s) ago - + %n month(s) ago + + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 переглядів - %n month(s) ago - + %n week(s) ago + @@ -252,22 +272,6 @@ DownloadManager - - This is just the demo version of %1. - Це демонстраційна версія %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Із метою тестування, Ви можете завантажити видиво тривалістю до %1 хв. - - - Continue - Продовжити - - - Get the full version - Отримати повну версію - %1 downloaded in %2 %1 завантажений до %2 @@ -278,7 +282,7 @@ %n Download(s) - + @@ -633,10 +637,6 @@ &Float on Top &Згори всіх вікон - - &Adjust Window Size - &Змінити розмір вікна - &Stop After This Video Зу&пинити після цього видиво @@ -673,6 +673,14 @@ Hide videos that may contain inappropriate content Приховати відео, які можуть містити небажаний контент + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Подобається %1? Оцініть ! @@ -797,6 +805,10 @@ Update Оновлення + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -812,22 +824,6 @@ The link will be valid only for a limited time. Посилання буде дійсне лише протягом обмеженого часу. - - This is just the demo version of %1. - >Це демонстраційна версія %1. - - - It allows you to test the application and see if it works for you. - Ви маєте змогу протестувати проґраму та перевірити працездатність. - - - Get the full version - Отримати повну версію - - - Continue - Продовжити - Downloading %1 Завантаження %1 @@ -859,6 +855,10 @@ Subscribe to %1 Підписуватися на %1 + + Switched to %1 + + Unsubscribed from %1 Відписані від %1 @@ -903,11 +903,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 переглядів + Pick a video + Вибрати відео + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 з %2 (%3) — %4 @@ -947,10 +950,6 @@ PlaylistModel - - Searching... - Пошук... - Show %1 More Наступні %1 @@ -1072,25 +1071,16 @@ Ласкаво просимо до <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Уведіть + to start watching videos. + аби розпочати перегляд. a keyword запит - a channel - канал - - - to start watching videos. - аби розпочати перегляд. - - - Watch - Дивитися + Enter + Уведіть Recent keywords @@ -1111,6 +1101,10 @@ &Back Н&азад + + &Forward + &Вперед + Forward to %1 Уперед до %1 @@ -1164,13 +1158,6 @@ Завантаження %1... - - Video - - Cannot get video stream for %1 - Не вдалося отримати видивопотік для %1 - - YTRegions @@ -1366,4 +1353,11 @@ Світовий + + YTVideo + + Cannot get video stream for %1 + Не вдалося отримати видиво потік для %1 + + \ No newline at end of file diff --git a/locale/uk_UA.ts b/locale/uk_UA.ts index 50a6fb7..9c41c38 100644 --- a/locale/uk_UA.ts +++ b/locale/uk_UA.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Перекласти %1 Вашою рідною мовою за допомогою %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Розробник піктоґрам %1. @@ -108,7 +116,7 @@ You have %n new video(s) - + @@ -156,6 +164,10 @@ Show Updated Показати оновлені + + You have no subscriptions. Use the star symbol to subscribe to channels. + У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали. + All Videos Усі видива @@ -176,17 +188,6 @@ There are no updated subscriptions at this time. Наразі оновлень підписок немає. - - You have no subscriptions. Use the star symbol to subscribe to channels. - У Вас немає підписок. Використовуйте символ зірочки, аби підписуватися на канали. - - - - ClearButton - - Clear - Очистити - DataUtils @@ -196,19 +197,38 @@ %n hour(s) ago - + %n day(s) ago - + - %n weeks(s) ago - + %n month(s) ago + + + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 переглядів - %n month(s) ago - + %n week(s) ago + @@ -252,22 +272,6 @@ DownloadManager - - This is just the demo version of %1. - Це демонстраційна версія %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - Із метою тестування, Ви можете завантажити видиво тривалістю до %1 хв. - - - Continue - Продовжити - - - Get the full version - Отримати повну версію - %1 downloaded in %2 %1 завантажений до %2 @@ -278,7 +282,7 @@ %n Download(s) - + @@ -633,10 +637,6 @@ &Float on Top &Згори всіх вікон - - &Adjust Window Size - &Змінити розмір вікна - &Stop After This Video Зу&пинити після цього видиво @@ -673,6 +673,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Подобається %1? Оцініть ! @@ -797,6 +805,10 @@ Update Оновлення + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -812,22 +824,6 @@ The link will be valid only for a limited time. Посилання буде дійсне лише протягом обмеженого часу. - - This is just the demo version of %1. - >Це демонстраційна версія %1. - - - It allows you to test the application and see if it works for you. - Ви маєте змогу протестувати проґраму та перевірити працездатність. - - - Get the full version - Отримати повну версію - - - Continue - Продовжити - Downloading %1 Завантаження %1 @@ -859,6 +855,10 @@ Subscribe to %1 Підписуватися на %1 + + Switched to %1 + + Unsubscribed from %1 Відписані від %1 @@ -903,11 +903,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 переглядів + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 з %2 (%3) — %4 @@ -947,10 +950,6 @@ PlaylistModel - - Searching... - Пошук... - Show %1 More Наступні %1 @@ -1072,25 +1071,16 @@ Ласкаво просимо до <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Уведіть + to start watching videos. + аби розпочати перегляд. a keyword запит - a channel - канал - - - to start watching videos. - аби розпочати перегляд. - - - Watch - Дивитися + Enter + Уведіть Recent keywords @@ -1111,6 +1101,10 @@ &Back Н&азад + + &Forward + + Forward to %1 Уперед до %1 @@ -1164,13 +1158,6 @@ - - Video - - Cannot get video stream for %1 - Не вдалося отримати видивопотік для %1 - - YTRegions @@ -1366,4 +1353,11 @@ Світовий + + YTVideo + + Cannot get video stream for %1 + Не вдалося отримати видивопотік для %1 + + \ No newline at end of file diff --git a/locale/vi.ts b/locale/vi.ts index 5517eaf..dce52fb 100644 --- a/locale/vi.ts +++ b/locale/vi.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 Chuyển ngữ %1 sang ngôn ngữ của bạn bằng cách sá»­ dụng %2 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. Biểu tượng được thiết kế bởi %1. @@ -155,6 +163,10 @@ Show Updated Hiển thị Cập Nhật + + You have no subscriptions. Use the star symbol to subscribe to channels. + 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 đó. + All Videos Tất cả các video @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. 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. - - You have no subscriptions. Use the star symbol to subscribe to channels. - 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 đó. - - - - ClearButton - - Clear - Dọn dẹp phần nội dung - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 lượt xem + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - Đây là phiên bản dùng thá»­ của %1. - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - 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ề. - - - Continue - Tiếp tục - - - Get the full version - Nhận phiên bản đầy đủ - %1 downloaded in %2 %1 đã tải xuống trong %2 @@ -632,10 +636,6 @@ &Float on Top &Nổi lên trên cùng - - &Adjust Window Size - - &Stop After This Video &Dừng phát sau video này @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! &Yêu thích %1? Hãy đánh giá! @@ -796,6 +804,10 @@ Update Cập nhật + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. Liên kết này chỉ hợp lệ trong một khoảng thời gian hạn định. - - This is just the demo version of %1. - Đây chỉ là phần dùng thá»­ của phiên bản %1. - - - It allows you to test the application and see if it works for you. - 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. - - - Get the full version - Nhận phiên bản đầy đủ - - - Continue - Tiếp tục - Downloading %1 Đang tải về %1 @@ -858,6 +854,10 @@ Subscribe to %1 Đăng ký theo dõi đối với %1 + + Switched to %1 + + Unsubscribed from %1 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 lượt xem + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 của %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - Đang tìm kiếm... - Show %1 More Hiển thị %1 thêm @@ -1071,25 +1070,16 @@ Chào mừng đến với <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - Nhập vào + to start watching videos. + để bắt đầu xem các đoạn video. a keyword một từ khóa - a channel - một kênh - - - to start watching videos. - để bắt đầu xem các đoạn video. - - - Watch - Xem + Enter + Nhập vào Recent keywords @@ -1110,6 +1100,10 @@ &Back &Quay lại + + &Forward + + Forward to %1 Chuyển tới %1 @@ -1163,13 +1157,6 @@ - - Video - - Cannot get video stream for %1 - Không thể tiếp nhận luồng video từ %1 - - YTRegions @@ -1365,4 +1352,11 @@ Toàn thế giới + + YTVideo + + Cannot get video stream for %1 + Không thể tiếp nhận luồng video từ %1 + + \ No newline at end of file diff --git a/locale/zh_CN.ts b/locale/zh_CN.ts index 36d4e3e..27a7517 100644 --- a/locale/zh_CN.ts +++ b/locale/zh_CN.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 使用 %2 将 %1 翻译为您的母语 + + Powered by %1 + + + + Open-source software + + Icon designed by %1. 图标设计:%1。 @@ -155,6 +163,10 @@ Show Updated 显示更新 + + You have no subscriptions. Use the star symbol to subscribe to channels. + 你没有任何订阅,使用星形符号订阅频道 + All Videos 所有视频 @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. 当前没有订阅更新 - - You have no subscriptions. Use the star symbol to subscribe to channels. - 你没有任何订阅,使用星形符号订阅频道 - - - - ClearButton - - Clear - 清除 - DataUtils @@ -202,11 +203,30 @@ - %n weeks(s) ago + %n month(s) ago + + K + K as in Kilo, i.e. thousands + + + + M + M stands for Millions + + + + B + B stands for Billions + + + + %1 views + %1 人次观看 + - %n month(s) ago + %n week(s) ago @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - 这只是 %1 的演示版。 - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - 本版本只能下载 %1 分钟以下的视频,仅用于测试下载功能。 - - - Continue - 继续 - - - Get the full version - 获取完整版 - %1 downloaded in %2 %1 已下载(%2) @@ -632,10 +636,6 @@ &Float on Top 窗口置顶(&F) - - &Adjust Window Size - 调整窗口大小(&A) - &Stop After This Video 该视频后停止播放(&S) @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content 隐藏可能含有不恰当内容的视频 + + Toggle &Menu Bar + + + + Menu + + &Love %1? Rate it! 喜欢 %1? 为其评分! @@ -796,6 +804,10 @@ Update 更新 + + You can still access the menu bar by pressing the ALT key + + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. 此链接仅能保持短时间的有效性。 - - This is just the demo version of %1. - 这仅是 %1 的演示版。 - - - It allows you to test the application and see if it works for you. - 本版本允许您测试,以确认本应用是否适合您。 - - - Get the full version - 获取完整版 - - - Continue - 继续 - Downloading %1 正在下载 %1 @@ -858,6 +854,10 @@ Subscribe to %1 订阅 %1 + + Switched to %1 + + Unsubscribed from %1 从 %1 退订 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - %1 人次观看 + Pick a video + + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 之 %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - 搜索中…… - Show %1 More 再多显示 %1 @@ -1071,25 +1070,16 @@ 欢迎使用<a href='%1'>%2</a>! - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - 输入 + to start watching videos. + 开始观看视频。 a keyword 关键字 - a channel - 频道名称 - - - to start watching videos. - 开始观看视频。 - - - Watch - 观看 + Enter + 输入 Recent keywords @@ -1110,6 +1100,10 @@ &Back 后退(_B) + + &Forward + + Forward to %1 前进至 %1 @@ -1163,13 +1157,6 @@ 正在下载 %1... - - Video - - Cannot get video stream for %1 - 无法获得视频流。可能原因:%1 - - YTRegions @@ -1365,4 +1352,11 @@ 全球 + + YTVideo + + Cannot get video stream for %1 + 无法获得视频流。可能原因:%1 + + \ No newline at end of file diff --git a/locale/zh_TW.ts b/locale/zh_TW.ts index 4e9424e..854e248 100644 --- a/locale/zh_TW.ts +++ b/locale/zh_TW.ts @@ -25,6 +25,14 @@ Translate %1 to your native language using %2 使用 %2 翻譯 %1 介面成為您的本國語言 + + Powered by %1 + 威力本源 %1 + + + Open-source software + 開放原始碼軟體 + Icon designed by %1. 圖示由 %1 所設計。 @@ -107,7 +115,7 @@ You have %n new video(s) - + 您有 %n 個新影片 @@ -155,6 +163,10 @@ Show Updated 顯示更新 + + You have no subscriptions. Use the star symbol to subscribe to channels. + 您暫時沒有任何訂閱。使用星星符號訂閱頻道。 + All Videos 全部影片 @@ -175,17 +187,6 @@ There are no updated subscriptions at this time. 目前沒有更新的訂閱。 - - You have no subscriptions. Use the star symbol to subscribe to channels. - 您暫時沒有任何訂閱。使用星星符號訂閱頻道。 - - - - ClearButton - - Clear - 清除 - DataUtils @@ -195,19 +196,38 @@ %n hour(s) ago - + %n 小時前 %n day(s) ago - + %n 天前 - %n weeks(s) ago - + %n month(s) ago + %n 個月前 + + + K + K as in Kilo, i.e. thousands + K + + + M + M stands for Millions + M + + + B + B stands for Billions + B + + + %1 views + 瀏覽次數:%1 次 - %n month(s) ago - + %n week(s) ago + %n 週前 @@ -251,22 +271,6 @@ DownloadManager - - This is just the demo version of %1. - 這僅僅是展示版的 %1。 - - - It can only download videos shorter than %1 minutes so you can test the download functionality. - 它只能下載影片少於 %1 分鐘,使您可以測試下載功能。 - - - Continue - 繼續 - - - Get the full version - 取得完整版 - %1 downloaded in %2 %1 下載在 %2 @@ -277,7 +281,7 @@ %n Download(s) - + %n 次下載 @@ -632,10 +636,6 @@ &Float on Top 浮在上面(&F) - - &Adjust Window Size - 調整視窗大小 (&A) - &Stop After This Video 在這個影片播完之後停止(&S) @@ -672,6 +672,14 @@ Hide videos that may contain inappropriate content 隱藏可能包含不適當內容的影片 + + Toggle &Menu Bar + 切換選單列(&M) + + + Menu + 選單 + &Love %1? Rate it! 喜歡 %1 ?為它評分!(&L) @@ -796,6 +804,10 @@ Update 更新 + + You can still access the menu bar by pressing the ALT key + 您還是可以透過按下 ALT 鍵存取選單列 + MediaView @@ -811,22 +823,6 @@ The link will be valid only for a limited time. 這個連結將只在有限的時間內有效。 - - This is just the demo version of %1. - 這僅僅是展示版的 %1。 - - - It allows you to test the application and see if it works for you. - 它可以讓您測試應用程式,看它是否適合您。 - - - Get the full version - 取得完整版 - - - Continue - 繼續 - Downloading %1 正在下載 %1 @@ -858,6 +854,10 @@ Subscribe to %1 訂閱 %1 + + Switched to %1 + 切換至 %1 + Unsubscribed from %1 從 %1 取消訂閱 @@ -902,11 +902,14 @@ - PlaylistItemDelegate + PickMessage - %1 views - 瀏覽次數:%1 次 + Pick a video + 挑選影片 + + + PlaylistItemDelegate %1 of %2 (%3) — %4 %1 / %2 (%3) — %4 @@ -946,10 +949,6 @@ PlaylistModel - - Searching... - 搜尋中... - Show %1 More 顯示再多 %1個影片 @@ -1071,25 +1070,16 @@ 歡迎使用 <a href='%1'>%2</a>, - Enter - "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos" - 輸入 + to start watching videos. + 以開始觀看影片。 a keyword 一個關鍵字 - a channel - 一個頻道 - - - to start watching videos. - 以開始觀看影片。 - - - Watch - 觀看 + Enter + 輸入 Recent keywords @@ -1110,6 +1100,10 @@ &Back 後退(&B) + + &Forward + 前進(&F) + Forward to %1 向前到 %1 @@ -1163,13 +1157,6 @@ 正在下載 %1... - - Video - - Cannot get video stream for %1 - 無法為 %1 獲得影片串流 - - YTRegions @@ -1365,4 +1352,11 @@ 全世界 + + YTVideo + + Cannot get video stream for %1 + 無法為 %1 取得影片串流 + + \ No newline at end of file diff --git a/minitube.appdata.xml b/minitube.appdata.xml index c6547b6..9789fa1 100644 --- a/minitube.appdata.xml +++ b/minitube.appdata.xml @@ -1,19 +1,20 @@ - - minitube.desktop + + Minitube + org.tordini.flavio.minitube CC0-1.0 - GPL-3.0+ + GPL-3.0+ YouTube app

- Minitube is a YouTube desktop application. + Minitube is a YouTube desktop application. It is written in C++ using the Qt framework.

http://flavio.tordini.org/minitube - http://flavio.tordini.org/files/minitube/minitube-04.jpg - http://flavio.tordini.org/files/minitube/minitube-03.jpg - http://flavio.tordini.org/files/minitube/minitube-02.jpg - http://flavio.tordini.org/files/minitube/minitube-01.jpg + http://flavio.tordini.org/files/minitube/minitube-04.jpg + http://flavio.tordini.org/files/minitube/minitube-03.jpg + http://flavio.tordini.org/files/minitube/minitube-02.jpg + http://flavio.tordini.org/files/minitube/minitube-01.jpg -
+ diff --git a/minitube.pro b/minitube.pro index 453cf15..27c5a01 100644 --- a/minitube.pro +++ b/minitube.pro @@ -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) diff --git a/resources.qrc b/resources.qrc index 76556e8..9933d32 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,11 +1,6 @@ images/app.png - images/refine-search.png - images/search-time.png - images/search-sortBy.png - images/search-quality.png - images/search-duration.png flags/dz.png flags/ar.png flags/au.png @@ -54,89 +49,8 @@ flags/gb.png flags/ye.png style.css - images/worldwide.png - images/show-updated.png - images/sort.png - images/mark-watched.png - images/channels.png - images/unwatched.png - sounds/snapshot.wav images/app@2x.png - images/sort@2x.png - images/unwatched@2x.png - images/channels@2x.png - images/mark-watched@2x.png - images/show-updated@2x.png - images/worldwide@2x.png - images/refine-search@2x.png - images/search-duration@2x.png - images/search-quality@2x.png - images/search-time@2x.png - images/search-sortBy@2x.png - images/audio-volume-high.png - images/audio-volume-high@2x.png - images/audio-volume-muted.png - images/audio-volume-muted@2x.png - images/bookmark-new.png - images/bookmark-new@2x.png - images/bookmark-new_active.png - images/bookmark-new_active@2x.png - images/bookmark-remove.png - images/bookmark-remove@2x.png - images/content-loading.png - images/content-loading@2x.png - images/document-save.png - images/document-save@2x.png - images/edit-clear.png - images/edit-find.png - images/go-next.png - images/go-next@2x.png - images/go-next_active.png - images/go-next_active@2x.png - images/go-previous.png - images/go-previous@2x.png - images/go-previous_active.png - images/go-previous_active@2x.png - images/go-top.png - images/go-top@2x.png - images/media-playback-pause.png - images/media-playback-pause@2x.png - images/media-playback-start.png - images/media-playback-start@2x.png - images/media-playback-stop.png - images/media-playback-stop@2x.png - images/media-skip-forward.png - images/media-skip-forward@2x.png - images/system-search.png - images/system-search_active.png - images/system-search_selected.png - images/video-display.png - images/video-display@2x.png - images/view-fullscreen.png - images/view-fullscreen@2x.png - images/view-list.png - images/view-list@2x.png - images/view-refresh.png - images/view-refresh_active.png - images/view-refresh_selected.png - images/view-restore.png - images/view-restore@2x.png - images/window-close.png - images/window-close_active.png - images/window-close_selected.png images/64x64/app.png images/64x64/app@2x.png - images/safesearch.png - images/safesearch@2x.png - images/view-more.png - images/view-more@2x.png - images/email.png - images/email@2x.png - images/facebook.png - images/facebook@2x.png - images/link.png - images/link@2x.png - images/twitter.png - images/twitter@2x.png diff --git a/src/aboutview.cpp b/src/aboutview.cpp index 1fe723a..2c5b456 100644 --- a/src/aboutview.cpp +++ b/src/aboutview.cpp @@ -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 = "" - "

" + QString(Constants::NAME) + "

" - "

" + tr("There's life outside the browser!") + "

" - "

" + tr("Version %1").arg(Constants::VERSION) + "

" - + QString("

%1

").arg(Constants::WEBSITE); + QString info = ""; + + info += "

" + QString(Constants::NAME) + + "

" + "

" + + tr("There's life outside the browser!") + + "

" + "

" + + tr("Version %1").arg(Constants::VERSION) + "

" + + QString("

%1

").arg(Constants::WEBSITE); #ifdef APP_ACTIVATION QString email = Activation::instance().getEmail(); - if (!email.isEmpty()) - info += "

" + tr("Licensed to: %1").arg("" + email + ""); + if (!email.isEmpty()) info += "

" + tr("Licensed to: %1").arg("" + email + ""); #endif #ifndef APP_EXTRA - info += "

" + tr("%1 is Free Software but its development takes precious time.").arg(Constants::NAME) + "
" - + tr("Please donate to support the continued development of %2.") - .arg(QString(Constants::WEBSITE).append("#donate"), Constants::NAME) + "

"; + info += "

" + + tr("%1 is Free Software but its development takes precious time.") + .arg(Constants::NAME) + + "
" + + tr("Please donate to support the continued development of %2.") + .arg(QString(Constants::WEBSITE).append("#donate"), Constants::NAME) + + "

"; #endif - info += "

" + tr("Translate %1 to your native language using %2").arg(Constants::NAME) - .arg("Transifex") - + "

" + info += "

" + + tr("Translate %1 to your native language using %2") + .arg(Constants::NAME) + .arg("Transifex") + + "

"; + + info += "

" + + tr("Powered by %1") + .arg("" + tr("Open-source software") + "") + + "

"; - "

" - + tr("Icon designed by %1.").arg("David Nel") - + "

" + info += "

" + + tr("Icon designed by %1.").arg("David Nel") + + "

" - #ifndef APP_EXTRA - "

" + tr("Released under the GNU General Public License") - .arg("http://www.gnu.org/licenses/gpl.html") + "

" - #endif - "

© " + buildYear + " " + Constants::ORG_NAME + "

" +#ifndef APP_EXTRA + "

" + + tr("Released under the GNU General Public License") + .arg("http://www.gnu.org/licenses/gpl.html") + + "

" +#endif + "

© " + + buildYear + " " + Constants::ORG_NAME + + "

" ""; 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")); } diff --git a/src/aboutview.h b/src/aboutview.h index 1f2a0fe..4d06db0 100644 --- a/src/aboutview.h +++ b/src/aboutview.h @@ -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 diff --git a/src/appwidget.cpp b/src/appwidget.cpp index 43b3b58..295388c 100644 --- a/src/appwidget.cpp +++ b/src/appwidget.cpp @@ -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; diff --git a/src/autocomplete.cpp b/src/autocomplete.cpp index 50f4e72..2c8e522 100644 --- a/src/autocomplete.cpp +++ b/src/autocomplete.cpp @@ -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(ev); + QKeyEvent *keyEvent = static_cast(ev); // qWarning() << keyEvent->text(); switch (keyEvent->key()) { case Qt::Key_Enter: @@ -165,10 +167,9 @@ void AutoComplete::showSuggestions(const QVector &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 &suggestions) { void AutoComplete::acceptSuggestion() { int index = popup->currentIndex().row(); if (index >= 0 && index < suggestions.size()) { - Suggestion* suggestion = 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(Suggester* suggester) { +void AutoComplete::setSuggester(Suggester *suggester) { if (this->suggester) this->suggester->disconnect(); this->suggester = suggester; - connect(suggester, SIGNAL(ready(QVector)), SLOT(suggestionsReady(QVector))); + connect(suggester, SIGNAL(ready(QVector)), + SLOT(suggestionsReady(QVector))); } void AutoComplete::suggest() { if (!enabled) return; - popup->setCurrentItem(0); + popup->setCurrentItem(nullptr); popup->clearSelection(); originalText = buddy->text(); diff --git a/src/channelitemdelegate.cpp b/src/channelitemdelegate.cpp index 664efc4..76a5012 100644 --- a/src/channelitemdelegate.cpp +++ b/src/channelitemdelegate.cpp @@ -19,30 +19,28 @@ along with Minitube. If not, see . $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(QPainter* painter, - 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(QPainter* painter, - 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(QPainter* painter, - 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().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(); } diff --git a/src/channellistview.cpp b/src/channellistview.cpp index f791f71..7fa5fa9 100644 --- a/src/channellistview.cpp +++ b/src/channellistview.cpp @@ -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) { diff --git a/src/channellistview.h b/src/channellistview.h index ebb4d8b..dcbc8bf 100644 --- a/src/channellistview.h +++ b/src/channellistview.h @@ -24,7 +24,6 @@ $END_LICENSE */ #include class ChannelListView : public QListView { - Q_OBJECT public: @@ -42,7 +41,6 @@ protected: private: QString errorMessage; - }; #endif // CHANNELLISTVIEW_H diff --git a/src/channelview.cpp b/src/channelview.cpp index d6dcd46..eb6ccf9 100644 --- a/src/channelview.cpp +++ b/src/channelview.cpp @@ -19,30 +19,27 @@ along with Minitube. If not, see . $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()); diff --git a/src/channelview.h b/src/channelview.h index 35c709b..6285fc3 100644 --- a/src/channelview.h +++ b/src/channelview.h @@ -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 statusActions; + QVector statusActions; bool showUpdated; SortBy sortBy; QAction *markAsWatchedAction; - }; #endif // CHANNELSVIEW_H diff --git a/src/clickablelabel.cpp b/src/clickablelabel.cpp index d1f6914..bc07301 100644 --- a/src/clickablelabel.cpp +++ b/src/clickablelabel.cpp @@ -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); } diff --git a/src/clickablelabel.h b/src/clickablelabel.h index 747c861..9a24a5c 100644 --- a/src/clickablelabel.h +++ b/src/clickablelabel.h @@ -4,18 +4,20 @@ #include 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 diff --git a/src/constants.cpp b/src/constants.cpp index 0a54292..a9bfb0b 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -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"; diff --git a/src/datautils.cpp b/src/datautils.cpp index d35fc13..3101880 100644 --- a/src/datautils.cpp +++ b/src/datautils.cpp @@ -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); +} diff --git a/src/datautils.h b/src/datautils.h index ad6d203..f41adf1 100644 --- a/src/datautils.h +++ b/src/datautils.h @@ -4,7 +4,6 @@ #include 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 index 1984eb6..0000000 --- a/src/exlineedit.cpp +++ /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 index 94b3f46..0000000 --- a/src/exlineedit.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef EXLINEEDIT_H -#define EXLINEEDIT_H - -#include - -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 - diff --git a/src/fontutils.cpp b/src/fontutils.cpp index c9bf5c2..505e8d9 100644 --- a/src/fontutils.cpp +++ b/src/fontutils.cpp @@ -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; } diff --git a/src/gridwidget.cpp b/src/gridwidget.cpp index 3e445aa..032fdba 100644 --- a/src/gridwidget.cpp +++ b/src/gridwidget.cpp @@ -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(); } diff --git a/src/gridwidget.h b/src/gridwidget.h index 28462fb..c23a785 100644 --- a/src/gridwidget.h +++ b/src/gridwidget.h @@ -24,15 +24,14 @@ $END_LICENSE */ #include 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; }; diff --git a/src/homeview.cpp b/src/homeview.cpp index a8f061b..8ee3241 100644 --- a/src/homeview.cpp +++ b/src/homeview.cpp @@ -19,23 +19,21 @@ along with Minitube. If not, see . $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 (QAction* action : 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/httputils.cpp b/src/httputils.cpp index fe007cb..d522f6e 100644 --- a/src/httputils.cpp +++ b/src/httputils.cpp @@ -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; } diff --git a/src/iconutils.cpp b/src/iconutils.cpp index b09d400..6b53645 100644 --- a/src/iconutils.cpp +++ b/src/iconutils.cpp @@ -19,44 +19,61 @@ along with Minitube. If not, see . $END_LICENSE */ #include "iconutils.h" -#include #include "mainwindow.h" +#include + +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 &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 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 &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() << size); +QIcon IconUtils::tintedIcon(const char *name, const QColor &color, const QSize &size) { + return IconUtils::tintedIcon(name, color, QVector() << 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); } diff --git a/src/iconutils.h b/src/iconutils.h index 2fbe0b2..52d915c 100644 --- a/src/iconutils.h +++ b/src/iconutils.h @@ -24,26 +24,39 @@ $END_LICENSE */ #include 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 sizes = QList()); - static QIcon tintedIcon(const QString &name, const QColor &color, const QSize &size); - static void setupAction(QAction *action); + static QIcon fromResources(const char *name); + + template 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 &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 &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/jsfunctions.cpp b/src/jsfunctions.cpp index e63f14e..d0cef41 100644 --- a/src/jsfunctions.cpp +++ b/src/jsfunctions.cpp @@ -19,17 +19,18 @@ along with Minitube. If not, see . $END_LICENSE */ #include "jsfunctions.h" -#include "http.h" #include "constants.h" +#include "http.h" #include -JsFunctions* JsFunctions::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); - QObject* reply = 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()"); } diff --git a/src/jsfunctions.h b/src/jsfunctions.h index 3cb0d45..4bc369a 100644 --- a/src/jsfunctions.h +++ b/src/jsfunctions.h @@ -21,18 +21,17 @@ $END_LICENSE */ #ifndef JSFUNCTIONS_H #define JSFUNCTIONS_H -#include -#include #include #include +#include +#include class JsFunctions : public QObject { - Q_OBJECT public: - static JsFunctions* instance(); - 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: diff --git a/src/loadingwidget.cpp b/src/loadingwidget.cpp index 13e7cb6..66ec79d 100644 --- a/src/loadingwidget.cpp +++ b/src/loadingwidget.cpp @@ -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() { diff --git a/src/loadingwidget.h b/src/loadingwidget.h index ca19c06..ad4c60a 100644 --- a/src/loadingwidget.h +++ b/src/loadingwidget.h @@ -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 diff --git a/src/main.cpp b/src/main.cpp index 3a0873f..717179e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,14 +18,14 @@ along with Minitube. If not, see . $END_LICENSE */ -#include #include +#include -#include #include "constants.h" +#include "iconutils.h" #include "mainwindow.h" #include "searchparams.h" -#include "iconutils.h" +#include #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(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3fcf2fe..4a3a480 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -60,6 +60,7 @@ $END_LICENSE */ #endif #include #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(Qt::Key_Escape) - << QKeySequence(Qt::Key_MediaStop)); + stopAct->setShortcuts(QList() + << 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(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(Qt::Key_Space) - << QKeySequence(Qt::Key_MediaPlay)); + pauseAct->setShortcuts(QList() + << 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 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("Del") - << QKeySequence("Backspace")); + removeAct->setShortcuts(QList() + << 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(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(); - 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(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 &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(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(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(Qt::Key_MediaStop)); } else { - stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) - << QKeySequence(Qt::Key_MediaStop)); + stopAct->setShortcuts(QList() + << 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." "

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 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(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 &definitions = VideoDefinition::getDefinitions(); - const VideoDefinition ¤tDefinition = VideoDefinition::forName(definitionName); - if (currentDefinition.isEmpty()) { - setDefinitionMode(definitions.at(0).getName()); - return; - } + const VideoDefinition ¤tDefinition = 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(Qt::CTRL + Qt::Key_J)); - stopAct->setShortcuts(QList() << QKeySequence(Qt::Key_Escape) - << QKeySequence(Qt::Key_MediaStop)); + stopAct->setShortcuts(QList() + << 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); } diff --git a/src/mainwindow.h b/src/mainwindow.h index e377a2f..e7d80b6 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -22,56 +22,47 @@ $END_LICENSE */ #define MAINWINDOW_H #include -#ifdef APP_PHONON -#include -#include -#include -#include -#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 MainWindow* instance(); + 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; } - MediaView* getMediaView() { return mediaView; } - HomeView* getHomeView() { return homeView; } - QAction* getRegionAction() { 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 &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 actionMap; - QHash menuMap; + QHash actionMap; + QHash menuMap; // view mechanism QStackedWidget *views; - QStack history; - // QVector viewActions; + QStack 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 diff --git a/src/mediaview.cpp b/src/mediaview.cpp index 286d56f..e6343ab 100644 --- a/src/mediaview.cpp +++ b/src/mediaview.cpp @@ -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

").arg(cssColor) + - tr("Welcome to %2,") - .replace(""); + 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("

").arg(cssColor) + + tr("Welcome to %2,") + .replace(""; + 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)), SLOT(onChannelSuggestions(QVector))); + connect(channelSuggest, SIGNAL(ready(QVector)), + SLOT(onChannelSuggestions(QVector))); 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(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 &suggestions) { diff --git a/src/searchview.h b/src/searchview.h index 172b0ca..d6841d2 100644 --- a/src/searchview.h +++ b/src/searchview.h @@ -18,8 +18,8 @@ along with Minitube. If not, see . $END_LICENSE */ -#ifndef __SEARCHVIEW_H__ -#define __SEARCHVIEW_H__ +#ifndef SEARCHVIEW_H +#define SEARCHVIEW_H #include @@ -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 &suggestions); + void onChannelSuggestions(const QVector &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 lastChannelSuggestions; + QVector lastChannelSuggestions; ClickableLabel *logo; }; -#endif // __SEARCHVIEW_H__ +#endif // SEARCHVIEW_H diff --git a/src/searchwidget.h b/src/searchwidget.h index dd42b70..b429d38 100644 --- a/src/searchwidget.h +++ b/src/searchwidget.h @@ -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 diff --git a/src/seekslider.cpp b/src/seekslider.cpp index 281673f..81fb00a 100644 --- a/src/seekslider.cpp +++ b/src/seekslider.cpp @@ -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()); } diff --git a/src/seekslider.h b/src/seekslider.h index a56b6c0..52835f9 100644 --- a/src/seekslider.h +++ b/src/seekslider.h @@ -4,12 +4,10 @@ #include class SeekSlider : public QSlider { - Q_OBJECT public: - SeekSlider(QWidget *parent = 0); - + SeekSlider(QWidget *parent = nullptr); }; #endif // SEEKSLIDER_H diff --git a/src/segmentedcontrol.cpp b/src/segmentedcontrol.cpp index 1af157e..e763f12 100644 --- a/src/segmentedcontrol.cpp +++ b/src/segmentedcontrol.cpp @@ -19,30 +19,25 @@ along with Minitube. If not, see . $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; } - QAction* newCheckedAction = 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 QRect& rect, 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(); diff --git a/src/segmentedcontrol.h b/src/segmentedcontrol.h index 0639146..8e9813f 100644 --- a/src/segmentedcontrol.h +++ b/src/segmentedcontrol.h @@ -24,18 +24,17 @@ $END_LICENSE */ #include 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 actionList; @@ -61,7 +61,6 @@ private: QColor selectedColor; QColor hoveredColor; QColor pressedColor; - }; #endif /* !SEGMENTEDCONTROL_H */ diff --git a/src/sharetoolbar.cpp b/src/sharetoolbar.cpp index e8d1b0d..84f232f 100644 --- a/src/sharetoolbar.cpp +++ b/src/sharetoolbar.cpp @@ -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); } diff --git a/src/sidebarheader.cpp b/src/sidebarheader.cpp index 399d65f..150db79 100644 --- a/src/sidebarheader.cpp +++ b/src/sidebarheader.cpp @@ -19,12 +19,13 @@ along with Minitube. If not, see . $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 (QAction* action : 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 &history = MediaView::instance()->getHistory(); + const QVector &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 history = MediaView::instance()->getHistory(); + QVector history = MediaView::instance()->getHistory(); int currentIndex = MediaView::instance()->getHistoryIndex(); VideoSource *currentVideoSource = history.at(currentIndex); - for (QAction* action : 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); - - } diff --git a/src/sidebarwidget.cpp b/src/sidebarwidget.cpp index 27283d7..2b1d546 100644 --- a/src/sidebarwidget.cpp +++ b/src/sidebarwidget.cpp @@ -19,22 +19,24 @@ along with Minitube. If not, see . $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 = - "" - "" - "%1" - ""; + QString html = "" + "" + "%1" + ""; html = html.arg(message); messageLabel->setText(html); messageLabel->show(); diff --git a/src/sidebarwidget.h b/src/sidebarwidget.h index f15e4da..aa66ee2 100644 --- a/src/sidebarwidget.h +++ b/src/sidebarwidget.h @@ -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(); - RefineSearchWidget* getRefineSearchWidget() { return refineSearchWidget; } - SidebarHeader* getHeader() { return sidebarHeader; } + RefineSearchWidget *getRefineSearchWidget() { return refineSearchWidget; } + SidebarHeader *getHeader() { return sidebarHeader; } void hideSuggestions(); public slots: diff --git a/src/snapshotpreview.cpp b/src/snapshotpreview.cpp index 3662648..373c0d6 100644 --- a/src/snapshotpreview.cpp +++ b/src/snapshotpreview.cpp @@ -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) { diff --git a/src/snapshotpreview.h b/src/snapshotpreview.h index 7bfab08..2039c0d 100644 --- a/src/snapshotpreview.h +++ b/src/snapshotpreview.h @@ -23,18 +23,13 @@ $END_LICENSE */ #include -#ifdef APP_PHONON -#include -#include -#include -#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 diff --git a/src/spacer.h b/src/spacer.h index cb87b18..5b33b2e 100644 --- a/src/spacer.h +++ b/src/spacer.h @@ -24,9 +24,8 @@ $END_LICENSE */ #include class Spacer : public QWidget { - public: - Spacer(QWidget *parent = 0, int minWidth = 60); + Spacer(QWidget *parent = nullptr, int minWidth = 60); protected: QSize sizeHint() const; diff --git a/src/standardfeedsview.cpp b/src/standardfeedsview.cpp index 3e83208..9080f1c 100644 --- a/src/standardfeedsview.cpp +++ b/src/standardfeedsview.cpp @@ -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() { diff --git a/src/toolbarmenu.cpp b/src/toolbarmenu.cpp index e03dbac..da26af2 100644 --- a/src/toolbarmenu.cpp +++ b/src/toolbarmenu.cpp @@ -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); } diff --git a/src/toolbarmenu.h b/src/toolbarmenu.h index ed76ba8..fa8ddb0 100644 --- a/src/toolbarmenu.h +++ b/src/toolbarmenu.h @@ -7,7 +7,7 @@ class ToolbarMenu : public QMenu { Q_OBJECT public: - ToolbarMenu(QWidget *parent = 0); + ToolbarMenu(QWidget *parent = nullptr); signals: void leftMarginChanged(int value); diff --git a/src/video.cpp b/src/video.cpp index 52432b8..05d9761 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -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; + } +} diff --git a/src/video.h b/src/video.h index e0c8e44..723be19 100644 --- a/src/video.h +++ b/src/video.h @@ -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/videoareawidget.cpp b/src/videoarea.cpp similarity index 63% rename from src/videoareawidget.cpp rename to src/videoarea.cpp index 19973fa..1b496ca 100644 --- a/src/videoareawidget.cpp +++ b/src/videoarea.cpp @@ -18,7 +18,7 @@ along with Minitube. If not, see . $END_LICENSE */ -#include "videoareawidget.h" +#include "videoarea.h" #include "loadingwidget.h" #include "mainwindow.h" #include "playlistmodel.h" @@ -27,20 +27,14 @@ $END_LICENSE */ #ifdef Q_OS_MAC #include "macutils.h" #endif -#include "snapshotpreview.h" #include "fontutils.h" +#include "snapshotpreview.h" namespace { -class MessageWidget : public QWidget { +class PickMessage : 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); + PickMessage(QWidget *parent = nullptr) : QWidget(parent) { setAutoFillBackground(true); QBoxLayout *l = new QHBoxLayout(this); @@ -50,36 +44,23 @@ public: 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); } }; -} +} // namespace -VideoAreaWidget::VideoAreaWidget(QWidget *parent) - : QWidget(parent), videoWidget(0), messageWidget(0) { +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); - // 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); @@ -96,74 +77,59 @@ VideoAreaWidget::VideoAreaWidget(QWidget *parent) SLOT(showContextMenu(const QPoint &))); } -void VideoAreaWidget::setVideoWidget(QWidget *videoWidget) { +void VideoArea::setVideoWidget(QWidget *videoWidget) { this->videoWidget = videoWidget; stackedLayout->addWidget(videoWidget); } -void VideoAreaWidget::setLoadingWidget(LoadingWidget *loadingWidget) { +void VideoArea::setLoadingWidget(LoadingWidget *loadingWidget) { this->loadingWidget = loadingWidget; stackedLayout->addWidget(loadingWidget); stackedLayout->setCurrentWidget(loadingWidget); } -void VideoAreaWidget::showVideo() { +void VideoArea::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() { +void VideoArea::showPickMessage() { if (!messageWidget) { - messageWidget = new MessageWidget(); + messageWidget = new PickMessage(); stackedLayout->addWidget(messageWidget); } stackedLayout->setCurrentWidget(messageWidget); } -void VideoAreaWidget::showLoading(Video *video) { - messageLabel->hide(); - messageLabel->clear(); - stackedLayout->setCurrentWidget(loadingWidget); +void VideoArea::showLoading(Video *video) { loadingWidget->setVideo(video); + stackedLayout->setCurrentWidget(loadingWidget); } #ifdef APP_SNAPSHOT -void VideoAreaWidget::showSnapshotPreview(const QPixmap &pixmap) { - bool soundOnly = false; -#ifdef APP_MAC - soundOnly = MainWindow::instance()->isReallyFullScreen(); -#endif +void VideoArea::showSnapshotPreview(const QPixmap &pixmap) { + bool soundOnly = MainWindow::instance()->isReallyFullScreen(); snapshotPreview->start(videoWidget, pixmap, soundOnly); } - -void VideoAreaWidget::hideSnapshotPreview() {} #endif -void VideoAreaWidget::clear() { +void VideoArea::clear() { loadingWidget->clear(); - messageLabel->hide(); - messageLabel->clear(); stackedLayout->setCurrentWidget(loadingWidget); } -void VideoAreaWidget::mouseDoubleClickEvent(QMouseEvent *event) { +void VideoArea::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) emit doubleClicked(); } -void VideoAreaWidget::dragEnterEvent(QDragEnterEvent *event) { +void VideoArea::dragEnterEvent(QDragEnterEvent *event) { // qDebug() << event->mimeData()->formats(); if (event->mimeData()->hasFormat("application/x-minitube-video")) { event->acceptProposedAction(); } } -void VideoAreaWidget::dropEvent(QDropEvent *event) { +void VideoArea::dropEvent(QDropEvent *event) { const VideoMimeData *videoMimeData = qobject_cast(event->mimeData()); if (!videoMimeData) return; @@ -175,7 +141,7 @@ void VideoAreaWidget::dropEvent(QDropEvent *event) { event->acceptProposedAction(); } -void VideoAreaWidget::showContextMenu(const QPoint &point) { +void VideoArea::showContextMenu(const QPoint &point) { QMenu *menu = MainWindow::instance()->getMenu("video"); menu->exec(mapToGlobal(point)); } diff --git a/src/videoareawidget.h b/src/videoarea.h similarity index 82% rename from src/videoareawidget.h rename to src/videoarea.h index 21b3d13..33bbf83 100644 --- a/src/videoareawidget.h +++ b/src/videoarea.h @@ -28,22 +28,18 @@ class LoadingWidget; class PlaylistModel; class SnapshotPreview; -class VideoAreaWidget : public QWidget { - +class VideoArea : public QWidget { Q_OBJECT public: - VideoAreaWidget(QWidget *parent = 0); + VideoArea(QWidget *parent = nullptr); void setVideoWidget(QWidget *videoWidget); void setLoadingWidget(LoadingWidget *loadingWidget); - void showLoading(Video* video); + void showLoading(Video *video); void showVideo(); - void showError(const QString &message); void showPickMessage(); void clear(); - void setListModel(PlaylistModel *listModel) { - this->listModel = listModel; - } + void setListModel(PlaylistModel *listModel) { this->listModel = listModel; } #ifdef APP_SNAPSHOT void showSnapshotPreview(const QPixmap &pixmap); #endif @@ -60,25 +56,16 @@ protected: private slots: void showContextMenu(const QPoint &point); -#ifdef APP_SNAPSHOT - void hideSnapshotPreview(); -#endif private: QStackedLayout *stackedLayout; QWidget *videoWidget; LoadingWidget *loadingWidget; - + QWidget *messageWidget; #ifdef APP_SNAPSHOT SnapshotPreview *snapshotPreview; #endif - PlaylistModel *listModel; - QLabel *messageLabel; - - QWidget *messageWidget; - - QPoint dragPosition; }; #endif // VIDEOAREAWIDGET_H diff --git a/src/videodefinition.cpp b/src/videodefinition.cpp index 12cfd12..360817f 100644 --- a/src/videodefinition.cpp +++ b/src/videodefinition.cpp @@ -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 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::getDefinitions() { + // List preferred equivalent format last: + // algo selects the last format with same name first static const QVector 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 &VideoDefinition::getDefinitionNames() { + static const QVector names = {"480p", "720p", "1080p", "1440p", "2160p"}; + return names; +} + const VideoDefinition &VideoDefinition::forName(const QString &name) { return getDefinitionForImpl(name); } -// static const VideoDefinition &VideoDefinition::forCode(int code) { return getDefinitionForImpl(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(); } diff --git a/src/videodefinition.h b/src/videodefinition.h index 321a116..b35a545 100644 --- a/src/videodefinition.h +++ b/src/videodefinition.h @@ -26,20 +26,23 @@ $END_LICENSE */ class VideoDefinition { public: static const QVector &getDefinitions(); + static const QVector &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) { diff --git a/src/yt3.cpp b/src/yt3.cpp index 3703a8c..c8081b6 100644 --- a/src/yt3.cpp +++ b/src/yt3.cpp @@ -3,11 +3,12 @@ #include #include -#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) { diff --git a/src/yt3.h b/src/yt3.h index c7de235..1750c8c 100644 --- a/src/yt3.h +++ b/src/yt3.h @@ -4,9 +4,9 @@ #include 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); diff --git a/src/yt3listparser.cpp b/src/yt3listparser.cpp index 74e11e6..53e9e24 100644 --- a/src/yt3listparser.cpp +++ b/src/yt3listparser.cpp @@ -2,6 +2,16 @@ #include "datautils.h" #include "video.h" +#include + +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(); diff --git a/src/ytchannel.cpp b/src/ytchannel.cpp index 2c0a745..bfc2d42 100644 --- a/src/ytchannel.cpp +++ b/src/ytchannel.cpp @@ -19,28 +19,22 @@ along with Minitube. If not, see . $END_LICENSE */ #include "ytchannel.h" +#include "database.h" #include "http.h" #include "httputils.h" -#include "database.h" #include #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 YTChannel::cache; +QHash YTChannel::cache; -YTChannel* YTChannel::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(); - YTChannel* channel = 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(); diff --git a/src/ytregions.cpp b/src/ytregions.cpp index e24147b..f968a72 100644 --- a/src/ytregions.cpp +++ b/src/ytregions.cpp @@ -19,6 +19,7 @@ along with Minitube. If not, see . $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 ®ionId) { - if (regionId.isEmpty()) return QIcon(":images/worldwide.png"); + if (regionId.isEmpty()) return IconUtils::icon("worldwide"); return QIcon(":flags/" + regionId.toLower() + ".png"); } diff --git a/src/ytsinglevideosource.cpp b/src/ytsinglevideosource.cpp index e7bf33e..09936ca 100644 --- a/src/ytsinglevideosource.cpp +++ b/src/ytsinglevideosource.cpp @@ -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 videos; + QVector