From 0943f9459813b5483bcc627331a27577940e56c1 Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Wed, 24 Sep 2025 10:36:07 +0200 Subject: [PATCH 01/11] Rewrite --- .gitignore | 13 +- .gitlab-ci.yml | 59 +- .gitlab/issue_templates/Bug.md | 19 +- .gitlab/merge_request_templates/Default.md | 9 + CONTRIBUTING.md | 14 +- LICENSE | 12 + README.md | 4 +- assets/screenshot1.png | Bin 52637 -> 70362 bytes assets/screenshot2.png | Bin 48962 -> 51963 bytes source/LICENSE | 339 ------- source/Makefile | 78 +- source/bin/monophony.py | 80 +- source/data/manifest.json | 93 +- source/data/metainfo.xml | 28 +- source/data/monophony.desktop | 9 +- source/data/python3-modules.json | 90 -- source/data/resources.gresource.xml | 6 + source/locales/all.pot | 346 +++---- source/locales/be/LC_MESSAGES/all.mo | Bin 5285 -> 0 bytes source/locales/be/LC_MESSAGES/all.po | 433 -------- source/locales/cs/LC_MESSAGES/all.mo | Bin 2751 -> 0 bytes source/locales/cs/LC_MESSAGES/all.po | 268 ----- source/locales/en/LC_MESSAGES/all.mo | Bin 98 -> 0 bytes source/locales/en/LC_MESSAGES/all.po | 2 + source/locales/en_GB/LC_MESSAGES/all.mo | Bin 3655 -> 0 bytes source/locales/en_GB/LC_MESSAGES/all.po | 321 ------ source/locales/fr/LC_MESSAGES/all.mo | Bin 3854 -> 0 bytes source/locales/fr/LC_MESSAGES/all.po | 416 -------- source/locales/ja/LC_MESSAGES/all.mo | Bin 4240 -> 0 bytes source/locales/ja/LC_MESSAGES/all.po | 328 ------- source/locales/nl/LC_MESSAGES/all.mo | Bin 1916 -> 0 bytes source/locales/nl/LC_MESSAGES/all.po | 141 --- source/locales/pl/LC_MESSAGES/all.po | 329 +++++++ source/locales/pt_BR/LC_MESSAGES/all.mo | Bin 2761 -> 0 bytes source/locales/pt_BR/LC_MESSAGES/all.po | 301 ------ source/locales/ru/LC_MESSAGES/all.mo | Bin 4883 -> 0 bytes source/locales/ru/LC_MESSAGES/all.po | 428 -------- source/locales/tr/LC_MESSAGES/all.mo | Bin 2510 -> 0 bytes source/locales/tr/LC_MESSAGES/all.po | 215 ---- source/monophony/__init__.py | 6 +- source/monophony/app.py | 47 + source/monophony/asynchronous.py | 62 ++ source/monophony/backend/cache.py | 122 --- source/monophony/backend/history.py | 95 -- source/monophony/backend/mpris.py | 103 -- source/monophony/backend/player.py | 495 ---------- source/monophony/backend/playlists.py | 280 ------ source/monophony/backend/settings.py | 37 - source/monophony/backend/utils.py | 32 - source/monophony/backend/yt.py | 346 ------- source/monophony/data.py | 146 +++ source/monophony/debug.py | 48 + source/monophony/downloads.py | 292 ++++++ source/monophony/frontend/app.py | 24 - .../monophony/frontend/pages/artist_page.py | 99 -- .../monophony/frontend/pages/results_page.py | 144 --- .../popovers/importable_group_popover.py | 25 - .../frontend/popovers/local_song_popover.py | 18 - .../frontend/popovers/queue_song_popover.py | 21 - .../frontend/popovers/song_popover.py | 53 - source/monophony/frontend/rows/artist_row.py | 33 - .../frontend/rows/external_group_row.py | 110 --- source/monophony/frontend/rows/group_row.py | 32 - .../frontend/rows/importable_group_row.py | 27 - .../frontend/rows/local_group_row.py | 126 --- .../monophony/frontend/rows/local_song_row.py | 79 -- .../frontend/rows/locked_group_row.py | 16 - .../monophony/frontend/rows/queue_song_row.py | 80 -- source/monophony/frontend/rows/song_row.py | 70 -- source/monophony/frontend/tabs/library_tab.py | 231 ----- source/monophony/frontend/tabs/queue_tab.py | 91 -- source/monophony/frontend/tabs/search_tab.py | 109 --- source/monophony/frontend/widgets/__init__.py | 1 - source/monophony/frontend/widgets/player.py | 335 ------- .../frontend/widgets/recent_searches.py | 63 -- source/monophony/frontend/windows/__init__.py | 1 - .../monophony/frontend/windows/add_window.py | 143 --- .../frontend/windows/import_window.py | 132 --- .../monophony/frontend/windows/main_window.py | 277 ------ .../frontend/windows/message_window.py | 23 - source/monophony/logging.py | 133 +++ source/monophony/mpris.py | 112 +++ source/monophony/player.py | 503 ++++++++++ source/monophony/playlists.py | 320 ++++++ source/monophony/recents.py | 68 ++ source/monophony/recommendations.py | 54 + source/monophony/settings.py | 46 + source/monophony/{backend => ui}/__init__.py | 0 .../{frontend/pages => ui/bars}/__init__.py | 0 source/monophony/ui/bars/header_bar.py | 29 + source/monophony/ui/bars/player_bar.py | 302 ++++++ source/monophony/ui/bars/search_bar.py | 37 + .../popovers => ui/pages}/__init__.py | 0 source/monophony/ui/pages/artist_page.py | 11 + source/monophony/ui/pages/home_page.py | 493 ++++++++++ source/monophony/ui/pages/loading_page.py | 24 + source/monophony/ui/pages/page.py | 38 + source/monophony/ui/pages/results_page.py | 315 ++++++ source/monophony/ui/pages/status_page.py | 19 + .../{frontend => ui/popovers}/__init__.py | 0 .../ui/popovers/editable_group_row_popover.py | 26 + .../ui/popovers/editable_song_row_popover.py | 19 + .../ui/popovers/group_row_popover.py | 42 + .../popovers/importable_group_row_popover.py | 20 + .../ui/popovers/queue_song_row_popover.py | 20 + source/monophony/ui/popovers/row_popover.py | 19 + .../monophony/ui/popovers/song_row_popover.py | 51 + .../synchronized_group_row_popover.py | 20 + source/monophony/ui/queue_sidebar.py | 206 ++++ .../rows => ui/row_groups}/__init__.py | 0 .../ui/row_groups/editable_group_row_group.py | 26 + .../ui/row_groups/group_row_group.py | 64 ++ .../row_groups/importable_group_row_group.py | 25 + .../ui/row_groups/playable_row_group.py | 55 ++ .../ui/row_groups/queue_row_group.py | 41 + .../ui/row_groups/queueable_row_group.py | 45 + source/monophony/ui/row_groups/row_group.py | 53 + .../synchronized_group_row_group.py | 8 + source/monophony/ui/rows/__init__.py | 0 source/monophony/ui/rows/artist_row.py | 29 + .../monophony/ui/rows/draggable_song_row.py | 80 ++ .../monophony/ui/rows/editable_group_row.py | 118 +++ source/monophony/ui/rows/editable_song_row.py | 59 ++ source/monophony/ui/rows/group_row.py | 163 +++ .../monophony/ui/rows/importable_group_row.py | 55 ++ source/monophony/ui/rows/queue_song_row.py | 54 + source/monophony/ui/rows/song_row.py | 113 +++ .../ui/rows/synchronized_group_row.py | 73 ++ .../{frontend/tabs => ui/windows}/__init__.py | 0 source/monophony/ui/windows/add_window.py | 100 ++ source/monophony/ui/windows/import_window.py | 110 +++ source/monophony/ui/windows/main_window.py | 925 ++++++++++++++++++ source/monophony/ui/windows/message_window.py | 12 + source/monophony/ui/windows/rename_window.py | 45 + source/monophony/yt.py | 553 +++++++++++ source/pyproject.toml | 51 +- source/requirements-dev.txt | 1 - source/requirements.txt | 2 - source/tests/tests.py | 188 ++++ 139 files changed, 7383 insertions(+), 7447 deletions(-) create mode 100644 .gitlab/merge_request_templates/Default.md create mode 100644 LICENSE delete mode 100644 source/LICENSE delete mode 100644 source/data/python3-modules.json create mode 100644 source/data/resources.gresource.xml delete mode 100644 source/locales/be/LC_MESSAGES/all.mo delete mode 100644 source/locales/be/LC_MESSAGES/all.po delete mode 100644 source/locales/cs/LC_MESSAGES/all.mo delete mode 100644 source/locales/cs/LC_MESSAGES/all.po delete mode 100644 source/locales/en/LC_MESSAGES/all.mo create mode 100644 source/locales/en/LC_MESSAGES/all.po delete mode 100644 source/locales/en_GB/LC_MESSAGES/all.mo delete mode 100644 source/locales/en_GB/LC_MESSAGES/all.po delete mode 100644 source/locales/fr/LC_MESSAGES/all.mo delete mode 100644 source/locales/fr/LC_MESSAGES/all.po delete mode 100644 source/locales/ja/LC_MESSAGES/all.mo delete mode 100644 source/locales/ja/LC_MESSAGES/all.po delete mode 100644 source/locales/nl/LC_MESSAGES/all.mo delete mode 100644 source/locales/nl/LC_MESSAGES/all.po create mode 100644 source/locales/pl/LC_MESSAGES/all.po delete mode 100644 source/locales/pt_BR/LC_MESSAGES/all.mo delete mode 100644 source/locales/pt_BR/LC_MESSAGES/all.po delete mode 100644 source/locales/ru/LC_MESSAGES/all.mo delete mode 100644 source/locales/ru/LC_MESSAGES/all.po delete mode 100644 source/locales/tr/LC_MESSAGES/all.mo delete mode 100644 source/locales/tr/LC_MESSAGES/all.po create mode 100644 source/monophony/app.py create mode 100644 source/monophony/asynchronous.py delete mode 100644 source/monophony/backend/cache.py delete mode 100644 source/monophony/backend/history.py delete mode 100644 source/monophony/backend/mpris.py delete mode 100644 source/monophony/backend/player.py delete mode 100644 source/monophony/backend/playlists.py delete mode 100644 source/monophony/backend/settings.py delete mode 100644 source/monophony/backend/utils.py delete mode 100644 source/monophony/backend/yt.py create mode 100644 source/monophony/data.py create mode 100644 source/monophony/debug.py create mode 100644 source/monophony/downloads.py delete mode 100644 source/monophony/frontend/app.py delete mode 100644 source/monophony/frontend/pages/artist_page.py delete mode 100644 source/monophony/frontend/pages/results_page.py delete mode 100644 source/monophony/frontend/popovers/importable_group_popover.py delete mode 100644 source/monophony/frontend/popovers/local_song_popover.py delete mode 100644 source/monophony/frontend/popovers/queue_song_popover.py delete mode 100644 source/monophony/frontend/popovers/song_popover.py delete mode 100644 source/monophony/frontend/rows/artist_row.py delete mode 100644 source/monophony/frontend/rows/external_group_row.py delete mode 100644 source/monophony/frontend/rows/group_row.py delete mode 100644 source/monophony/frontend/rows/importable_group_row.py delete mode 100644 source/monophony/frontend/rows/local_group_row.py delete mode 100644 source/monophony/frontend/rows/local_song_row.py delete mode 100644 source/monophony/frontend/rows/locked_group_row.py delete mode 100644 source/monophony/frontend/rows/queue_song_row.py delete mode 100644 source/monophony/frontend/rows/song_row.py delete mode 100644 source/monophony/frontend/tabs/library_tab.py delete mode 100644 source/monophony/frontend/tabs/queue_tab.py delete mode 100644 source/monophony/frontend/tabs/search_tab.py delete mode 100644 source/monophony/frontend/widgets/__init__.py delete mode 100644 source/monophony/frontend/widgets/player.py delete mode 100644 source/monophony/frontend/widgets/recent_searches.py delete mode 100644 source/monophony/frontend/windows/__init__.py delete mode 100644 source/monophony/frontend/windows/add_window.py delete mode 100644 source/monophony/frontend/windows/import_window.py delete mode 100644 source/monophony/frontend/windows/main_window.py delete mode 100644 source/monophony/frontend/windows/message_window.py create mode 100644 source/monophony/logging.py create mode 100644 source/monophony/mpris.py create mode 100644 source/monophony/player.py create mode 100644 source/monophony/playlists.py create mode 100644 source/monophony/recents.py create mode 100644 source/monophony/recommendations.py create mode 100644 source/monophony/settings.py rename source/monophony/{backend => ui}/__init__.py (100%) rename source/monophony/{frontend/pages => ui/bars}/__init__.py (100%) create mode 100644 source/monophony/ui/bars/header_bar.py create mode 100644 source/monophony/ui/bars/player_bar.py create mode 100644 source/monophony/ui/bars/search_bar.py rename source/monophony/{frontend/popovers => ui/pages}/__init__.py (100%) create mode 100644 source/monophony/ui/pages/artist_page.py create mode 100644 source/monophony/ui/pages/home_page.py create mode 100644 source/monophony/ui/pages/loading_page.py create mode 100644 source/monophony/ui/pages/page.py create mode 100644 source/monophony/ui/pages/results_page.py create mode 100644 source/monophony/ui/pages/status_page.py rename source/monophony/{frontend => ui/popovers}/__init__.py (100%) create mode 100644 source/monophony/ui/popovers/editable_group_row_popover.py create mode 100644 source/monophony/ui/popovers/editable_song_row_popover.py create mode 100644 source/monophony/ui/popovers/group_row_popover.py create mode 100644 source/monophony/ui/popovers/importable_group_row_popover.py create mode 100644 source/monophony/ui/popovers/queue_song_row_popover.py create mode 100644 source/monophony/ui/popovers/row_popover.py create mode 100644 source/monophony/ui/popovers/song_row_popover.py create mode 100644 source/monophony/ui/popovers/synchronized_group_row_popover.py create mode 100644 source/monophony/ui/queue_sidebar.py rename source/monophony/{frontend/rows => ui/row_groups}/__init__.py (100%) create mode 100644 source/monophony/ui/row_groups/editable_group_row_group.py create mode 100644 source/monophony/ui/row_groups/group_row_group.py create mode 100644 source/monophony/ui/row_groups/importable_group_row_group.py create mode 100644 source/monophony/ui/row_groups/playable_row_group.py create mode 100644 source/monophony/ui/row_groups/queue_row_group.py create mode 100644 source/monophony/ui/row_groups/queueable_row_group.py create mode 100644 source/monophony/ui/row_groups/row_group.py create mode 100644 source/monophony/ui/row_groups/synchronized_group_row_group.py create mode 100644 source/monophony/ui/rows/__init__.py create mode 100644 source/monophony/ui/rows/artist_row.py create mode 100644 source/monophony/ui/rows/draggable_song_row.py create mode 100644 source/monophony/ui/rows/editable_group_row.py create mode 100644 source/monophony/ui/rows/editable_song_row.py create mode 100644 source/monophony/ui/rows/group_row.py create mode 100644 source/monophony/ui/rows/importable_group_row.py create mode 100644 source/monophony/ui/rows/queue_song_row.py create mode 100644 source/monophony/ui/rows/song_row.py create mode 100644 source/monophony/ui/rows/synchronized_group_row.py rename source/monophony/{frontend/tabs => ui/windows}/__init__.py (100%) create mode 100644 source/monophony/ui/windows/add_window.py create mode 100644 source/monophony/ui/windows/import_window.py create mode 100644 source/monophony/ui/windows/main_window.py create mode 100644 source/monophony/ui/windows/message_window.py create mode 100644 source/monophony/ui/windows/rename_window.py create mode 100644 source/monophony/yt.py delete mode 100644 source/requirements-dev.txt delete mode 100644 source/requirements.txt create mode 100755 source/tests/tests.py diff --git a/.gitignore b/.gitignore index 5bd2c2a..94769b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,10 @@ -__pycache__/ - *.build/ -build/ -dist/ -repo/ -.flatpak-builder/ -.*_cache/ *.egg-info/ *.flatpak *~ +.*_cache/ +.flatpak-builder/ +.repo/ +__pycache__/ +build/ +dist/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9cb6358..f2a3e51 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,21 +1,41 @@ variables: APP_ID: io.gitlab.zehkira.Monophony + RELEASE_BRANCH: release # Branch on which to build and deploy automatically + FLATPAK_BRANCH: master # Branch of exported flatpak repo, same as in flatpakref file + IMAGE: alpine:3.22.1 stages: + - test - build-x86_64 - build-aarch64 - deploy +test: + stage: test + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + image: ${IMAGE} + script: + - apk add appstream bash desktop-file-utils flatpak flatpak-builder make ruff + + - ruff check source + - appstreamcli validate --explain --strict source/data/metainfo.xml + - desktop-file-validate source/data/*.desktop + + - cd source/ + - make flatpak + - flatpak run --filesystem=$CI_PROJECT_DIR/source/tests:ro --command=tests/tests.py --env=LOG_LEVELS=ERRO ${APP_ID} + + .build: rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - when: manual - - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH - when: never - image: bilelmoussaoui/flatpak-github-actions:gnome-46 + - if: $CI_COMMIT_BRANCH == $RELEASE_BRANCH + image: ${IMAGE} script: + - apk add flatpak flatpak-builder + - flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - gpg --list-keys --with-keygrip @@ -24,8 +44,8 @@ stages: - cat $GPG_PASSPHRASE | /usr/libexec/gpg-preset-passphrase --preset $GPG_KEY_GREP - gpg --import --batch ${GPG_PRIVATE_KEY} - - flatpak-builder build --arch=${ARCH} --user --install-deps-from=flathub --gpg-sign=${GPG_KEY_ID} --disable-rofiles-fuse --disable-updates --force-clean --repo=repo ${BRANCH:+--default-branch=$BRANCH} $CI_PROJECT_DIR/source/data/manifest.json - - flatpak build-bundle --arch=${ARCH} --gpg-sign=${GPG_KEY_ID} repo ${APP_ID}.flatpak --runtime-repo=flathub ${APP_ID} ${BRANCH} + - flatpak-builder build --arch=${ARCH} --user --install-deps-from=flathub --gpg-sign=${GPG_KEY_ID} --disable-rofiles-fuse --disable-updates --force-clean --repo=repo --default-branch=${FLATPAK_BRANCH} $CI_PROJECT_DIR/source/data/manifest.json + - flatpak build-bundle --arch=${ARCH} --gpg-sign=${GPG_KEY_ID} repo ${APP_ID}.flatpak --runtime-repo=flathub ${APP_ID} ${FLATPAK_BRANCH} - flatpak build-update-repo --gpg-sign=${GPG_KEY_ID} --generate-static-deltas --prune repo/ artifacts: paths: @@ -33,36 +53,29 @@ stages: build-x86_64: - variables: - ARCH: x86_64 extends: .build stage: build-x86_64 + variables: + ARCH: x86_64 build-aarch64: - variables: - ARCH: aarch64 extends: .build stage: build-aarch64 - dependencies: - - "build-x86_64" + variables: + ARCH: aarch64 pages: - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - when: manual - - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH - when: never - variables: - BUILD_OUTPUT_PATH: ${CI_PROJECT_DIR}/repo stage: deploy - image: alpine:latest + rules: + - if: $CI_COMMIT_BRANCH == $RELEASE_BRANCH + image: ${IMAGE} script: - apk add rsync - find $BUILD_OUTPUT_PATH \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i -e "s#href=\"\/#href=\"$CI_PAGES_URL/#g" -e "s#src=\"\/#src=\"$CI_PAGES_URL/#g" - - mkdir public || true - - rsync -av --exclude='public' --exclude='.git' $BUILD_OUTPUT_PATH/ public + - mkdir --parents public + - rsync -av --exclude='public' --exclude='.git' ${CI_PROJECT_DIR}/repo/ public artifacts: paths: - public diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 945f90a..2160ff1 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -1,23 +1,8 @@ -# System information +**Bug description:** -**Operating system name and version:** - -**Desktop environment name and version:** - -**Installation source/method:** - - -**App version:** - - - -# Bug description - -**Description:** - -**Terminal output:** +**App logs (About → Troubleshooting → Debugging Information):** ```sh diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md new file mode 100644 index 0000000..1460f12 --- /dev/null +++ b/.gitlab/merge_request_templates/Default.md @@ -0,0 +1,9 @@ + + + + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1f6bf4..1cc4900 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,18 @@ Before making any changes, make sure that your editor supports [EditorConfig](https://editorconfig.org/). +## Debugging + +The following environment variables are available: +- `DEBUG`: Print memory information to help with finding leaks (unset by default) +- `LOG_LEVELS`: Only print log messages of specified levels (`INFO,WARN,ERRO` by default) + +When running the app, environment variables can be set with `--env`: + +```sh +flatpak run --env=LOG_LEVELS=WARN,ERRO io.gitlab.zehkira.Monophony +``` + ## Translation -Standard translation files are located in `source/locales`. +Standard translation files are located in `source/locales`. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..32683a5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,12 @@ +Copyright © Zehkira and contributors + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md index 77cc836..024aa1a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,6 @@ Monophony allows you to stream and download music from YouTube Music without ads, as well as create and import playlists without signing in. -**Copyright** © Zehkira and contributors, [GPL-2.0-or-later](https://gitlab.com/zehkira/monophony/-/blob/master/source/LICENSE). [Metadata](https://gitlab.com/zehkira/monophony/-/blob/master/source/data/metainfo.xml) provided under the [0BSD](https://opensource.org/license/0bsd/) license. Information about dependency licenses is available in the app. +**Copyright** © Zehkira and contributors, [0BSD](https://gitlab.com/zehkira/monophony/-/blob/master/LICENSE) license. Information about dependency licenses is available in the app. -screenshot +Screenshot diff --git a/assets/screenshot1.png b/assets/screenshot1.png index 03ce40cb19d174d6108347a902b4ef37d624bbbe..f8e534bf3ab3141d13a63c93be278fc6ade9a358 100644 GIT binary patch literal 70362 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 literal 52637 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

A7M4GIe3s3* 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

  • 3dzn{Cm&2Ui0(wyQe%Up1CD_ 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�dxr3KnuV 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 diff --git a/source/LICENSE b/source/LICENSE deleted file mode 100644 index d159169..0000000 --- a/source/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/source/Makefile b/source/Makefile index fa352de..07dc4e4 100644 --- a/source/Makefile +++ b/source/Makefile @@ -4,55 +4,71 @@ SHELL = bash MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules -INSTALL = install -D +INSTALL_PROGRAM = install -D +INSTALL_DATA = install -D -m 644 ID = io.gitlab.zehkira.Monophony -ICONS_DIR = $(prefix)/share/icons/hicolor -LOCALES_DIR = $(prefix)/share/locale -APPS_DIR = $(prefix)/share/applications -META_DIR = $(prefix)/share/metainfo -LICENSE_DIR = $(prefix)/share/licenses +NAME = monophony BIN_DIR = $(prefix)/bin +DATA_DIR = $(prefix)/share +ICONS_DIR = $(DATA_DIR)/icons/hicolor +LOCALES_DIR = $(DATA_DIR)/locale +APPS_DIR = $(DATA_DIR)/applications +META_DIR = $(DATA_DIR)/metainfo +LICENSE_DIR = $(DATA_DIR)/licenses # Only use for packaging. For manual builds, see "flatpak" instead install: + @# Python module + pip3 install --prefix=$(prefix) --no-build-isolation --no-deps . + @# Executable - $(INSTALL) bin/monophony.py $(BIN_DIR)/monophony + $(INSTALL_PROGRAM) bin/$(NAME).py $(BIN_DIR)/$(NAME) + + @# GResources + mkdir --parents $(DATA_DIR)/$(NAME) + glib-compile-resources \ + --sourcedir=data \ + --target=$(DATA_DIR)/$(NAME)/resources.gresource \ + data/resources.gresource.xml @# Desktop file - $(INSTALL) data/monophony.desktop $(APPS_DIR)/$(ID).desktop + $(INSTALL_DATA) data/$(NAME).desktop $(APPS_DIR)/$(ID).desktop @# Metainfo - $(INSTALL) data/metainfo.xml $(META_DIR)/$(ID).metainfo.xml + $(INSTALL_DATA) data/metainfo.xml $(META_DIR)/$(ID).metainfo.xml @# Icons - $(INSTALL) data/icons/scalable.svg $(ICONS_DIR)/scalable/apps/$(ID).svg - $(INSTALL) \ + $(INSTALL_DATA) data/icons/scalable.svg $(ICONS_DIR)/scalable/apps/$(ID).svg + $(INSTALL_DATA) \ data/icons/symbolic.svg $(ICONS_DIR)/symbolic/apps/$(ID)-symbolic.svg - $(INSTALL) data/icons/128.png $(ICONS_DIR)/128x128/apps/$(ID).png - $(INSTALL) data/icons/16.png $(ICONS_DIR)/16x16/apps/$(ID).png - $(INSTALL) data/icons/192.png $(ICONS_DIR)/192x192/apps/$(ID).png - $(INSTALL) data/icons/22.png $(ICONS_DIR)/22x22/apps/$(ID).png - $(INSTALL) data/icons/24.png $(ICONS_DIR)/24x24/apps/$(ID).png - $(INSTALL) data/icons/256.png $(ICONS_DIR)/256x256/apps/$(ID).png - $(INSTALL) data/icons/32.png $(ICONS_DIR)/32x32/apps/$(ID).png - $(INSTALL) data/icons/36.png $(ICONS_DIR)/36x36/apps/$(ID).png - $(INSTALL) data/icons/384.png $(ICONS_DIR)/384x384/apps/$(ID).png - $(INSTALL) data/icons/48.png $(ICONS_DIR)/48x48/apps/$(ID).png - $(INSTALL) data/icons/512.png $(ICONS_DIR)/512x512/apps/$(ID).png - $(INSTALL) data/icons/64.png $(ICONS_DIR)/64x64/apps/$(ID).png - $(INSTALL) data/icons/72.png $(ICONS_DIR)/72x72/apps/$(ID).png - $(INSTALL) data/icons/96.png $(ICONS_DIR)/96x96/apps/$(ID).png + $(INSTALL_DATA) data/icons/128.png $(ICONS_DIR)/128x128/apps/$(ID).png + $(INSTALL_DATA) data/icons/16.png $(ICONS_DIR)/16x16/apps/$(ID).png + $(INSTALL_DATA) data/icons/192.png $(ICONS_DIR)/192x192/apps/$(ID).png + $(INSTALL_DATA) data/icons/22.png $(ICONS_DIR)/22x22/apps/$(ID).png + $(INSTALL_DATA) data/icons/24.png $(ICONS_DIR)/24x24/apps/$(ID).png + $(INSTALL_DATA) data/icons/256.png $(ICONS_DIR)/256x256/apps/$(ID).png + $(INSTALL_DATA) data/icons/32.png $(ICONS_DIR)/32x32/apps/$(ID).png + $(INSTALL_DATA) data/icons/36.png $(ICONS_DIR)/36x36/apps/$(ID).png + $(INSTALL_DATA) data/icons/384.png $(ICONS_DIR)/384x384/apps/$(ID).png + $(INSTALL_DATA) data/icons/48.png $(ICONS_DIR)/48x48/apps/$(ID).png + $(INSTALL_DATA) data/icons/512.png $(ICONS_DIR)/512x512/apps/$(ID).png + $(INSTALL_DATA) data/icons/64.png $(ICONS_DIR)/64x64/apps/$(ID).png + $(INSTALL_DATA) data/icons/72.png $(ICONS_DIR)/72x72/apps/$(ID).png + $(INSTALL_DATA) data/icons/96.png $(ICONS_DIR)/96x96/apps/$(ID).png @# Translations cd locales; \ for d in */; do \ - $(INSTALL) \ - $${d}LC_MESSAGES/all.mo $(LOCALES_DIR)/$${d}LC_MESSAGES/monophony.mo; \ + mkdir --parents $(LOCALES_DIR)/$${d}LC_MESSAGES/; \ + msgfmt \ + $${d}LC_MESSAGES/all.po \ + --output-file $(LOCALES_DIR)/$${d}LC_MESSAGES/$(NAME).mo; \ done @# License - $(INSTALL) LICENSE $(LICENSE_DIR)/monophony/LICENSE + cd ..; \ + $(INSTALL_DATA) LICENSE $(LICENSE_DIR)/$(NAME)/LICENSE .PHONY: install @@ -71,8 +87,8 @@ flatpak: --install \ --install-deps-from=flathub \ --force-clean \ - --repo=repo/ \ - build/ \ + --repo=.repo/ \ + .build/ \ data/manifest.json .PHONY: flatpak @@ -81,5 +97,5 @@ flatpak: translation: printf 'msgid \"\"\nmsgstr \"Content-Type: text/plain; charset=UTF-8\\n\"' \ > locales/all.pot - find monophony -iname "*.py" | xargs xgettext --omit-header -j -o locales/all.pot + find $(NAME) -iname "*.py" | xargs xgettext --omit-header -j -o locales/all.pot .PHONY: translation diff --git a/source/bin/monophony.py b/source/bin/monophony.py index bf594a1..d5c72d4 100755 --- a/source/bin/monophony.py +++ b/source/bin/monophony.py @@ -1,25 +1,73 @@ #!/usr/bin/env python3 +# ruff: noqa: E402 - Allow gi.require_versions() -import gettext, os +import gettext +import os +import sys +import threading +import traceback -import monophony.backend.cache -from monophony.frontend.app import MonophonyApplication +import gi -def main(): - path = None - snap_path = os.getenv('SNAP') +gi.require_versions({ + 'Adw': '1', 'Gdk': '4.0', 'Gst': '1.0', 'GstAudio': '1.0', 'Gtk': '4.0' +}) - # yes, "container" env really is lowercase for some reason - if os.getenv('container', '') == 'flatpak': # noqa: SIM112 - path = '/app/share/locale' - elif snap_path: - path = os.path.join(snap_path, 'share/locale') - gettext.translation('monophony', path, fallback=True).install() - monophony.backend.cache.clean_up() - MonophonyApplication().run() +from monophony import NAME, logging +from monophony.app import Application +from gi.repository import Gio -if __name__ == '__main__': - main() + +sys.excepthook = lambda exception, value, trace: logging.error( + __name__, + 'Unhandled exception', + ''.join(traceback.format_exception(exception, value, trace)) +) +threading.excepthook = lambda args: logging.error( + f'{__name__} (thread "{args.thread.name}")', + 'Unhandled exception in thread', + ''.join( + traceback.format_exception(args.exc_type, args.exc_value, args.exc_traceback) + ) +) + +container = os.getenv('container', 'unknown') # noqa: SIM112 - Container is lowercase +if container != 'flatpak': + logging.warning( + __name__, + f'App was installed from unofficial source. Container type: {container}' + ) + +logging.info(__name__, 'Loading GResources...') +resources_file = 'resources.gresource' +for path in os.getenv('XDG_DATA_DIRS', '/usr/share/').split(':'): + data_path = f'{path}{NAME}' if path.endswith('/') else f'{path}/{NAME}' + logging.info(__name__, f'Trying to load GResources from "{data_path}"...') + resource = Gio.Resource.load(data_path + '/' + resources_file) + if resource: + Gio.resources_register(resource) + logging.info(__name__, f'Loaded GResources from "{data_path}/{resources_file}"') + break +else: + logging.error(__name__, 'Failed to load GResources: not found') + sys.exit(1) + +logging.info(__name__, 'Installing translation...') +for path in os.getenv('XDG_DATA_DIRS', '/usr/share/').split(':'): + locale_path = f'{path}locale' if path.endswith('/') else f'{path}/locale' + logging.info(__name__, f'Trying to install translation from "{locale_path}"...') + if 'share' in path and os.path.isdir(locale_path): + gettext.translation(NAME, locale_path, fallback=True).install() + logging.info( + __name__, f'Installed translation from "{locale_path}/{NAME}/"' + ) + break +else: + logging.error(__name__, 'Failed to install translation: not found') + sys.exit(1) + +Application().run() +logging.info(__name__, 'Exited') diff --git a/source/data/manifest.json b/source/data/manifest.json index 294247d..06fba11 100644 --- a/source/data/manifest.json +++ b/source/data/manifest.json @@ -1,7 +1,7 @@ { "app-id": "io.gitlab.zehkira.Monophony", "runtime": "org.gnome.Platform", - "runtime-version": "48", + "runtime-version": "49", "sdk": "org.gnome.Sdk", "command": "monophony", "finish-args": [ @@ -14,7 +14,90 @@ "--own-name=org.mpris.MediaPlayer2.Monophony" ], "modules": [ - "python3-modules.json", + { + "name": "ytmusicapi", + "buildsystem": "simple", + "build-commands": [ + "pip3 install ytmusicapi --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} --no-build-isolation" + ], + "sources": [ + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", + "sha256": "f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", + "sha256": "6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", + "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", + "sha256": "2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", + "sha256": "e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/88/ac/a91784a8f9be3eff10959ccd2a680181941194d7ee7da2c7be10e9c32ab5/ytmusicapi-1.11.1-py3-none-any.whl", + "sha256": "bcaf208fd18c9b5b8971bc4436b17af132a4296de98708049d7638fdba90f6c9" + } + ] + }, + { + "name": "mprisify", + "buildsystem": "simple", + "build-commands": [ + "pip3 install . --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} --no-build-isolation" + ], + "sources": [ + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/81/69/297302c5f5f59c862faa31e6cb9a4cd74721cd1e052b38e464c5b402df8b/StrEnum-0.4.15-py3-none-any.whl", + "sha256": "a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/40/d9/412da520de9052b7e80bfc810ec10f5cb3dbfa4aa3e23c2820dc61cdb3d0/pycairo-1.28.0.tar.gz", + "sha256": "26ec5c6126781eb167089a123919f87baa2740da2cca9098be8b3a6b91cc5fbc" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/92/56/27148014c2f85ce70332f18612f921f682395c7d4e91ec103783be4fce00/pydbus-0.6.0-py2.py3-none-any.whl", + "sha256": "66b80106352a718d80d6c681dc2a82588048e30b75aab933e4020eb0660bf85e" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/4a/36/fec530a313d3d48f12e112ac0a65ee3ccc87f385123a0493715609e8e99c/pygobject-3.52.3.tar.gz", + "sha256": "00e427d291e957462a8fad659a9f9c8be776ff82a8b76bdf402f1eaeec086d82" + }, + { + "type": "git", + "url": "https://gitlab.com/zehkira/mprisify.git", + "tag": "v1.0.0", + "commit": "f1ea109c499055e2a4563ec6680fb31ee766e59a" + } + ] + }, + { + "name": "adwaita-icon-theme", + "buildsystem": "meson", + "sources": [{ + "type": "git", + "url": "https://gitlab.gnome.org/GNOME/adwaita-icon-theme.git", + "tag": "49.0", + "commit": "084370e79b7f02cec50ab70c82da49f64b45f120" + }] + }, { "name": "yt-dlp", "buildsystem": "simple", @@ -32,13 +115,13 @@ "name": "monophony", "buildsystem": "simple", "build-commands": [ - "pip install --prefix=${FLATPAK_DEST} --no-build-isolation .", - "make prefix=${FLATPAK_DEST} install" + "cd source && make prefix=${FLATPAK_DEST} install" ], "sources": [{ "type": "dir", - "path": ".." + "path": "../.." }] } ] } + diff --git a/source/data/metainfo.xml b/source/data/metainfo.xml index 4e5a1ac..0d7be22 100644 --- a/source/data/metainfo.xml +++ b/source/data/metainfo.xml @@ -1,17 +1,36 @@ - + io.gitlab.zehkira.Monophony 0BSD - GPL-2.0-or-later + 0BSD Monophony - Zehkira - https://gitlab.com/zehkira/monophony + + Zehkira + + + AudioVideo + Audio + Music + GNOME + GTK + + + music + stream + youtube + yt + + https://zeh-kira.itch.io/monophony https://gitlab.com/zehkira/monophony/-/issues + https://zeh-kira.itch.io/monophony/purchase pointing keyboard touch + + always + 360 @@ -20,7 +39,6 @@ #911221 Stream music from YouTube Music - Musik von YouTube streamen

    Monophony allows you to stream and download music from YouTube Music without ads, as well as create and import playlists without signing in. diff --git a/source/data/monophony.desktop b/source/data/monophony.desktop index a973074..54f3501 100644 --- a/source/data/monophony.desktop +++ b/source/data/monophony.desktop @@ -1,12 +1,13 @@ [Desktop Entry] Name=Monophony -GenericName=Music streaming app -Comment=Music streaming app +GenericName=Client for YouTube Music +Comment=Stream music from YouTube Music Exec=monophony Terminal=false Type=Application StartupNotify=false +SingleMainWindow=true Icon=io.gitlab.zehkira.Monophony -Categories=AudioVideo;Audio;Music; +Categories=AudioVideo;Audio;Music;GNOME;GTK; X-Purism-FormFactor=Workstation;Mobile; -Keywords=Music;Stream;Youtube;yt; +Keywords=music;stream;youtube;yt; diff --git a/source/data/python3-modules.json b/source/data/python3-modules.json deleted file mode 100644 index 8b8fef2..0000000 --- a/source/data/python3-modules.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "name": "python3-requirements", - "buildsystem": "simple", - "build-commands": [], - "modules": [ - { - "name": "python3-mpris_server", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"mpris_server==0.9.0\" --no-build-isolation" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/81/69/297302c5f5f59c862faa31e6cb9a4cd74721cd1e052b38e464c5b402df8b/StrEnum-0.4.15-py3-none-any.whl", - "sha256": "a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/8f/b7/559f59d57d18b44c6d1250d2eeaa676e028b9c527431f5d0736478a73ba1/Unidecode-1.4.0-py3-none-any.whl", - "sha256": "c3c7606c27503ad8d501270406e345ddb480a7b5f38827eafe4fa82a137f0021" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/91/db/a0335710caaa6d0aebdaa65ad4df789c15d89b7babd9a30277838a7d9aac/emoji-2.14.1-py3-none-any.whl", - "sha256": "35a8a486c1460addb1499e3bf7929d3889b2e2841a57401903699fef595e942b" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/5f/ef/6a87603849b242bbf4080d1dfbec0a82ccf0348b60793376bdeb0226f669/mpris_server-0.9.0-py2.py3-none-any.whl", - "sha256": "97b6764b05fc35f8ae30c78d7b7b7b55a12796aa22979404bec6db63d039e842" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/40/d9/412da520de9052b7e80bfc810ec10f5cb3dbfa4aa3e23c2820dc61cdb3d0/pycairo-1.28.0.tar.gz", - "sha256": "26ec5c6126781eb167089a123919f87baa2740da2cca9098be8b3a6b91cc5fbc" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/92/56/27148014c2f85ce70332f18612f921f682395c7d4e91ec103783be4fce00/pydbus-0.6.0-py2.py3-none-any.whl", - "sha256": "66b80106352a718d80d6c681dc2a82588048e30b75aab933e4020eb0660bf85e" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/4a/36/fec530a313d3d48f12e112ac0a65ee3ccc87f385123a0493715609e8e99c/pygobject-3.52.3.tar.gz", - "sha256": "00e427d291e957462a8fad659a9f9c8be776ff82a8b76bdf402f1eaeec086d82" - } - ] - }, - { - "name": "python3-ytmusicapi", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"ytmusicapi==1.11.1\" --no-build-isolation" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", - "sha256": "f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", - "sha256": "6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", - "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", - "sha256": "2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", - "sha256": "e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" - }, - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/88/ac/a91784a8f9be3eff10959ccd2a680181941194d7ee7da2c7be10e9c32ab5/ytmusicapi-1.11.1-py3-none-any.whl", - "sha256": "bcaf208fd18c9b5b8971bc4436b17af132a4296de98708049d7638fdba90f6c9" - } - ] - } - ] -} diff --git a/source/data/resources.gresource.xml b/source/data/resources.gresource.xml new file mode 100644 index 0000000..5f9ef20 --- /dev/null +++ b/source/data/resources.gresource.xml @@ -0,0 +1,6 @@ + + + + metainfo.xml + + diff --git a/source/locales/all.pot b/source/locales/all.pot index d6d405c..517c891 100644 --- a/source/locales/all.pot +++ b/source/locales/all.pot @@ -1,317 +1,327 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8\n" -#: monophony/frontend/pages/artist_page.py:19 -#: monophony/frontend/pages/results_page.py:20 -msgid "No Results" +#: monophony/ui/pages/artist_page.py:11 +msgid "Artist Page" msgstr "" -#: monophony/frontend/pages/artist_page.py:58 -msgid "Other" +#: monophony/ui/pages/home_page.py:26 +msgid "Recommended" msgstr "" -#: monophony/frontend/pages/artist_page.py:59 -#: monophony/frontend/pages/results_page.py:89 -msgid "Albums" +#: monophony/ui/pages/home_page.py:76 monophony/ui/pages/home_page.py:131 +msgid "Your Playlists" msgstr "" -#: monophony/frontend/pages/artist_page.py:60 -msgid "Playlists" +#: monophony/ui/pages/home_page.py:133 +msgid "Playlists you create will appear here" msgstr "" -#: monophony/frontend/pages/artist_page.py:83 -#: monophony/frontend/pages/results_page.py:88 -msgid "Songs" +#: monophony/ui/pages/home_page.py:148 monophony/ui/windows/import_window.py:52 +msgid "Import" msgstr "" -#: monophony/frontend/pages/artist_page.py:89 -#: monophony/frontend/pages/results_page.py:90 -msgid "Videos" +#: monophony/ui/pages/home_page.py:161 +msgid "Synchronized Playlists" msgstr "" -#: monophony/frontend/pages/artist_page.py:95 -msgid "Artist Not Found" +#: monophony/ui/pages/home_page.py:217 +msgid "Downloads Directory" msgstr "" -#: monophony/frontend/pages/results_page.py:61 -msgid "Show All" +#: monophony/ui/pages/home_page.py:227 +msgid "Downloads" msgstr "" -#: monophony/frontend/pages/results_page.py:87 -msgid "Top Result" +#: monophony/ui/pages/home_page.py:259 +msgid "Clear" msgstr "" -#: monophony/frontend/pages/results_page.py:91 -msgid "Community Playlists" +#: monophony/ui/pages/home_page.py:267 +msgid "Recently Played" msgstr "" -#: monophony/frontend/pages/results_page.py:92 -msgid "Artists" +#: monophony/ui/pages/home_page.py:304 monophony/ui/windows/main_window.py:712 +msgid "Donate" msgstr "" -#: monophony/frontend/widgets/recent_searches.py:36 -msgid "Remove" +#: monophony/ui/pages/home_page.py:348 +msgid "Home" msgstr "" -#: monophony/frontend/widgets/player.py:68 -msgid "Toggle pause" +#: monophony/ui/pages/home_page.py:407 monophony/ui/pages/home_page.py:436 +#, python-brace-format +msgid "Deleted playlist \"{name}\"" msgstr "" -#: monophony/frontend/widgets/player.py:80 -msgid "Next song" +#: monophony/ui/pages/home_page.py:411 monophony/ui/pages/home_page.py:440 +msgid "Undo" msgstr "" -#: monophony/frontend/widgets/player.py:85 -msgid "Previous song" +#: monophony/ui/pages/loading_page.py:21 +msgid "Loading..." msgstr "" -#: monophony/frontend/widgets/player.py:97 -msgid "Change volume" +#: monophony/ui/pages/results_page.py:27 monophony/ui/pages/results_page.py:47 +#: monophony/ui/pages/results_page.py:67 monophony/ui/pages/results_page.py:87 +#: monophony/ui/pages/results_page.py:107 +msgid "Show All" msgstr "" -#: monophony/frontend/widgets/player.py:103 -msgid "Playback mode" +#: monophony/ui/pages/results_page.py:126 +msgid "Songs" msgstr "" -#: monophony/frontend/widgets/player.py:188 -msgid "Normal Playback" +#: monophony/ui/pages/results_page.py:129 +msgid "Videos" msgstr "" -#: monophony/frontend/widgets/player.py:195 -msgid "Radio Mode" +#: monophony/ui/pages/results_page.py:132 +msgid "Albums and Singles" msgstr "" -#: monophony/frontend/widgets/player.py:205 -msgid "Repeat Song" +#: monophony/ui/pages/results_page.py:135 +msgid "Playlists" msgstr "" -#: monophony/frontend/widgets/player.py:215 -msgid "Repeat Queue" +#: monophony/ui/pages/results_page.py:138 +msgid "Artists" msgstr "" -#: monophony/frontend/windows/import_window.py:21 -msgid "Enter Playlist Name..." +#: monophony/ui/pages/results_page.py:147 +msgid "Top Result" msgstr "" -#: monophony/frontend/windows/import_window.py:28 -msgid "Enter Playlist URL..." +#: monophony/ui/pages/results_page.py:221 +msgid "Search Results" msgstr "" -#: monophony/frontend/windows/import_window.py:33 -msgid "Synchronized" +#: monophony/ui/rows/artist_row.py:15 monophony/ui/rows/artist_row.py:20 +#: monophony/ui/popovers/group_row_popover.py:23 +#: monophony/ui/popovers/song_row_popover.py:23 +msgid "View Artist" msgstr "" -#: monophony/frontend/windows/import_window.py:34 -msgid "Editable" +#: monophony/ui/rows/group_row.py:22 monophony/ui/rows/song_row.py:34 +msgid "More" msgstr "" -#: monophony/frontend/windows/import_window.py:47 -#: monophony/frontend/windows/add_window.py:23 -msgid "Cancel" +#: monophony/ui/rows/song_row.py:29 +msgid "Downloaded" msgstr "" -#: monophony/frontend/windows/import_window.py:49 -#: monophony/frontend/tabs/library_tab.py:59 -msgid "Import" +#: monophony/ui/rows/song_row.py:46 +msgid "Play" msgstr "" -#: monophony/frontend/windows/import_window.py:71 -msgid "Import Playlist..." +#: monophony/ui/windows/add_window.py:24 +msgid "New Playlist" +msgstr "" + +#: monophony/ui/windows/add_window.py:40 +msgid "Add" +msgstr "" + +#: monophony/ui/windows/add_window.py:55 +msgid "Add to Playlists..." +msgstr "" + +#: monophony/ui/windows/import_window.py:18 +msgid "Playlist URL" msgstr "" -#: monophony/frontend/windows/import_window.py:100 -#: monophony/frontend/windows/import_window.py:118 -#: monophony/frontend/windows/import_window.py:124 -msgid "Could not import playlist" +#: monophony/ui/windows/import_window.py:29 +#: monophony/ui/windows/rename_window.py:15 +msgid "Playlist Name" msgstr "" -#: monophony/frontend/windows/import_window.py:101 -msgid "Failed to retrieve playlist data from server." +#: monophony/ui/windows/import_window.py:33 +msgid "Synchronized" msgstr "" -#: monophony/frontend/windows/import_window.py:118 -msgid "A name is required." +#: monophony/ui/windows/import_window.py:35 +msgid "Synchronized playlists are updated automatically and can't be edited" msgstr "" -#: monophony/frontend/windows/import_window.py:124 -msgid "A URL is required." +#: monophony/ui/windows/import_window.py:69 +msgid "Import Playlist..." msgstr "" -#: monophony/frontend/windows/message_window.py:13 +#: monophony/ui/windows/message_window.py:12 msgid "Ok" msgstr "" -#: monophony/frontend/windows/main_window.py:38 -msgid "Library" +#: monophony/ui/windows/rename_window.py:35 +msgid "Rename Playlist..." msgstr "" -#: monophony/frontend/windows/main_window.py:43 -msgid "Search" +#: monophony/ui/windows/main_window.py:373 +msgid "Playing" msgstr "" -#: monophony/frontend/windows/main_window.py:49 -#: monophony/frontend/windows/add_window.py:36 -#: monophony/frontend/tabs/queue_tab.py:44 -msgid "Queue" +#: monophony/ui/windows/main_window.py:454 +msgid "Download Failed" msgstr "" -#: monophony/frontend/windows/main_window.py:58 -msgid "About" +#: monophony/ui/windows/main_window.py:454 +msgid "Some songs could not be downloaded" msgstr "" -#: monophony/frontend/windows/main_window.py:192 -msgid "translator-credits" +#: monophony/ui/windows/main_window.py:503 +msgid "Failed to Import" msgstr "" -#: monophony/frontend/windows/main_window.py:239 -#, python-brace-format -msgid "Deleted playlist \"{playlist_name}\"" +#: monophony/ui/windows/main_window.py:504 +#: monophony/ui/windows/main_window.py:619 +#: monophony/ui/windows/main_window.py:836 +msgid "Check your internet connection and try again" msgstr "" -#: monophony/frontend/windows/main_window.py:244 -msgid "Undo" +#: monophony/ui/windows/main_window.py:618 +msgid "Failed to Search" msgstr "" -#: monophony/frontend/windows/main_window.py:262 -msgid "Added" +#: monophony/ui/windows/main_window.py:625 +msgid "No Results" msgstr "" -#: monophony/frontend/windows/add_window.py:20 -#: monophony/frontend/popovers/song_popover.py:31 -msgid "Add to..." +#: monophony/ui/windows/main_window.py:626 +msgid "Try searching for something else" msgstr "" -#: monophony/frontend/windows/add_window.py:25 -msgid "Add" +#: monophony/ui/windows/main_window.py:709 +msgid "translator-credits" msgstr "" -#: monophony/frontend/windows/add_window.py:43 -#: monophony/frontend/tabs/library_tab.py:79 -msgid "Your Playlists" +#: monophony/ui/windows/main_window.py:835 +msgid "Failed to Load Artist Page" msgstr "" -#: monophony/frontend/windows/add_window.py:49 -msgid "New Playlist Name..." +#: monophony/ui/windows/main_window.py:842 +msgid "Empty Artist Page" msgstr "" -#: monophony/frontend/windows/add_window.py:51 -msgid "Create" +#: monophony/ui/windows/main_window.py:843 +msgid "No content found from this artist" msgstr "" -#: monophony/frontend/popovers/local_song_popover.py:17 -msgid "Remove From Playlist" +#: monophony/ui/bars/header_bar.py:15 +msgid "About" msgstr "" -#: monophony/frontend/popovers/queue_song_popover.py:18 -msgid "Remove From Queue" +#: monophony/ui/bars/player_bar.py:93 monophony/ui/queue_sidebar.py:138 +msgid "Queue" msgstr "" -#: monophony/frontend/popovers/song_popover.py:21 -msgid "Remove From Downloads" +#: monophony/ui/bars/player_bar.py:120 +msgid "Playback Mode" msgstr "" -#: monophony/frontend/popovers/song_popover.py:26 -#: monophony/frontend/rows/importable_group_row.py:29 -#: monophony/frontend/rows/local_group_row.py:37 -#: monophony/frontend/rows/external_group_row.py:47 -msgid "Download" +#: monophony/ui/bars/player_bar.py:127 +msgid "Previous" msgstr "" -#: monophony/frontend/popovers/song_popover.py:35 -msgid "View Artist" +#: monophony/ui/bars/player_bar.py:143 +msgid "Pause" msgstr "" -#: monophony/frontend/rows/artist_row.py:14 -#: monophony/frontend/rows/artist_row.py:23 -msgid "View artist" +#: monophony/ui/bars/player_bar.py:152 +msgid "Next" msgstr "" -#: monophony/frontend/rows/song_row.py:18 -#: monophony/frontend/rows/group_row.py:24 -msgid "Play" +#: monophony/ui/bars/player_bar.py:165 +msgid "Volume" msgstr "" -#: monophony/frontend/rows/song_row.py:36 -msgid "Downloaded" +#: monophony/ui/bars/player_bar.py:236 +msgid "Normal Playback" msgstr "" -#: monophony/frontend/rows/song_row.py:44 -#: monophony/frontend/rows/importable_group_row.py:17 -#: monophony/frontend/rows/local_group_row.py:23 -#: monophony/frontend/rows/external_group_row.py:18 -msgid "More actions" +#: monophony/ui/bars/player_bar.py:237 +msgid "Repeat Song" msgstr "" -#: monophony/frontend/rows/importable_group_row.py:35 -msgid "Import..." +#: monophony/ui/bars/player_bar.py:238 +msgid "Repeat Queue" msgstr "" -#: monophony/frontend/rows/local_group_row.py:35 -#: monophony/frontend/rows/external_group_row.py:43 -msgid "Delete" +#: monophony/ui/bars/player_bar.py:239 +msgid "Autoplay Similar" msgstr "" -#: monophony/frontend/rows/local_group_row.py:43 -msgid "Duplicate" +#: monophony/ui/bars/search_bar.py:11 +msgid "Search..." msgstr "" -#: monophony/frontend/rows/local_group_row.py:49 -#: monophony/frontend/rows/external_group_row.py:53 +#: monophony/ui/popovers/editable_group_row_popover.py:14 msgid "Rename..." msgstr "" -#: monophony/frontend/rows/local_group_row.py:66 -#: monophony/frontend/rows/external_group_row.py:70 -msgid "Rename" +#: monophony/ui/popovers/editable_group_row_popover.py:17 +#: monophony/ui/popovers/synchronized_group_row_popover.py:14 +msgid "Delete" msgstr "" -#: monophony/frontend/rows/local_group_row.py:96 -#: monophony/frontend/rows/external_group_row.py:103 -msgid "Could not Rename" +#: monophony/ui/popovers/editable_song_row_popover.py:14 +msgid "Remove From Playlist" msgstr "" -#: monophony/frontend/rows/local_group_row.py:97 -#: monophony/frontend/rows/external_group_row.py:104 -msgid "Playlist already exists" +#: monophony/ui/popovers/group_row_popover.py:20 +#: monophony/ui/popovers/song_row_popover.py:21 +msgid "Add to Queue" msgstr "" -#: monophony/frontend/rows/external_group_row.py:25 -msgid "(Synchronized)" +#: monophony/ui/popovers/group_row_popover.py:21 +#: monophony/ui/popovers/song_row_popover.py:22 +#: monophony/ui/queue_sidebar.py:41 +msgid "Add to..." msgstr "" -#: monophony/frontend/tabs/library_tab.py:55 -msgid "Recommended" +#: monophony/ui/popovers/group_row_popover.py:24 +#: monophony/ui/popovers/song_row_popover.py:29 +msgid "Download" msgstr "" -#: monophony/frontend/tabs/library_tab.py:70 -msgid "Play all" +#: monophony/ui/popovers/importable_group_row_popover.py:14 +msgid "Import..." msgstr "" -#: monophony/frontend/tabs/library_tab.py:85 -#: monophony/frontend/tabs/queue_tab.py:30 -msgid "Clear" +#: monophony/ui/popovers/queue_song_row_popover.py:15 +msgid "Remove From Queue" msgstr "" -#: monophony/frontend/tabs/library_tab.py:92 -msgid "Recently Played" +#: monophony/ui/popovers/song_row_popover.py:26 +msgid "Remove From Downloads" msgstr "" -#: monophony/frontend/tabs/library_tab.py:98 -msgid "Show Downloaded Songs" +#: monophony/ui/row_groups/queueable_row_group.py:20 +msgid "Play All" msgstr "" -#: monophony/frontend/tabs/search_tab.py:18 -msgid "Enter text or paste a URL..." +#: monophony/ui/queue_sidebar.py:18 +msgid "Stop" +msgstr "" + +#: monophony/ui/queue_sidebar.py:31 +msgid "Shuffle" msgstr "" -#: monophony/frontend/tabs/search_tab.py:24 -msgid "Go back" +#: monophony/ui/queue_sidebar.py:65 +msgid "Currently Playing" msgstr "" -#: monophony/frontend/tabs/queue_tab.py:25 +#: monophony/ui/queue_sidebar.py:116 msgid "Queue Empty" msgstr "" -#: monophony/frontend/tabs/queue_tab.py:34 -msgid "Shuffle" +#: monophony/ui/queue_sidebar.py:117 +msgid "Nothing is playing right now" +msgstr "" + +#: monophony/ui/queue_sidebar.py:135 +msgid "Back" +msgstr "" + +#: monophony/playlists.py:167 +msgid "Playlist" msgstr "" diff --git a/source/locales/be/LC_MESSAGES/all.mo b/source/locales/be/LC_MESSAGES/all.mo deleted file mode 100644 index 481cc0097b035a03d69051b27567b92bd6be73d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5285 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 J85kKe003`aAF2QV diff --git a/source/locales/be/LC_MESSAGES/all.po b/source/locales/be/LC_MESSAGES/all.po deleted file mode 100644 index 597fb5b..0000000 --- a/source/locales/be/LC_MESSAGES/all.po +++ /dev/null @@ -1,433 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Language: be\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.4\n" - -#: monophony/frontend/pages/results_page.py:20 -#: monophony/frontend/pages/results_page.py:81 -#: monophony/frontend/pages/results_page.py:21 -#: monophony/frontend/pages/artist_page.py:20 -msgid "No Results" -msgstr "Няма вынікаў" - -#: monophony/frontend/pages/results_page.py:90 -#: monophony/frontend/pages/results_page.py:91 -#: monophony/frontend/pages/results_page.py:72 -msgid "Top Result" -msgstr "Лепшы вынік" - -#: monophony/frontend/pages/results_page.py:91 -#: monophony/frontend/pages/results_page.py:92 -#: monophony/frontend/pages/results_page.py:73 -msgid "Songs" -msgstr "Песні" - -#: monophony/frontend/pages/results_page.py:92 -#: monophony/frontend/pages/results_page.py:93 -#: monophony/frontend/pages/results_page.py:74 -#: monophony/frontend/pages/artist_page.py:77 -msgid "Albums" -msgstr "Альбомы" - -#: monophony/frontend/pages/results_page.py:93 -#: monophony/frontend/pages/results_page.py:94 -#: monophony/frontend/pages/results_page.py:75 -msgid "Videos" -msgstr "Відэа" - -#: monophony/frontend/pages/results_page.py:94 -#: monophony/frontend/pages/results_page.py:95 -#: monophony/frontend/pages/results_page.py:76 -#: monophony/frontend/pages/artist_page.py:78 -msgid "Community Playlists" -msgstr "Супольныя плэйлісты" - -#: monophony/frontend/pages/results_page.py:95 -#: monophony/frontend/pages/results_page.py:96 -#: monophony/frontend/pages/results_page.py:77 -msgid "Artists" -msgstr "Выканаўцы" - -#: monophony/frontend/pages/results_page.py:99 -#: monophony/frontend/pages/results_page.py:107 -#: monophony/frontend/pages/results_page.py:115 -#: monophony/frontend/pages/results_page.py:123 -#: monophony/frontend/pages/results_page.py:131 -#: monophony/frontend/pages/results_page.py:100 -#: monophony/frontend/pages/results_page.py:108 -#: monophony/frontend/pages/results_page.py:116 -#: monophony/frontend/pages/results_page.py:124 -#: monophony/frontend/pages/results_page.py:132 -#: monophony/frontend/pages/results_page.py:81 -#: monophony/frontend/pages/results_page.py:89 -#: monophony/frontend/pages/results_page.py:97 -#: monophony/frontend/pages/results_page.py:105 -#: monophony/frontend/pages/results_page.py:113 -msgid "More" -msgstr "Больш" - -#: monophony/frontend/pages/library_page.py:31 -#: monophony/frontend/pages/library_page.py:35 -msgid "Your Library is Empty" -msgstr "Ваша бібліятэка пустая" - -#: monophony/frontend/pages/library_page.py:33 -#: monophony/frontend/pages/library_page.py:37 -msgid "Find songs to play using the search bar above" -msgstr "Знайдзіце песні для прайгравання з дапамогай радка пошуку вышэй" - -#: monophony/frontend/pages/library_page.py:45 -#: monophony/frontend/pages/library_page.py:51 -msgid "Play All" -msgstr "Прайграць усе" - -#: monophony/frontend/pages/library_page.py:48 -#: monophony/frontend/windows/add_window.py:48 -#: monophony/frontend/pages/library_page.py:54 -msgid "Your Playlists" -msgstr "Вашыя плэйлісты" - -#: monophony/frontend/pages/library_page.py:54 -#: monophony/frontend/pages/library_page.py:60 -msgid "Recently Played" -msgstr "Нядаўна прайграныя" - -#: monophony/frontend/windows/main_window.py:44 -#: monophony/frontend/windows/main_window.py:45 -#: monophony/frontend/windows/main_window.py:46 -msgid "Go back" -msgstr "Назад" - -#: monophony/frontend/windows/main_window.py:49 -msgid "About" -msgstr "Аб праграме" - -#: monophony/frontend/windows/main_window.py:54 -#: monophony/frontend/windows/main_window.py:64 -#: monophony/frontend/windows/main_window.py:65 -#: monophony/frontend/windows/main_window.py:66 -msgid "Search for Content..." -msgstr "Пошук змесціва..." - -#: monophony/frontend/windows/main_window.py:138 -#: monophony/frontend/windows/main_window.py:148 -#: monophony/frontend/windows/main_window.py:149 -#: monophony/frontend/windows/main_window.py:150 -msgid "translator-credits" -msgstr "yahor Haurylenka 2023" - -#: monophony/frontend/windows/main_window.py:174 -#: monophony/frontend/windows/main_window.py:191 -#: monophony/frontend/windows/main_window.py:192 -#: monophony/frontend/windows/main_window.py:193 -#, python-brace-format -msgid "Deleted \"{playlist_name}\"" -msgstr "Выдалены \"{playlist_name}\"" - -#: monophony/frontend/windows/main_window.py:177 -#: monophony/frontend/windows/main_window.py:194 -#: monophony/frontend/windows/main_window.py:195 -#: monophony/frontend/windows/main_window.py:196 -msgid "Undo" -msgstr "Адрабіць" - -#: monophony/frontend/windows/main_window.py:199 -#: monophony/frontend/windows/main_window.py:222 -#: monophony/frontend/windows/main_window.py:223 -#: monophony/frontend/windows/main_window.py:224 -msgid "Added" -msgstr "Дададзена" - -#: monophony/frontend/windows/add_window.py:19 -#: monophony/frontend/popovers/song_popover.py:36 -#: monophony/frontend/widgets/player.py:183 -#: monophony/frontend/widgets/player.py:167 -#: monophony/frontend/widgets/player.py:187 -#: monophony/frontend/widgets/player.py:188 -msgid "Add to..." -msgstr "Дадаць да..." - -#: monophony/frontend/windows/add_window.py:25 -#: monophony/frontend/windows/import_window.py:53 -msgid "Cancel" -msgstr "Скасаваць" - -#: monophony/frontend/windows/add_window.py:27 -msgid "Add" -msgstr "Дадаць" - -#: monophony/frontend/windows/add_window.py:54 -msgid "New Playlist Name..." -msgstr "Новая назва плэйліста..." - -#: monophony/frontend/windows/add_window.py:56 -msgid "Create" -msgstr "Стварыць" - -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "Ок" - -#: monophony/frontend/popovers/queue_song_popover.py:18 -msgid "Remove From Queue" -msgstr "Выдаліць з чэргі" - -#: monophony/frontend/popovers/local_song_popover.py:17 -msgid "Remove From Playlist" -msgstr "Выдаліць з плэйліста" - -#: monophony/frontend/popovers/song_popover.py:22 -msgid "Remove From Downloads" -msgstr "Выдаліць з запамповак" - -#: monophony/frontend/popovers/song_popover.py:29 -#: monophony/frontend/rows/local_group_row.py:42 -#: monophony/frontend/rows/importable_group_row.py:28 -#: monophony/frontend/rows/external_group_row.py:44 -msgid "Download" -msgstr "Спампаваць" - -#: monophony/frontend/popovers/song_popover.py:42 -#: monophony/frontend/widgets/player.py:195 -#: monophony/frontend/widgets/player.py:179 -#: monophony/frontend/widgets/player.py:199 -#: monophony/frontend/widgets/player.py:200 -msgid "Show Artist" -msgstr "Паказаць выканаўцу" - -#: monophony/frontend/widgets/player.py:82 -#: monophony/frontend/widgets/player.py:66 -#: monophony/frontend/widgets/player.py:76 -#: monophony/frontend/widgets/player.py:77 -msgid "Toggle pause" -msgstr "Пераключыць паўзу" - -#: monophony/frontend/widgets/player.py:86 -#: monophony/frontend/widgets/player.py:70 -#: monophony/frontend/widgets/player.py:89 -#: monophony/frontend/widgets/player.py:90 -msgid "Next song" -msgstr "Наступная песня" - -#: monophony/frontend/widgets/player.py:91 -#: monophony/frontend/widgets/player.py:75 -#: monophony/frontend/widgets/player.py:94 -#: monophony/frontend/widgets/player.py:95 -msgid "Previous song" -msgstr "Папярэдняя песня" - -#: monophony/frontend/widgets/player.py:98 -#: monophony/frontend/rows/song_row.py:42 -#: monophony/frontend/rows/local_group_row.py:23 -#: monophony/frontend/widgets/player.py:82 -#: monophony/frontend/rows/importable_group_row.py:17 -#: monophony/frontend/rows/external_group_row.py:24 -#: monophony/frontend/widgets/player.py:101 -#: monophony/frontend/widgets/player.py:102 -msgid "More actions" -msgstr "Больш дзеянняў" - -#: monophony/frontend/widgets/player.py:189 -#: monophony/frontend/widgets/player.py:173 -#: monophony/frontend/widgets/player.py:193 -#: monophony/frontend/widgets/player.py:194 -msgid "Show Queue" -msgstr "Паказаць чаргу" - -#: monophony/frontend/widgets/player.py:203 -#: monophony/frontend/widgets/player.py:187 -#: monophony/frontend/widgets/player.py:207 -#: monophony/frontend/widgets/player.py:208 -msgid "Normal Playback" -msgstr "Звычайнае прайграванне" - -#: monophony/frontend/widgets/player.py:210 -#: monophony/frontend/widgets/player.py:194 -#: monophony/frontend/widgets/player.py:214 -#: monophony/frontend/widgets/player.py:215 -msgid "Radio Mode" -msgstr "Рэжым радыё" - -#: monophony/frontend/widgets/player.py:220 -#: monophony/frontend/widgets/player.py:204 -#: monophony/frontend/widgets/player.py:224 -#: monophony/frontend/widgets/player.py:225 -msgid "Repeat Song" -msgstr "Паўтарэнне песні" - -#: monophony/frontend/widgets/player.py:230 -#: monophony/frontend/widgets/player.py:214 -#: monophony/frontend/widgets/player.py:234 -#: monophony/frontend/widgets/player.py:235 -msgid "Shuffle" -msgstr "Ператасаваць" - -#: monophony/frontend/rows/song_row.py:18 -#: monophony/frontend/rows/group_row.py:23 -msgid "Play" -msgstr "Прайграць" - -#: monophony/frontend/rows/artist_row.py:14 -#: monophony/frontend/rows/artist_row.py:23 -msgid "View Artist" -msgstr "Паказаць выканаўцу" - -#: monophony/frontend/rows/local_group_row.py:36 -#: monophony/frontend/rows/external_group_row.py:38 -msgid "Delete" -msgstr "Выдаліць" - -#: monophony/frontend/rows/local_group_row.py:48 -msgid "Duplicate" -msgstr "Дубляваць" - -#: monophony/frontend/rows/local_group_row.py:54 -#: monophony/frontend/rows/external_group_row.py:50 -msgid "Rename..." -msgstr "Перайменаваць..." - -#: monophony/frontend/rows/local_group_row.py:71 -#: monophony/frontend/rows/external_group_row.py:67 -msgid "Rename" -msgstr "Перайменаваць" - -#: monophony/frontend/rows/local_group_row.py:100 -#: monophony/frontend/rows/external_group_row.py:96 -msgid "Could not Rename" -msgstr "Не ўдалося перайменаваць" - -#: monophony/frontend/rows/local_group_row.py:101 -#: monophony/frontend/rows/external_group_row.py:97 -msgid "Playlist already exists" -msgstr "Плэйліст ужо існуе" - -#: monophony/frontend/rows/external_group_row.py:18 -msgid "Add to library" -msgstr "Дадаць у бібліятэку" - -#: monophony/frontend/pages/queue_page.py:24 -#: monophony/frontend/windows/add_window.py:42 -msgid "Queue" -msgstr "Чарга" - -#: monophony/frontend/pages/results_page.py:31 -msgid "Searching..." -msgstr "Пошук..." - -#: monophony/frontend/pages/results_page.py:62 -#: monophony/frontend/pages/artist_page.py:30 -msgid "Loading..." -msgstr "Загрузка..." - -#: monophony/frontend/pages/results_page.py:68 -#: monophony/frontend/pages/artist_page.py:56 -msgid "Artist Not Found" -msgstr "Выканаўца не знойдзены" - -#: monophony/frontend/pages/library_page.py:41 -msgid "Loading Library..." -msgstr "Загрузка бібліятэкі..." - -#: monophony/frontend/windows/main_window.py:50 -#: monophony/frontend/windows/main_window.py:51 -msgid "Import Playlist" -msgstr "Імпарт плэйліста" - -#: monophony/frontend/windows/main_window.py:54 -#: monophony/frontend/windows/main_window.py:55 -msgid "About Monophony" -msgstr "Пра Манафонію" - -#: monophony/frontend/windows/import_window.py:21 -msgid "Enter Playlist Name..." -msgstr "Увядзіце назву плэйліста..." - -#: monophony/frontend/windows/import_window.py:28 -msgid "Enter Playlist URL..." -msgstr "Увядзіце URL плэйліста..." - -#: monophony/frontend/windows/import_window.py:33 -msgid "External Playlist" -msgstr "Знешні плэйліст" - -#: monophony/frontend/windows/import_window.py:34 -msgid "Local Playlist" -msgstr "Лакальны плэйліст" - -#: monophony/frontend/windows/import_window.py:55 -msgid "Import" -msgstr "Імпарт" - -#: monophony/frontend/windows/import_window.py:73 -msgid "Import playlist..." -msgstr "Імпарт плэйліста..." - -#: monophony/frontend/windows/import_window.py:97 -#: monophony/frontend/windows/import_window.py:115 -#: monophony/frontend/windows/import_window.py:121 -msgid "Could not Import Playlist" -msgstr "Не атрымалася імпартаваць плэйліст" - -#: monophony/frontend/windows/import_window.py:98 -msgid "Failed to retrieve playlist data from server." -msgstr "Не ўдалося атрымаць даныя плэйліста з сервера." - -#: monophony/frontend/windows/import_window.py:115 -msgid "A name is required." -msgstr "Патрабуецца назва." - -#: monophony/frontend/windows/import_window.py:121 -msgid "A URL is required." -msgstr "Патрабуецца URL." - -#: monophony/frontend/rows/importable_group_row.py:34 -msgid "Import..." -msgstr "Імпарт..." - -#: monophony/frontend/rows/external_group_row.py:31 -msgid "External" -msgstr "Знешні" - -#: monophony/backend/yt.py:14 -msgid "Parsing Results..." -msgstr "Апрацоўка вынікаў..." - -#: monophony/frontend/pages/artist_page.py:76 -msgid "Other" -msgstr "Іншае" - -#: monophony/frontend/pages/artist_page.py:101 -msgid "All Songs" -msgstr "Усе песні" - -#: monophony/frontend/pages/artist_page.py:107 -msgid "All Videos" -msgstr "Усе відэа" - -#: monophony/frontend/rows/external_group_row.py:31 -msgid "Synchronized" -msgstr "Сінхранізавана" - -#: monophony/frontend/widgets/player.py:56 -msgid "Volume" -msgstr "Гучнасць" - -#: monophony/frontend/windows/import_window.py:33 -msgid "As Synchronized Playlist" -msgstr "Як сінхранізаваны плэйліст" - -#: monophony/frontend/windows/import_window.py:34 -msgid "As Editable Playlist" -msgstr "Як рэдагуемы плэйліст" - -#: monophony/frontend/windows/main_window.py:60 -msgid "Primary Menu" -msgstr "Галоўнае меню" diff --git a/source/locales/cs/LC_MESSAGES/all.mo b/source/locales/cs/LC_MESSAGES/all.mo deleted file mode 100644 index 3f52d06bd7d7332a977c04598a7e7acd38611bec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2751 zcmca7#4?qEfq}u0fq_AWfq`KOBLjmo0|SE&Gf0$y!GeW>ft7)Q!IFi6fro*C!JdVI zfscWK!H0!`fuDhaAsQ;5&BDOI#=yW($il$D&cMJ>0p&NaFfa%+Ffg>UFfa%(Ffh!9 zim!q4H$(YHSQr>27#J8%voJ95GB7YaVPRm3usnxW>ju`)1lFfcH5vobJCI_A?^Tq4iq0891wK^91IN73=9ma91!)691wH7q5OCb z1_oIM28K)yh&}Bb3=B>T3=A_lApW_-0WtRxl>dqY;=d0ZknsKsrFl6a{*dK_n4`l9 zk+{@{ef zBL^469ziaMxr$s6_iA%N%r)kMgo76s#6R&|5c5)@>dUww;nM&$XAT#{y=%A_7#Ja0 zfVk4^Vc@rcq$pA?=It&a9av%W)NH~MiHYopq;ua(fO4Fcp4q}5a zC@<+VFfhn7K*9$kW&k1>An9J20TLe?3=9lfP;pRtmttUGP-1|DHAom#EP&D_gJV*D zX$ga4N{T{BzCvJWYH2ErlarZLlvq>=;}+y3R_0_DgVm;{FgWHUmF5;RI2M&;7MBz= zI49;Mr{*v?=jY~@=4F;tDg;247c)5Lm*%7>g&`Q?`?UNb1?T*{lGMBsuv|uIT3SvjLog`J z7((*X({oZ43KC0;QyD_?3!vU$2+K@K%`avM%S>ExhORyv!s|IpeR2pHMvCBGetKnwWv5V zKhH{mE5KhjD77pT#M5<2EJ?Le;POcj#VErI#kAr|O2JCgvjXtrU`rxqLl+ zT@eNv>KSlB{i+*MS&(X_P?B0vqF<1cn3-p-kercNRGeC38ye!KYk^P)vMVjMNY^zl zIX@*cFWpMPA}O=g_YtQ7L> zY>gDu)D-e;Y)usG6$}-u6pS>jxgvDkQ}a@bKmlQ;5ReZF2?b+4Lp?(-27iUbf}G6k z#NdEm3ed@B&U~VrGizZBxmGT9bO9!fkcL&)T8?{ za}|mXFHFfyWbjW(O)5$(QK(8)C@x7nx-T{F@ZRJMgfLRdfX5cXF9Aq#2=YlNC?Znx z@);hcwt6=2 && n<=4 ? 1 : 2);\n" -"X-Generator: Poedit 3.1.1\n" - -#: monophony/frontend/pages/search_page.py:24 -#: monophony/frontend/pages/search_page.py:96 -#: monophony/frontend/pages/search_page.py:20 -#: monophony/frontend/pages/search_page.py:123 -#: monophony/frontend/pages/search_page.py:129 -msgid "No Results" -msgstr "Žádné výsledky" - -#: monophony/frontend/pages/search_page.py:108 -#: monophony/frontend/pages/search_page.py:133 -#: monophony/frontend/pages/search_page.py:139 -msgid "Songs" -msgstr "Skladby" - -#: monophony/frontend/pages/search_page.py:109 -#: monophony/frontend/pages/search_page.py:118 -#: monophony/frontend/pages/search_page.py:127 -#: monophony/frontend/pages/search_page.py:136 -#: monophony/frontend/pages/search_page.py:139 -#: monophony/frontend/pages/search_page.py:147 -#: monophony/frontend/pages/search_page.py:156 -#: monophony/frontend/pages/search_page.py:164 -#: monophony/frontend/pages/search_page.py:172 -#: monophony/frontend/pages/search_page.py:145 -#: monophony/frontend/pages/search_page.py:153 -#: monophony/frontend/pages/search_page.py:162 -#: monophony/frontend/pages/search_page.py:170 -#: monophony/frontend/pages/search_page.py:178 -msgid "More" -msgstr "Více" - -#: monophony/frontend/pages/search_page.py:117 -#: monophony/frontend/pages/search_page.py:134 -#: monophony/frontend/pages/search_page.py:140 -msgid "Albums" -msgstr "Alba" - -#: monophony/frontend/pages/search_page.py:126 -#: monophony/frontend/pages/search_page.py:155 -#: monophony/frontend/pages/search_page.py:161 -msgid "Community Playlists" -msgstr "Komunitní playlisty" - -#: monophony/frontend/pages/search_page.py:135 -#: monophony/frontend/pages/search_page.py:141 -msgid "Videos" -msgstr "Videa" - -#: monophony/frontend/pages/library_page.py:25 -msgid "Your Library is Empty" -msgstr "Vaše knihovna je prázdná" - -#: monophony/frontend/pages/library_page.py:26 -msgid "Find songs to play using the search bar above" -msgstr "Najděte skladby k přehrání pomocí vyhledávací lišty výše" - -#: monophony/frontend/pages/library_page.py:29 -#: monophony/frontend/pages/library_page.py:37 -msgid "Play All" -msgstr "Přehrát vše" - -#: monophony/frontend/pages/library_page.py:33 -#: monophony/frontend/pages/library_page.py:41 -msgid "Your Playlists" -msgstr "Vaše playlisty" - -#: monophony/frontend/windows/delete_window.py:15 -msgid "Delete Playlist?" -msgstr "Odstranit playlist?" - -#: monophony/frontend/windows/delete_window.py:16 -#: monophony/frontend/windows/rename_window.py:18 -msgid "Cancel" -msgstr "Zrušit" - -#: monophony/frontend/windows/delete_window.py:17 -#: monophony/frontend/widgets/group_row.py:60 -#: monophony/frontend/widgets/group_row.py:79 -msgid "Delete" -msgstr "Odstranit" - -#: monophony/frontend/windows/main_window.py:41 -#: monophony/frontend/windows/main_window.py:50 -msgid "Go back" -msgstr "Zpět" - -#: monophony/frontend/windows/main_window.py:46 -#: monophony/frontend/windows/main_window.py:55 -msgid "About" -msgstr "O aplikaci" - -#: monophony/frontend/windows/main_window.py:51 -#: monophony/frontend/windows/main_window.py:60 -msgid "Search for Content..." -msgstr "Hledat obsah..." - -#: monophony/frontend/windows/main_window.py:141 -#: monophony/frontend/windows/main_window.py:138 -#: monophony/frontend/windows/main_window.py:147 -msgid "translator-credits" -msgstr "translator-credits" - -#: monophony/frontend/windows/main_window.py:143 -#: monophony/frontend/windows/main_window.py:140 -#: monophony/frontend/windows/main_window.py:149 -msgid "Donate" -msgstr "Přispět" - -#: monophony/frontend/windows/main_window.py:174 -#: monophony/frontend/windows/main_window.py:171 -#: monophony/frontend/windows/main_window.py:180 -msgid "New Playlist" -msgstr "Nový playlist" - -#: monophony/frontend/windows/main_window.py:191 -#: monophony/frontend/windows/main_window.py:188 -#: monophony/frontend/windows/main_window.py:197 -msgid "Could not Rename" -msgstr "Nepodařilo se přejmenovat" - -#: monophony/frontend/windows/main_window.py:192 -#: monophony/frontend/windows/main_window.py:189 -#: monophony/frontend/windows/main_window.py:198 -msgid "Playlist already exists" -msgstr "Playlist již existuje" - -#: monophony/frontend/windows/main_window.py:196 -#: monophony/frontend/windows/main_window.py:193 -#: monophony/frontend/windows/main_window.py:202 -msgid "Rename Playlist" -msgstr "Přejmenovat playlist" - -#: monophony/frontend/windows/main_window.py:201 -msgid "Saved" -msgstr "Uloženo" - -#: monophony/frontend/windows/rename_window.py:15 -msgid "Enter Name..." -msgstr "Zadejte název..." - -#: monophony/frontend/windows/rename_window.py:19 -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "Budiž" - -#: monophony/frontend/widgets/player.py:33 -#: monophony/frontend/widgets/player.py:61 -msgid "Toggle pause" -msgstr "Přepnout pozastavení" - -#: monophony/frontend/widgets/player.py:37 -#: monophony/frontend/widgets/player.py:65 -msgid "Next song" -msgstr "Další skladba" - -#: monophony/frontend/widgets/player.py:42 -#: monophony/frontend/widgets/player.py:70 -msgid "Previous song" -msgstr "Předchozí skladba" - -#: monophony/frontend/widgets/player.py:47 -#: monophony/frontend/widgets/player.py:75 -msgid "Add to playlist" -msgstr "Přidat do playlistu" - -#: monophony/frontend/widgets/player.py:53 -#: monophony/frontend/widgets/player.py:81 -msgid "Remove From Queue" -msgstr "Odebrat z fronty" - -#: monophony/frontend/widgets/player.py:59 -#: monophony/frontend/widgets/player.py:87 -msgid "Volume" -msgstr "Hlasitost" - -#: monophony/frontend/widgets/player.py:82 -#: monophony/frontend/widgets/player.py:110 -msgid "Radio Mode" -msgstr "Režim rádia" - -#: monophony/frontend/widgets/player.py:92 -#: monophony/frontend/widgets/player.py:120 -msgid "Loop" -msgstr "Smyčka" - -#: monophony/frontend/widgets/player.py:99 -#: monophony/frontend/widgets/player.py:127 -msgid "Shuffle" -msgstr "Náhodně" - -#: monophony/frontend/widgets/player.py:115 -#: monophony/frontend/widgets/song_row.py:41 -#: monophony/frontend/widgets/group_row.py:34 -#: monophony/frontend/widgets/player.py:143 -#: monophony/frontend/widgets/group_row.py:41 -msgid "More actions" -msgstr "Další akce" - -#: monophony/frontend/widgets/song_row.py:21 -#: monophony/frontend/widgets/group_row.py:33 -msgid "Play" -msgstr "Přehrát" - -#: monophony/frontend/widgets/song_popover.py:23 -msgid "Move Up" -msgstr "Posunout výše" - -#: monophony/frontend/widgets/song_popover.py:24 -msgid "Move Down" -msgstr "Posunout níže" - -#: monophony/frontend/widgets/song_popover.py:39 -msgid "Remove From Downloads" -msgstr "Odebrat ze stažených" - -#: monophony/frontend/widgets/song_popover.py:46 -msgid "Download to Music Folder" -msgstr "Stáhnout do hudební složky" - -#: monophony/frontend/widgets/song_popover.py:53 -msgid "Add to Queue" -msgstr "Přidat do fronty" - -#: monophony/frontend/widgets/song_popover.py:59 -msgid "New Playlist..." -msgstr "Nový playlist..." - -#: monophony/frontend/widgets/group_row.py:45 -#: monophony/frontend/widgets/group_row.py:52 -msgid "Add to library" -msgstr "Přidat do knihovny" - -#: monophony/frontend/widgets/group_row.py:66 -#: monophony/frontend/widgets/group_row.py:85 -msgid "Rename..." -msgstr "Přejmenovat..." - -#: monophony/frontend/pages/search_page.py:136 -#: monophony/frontend/pages/search_page.py:142 -msgid "Artists" -msgstr "Umělci" - -#: monophony/frontend/windows/main_window.py:198 -#: monophony/frontend/windows/main_window.py:207 -msgid "Added" -msgstr "Přidáno" - -#: monophony/frontend/widgets/artist_row.py:14 -msgid "View Artist" -msgstr "Zobrazit umělce" - -#: monophony/frontend/pages/search_page.py:138 -msgid "Top Result" -msgstr "Nejlepší výsledek" diff --git a/source/locales/en/LC_MESSAGES/all.mo b/source/locales/en/LC_MESSAGES/all.mo deleted file mode 100644 index 60423fa3af6c86b0862813727a4ac4afdc3b8906..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98 zcmca7#4?qEfq{XMfq_AWfq_AVfq{XUfq_8>B*?(PV8Ot^puxbvU<5P$#x diff --git a/source/locales/en/LC_MESSAGES/all.po b/source/locales/en/LC_MESSAGES/all.po new file mode 100644 index 0000000..0923b60 --- /dev/null +++ b/source/locales/en/LC_MESSAGES/all.po @@ -0,0 +1,2 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8\n" diff --git a/source/locales/en_GB/LC_MESSAGES/all.mo b/source/locales/en_GB/LC_MESSAGES/all.mo deleted file mode 100644 index 0476f4235a1e2917aea4e1695e3b333e1481b3be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3655 zcmca7#4?qEfq}t|fq_AWfq}t?iGd-Jfq|ih1tiMAz`(}9AjH7Hz{tkHAk4tPAi~DL zz{=1voutVHG9V)+&oq>Uefq`KqI|G9V z0|Uc0sQhVmh`m?Y85sB(7#JR~GcYhQFfe>zhlIyxb_NDeko{zbxbq)70|PGu0|Pq; z1A{mN1A{0B#2#G^h`2ciM4u}M0|Pe$1A{jQ#Qbm$i1|rS@gfe0d+RtL{%YlbxN9~C z1A`y~1H%TW{2{3MC!zW-L&fiNK-}|;1LFVpPYa^N^@~S>=);R_(O>kVy`+U z#C>{D+6pS}#>v3I$H2hg%Lxg`7)}O;XhsHxd?^1k7bLyh6vSt>lK- zy9Fw~j~kL+j&no8^&U4Qy?o(@q!&RRh&$|gAntSLf%wyp2VzblR6d&r5{|_@ka%f; z%1`Bi*gKyG5}&JhAn9cn55zxvc_86@m~3=E9WA_kPt z^q_1lC=DuO92p>PG-ZIe2_&V;0P#PF4JubaG$@}*ff!J%$iToL4Q0b9X9h@kfyyvB z1_lOY1_lOKm?Q(lZ=iC*0F+mu;-KVD5X!eOz~BI-bwJ?W@eg8K zLCbq<5XVs=G{{FGvsj@hwXig^C^bco!BHVEF*g-WEGfUVguyW-1%wq!^7ZueK&;dh z2FIMF(%fPO$D)$V;t~bF{1OGX{L;J>s6a7;b7Ed{Y7T>QPHJKigL8gvZfRa-Nu@$S zPGV(FW^qX|gL8gqPKrWaeu+X*D#&6afy~^3{Gt+t0;pOB=c3fal2itl)SOf}m7<_j z4O0*g(qF5@;F4dSmy@5E0%xSAFu0T!Fft*&HpO?75uEB7!t!%Ub5j{2^GgxQshFXpC^4@%C$S{INH@7CH6^nI6mI!h zsmUd}o+-LvsYS(^`FU0fx%qkd1sVBym0SV-xFbJcwV|E?mveqzNorn+Zb)T8s+9sLYv>o`BxdGWDy}@XTWqC}R{-V~+ZtIbK$x}~ zc?ycQ3Wl22ToJnNsd=eIpg^@!2*?M8s)DheiJl1;!)Qqc%O&t4VYDQJ)*K_IBrB## KNk*uAVE_P$**53^ diff --git a/source/locales/en_GB/LC_MESSAGES/all.po b/source/locales/en_GB/LC_MESSAGES/all.po deleted file mode 100644 index 8391235..0000000 --- a/source/locales/en_GB/LC_MESSAGES/all.po +++ /dev/null @@ -1,321 +0,0 @@ -# -# Bruce Cowan , 2024. -# -msgid "" -msgstr "" -"Content-Type: text/plain; charset=UTF-8\n" -"MIME-Version: 1.0\n" -"Project-Id-Version: monophony\n" -"Last-Translator: Bruce Cowan \n" -"Language-Team: English - United Kingdom \n" -"Language: en_GB\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"PO-Revision-Date: 2024-08-16 22:56+0100\n" -"X-Generator: Gtranslator 46.1\n" - -#: monophony/frontend/popovers/local_song_popover.py:17 -msgid "Remove From Playlist" -msgstr "Remove From Playlist" - -#: monophony/frontend/popovers/queue_song_popover.py:18 -msgid "Remove From Queue" -msgstr "Remove From Queue" - -#: monophony/frontend/popovers/song_popover.py:22 -msgid "Remove From Downloads" -msgstr "Remove From Downloads" - -#: monophony/frontend/popovers/song_popover.py:29 -#: monophony/frontend/rows/external_group_row.py:44 -#: monophony/frontend/rows/importable_group_row.py:28 -#: monophony/frontend/rows/local_group_row.py:42 -msgid "Download" -msgstr "Download" - -#: monophony/frontend/popovers/song_popover.py:36 -#: monophony/frontend/windows/add_window.py:19 -msgid "Add to..." -msgstr "Add to..." - -#: monophony/frontend/popovers/song_popover.py:42 -msgid "View Artist" -msgstr "View Artist" - -#: monophony/frontend/rows/external_group_row.py:24 -#: monophony/frontend/rows/importable_group_row.py:17 -#: monophony/frontend/rows/local_group_row.py:23 -#: monophony/frontend/rows/song_row.py:45 -msgid "More actions" -msgstr "More actions" - -#: monophony/frontend/rows/external_group_row.py:31 -#: monophony/frontend/windows/import_window.py:33 -msgid "Synchronized" -msgstr "Synchronised" - -#: monophony/frontend/rows/external_group_row.py:38 -#: monophony/frontend/rows/local_group_row.py:36 -msgid "Delete" -msgstr "Delete" - -#: monophony/frontend/rows/external_group_row.py:50 -#: monophony/frontend/rows/local_group_row.py:54 -msgid "Rename..." -msgstr "Rename..." - -#: monophony/frontend/rows/external_group_row.py:67 -#: monophony/frontend/rows/local_group_row.py:71 -msgid "Rename" -msgstr "Rename" - -#: monophony/frontend/rows/external_group_row.py:96 -#: monophony/frontend/rows/local_group_row.py:97 -msgid "Could not Rename" -msgstr "Could not Rename" - -#: monophony/frontend/rows/external_group_row.py:97 -#: monophony/frontend/rows/local_group_row.py:98 -msgid "Playlist already exists" -msgstr "Playlist already exists" - -#: monophony/frontend/rows/importable_group_row.py:34 -msgid "Import..." -msgstr "Import..." - -#: monophony/frontend/rows/local_group_row.py:48 -msgid "Duplicate" -msgstr "Duplicate" - -#: monophony/frontend/rows/artist_row.py:14 -#: monophony/frontend/rows/artist_row.py:23 -msgid "View artist" -msgstr "View artist" - -#: monophony/frontend/rows/group_row.py:23 -#: monophony/frontend/rows/song_row.py:18 -msgid "Play" -msgstr "Play" - -#: monophony/frontend/rows/song_row.py:36 -msgid "Downloaded" -msgstr "Downloaded" - -#: monophony/frontend/widgets/recent_searches.py:36 -msgid "Remove" -msgstr "Remove" - -#: monophony/frontend/widgets/player.py:70 -msgid "Volume" -msgstr "Volume" - -#: monophony/frontend/widgets/player.py:91 -msgid "Toggle pause" -msgstr "Toggle pause" - -#: monophony/frontend/widgets/player.py:103 -msgid "Next song" -msgstr "Next song" - -#: monophony/frontend/widgets/player.py:108 -msgid "Previous song" -msgstr "Previous song" - -#: monophony/frontend/widgets/player.py:115 -msgid "Playback mode" -msgstr "Playback mode" - -#: monophony/frontend/widgets/player.py:200 -msgid "Normal Playback" -msgstr "Normal Playback" - -#: monophony/frontend/widgets/player.py:207 -msgid "Radio Mode" -msgstr "Radio Mode" - -#: monophony/frontend/widgets/player.py:217 -msgid "Repeat Song" -msgstr "Repeat Song" - -#: monophony/frontend/widgets/player.py:227 -msgid "Shuffle" -msgstr "Shuffle" - -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "Ok" - -#: monophony/frontend/windows/main_window.py:37 -msgid "Library" -msgstr "Library" - -#: monophony/frontend/windows/main_window.py:43 -msgid "Search" -msgstr "Search" - -#: monophony/frontend/windows/main_window.py:49 -#: monophony/frontend/windows/add_window.py:42 -msgid "Queue" -msgstr "Queue" - -#: monophony/frontend/windows/main_window.py:58 -msgid "About" -msgstr "About" - -#: monophony/frontend/windows/main_window.py:145 -msgid "translator-credits" -msgstr "Bruce Cowan " - -#: monophony/frontend/windows/main_window.py:188 -#, python-brace-format -msgid "Deleted \"{playlist_name}\"" -msgstr "Deleted \"{playlist_name}\"" - -#: monophony/frontend/windows/main_window.py:191 -msgid "Undo" -msgstr "Undo" - -#: monophony/frontend/windows/main_window.py:219 -msgid "Added" -msgstr "Added" - -#: monophony/frontend/windows/add_window.py:25 -#: monophony/frontend/windows/import_window.py:53 -msgid "Cancel" -msgstr "Cancel" - -#: monophony/frontend/windows/add_window.py:27 -msgid "Add" -msgstr "Add" - -#: monophony/frontend/windows/add_window.py:48 -#: monophony/frontend/tabs/library_tab.py:73 -msgid "Your Playlists" -msgstr "Your Playlists" - -#: monophony/frontend/windows/add_window.py:54 -msgid "New Playlist Name..." -msgstr "New Playlist Name..." - -#: monophony/frontend/windows/add_window.py:56 -msgid "Create" -msgstr "Create" - -#: monophony/frontend/windows/import_window.py:21 -msgid "Enter Playlist Name..." -msgstr "Enter Playlist Name..." - -#: monophony/frontend/windows/import_window.py:28 -msgid "Enter Playlist URL..." -msgstr "Enter Playlist URL..." - -#: monophony/frontend/windows/import_window.py:34 -msgid "Editable" -msgstr "Editable" - -#: monophony/frontend/windows/import_window.py:55 -#: monophony/frontend/tabs/library_tab.py:55 -msgid "Import" -msgstr "Import" - -#: monophony/frontend/windows/import_window.py:77 -msgid "Import Playlist..." -msgstr "Import Playlist..." - -#: monophony/frontend/windows/import_window.py:105 -#: monophony/frontend/windows/import_window.py:123 -#: monophony/frontend/windows/import_window.py:129 -msgid "Could not import playlist" -msgstr "Could not import playlist" - -#: monophony/frontend/windows/import_window.py:106 -msgid "Failed to retrieve playlist data from server." -msgstr "Failed to retrieve playlist data from server." - -#: monophony/frontend/windows/import_window.py:123 -msgid "A name is required." -msgstr "A name is required." - -#: monophony/frontend/windows/import_window.py:129 -msgid "A URL is required." -msgstr "A URL is required." - -#: monophony/frontend/pages/artist_page.py:20 -#: monophony/frontend/pages/results_page.py:21 -msgid "No Results" -msgstr "No Results" - -#: monophony/frontend/pages/artist_page.py:55 -msgid "Other" -msgstr "Other" - -#: monophony/frontend/pages/artist_page.py:56 -#: monophony/frontend/pages/results_page.py:66 -msgid "Albums" -msgstr "Albums" - -#: monophony/frontend/pages/artist_page.py:57 -msgid "Playlists" -msgstr "Playlists" - -#: monophony/frontend/pages/artist_page.py:80 -#: monophony/frontend/pages/results_page.py:65 -msgid "Songs" -msgstr "Songs" - -#: monophony/frontend/pages/artist_page.py:86 -#: monophony/frontend/pages/results_page.py:67 -msgid "Videos" -msgstr "Videos" - -#: monophony/frontend/pages/artist_page.py:92 -msgid "Artist Not Found" -msgstr "Artist Not Found" - -#: monophony/frontend/pages/results_page.py:64 -msgid "Top Result" -msgstr "Top Result" - -#: monophony/frontend/pages/results_page.py:68 -msgid "Community Playlists" -msgstr "Community Playlists" - -#: monophony/frontend/pages/results_page.py:69 -msgid "Artists" -msgstr "Artists" - -#: monophony/frontend/pages/results_page.py:74 -#: monophony/frontend/pages/results_page.py:83 -#: monophony/frontend/pages/results_page.py:92 -#: monophony/frontend/pages/results_page.py:101 -#: monophony/frontend/pages/results_page.py:110 -msgid "Show All" -msgstr "Show All" - -#: monophony/frontend/tabs/queue_tab.py:24 -msgid "Queue Empty" -msgstr "Queue Empty" - -#: monophony/frontend/tabs/library_tab.py:51 -msgid "Recommended" -msgstr "Recommended" - -#: monophony/frontend/tabs/library_tab.py:64 -msgid "Play all" -msgstr "Play all" - -#: monophony/frontend/tabs/library_tab.py:78 -msgid "Clear" -msgstr "Clear" - -#: monophony/frontend/tabs/library_tab.py:85 -msgid "Recently Played" -msgstr "Recently Played" - -#: monophony/frontend/tabs/search_tab.py:18 -msgid "Enter text or paste a URL..." -msgstr "Enter text or paste a URL..." - -#: monophony/frontend/tabs/search_tab.py:24 -msgid "Go back" -msgstr "Go back" diff --git a/source/locales/fr/LC_MESSAGES/all.mo b/source/locales/fr/LC_MESSAGES/all.mo deleted file mode 100644 index e6d3594e71854e07c654c6ba0276e0645000a673..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3854 zcmca7#4?qEfq}uDfq_AWfq@}}iGd-Jfq|ii1tiMAAi&1JAjH7HAjrnRAk4tPpu)z$ zz{KHRNZALeG4l8kd1+XgMoqJH`E>$b_k!3 zoq<7&fq_Asoq<7=fq_Afoq<7@fq}t=9b#`0JH&le>=6G=WQX`;2~^!W zb_NC>1_p+0>c0P!I)c7Fo*XM)8lQkd2em_2leRTQa(s{)bl~y z*Tx6&&t$0n#e9(X*#@Qe@j={q3`$>z>VE+>_dQhnFCPN~BLhUOAp--0KBV|z(1y~W zGRKJl;#N}zh}%I@Y77u}g4m#P1w@1LH7M*sY!FsrfTR(75Ql+*!5UN^FhJZ7DkDJQ zqr$+z;0BckQJ^%f2VyWl!p#a=K58&9Fc?F{ZK1R?10*egxZpC8fq}u20aEUO@(`#z zRA7L_H>j)xVz`&r&z`$Sxm2rg9pz>86 z%64U7V9;VF*qvZCFZ80i6!NimM}P`q=2wONxq(*9*C8i!r+*bRGM4N;Fy!65S*Wv z4q=65rljT{3WcEJ;*IE6UGRC{8UZOD)o4aL-prN=(jX zfcOJS!8M@sLFPkLf>=J8Nkxf8l?=Z5MX3sj$t9WjdBqHVspXh1^-HZNQ7F#OOK0%Q zR|rZiF3l+^X7I}|%1z7xD+W8kKbyh7BqOzm0Tj+)LLo6H2h0G;E9B;%I8aE; zDN0RDsZ>a<0LKVO4@kV2A)qL=EHl5f7-C;wX=-UI1DI5B%`GUYWC%)3$;?;q1sN8U znw*+fk^_m^)D(uG)ZF~CR4Aq31_~!wv=pO=!fZnk1Y5!YjdlnH3Z5CqafK|EcT#FA7i1umb&;u75u zxPB`I&!WRC^A4{}RS3z*EXvH!Q?SV_O3h16)hmJU9a8mDa}zUj?6`aq^U_Nb(^GXr zQWJB*MnU*i3TZ`LzMj6W2%8P{47i;0^GZ_lN_0ai3sS8VN>VFI^b2wlGxMw!k~0#E zic?E$LqptjEfDHJj!H`{(sj*C&QHnAOSe+6NXjhX3dkufO3cx9%P-0;wo=F|0CS6N zjjR>pO>3?QUH8HBEQNs5X-;af zLP=&xQED+m2uM${0!XARGv)9~kU8LpOjXFs&r>KV$}cSg>4u0EGdSkumFA=tF}S9s zB_>024mcMmq@*h3Bq}6>%dW)IlEW*(S-H44GbtxkA>~oS%uH~>m0AQ+1}h&>)TAmD zrRL@5=B5@gI2RpWnOejUTv||2lnD}mvlI@kKC}R>EIYK|&~Ap1!z*(RuLQ+ZdTJ4* zOe-u+En@J^PsvQngq3PVsZ|Q4kWfrP6jPvpa?LA&NJ7FLP4;kSaz<)0)RRSrS0VgJ%*svlKj#lu+NJ?@dYW~ zQi~Ky^AIkAOQVQ_+zC?-VmoCf~Q4ia%HdVJ4&yPG!I%04|op@{8e7RLp>0k}?1QR2Q=c diff --git a/source/locales/fr/LC_MESSAGES/all.po b/source/locales/fr/LC_MESSAGES/all.po deleted file mode 100644 index 5ed6185..0000000 --- a/source/locales/fr/LC_MESSAGES/all.po +++ /dev/null @@ -1,416 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: Irénée Thirion \n" -"Language-Team: \n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 3.4.1\n" - -#: monophony/frontend/popovers/local_song_popover.py:17 -msgid "Remove From Playlist" -msgstr "Retirer de la playlist" - -#: monophony/frontend/popovers/queue_song_popover.py:18 -msgid "Remove From Queue" -msgstr "Retirer de la file d’attente" - -#: monophony/frontend/popovers/song_popover.py:22 -msgid "Remove From Downloads" -msgstr "Supprimer des téléchargements" - -#: monophony/frontend/popovers/song_popover.py:29 -#: monophony/frontend/rows/external_group_row.py:44 -#: monophony/frontend/rows/importable_group_row.py:28 -#: monophony/frontend/rows/local_group_row.py:42 -msgid "Download" -msgstr "Télécharger" - -#: monophony/frontend/popovers/song_popover.py:36 -#: monophony/frontend/windows/add_window.py:19 -msgid "Add to..." -msgstr "Ajouter à…" - -#: monophony/frontend/popovers/song_popover.py:42 -msgid "View Artist" -msgstr "Voir l’artiste" - -#: monophony/frontend/rows/external_group_row.py:24 -#: monophony/frontend/rows/importable_group_row.py:17 -#: monophony/frontend/rows/local_group_row.py:23 -#: monophony/frontend/rows/song_row.py:42 -msgid "More actions" -msgstr "Plus d’actions" - -#: monophony/frontend/rows/external_group_row.py:31 -#: monophony/frontend/windows/import_window.py:33 -msgid "Synchronized" -msgstr "Synchronisé" - -#: monophony/frontend/rows/external_group_row.py:38 -#: monophony/frontend/rows/local_group_row.py:36 -msgid "Delete" -msgstr "Supprimer" - -#: monophony/frontend/rows/external_group_row.py:50 -#: monophony/frontend/rows/local_group_row.py:54 -msgid "Rename..." -msgstr "Renommer…" - -#: monophony/frontend/rows/external_group_row.py:67 -#: monophony/frontend/rows/local_group_row.py:71 -msgid "Rename" -msgstr "Renommer" - -#: monophony/frontend/rows/external_group_row.py:96 -#: monophony/frontend/rows/local_group_row.py:100 -msgid "Could not Rename" -msgstr "Impossible de renommer" - -#: monophony/frontend/rows/external_group_row.py:97 -#: monophony/frontend/rows/local_group_row.py:101 -msgid "Playlist already exists" -msgstr "La playlist existe déjà" - -#: monophony/frontend/rows/importable_group_row.py:34 -msgid "Import..." -msgstr "Importer…" - -#: monophony/frontend/rows/local_group_row.py:48 -msgid "Duplicate" -msgstr "Dupliquer" - -#: monophony/frontend/rows/song_row.py:18 -#: monophony/frontend/rows/group_row.py:23 -msgid "Play" -msgstr "Jouer" - -#: monophony/frontend/rows/artist_row.py:14 -#: monophony/frontend/rows/artist_row.py:23 -msgid "View artist" -msgstr "Voir l’artiste" - -#: monophony/frontend/widgets/player.py:67 -msgid "Volume" -msgstr "Volume" - -#: monophony/frontend/widgets/player.py:88 -msgid "Toggle pause" -msgstr "Mettre en pause" - -#: monophony/frontend/widgets/player.py:101 -msgid "Next song" -msgstr "Titre suivant" - -#: monophony/frontend/widgets/player.py:106 -msgid "Previous song" -msgstr "Titre précédent" - -#: monophony/frontend/widgets/player.py:113 -msgid "Playback mode" -msgstr "Mode lecture" - -#: monophony/frontend/widgets/player.py:196 -msgid "Normal Playback" -msgstr "Lecture normale" - -#: monophony/frontend/widgets/player.py:203 -msgid "Radio Mode" -msgstr "Mode radio" - -#: monophony/frontend/widgets/player.py:213 -msgid "Repeat Song" -msgstr "Répéter le titre" - -#: monophony/frontend/widgets/player.py:223 -msgid "Shuffle" -msgstr "Mélanger" - -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "OK" - -#: monophony/frontend/windows/import_window.py:21 -msgid "Enter Playlist Name..." -msgstr "Entrez un nom de playlist…" - -#: monophony/frontend/windows/import_window.py:28 -msgid "Enter Playlist URL..." -msgstr "Entrez une URL de playlist…" - -#: monophony/frontend/windows/import_window.py:34 -msgid "Editable" -msgstr "Modifiable" - -#: monophony/frontend/windows/import_window.py:53 -#: monophony/frontend/windows/add_window.py:25 -msgid "Cancel" -msgstr "Annuler" - -#: monophony/frontend/windows/import_window.py:55 -#: monophony/frontend/tabs/library_tab.py:47 -msgid "Import" -msgstr "Importer" - -#: monophony/frontend/windows/import_window.py:73 -msgid "Import playlist..." -msgstr "Importer une playlist…" - -#: monophony/frontend/windows/import_window.py:97 -#: monophony/frontend/windows/import_window.py:115 -#: monophony/frontend/windows/import_window.py:121 -msgid "Could not Import Playlist" -msgstr "Impossible d’importer la playlist" - -#: monophony/frontend/windows/import_window.py:98 -msgid "Failed to retrieve playlist data from server." -msgstr "Échec de la récupération des données de la playlist depuis le serveur." - -#: monophony/frontend/windows/import_window.py:115 -msgid "A name is required." -msgstr "Un nom est requis." - -#: monophony/frontend/windows/import_window.py:121 -msgid "A URL is required." -msgstr "Une URL est requise." - -#: monophony/frontend/windows/add_window.py:27 -msgid "Add" -msgstr "Ajouter" - -#: monophony/frontend/windows/add_window.py:42 -#: monophony/frontend/windows/main_window.py:49 -msgid "Queue" -msgstr "File d’attente" - -#: monophony/frontend/windows/add_window.py:48 -#: monophony/frontend/tabs/library_tab.py:62 -#: monophony/frontend/tabs/library_tab.py:65 -msgid "Your Playlists" -msgstr "Vos playlists" - -#: monophony/frontend/windows/add_window.py:54 -msgid "New Playlist Name..." -msgstr "Nouveau nom de playlist…" - -#: monophony/frontend/windows/add_window.py:56 -msgid "Create" -msgstr "Créer" - -#: monophony/frontend/windows/main_window.py:37 -msgid "Library" -msgstr "Bibliothèque" - -#: monophony/frontend/windows/main_window.py:43 -msgid "Search" -msgstr "Rechercher" - -#: monophony/frontend/windows/main_window.py:58 -msgid "About" -msgstr "À propos" - -#: monophony/frontend/windows/main_window.py:133 -msgid "translator-credits" -msgstr "Irénée Thirion" - -#: monophony/frontend/windows/main_window.py:176 -#, python-brace-format -msgid "Deleted \"{playlist_name}\"" -msgstr "Supprimer « {playlist_name} »" - -#: monophony/frontend/windows/main_window.py:179 -msgid "Undo" -msgstr "Annuler" - -#: monophony/frontend/windows/main_window.py:207 -msgid "Added" -msgstr "Ajouté" - -#: monophony/frontend/pages/artist_page.py:20 -#: monophony/frontend/pages/results_page.py:21 -msgid "No Results" -msgstr "Aucun résultat" - -#: monophony/frontend/pages/artist_page.py:49 -msgid "Artist Not Found" -msgstr "Artiste non trouvé" - -#: monophony/frontend/pages/artist_page.py:69 -msgid "Other" -msgstr "Autre" - -#: monophony/frontend/pages/artist_page.py:70 -#: monophony/frontend/pages/results_page.py:72 -msgid "Albums" -msgstr "Albums" - -#: monophony/frontend/pages/artist_page.py:71 -#: monophony/frontend/pages/results_page.py:74 -msgid "Community Playlists" -msgstr "Playlists de la communauté" - -#: monophony/frontend/pages/artist_page.py:94 -msgid "All Songs" -msgstr "Tous les titres" - -#: monophony/frontend/pages/artist_page.py:100 -msgid "All Videos" -msgstr "Toutes les vidéos" - -#: monophony/frontend/pages/results_page.py:70 -msgid "Top Result" -msgstr "Résultat en tête" - -#: monophony/frontend/pages/results_page.py:71 -#: monophony/frontend/pages/artist_page.py:94 -msgid "Songs" -msgstr "Titres" - -#: monophony/frontend/pages/results_page.py:73 -#: monophony/frontend/pages/artist_page.py:100 -msgid "Videos" -msgstr "Vidéos" - -#: monophony/frontend/pages/results_page.py:75 -msgid "Artists" -msgstr "Artistes" - -#: monophony/frontend/pages/results_page.py:80 -#: monophony/frontend/pages/results_page.py:89 -#: monophony/frontend/pages/results_page.py:98 -#: monophony/frontend/pages/results_page.py:107 -#: monophony/frontend/pages/results_page.py:116 -msgid "Show all" -msgstr "Afficher tout" - -#: monophony/frontend/tabs/library_tab.py:47 -msgid "Import playlist" -msgstr "Importer une playlist" - -#: monophony/frontend/tabs/library_tab.py:53 -#: monophony/frontend/tabs/library_tab.py:56 -msgid "Play all" -msgstr "Jouer tout" - -#: monophony/frontend/tabs/library_tab.py:68 -#: monophony/frontend/tabs/library_tab.py:77 -msgid "Recently Played" -msgstr "Joué récemment" - -#: monophony/frontend/tabs/queue_tab.py:23 -msgid "Queue Empty" -msgstr "File d’attente vide" - -#: monophony/frontend/tabs/search_tab.py:15 -#: monophony/frontend/tabs/search_tab.py:18 -msgid "Search..." -msgstr "Rechercher…" - -#: monophony/frontend/tabs/search_tab.py:21 -#: monophony/frontend/tabs/search_tab.py:24 -msgid "Go back" -msgstr "Retour" - -#: monophony/frontend/widgets/recent_searches.py:36 -msgid "Remove" -msgstr "Supprimer" - -#: monophony/frontend/pages/artist_page.py:71 -msgid "Playlists" -msgstr "Playlists" - -#: monophony/frontend/tabs/library_tab.py:70 -msgid "Clear" -msgstr "Effacer" - -#~ msgid "More" -#~ msgstr "Plus" - -#~ msgid "Your Library is Empty" -#~ msgstr "Votre bibliothèque est vide" - -#~ msgid "Find songs to play using the search bar above" -#~ msgstr "Recherchez des titres à jouer depuis la barre de recherche ci-dessus" - -#~ msgid "Search for Content..." -#~ msgstr "Rechercher du contenu…" - -#~ msgid "Show Artist" -#~ msgstr "Afficher l’artiste" - -#~ msgid "Show Queue" -#~ msgstr "Afficher la file d’attente" - -#~ msgid "Add to library" -#~ msgstr "Ajouter à la bibliothèque" - -#~ msgid "Loading..." -#~ msgstr "Chargement…" - -#~ msgid "Loading Library..." -#~ msgstr "Chargement de la bibliothèque…" - -#~ msgid "About Monophony" -#~ msgstr "À propos de Monophony" - -#~ msgid "External Playlist" -#~ msgstr "Playlist externe" - -#~ msgid "Local Playlist" -#~ msgstr "Playlists locales" - -#~ msgid "External" -#~ msgstr "Externe" - -#~ msgid "Parsing Results..." -#~ msgstr "Analyse des résultats…" - -#~ msgid "As Synchronized Playlist" -#~ msgstr "Playlist synchronisée" - -#~ msgid "As Editable Playlist" -#~ msgstr "Playlist modifiable" - -#~ msgid "Primary Menu" -#~ msgstr "Menu principal" - -#~ msgid "Donate" -#~ msgstr "Faire un don" - -#~ msgid "New Playlist" -#~ msgstr "Nouvelle playlist" - -#~ msgid "Saved" -#~ msgstr "Enregistré" - -#~ msgid "Add to playlist" -#~ msgstr "Ajouter à la playlist" - -#~ msgid "Loop" -#~ msgstr "Jouer en boucle" - -#~ msgid "Move Up" -#~ msgstr "Déplacer vers le haut" - -#~ msgid "Move Down" -#~ msgstr "Déplacer vers le bas" - -#~ msgid "Download to Music Folder" -#~ msgstr "Télécharger dans le dossier Musique" - -#~ msgid "Add to Queue" -#~ msgstr "Ajouter à la file d’attente" - -#~ msgid "Enter new Name..." -#~ msgstr "Entrez un nouveau nom…" - -#~ msgid "No playlists found" -#~ msgstr "Aucune playlist trouvée" - -#~ msgid "Patrons" -#~ msgstr "Donateurs" diff --git a/source/locales/ja/LC_MESSAGES/all.mo b/source/locales/ja/LC_MESSAGES/all.mo deleted file mode 100644 index e6b7865263dc2e230d475025db1f883c05728c7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4240 zcmca7#4?qEfq}u3fq_AWfq@}~iGd-Jfq`KS3rLiK!GMi{fscWK!H|uCL5P8Y!GVo| zL70JoA()MUft7)QA)SqZfti7UA(xGTfs=uOp%5xw&BnmM#=yYP$i~1Rz`(%J!N$PA z&cMJh6Dq!tje&uefq`KKRQ)!nxqG1MFR(E%NHQ=mJcO$M4W*gb85oop7#KL&85lSi z7#MWe85p=27#K|1A@(~!#l6@W7{oyCWM^OyWnf^)Vu$#zft`Utmw|y{E<40Mr`aLy zyvz>q-z}&)Z=v$v*%=sk7#JA-vokP=FfcIia6s%)@kx|tK=-wB)$f6a!PvjA%Dawxr-6Ovx`b3(%N97vpzf#D(4zPDTu z`@V58Fi3#HlN;h*RVb~)4UsqIhUoX?hPXe5n}I=#fq@|tDn5l967I{P^meHJN{0riO$_o$;%CDfZ4#WmwWd;TYSq4aaS}{Pv-<<)HUqEGt0s{ks zDgy(92S_m#gUUpGC>xZYK}=A3(_~;^Fa~izWhJOw0+k62kg^OU3@Q^G85kIBKxu~o zlGi}xA}DQv%26{0NO=j$8=&+K(j&zHDf5g#94NMCfaH4+*BKOV3=9nN3=9lv3=9k= zAVCHO1{Wx;$H2g#0cC?IZ3akL0b*M)FfiCLK=KHP4=Uq9G^mUOmGhu7+7?uW>QgNQ6+A7g(BFn#uqs6qns6?{R41*ImZ=9T0? zqCPc+At*IDKQ}iuF9pob%`Z!32ucM}3T~h5S)=;uHcxH17X1IOHEM#DJo_N&L~Yw%SmN`@{sZnLr8vl zdQPfBL1JlfDnm$q0W_2uLi1Ac8NxDCQuB)$!ZK6K6(D&8!cGL|7lz3EQbbZKW+*92 z%qz}GEXgm@O)g4J$t(edX?|8}a*3{Iif&kHQE_H|o|OVufWK}~YFQ?Tr|Xhfl4_;E z<&#)kq8kF&Z>5l%n3P|XU!0Mdr(jc%te2jemz|hhs+XB($K{ilmtLBfo~j#?nwSgL z3*lQSWF>O>diuH|tTNOy;BwB-D@n~O(G96ANVQS`rFH#+oW#sLYlY;D#G>NV65G%a zH(d+T)qz}_mRh9inwOlPl9`uorC^biS;7^e>zzQ#eXgrSWp2y8?kDC@h zZrb^{x#MwT&x>W3lYO3&XQ*9Z&mqGdynE{J44k4cd@EAkr*^Z9Kjr$p1ENOqScrU~A z?JHi+ZhPD`>BWkTPy5$1pu4(p9W*wZCOvIk`m}2{D2^}{ff53;if2paJm0eD#j1Hv z`hvC_@4PfhA&|LX+LNCa; zXKNNdZd~_l_9h0bNf8mZFB@h(-Lc~_EHyFsdqb^)I-3EMhZ=W2Zd?Yj2rB%zc@Zd_ znmgfqXexZ%xc=GnMW7&BJLBn`6<{~+d)zn^T^$2BM}l1lF&LZ^mOgIY1IZI0*5k%i zj~k~mfI^^Y$K$3|2uq*MX?U@J;)}*Dj~iPaH_iw7^KoMr*r_ugH#IW6oVf&?Z7>oW zC=8oAp(z<^F)RZ?!XNG%P&R8`^|*No zIJi0(p3UBbAp}mFh=6{vaK-Z_yKv?dNW6k_2vl9uc5o5W{J424s0aZC2BbWE+&J}d zQx7OfLVTnDv~kX}wTl=anc>Nft|xnY8J_k}d)(CZY+DC7lDelpp8_gwAhC;7rZr81 WISW+yp_X@WIfylg5(!b&FaQ7okDN;Y diff --git a/source/locales/ja/LC_MESSAGES/all.po b/source/locales/ja/LC_MESSAGES/all.po deleted file mode 100644 index 2cf99c4..0000000 --- a/source/locales/ja/LC_MESSAGES/all.po +++ /dev/null @@ -1,328 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: \n" -"Last-Translator: maboroshin \n" -"Language-Team: \n" -"Language: ja\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.6\n" - -#: monophony/frontend/pages/artist_page.py:19 -#: monophony/frontend/pages/results_page.py:20 -msgid "No Results" -msgstr "見つかりませんでした" - -#: monophony/frontend/pages/artist_page.py:58 -msgid "Other" -msgstr "ほか" - -#: monophony/frontend/pages/artist_page.py:59 -#: monophony/frontend/pages/results_page.py:89 -msgid "Albums" -msgstr "アルバム" - -#: monophony/frontend/pages/artist_page.py:60 -msgid "Playlists" -msgstr "再生リスト" - -#: monophony/frontend/pages/artist_page.py:83 -#: monophony/frontend/pages/results_page.py:88 -msgid "Songs" -msgstr "曲" - -#: monophony/frontend/pages/artist_page.py:89 -#: monophony/frontend/pages/results_page.py:90 -msgid "Videos" -msgstr "動画" - -#: monophony/frontend/pages/artist_page.py:95 -msgid "Artist Not Found" -msgstr "アーティストが見つかりません" - -#: monophony/frontend/pages/results_page.py:61 -msgid "Show All" -msgstr "すべて表示" - -#: monophony/frontend/pages/results_page.py:87 -msgid "Top Result" -msgstr "検索上位" - -#: monophony/frontend/pages/results_page.py:91 -msgid "Community Playlists" -msgstr "コミュニティの再生リスト" - -#: monophony/frontend/pages/results_page.py:92 -msgid "Artists" -msgstr "アーティスト" - -#: monophony/frontend/widgets/recent_searches.py:36 -msgid "Remove" -msgstr "除去" - -#: monophony/frontend/widgets/player.py:68 -msgid "Toggle pause" -msgstr "再生/停止" - -#: monophony/frontend/widgets/player.py:80 -msgid "Next song" -msgstr "次の曲" - -#: monophony/frontend/widgets/player.py:85 -msgid "Previous song" -msgstr "前の曲" - -#: monophony/frontend/widgets/player.py:97 -msgid "Change volume" -msgstr "音量を変更" - -#: monophony/frontend/widgets/player.py:103 -msgid "Playback mode" -msgstr "再生モード" - -#: monophony/frontend/widgets/player.py:188 -msgid "Normal Playback" -msgstr "通常の再生" - -#: monophony/frontend/widgets/player.py:195 -msgid "Radio Mode" -msgstr "ラジオモード" - -#: monophony/frontend/widgets/player.py:205 -msgid "Repeat Song" -msgstr "曲をリピート" - -#: monophony/frontend/widgets/player.py:215 -msgid "Repeat Queue" -msgstr "キューをリピート" - -#: monophony/frontend/windows/import_window.py:21 -msgid "Enter Playlist Name..." -msgstr "再生リストの名前を入力..." - -#: monophony/frontend/windows/import_window.py:28 -msgid "Enter Playlist URL..." -msgstr "再生リストのURLを入力..." - -#: monophony/frontend/windows/import_window.py:33 -msgid "Synchronized" -msgstr "同期される" - -#: monophony/frontend/windows/import_window.py:34 -msgid "Editable" -msgstr "編集を許可" - -#: monophony/frontend/windows/import_window.py:47 -#: monophony/frontend/windows/add_window.py:23 -msgid "Cancel" -msgstr "キャンセル" - -#: monophony/frontend/windows/import_window.py:49 -#: monophony/frontend/tabs/library_tab.py:59 -msgid "Import" -msgstr "インポート" - -#: monophony/frontend/windows/import_window.py:71 -msgid "Import Playlist..." -msgstr "再生リストをインポート..." - -#: monophony/frontend/windows/import_window.py:100 -#: monophony/frontend/windows/import_window.py:118 -#: monophony/frontend/windows/import_window.py:124 -msgid "Could not import playlist" -msgstr "再生リストをインポートできませんでした" - -#: monophony/frontend/windows/import_window.py:101 -msgid "Failed to retrieve playlist data from server." -msgstr "サーバーから再生リストの取得に失敗しました。" - -#: monophony/frontend/windows/import_window.py:118 -msgid "A name is required." -msgstr "名前は必須です。" - -#: monophony/frontend/windows/import_window.py:124 -msgid "A URL is required." -msgstr "URLは必須です。" - -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "OK" - -#: monophony/frontend/windows/main_window.py:38 -msgid "Library" -msgstr "ライブラリ" - -#: monophony/frontend/windows/main_window.py:43 -msgid "Search" -msgstr "検索" - -#: monophony/frontend/windows/main_window.py:49 -#: monophony/frontend/windows/add_window.py:36 -#: monophony/frontend/tabs/queue_tab.py:44 -msgid "Queue" -msgstr "再生キュー" - -#: monophony/frontend/windows/main_window.py:58 -msgid "About" -msgstr "情報" - -#: monophony/frontend/windows/main_window.py:192 -msgid "translator-credits" -msgstr "maboroshin" - -#: monophony/frontend/windows/main_window.py:239 -#, python-brace-format -msgid "Deleted playlist \"{playlist_name}\"" -msgstr "「{playlist_name}」を削除しました" - -#: monophony/frontend/windows/main_window.py:244 -msgid "Undo" -msgstr "取り消す" - -#: monophony/frontend/windows/main_window.py:262 -msgid "Added" -msgstr "追加しました" - -#: monophony/frontend/windows/add_window.py:20 -#: monophony/frontend/popovers/song_popover.py:31 -msgid "Add to..." -msgstr "追加先を選択..." - -#: monophony/frontend/windows/add_window.py:25 -msgid "Add" -msgstr "追加" - -#: monophony/frontend/windows/add_window.py:43 -#: monophony/frontend/tabs/library_tab.py:79 -msgid "Your Playlists" -msgstr "再生リスト" - -#: monophony/frontend/windows/add_window.py:49 -msgid "New Playlist Name..." -msgstr "新しい再生リスト名..." - -#: monophony/frontend/windows/add_window.py:51 -msgid "Create" -msgstr "作成" - -#: monophony/frontend/popovers/local_song_popover.py:17 -msgid "Remove From Playlist" -msgstr "再生リストから除去" - -#: monophony/frontend/popovers/queue_song_popover.py:18 -msgid "Remove From Queue" -msgstr "再生キューから除去" - -#: monophony/frontend/popovers/song_popover.py:21 -msgid "Remove From Downloads" -msgstr "ダウンロードから削除" - -#: monophony/frontend/popovers/song_popover.py:26 -#: monophony/frontend/rows/importable_group_row.py:29 -#: monophony/frontend/rows/local_group_row.py:37 -#: monophony/frontend/rows/external_group_row.py:47 -msgid "Download" -msgstr "ダウンロード" - -#: monophony/frontend/popovers/song_popover.py:35 -msgid "View Artist" -msgstr "アーティストを表示" - -#: monophony/frontend/rows/artist_row.py:14 -#: monophony/frontend/rows/artist_row.py:23 -msgid "View artist" -msgstr "アーティストを表示" - -#: monophony/frontend/rows/song_row.py:18 -#: monophony/frontend/rows/group_row.py:24 -msgid "Play" -msgstr "再生" - -#: monophony/frontend/rows/song_row.py:36 -msgid "Downloaded" -msgstr "ダウンロード済み" - -#: monophony/frontend/rows/song_row.py:44 -#: monophony/frontend/rows/importable_group_row.py:17 -#: monophony/frontend/rows/local_group_row.py:23 -#: monophony/frontend/rows/external_group_row.py:18 -msgid "More actions" -msgstr "ほかの操作" - -#: monophony/frontend/rows/importable_group_row.py:35 -msgid "Import..." -msgstr "インポート" - -#: monophony/frontend/rows/local_group_row.py:35 -#: monophony/frontend/rows/external_group_row.py:43 -msgid "Delete" -msgstr "削除" - -#: monophony/frontend/rows/local_group_row.py:43 -msgid "Duplicate" -msgstr "複製" - -#: monophony/frontend/rows/local_group_row.py:49 -#: monophony/frontend/rows/external_group_row.py:53 -msgid "Rename..." -msgstr "名前の変更..." - -#: monophony/frontend/rows/local_group_row.py:66 -#: monophony/frontend/rows/external_group_row.py:70 -msgid "Rename" -msgstr "名前の変更" - -#: monophony/frontend/rows/local_group_row.py:96 -#: monophony/frontend/rows/external_group_row.py:103 -msgid "Could not Rename" -msgstr "名前を変更できません" - -#: monophony/frontend/rows/local_group_row.py:97 -#: monophony/frontend/rows/external_group_row.py:104 -msgid "Playlist already exists" -msgstr "再生リストは既に存在します" - -#: monophony/frontend/rows/external_group_row.py:25 -msgid "(Synchronized)" -msgstr "(同期)" - -#: monophony/frontend/tabs/library_tab.py:55 -msgid "Recommended" -msgstr "おすすめ" - -#: monophony/frontend/tabs/library_tab.py:70 -msgid "Play all" -msgstr "すべて再生" - -#: monophony/frontend/tabs/library_tab.py:85 -#: monophony/frontend/tabs/queue_tab.py:30 -msgid "Clear" -msgstr "消去" - -#: monophony/frontend/tabs/library_tab.py:92 -msgid "Recently Played" -msgstr "最近聴いた曲" - -#: monophony/frontend/tabs/library_tab.py:98 -msgid "Show Downloaded Songs" -msgstr "ダウンロード済みの曲を表示" - -#: monophony/frontend/tabs/search_tab.py:18 -msgid "Enter text or paste a URL..." -msgstr "検索語句か、URLを入力..." - -#: monophony/frontend/tabs/search_tab.py:24 -msgid "Go back" -msgstr "戻る" - -#: monophony/frontend/tabs/queue_tab.py:25 -msgid "Queue Empty" -msgstr "キューなし" - -#: monophony/frontend/tabs/queue_tab.py:34 -msgid "Shuffle" -msgstr "シャッフル" diff --git a/source/locales/nl/LC_MESSAGES/all.mo b/source/locales/nl/LC_MESSAGES/all.mo deleted file mode 100644 index d2c21cc2bc282a3d804db2cbde0b02c1d4d676b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1916 zcmca7#4?qEfq_Aufq_AWfq_AUk%2*zfq{XC2_(wEuz`tzft7)QVIva*0}lfO!*(VH z1~vu;h9gk=G!p}ZFara_B_;+20R{$!XHfC)Q2swCpP!k5L4tvSL5!J!ftP`SL7$m{ zfrWvA!IqhUfuDha!HJoHL5P8YA&8lQfs28GA(fed0px@NW(Ed!1_p*=sQPMV1_llW z28IS^1_p5k28K>%1_n+B28M-DeXE%n7(^Ku7`8DpFbFa*Fr0zP--Fuof*InUAIuPc z{Dac0ED-y7SQr>A7#SGkSs?CsN>`vX1(E~BF)00j;uI9W zATdyQfM^f~g()a}861=HOG_9WQ&JR4@)Zh8Q%h4B9CMONbBh_A6Z4W&a~PcSb8}1c zGD|8I3UU%Fb25udiW!{qOLI~b^72a*ic<3ub5j{yQgc#EU=&=TJ%dYrULr_5zdSD| zKM`z)Z)tI6vO-#ZPD*MKgKJ(%YLNoS2t7SL2H*UmR0hA)a<~B?LBD)Bw^$)9zcep} z!7pE-D7CmWr=*y{Kbs*Sv7{(Jub3eqC$SPtC?w|OfEggiDkSC3JS5D%%q~k zqDqF~{JivHhOo?()cj(Gu>73T+*F2=qQt!7oWzp+BHiSo)RfGUVupaC{H)aE5?#*} z-LTZ6;>`R!D+R6qf8C(evP=+9*Cnwe)k?w0z{ptFz)07~K*7-1%FtBXz|g>e%O|n8 zL^lL(sFi|8YG!U~o5ggTJJ(o&0bUGtLjQ!?|?trRShGE2Aua!QL5b9CME zi*k#t6!Hqd++tfJYXu0?RwGYA(N@7w)0!(n*F7~awFneqRtf?6pdeE))-%#G;$rYG zOD$pu$xkiIPfbtFQ%FqAQz%bN&L}C$%z~v?$Gp7KoYbP!JO; zvOx-E2nle)2Ak(!P@DsDOlF=!ZfR9!YIbg70Yg}RYLP-}Y97R@%)Cbp%NTr9Q;Qh< zGE+;-Qxy`C+y|0#EmEk;%mO(AytvOr=, 2023. -msgid "" -msgstr "" -"Project-Id-Version: \n" -"POT-Creation-Date: \n" -"PO-Revision-Date: 2023-02-20 13:15+0100\n" -"Last-Translator: Heimen Stoffels \n" -"Language-Team: Dutch\n" -"Language: nl\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.2.2\n" - -#: ../monophony/frontend/pages/search_page.py:23 -#: ../monophony/frontend/pages/search_page.py:89 -msgid "No results" -msgstr "Er zijn geen zoekresultaten" - -#: ../monophony/frontend/pages/search_page.py:101 -msgid "Songs" -msgstr "Nummers" - -#: ../monophony/frontend/pages/search_page.py:102 -#: ../monophony/frontend/pages/search_page.py:111 -#: ../monophony/frontend/pages/search_page.py:120 -#: ../monophony/frontend/pages/search_page.py:129 -msgid "More" -msgstr "Meer" - -#: ../monophony/frontend/pages/search_page.py:110 -msgid "Albums" -msgstr "Albums" - -#: ../monophony/frontend/pages/search_page.py:119 -msgid "Community playlists" -msgstr "Afspeellijsten van anderen" - -#: ../monophony/frontend/pages/search_page.py:128 -msgid "Videos" -msgstr "Video's" - -#: ../monophony/frontend/pages/library_page.py:26 -msgid "No playlists found" -msgstr "Er zijn geen afspeellijsten gevonden" - -#: ../monophony/frontend/pages/library_page.py:29 -msgid "Play all" -msgstr "Alles beluisteren" - -#: ../monophony/frontend/pages/library_page.py:32 -msgid "Playlists" -msgstr "Afspeellijsten" - -#: ../monophony/frontend/windows/delete_window.py:15 -msgid "Delete playlist?" -msgstr "Afspeellijst verwijderen?" - -#: ../monophony/frontend/windows/delete_window.py:16 -#: ../monophony/frontend/windows/rename_window.py:18 -msgid "Cancel" -msgstr "Annuleren" - -#: ../monophony/frontend/windows/delete_window.py:17 -#: ../monophony/frontend/widgets/group_row.py:24 -msgid "Delete" -msgstr "Verwijderen" - -#: ../monophony/frontend/windows/main_window.py:29 -msgid "About" -msgstr "Over" - -#: ../monophony/frontend/windows/main_window.py:119 -msgid "translator-credits" -msgstr "Heimen Stoffels " - -#: ../monophony/frontend/windows/main_window.py:120 -msgid "Patrons" -msgstr "Patrons" - -#: ../monophony/frontend/windows/main_window.py:122 -msgid "Donate" -msgstr "Doneren" - -#: ../monophony/frontend/windows/rename_window.py:15 -msgid "Enter name..." -msgstr "Voer een naam in…" - -#: ../monophony/frontend/windows/rename_window.py:19 -#: ../monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "Oké" - -#: ../monophony/frontend/widgets/player.py:62 -msgid "Remove from queue" -msgstr "Verwijderen uit wachtrij" - -#: ../monophony/frontend/widgets/player.py:65 -msgid "Volume" -msgstr "Volumeniveau" - -#: ../monophony/frontend/widgets/player.py:81 -msgid "Radio mode" -msgstr "Radiomodus" - -#: ../monophony/frontend/widgets/song_row.py:21 -msgid "Play" -msgstr "Beluisteren" - -#: ../monophony/frontend/widgets/song_popover.py:37 -msgid "Remove from downloads" -msgstr "Verwijderen uit downloads" - -#: ../monophony/frontend/widgets/song_popover.py:39 -msgid "Download to Music folder" -msgstr "Opslaan in muziekmap" - -#: ../monophony/frontend/widgets/song_popover.py:51 -msgid "Add to queue" -msgstr "Toevoegen aan wachtrij" - -#: ../monophony/frontend/widgets/song_popover.py:55 -msgid "New playlist..." -msgstr "Nieuwe afspeellijst…" - -#: ../monophony/frontend/widgets/group_row.py:27 -msgid "Rename..." -msgstr "Naam wijzigen…" - -#: ../monophony/frontend/widgets/group_row.py:35 -msgid "Save to library" -msgstr "Toevoegen aan verzameling" - -#: ../monophony/frontend/widgets/group_row.py:82 -msgid "Could not rename" -msgstr "De naam kan niet worden gewijzigd" - -#: ../monophony/frontend/widgets/group_row.py:83 -msgid "Playlist already exists" -msgstr "Deze afspeellijst bestaat al" diff --git a/source/locales/pl/LC_MESSAGES/all.po b/source/locales/pl/LC_MESSAGES/all.po new file mode 100644 index 0000000..43079e1 --- /dev/null +++ b/source/locales/pl/LC_MESSAGES/all.po @@ -0,0 +1,329 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8\n" + +#: monophony/ui/pages/artist_page.py:11 +msgid "Artist Page" +msgstr "Strona Wykonawcy" + +#: monophony/ui/pages/home_page.py:26 +msgid "Recommended" +msgstr "Polecane" + +#: monophony/ui/pages/home_page.py:76 monophony/ui/pages/home_page.py:131 +msgid "Your Playlists" +msgstr "Twoje Playlisty" + +#: monophony/ui/pages/home_page.py:133 +msgid "Playlists you create will appear here" +msgstr "Utworzone playlisty pojawią się tutaj" + +#: monophony/ui/pages/home_page.py:148 monophony/ui/windows/import_window.py:52 +msgid "Import" +msgstr "Importuj" + +#: monophony/ui/pages/home_page.py:161 +msgid "Synchronized Playlists" +msgstr "Zsynchronizowane Playlisty" + +#: monophony/ui/pages/home_page.py:217 +msgid "Downloads Directory" +msgstr "Katalog Pobranych" + +#: monophony/ui/pages/home_page.py:227 +msgid "Downloads" +msgstr "Pobrane" + +#: monophony/ui/pages/home_page.py:259 +msgid "Clear" +msgstr "Wyczyść" + +#: monophony/ui/pages/home_page.py:267 +msgid "Recently Played" +msgstr "Ostatnio Odtwarzane" + +#: monophony/ui/pages/home_page.py:304 monophony/ui/windows/main_window.py:712 +msgid "Donate" +msgstr "Przekaż Darowiznę" + +#: monophony/ui/pages/home_page.py:348 +msgid "Home" +msgstr "Strona Główna" + +#: monophony/ui/pages/home_page.py:407 monophony/ui/pages/home_page.py:436 +#, python-brace-format +msgid "Deleted playlist \"{name}\"" +msgstr "Usunięto playlistę \"{name}\"" + +#: monophony/ui/pages/home_page.py:411 monophony/ui/pages/home_page.py:440 +msgid "Undo" +msgstr "Cofnij" + +#: monophony/ui/pages/loading_page.py:21 +msgid "Loading..." +msgstr "Ładowanie..." + +#: monophony/ui/pages/results_page.py:27 monophony/ui/pages/results_page.py:47 +#: monophony/ui/pages/results_page.py:67 monophony/ui/pages/results_page.py:87 +#: monophony/ui/pages/results_page.py:107 +msgid "Show All" +msgstr "Pokaż Wszystkie" + +#: monophony/ui/pages/results_page.py:126 +msgid "Songs" +msgstr "Utwory" + +#: monophony/ui/pages/results_page.py:129 +msgid "Videos" +msgstr "Wideo" + +#: monophony/ui/pages/results_page.py:132 +msgid "Albums and Singles" +msgstr "Albumy i Single" + +#: monophony/ui/pages/results_page.py:135 +msgid "Playlists" +msgstr "Playlisty" + +#: monophony/ui/pages/results_page.py:138 +msgid "Artists" +msgstr "Wyonawcy" + +#: monophony/ui/pages/results_page.py:147 +msgid "Top Result" +msgstr "Najlepszy Wynik" + +#: monophony/ui/pages/results_page.py:221 +msgid "Search Results" +msgstr "Wyniki Wyszukiwania" + +#: monophony/ui/rows/artist_row.py:15 monophony/ui/rows/artist_row.py:20 +#: monophony/ui/popovers/group_row_popover.py:23 +#: monophony/ui/popovers/song_row_popover.py:23 +msgid "View Artist" +msgstr "Zobacz Wykonawcę" + +#: monophony/ui/rows/group_row.py:22 monophony/ui/rows/song_row.py:34 +msgid "More" +msgstr "Więcej" + +#: monophony/ui/rows/song_row.py:29 +msgid "Downloaded" +msgstr "Pobrane" + +#: monophony/ui/rows/song_row.py:46 +msgid "Play" +msgstr "Odtwarzaj" + +#: monophony/ui/windows/add_window.py:24 +msgid "New Playlist" +msgstr "Nowa Playlista" + +#: monophony/ui/windows/add_window.py:40 +msgid "Add" +msgstr "Dodaj" + +#: monophony/ui/windows/add_window.py:55 +msgid "Add to Playlists..." +msgstr "Dodaj do Playlist..." + +#: monophony/ui/windows/import_window.py:18 +msgid "Playlist URL" +msgstr "URL Playlisty" + +#: monophony/ui/windows/import_window.py:29 +#: monophony/ui/windows/rename_window.py:15 +msgid "Playlist Name" +msgstr "Nazwa Playlisty" + +#: monophony/ui/windows/import_window.py:33 +msgid "Synchronized" +msgstr "Zsynchronizowane" + +#: monophony/ui/windows/import_window.py:35 +msgid "Synchronized playlists are updated automatically and can't be edited" +msgstr "" +"Zsynchronizowane playlisty są aktualizowane automatycznie i nie mogą być " +"edytowane" + +#: monophony/ui/windows/import_window.py:69 +msgid "Import Playlist..." +msgstr "Importuj Playlistę..." + +#: monophony/ui/windows/message_window.py:12 +msgid "Ok" +msgstr "Ok" + +#: monophony/ui/windows/rename_window.py:35 +msgid "Rename Playlist..." +msgstr "Zmień Nazwę Playlisty..." + +#: monophony/ui/windows/main_window.py:373 +msgid "Playing" +msgstr "Odtwarzanie" + +#: monophony/ui/windows/main_window.py:454 +msgid "Download Failed" +msgstr "Nie Udało Się Pobrać" + +#: monophony/ui/windows/main_window.py:454 +msgid "Some songs could not be downloaded" +msgstr "Niektóre utwory nie mogły być pobrane" + +#: monophony/ui/windows/main_window.py:503 +msgid "Failed to Import" +msgstr "Nie Udało Się Importować" + +#: monophony/ui/windows/main_window.py:504 +#: monophony/ui/windows/main_window.py:619 +#: monophony/ui/windows/main_window.py:836 +msgid "Check your internet connection and try again" +msgstr "Sprawdź swoje połączenie z internetem i spróbuj ponownie" + +#: monophony/ui/windows/main_window.py:618 +msgid "Failed to Search" +msgstr "Nie Udało Się Wyszukać" + +#: monophony/ui/windows/main_window.py:625 +msgid "No Results" +msgstr "Brak Wyników" + +#: monophony/ui/windows/main_window.py:626 +msgid "Try searching for something else" +msgstr "Spróbuj wyszukać coś innego" + +#: monophony/ui/windows/main_window.py:709 +msgid "translator-credits" +msgstr "Zehkira" + +#: monophony/ui/windows/main_window.py:835 +msgid "Failed to Load Artist Page" +msgstr "Nie Udało Się Załadować Strony Wykonawcy" + +#: monophony/ui/windows/main_window.py:842 +msgid "Empty Artist Page" +msgstr "Pusta Strona Wykonawcy" + +#: monophony/ui/windows/main_window.py:843 +msgid "No content found from this artist" +msgstr "Nie znaleziono żadnych treści od tego wykonawcy" + +#: monophony/ui/bars/header_bar.py:15 +msgid "About" +msgstr "O Aplikacji" + +#: monophony/ui/bars/player_bar.py:93 monophony/ui/queue_sidebar.py:138 +msgid "Queue" +msgstr "Kolejka" + +#: monophony/ui/bars/player_bar.py:120 +msgid "Playback Mode" +msgstr "Tryb Odtwarzania" + +#: monophony/ui/bars/player_bar.py:127 +msgid "Previous" +msgstr "Poprzednie" + +#: monophony/ui/bars/player_bar.py:143 +msgid "Pause" +msgstr "Pauza" + +#: monophony/ui/bars/player_bar.py:152 +msgid "Next" +msgstr "Następne" + +#: monophony/ui/bars/player_bar.py:165 +msgid "Volume" +msgstr "Głośność" + +#: monophony/ui/bars/player_bar.py:236 +msgid "Normal Playback" +msgstr "Normalne Odtwarzanie" + +#: monophony/ui/bars/player_bar.py:237 +msgid "Repeat Song" +msgstr "Powtarzaj Utwór" + +#: monophony/ui/bars/player_bar.py:238 +msgid "Repeat Queue" +msgstr "Powtarzaj Kolejkę" + +#: monophony/ui/bars/player_bar.py:239 +msgid "Autoplay Similar" +msgstr "Autoodtwarzaj Podobne" + +#: monophony/ui/bars/search_bar.py:11 +msgid "Search..." +msgstr "Wyszukaj..." + +#: monophony/ui/popovers/editable_group_row_popover.py:14 +msgid "Rename..." +msgstr "Zmień Nazwę..." + +#: monophony/ui/popovers/editable_group_row_popover.py:17 +#: monophony/ui/popovers/synchronized_group_row_popover.py:14 +msgid "Delete" +msgstr "Usuń" + +#: monophony/ui/popovers/editable_song_row_popover.py:14 +msgid "Remove From Playlist" +msgstr "Usuń z Playlisty" + +#: monophony/ui/popovers/group_row_popover.py:20 +#: monophony/ui/popovers/song_row_popover.py:21 +msgid "Add to Queue" +msgstr "Dodaj do Kolejki" + +#: monophony/ui/popovers/group_row_popover.py:21 +#: monophony/ui/popovers/song_row_popover.py:22 +#: monophony/ui/queue_sidebar.py:41 +msgid "Add to..." +msgstr "Dodaj do..." + +#: monophony/ui/popovers/group_row_popover.py:24 +#: monophony/ui/popovers/song_row_popover.py:29 +msgid "Download" +msgstr "Pobierz" + +#: monophony/ui/popovers/importable_group_row_popover.py:14 +msgid "Import..." +msgstr "Importuj..." + +#: monophony/ui/popovers/queue_song_row_popover.py:15 +msgid "Remove From Queue" +msgstr "Usuń z Kolejki" + +#: monophony/ui/popovers/song_row_popover.py:26 +msgid "Remove From Downloads" +msgstr "Usuń z Pobranych" + +#: monophony/ui/row_groups/queueable_row_group.py:20 +msgid "Play All" +msgstr "Odtwórz Wszystkie" + +#: monophony/ui/queue_sidebar.py:18 +msgid "Stop" +msgstr "Zatrzymaj" + +#: monophony/ui/queue_sidebar.py:31 +msgid "Shuffle" +msgstr "Wymieszaj" + +#: monophony/ui/queue_sidebar.py:65 +msgid "Currently Playing" +msgstr "Aktualnie Odtwarzane" + +#: monophony/ui/queue_sidebar.py:116 +msgid "Queue Empty" +msgstr "Kolejka Pusta" + +#: monophony/ui/queue_sidebar.py:117 +msgid "Nothing is playing right now" +msgstr "Nic nie jest w tej chwili odtwarzane" + +#: monophony/ui/queue_sidebar.py:135 +msgid "Back" +msgstr "Wróć" + +#: monophony/playlists.py:167 +msgid "Playlist" +msgstr "Playlista" diff --git a/source/locales/pt_BR/LC_MESSAGES/all.mo b/source/locales/pt_BR/LC_MESSAGES/all.mo deleted file mode 100644 index 10058cf4a52312ff27160f333a3218b66dede766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2761 zcmca7#4?qEfq}t@fq_AWfq`KSBLjmo0|SE%Gf0$y!Gnc?ft7)Q!IOo7fro*C!JmbJ zfscWKA%=y4fuDhaAsZ@R&BDOI#=yYP$il$D&cMLX0p(9%VPFtuU|^We!oVQFz`(E? zD!vEGKMdtxVPRm9U|?Xl&BDOI%fP_!g@u7Zmw|zSi4|g=0V@Lo3j+g#1(ddBg}BFw zm4Sg13usCPU4c#>&9J!N9;Uo0WkT{s{3aB~tY>;rC%ErJT1acP}1A`nR1H*QxdDqw>?zzJb zarZlRi2ZCF5c_2~Aogo;K>TL_r7bxi>YX?k7^E2(7^0x+$~YkA)p0<=c>)ImgB$|` z!&DAPylvrNV6bIiU^vPFaW^w3L>(8D=I4a?PlOYqUXc?L{zjaT@N|NT2SDjWPKf`C zI3e-T$O#F@7EXx!yEq}~W*SueDyaJ1oDhGWf|~z;6XNa2X5Oesr7#PGD7#Kvj zAogf;LBidN3lbi#P;oylhhd8C>%75<%km<#{>ziC_o$mKJ9wE4bz7q@)%xxaO6l7Ag28=BDcD=`pxv=A|eU z=jWvtgIo@BokD4GW?s5NNk*zdacW{wa)v@uVv#~(Qhr$~gL}S0Qetv8gHL{b0fTRT zQ7V{FNK7ut%+D)k@XaqvRdC5K&x5c+3mE)T%i&H#;ehP+ORXpY+s)vYuMm`4T$)o- z%;2BR0P-}LP;ku20W&}$rjVFZl$w}QsgPO$4y%Bo)UwR{(qf1SL5V4u`3k=IDX9!W zsktC`xE1B+f*k`2g<=#@NCZK`5#c-tALOv$#In>BhG2;A)AEZHob&TaQu9i{avAyM z3Xu3>2+k-?OUp@R2nK~8Lr8vldQPfBL1JlfDnm$q0n|$jVVNnZ`Na%jnW=D%Vfi_w zxv311`K3h)K9B^WkXfwYnp;p(2@wT_0m#?I3?)U0dBr)2CHY0V$wjFtnI**x0Y&*) zsmUd}o+-LvsYS(^`FU0fTmk;NL8)b#AfB#EVo9o%0+&x>afxmST)&lqOJ-_%zJjx! zLU3YkL1JdUf=zZ&WNNQp(*c=GoN}-@6 z-YJO7*VES(VY8v00T(oYbVDi&Qmqt9QY%XI3vv=O^Q;w;GZKr6Q%h_^L)>&N5b8jV zN=q%$bpQphU+bBk?_tQ8ZFO-2(3WdTsYBUGp4GaPQrNh-~Q<>=N~(hI;a$a<$%zb}dBvGUi3<4&Ak#o85)_1aB}J(Uxeyh_5Uu&8 zWtl|^rNxPPDftSCpiEo@Hm4x9xUe*{I8h-nIWsqrAuK6MvZ#NeBsk`D@&qQeVQGV`I? zyGX$$zZfIa7b&m{XEk1WIrLiKWGf3;{)%dC8dtpx}fQZeSmW9bOA54pI?@ nL5qXn(nJMBvQ$VdE;+nVAuO>fGm#;\n" -"Language-Team: \n" -"Language: pt_BR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 3.1.1\n" - -#: monophony/frontend/pages/search_page.py:24 -#: monophony/frontend/pages/search_page.py:96 -#: monophony/frontend/pages/search_page.py:20 -#: monophony/frontend/pages/search_page.py:123 -#: monophony/frontend/pages/search_page.py:129 -#: monophony/frontend/pages/search_page.py:130 -msgid "No Results" -msgstr "Sem Resultados" - -#: monophony/frontend/pages/search_page.py:108 -#: monophony/frontend/pages/search_page.py:133 -#: monophony/frontend/pages/search_page.py:139 -#: monophony/frontend/pages/search_page.py:140 -msgid "Songs" -msgstr "Músicas" - -#: monophony/frontend/pages/search_page.py:109 -#: monophony/frontend/pages/search_page.py:118 -#: monophony/frontend/pages/search_page.py:127 -#: monophony/frontend/pages/search_page.py:136 -#: monophony/frontend/pages/search_page.py:139 -#: monophony/frontend/pages/search_page.py:147 -#: monophony/frontend/pages/search_page.py:156 -#: monophony/frontend/pages/search_page.py:164 -#: monophony/frontend/pages/search_page.py:172 -#: monophony/frontend/pages/search_page.py:145 -#: monophony/frontend/pages/search_page.py:153 -#: monophony/frontend/pages/search_page.py:162 -#: monophony/frontend/pages/search_page.py:170 -#: monophony/frontend/pages/search_page.py:178 -#: monophony/frontend/pages/search_page.py:146 -#: monophony/frontend/pages/search_page.py:154 -#: monophony/frontend/pages/search_page.py:163 -#: monophony/frontend/pages/search_page.py:171 -#: monophony/frontend/pages/search_page.py:179 -msgid "More" -msgstr "Mais" - -#: monophony/frontend/pages/search_page.py:117 -#: monophony/frontend/pages/search_page.py:134 -#: monophony/frontend/pages/search_page.py:140 -#: monophony/frontend/pages/search_page.py:141 -msgid "Albums" -msgstr "Álbuns" - -#: monophony/frontend/pages/search_page.py:126 -#: monophony/frontend/pages/search_page.py:155 -#: monophony/frontend/pages/search_page.py:161 -#: monophony/frontend/pages/search_page.py:162 -msgid "Community Playlists" -msgstr "Playlists Comunitárias" - -#: monophony/frontend/pages/search_page.py:135 -#: monophony/frontend/pages/search_page.py:141 -#: monophony/frontend/pages/search_page.py:142 -msgid "Videos" -msgstr "Vídeos" - -#: monophony/frontend/pages/library_page.py:25 -msgid "Your Library is Empty" -msgstr "Sua Biblioteca está Vazia" - -#: monophony/frontend/pages/library_page.py:26 -msgid "Find songs to play using the search bar above" -msgstr "Encontre músicas para ouvir usando a barra de pesquisa acima" - -#: monophony/frontend/pages/library_page.py:29 -#: monophony/frontend/pages/library_page.py:37 -msgid "Play All" -msgstr "Tocar Todas" - -#: monophony/frontend/pages/library_page.py:33 -#: monophony/frontend/pages/library_page.py:41 -msgid "Your Playlists" -msgstr "Suas Playlists" - -#: monophony/frontend/windows/delete_window.py:15 -msgid "Delete Playlist?" -msgstr "Excluir Playlist?" - -#: monophony/frontend/windows/delete_window.py:16 -#: monophony/frontend/windows/rename_window.py:18 -msgid "Cancel" -msgstr "Cancelar" - -#: monophony/frontend/windows/delete_window.py:17 -#: monophony/frontend/widgets/group_row.py:60 -#: monophony/frontend/widgets/group_row.py:79 -msgid "Delete" -msgstr "Excluir" - -#: monophony/frontend/windows/main_window.py:41 -#: monophony/frontend/windows/main_window.py:50 -msgid "Go back" -msgstr "Voltar" - -#: monophony/frontend/windows/main_window.py:46 -#: monophony/frontend/windows/main_window.py:55 -msgid "About" -msgstr "Sobre" - -#: monophony/frontend/windows/main_window.py:51 -#: monophony/frontend/windows/main_window.py:60 -msgid "Search for Content..." -msgstr "Pesquisar Conteúdo..." - -#: monophony/frontend/windows/main_window.py:141 -#: monophony/frontend/windows/main_window.py:138 -#: monophony/frontend/windows/main_window.py:147 -#: monophony/frontend/windows/main_window.py:152 -msgid "translator-credits" -msgstr "Diego C. Sampaio" - -#: monophony/frontend/windows/main_window.py:143 -#: monophony/frontend/windows/main_window.py:140 -#: monophony/frontend/windows/main_window.py:149 -#: monophony/frontend/windows/main_window.py:154 -msgid "Donate" -msgstr "Doar" - -#: monophony/frontend/windows/main_window.py:174 -#: monophony/frontend/windows/main_window.py:171 -#: monophony/frontend/windows/main_window.py:180 -#: monophony/frontend/windows/main_window.py:185 -msgid "New Playlist" -msgstr "Nova Playlist" - -#: monophony/frontend/windows/main_window.py:191 -#: monophony/frontend/windows/main_window.py:188 -#: monophony/frontend/windows/main_window.py:197 -#: monophony/frontend/windows/main_window.py:202 -msgid "Could not Rename" -msgstr "Não foi possível renomear" - -#: monophony/frontend/windows/main_window.py:192 -#: monophony/frontend/windows/main_window.py:189 -#: monophony/frontend/windows/main_window.py:198 -#: monophony/frontend/windows/main_window.py:203 -msgid "Playlist already exists" -msgstr "Playlist já existe" - -#: monophony/frontend/windows/main_window.py:196 -#: monophony/frontend/windows/main_window.py:193 -#: monophony/frontend/windows/main_window.py:202 -#: monophony/frontend/windows/main_window.py:207 -msgid "Rename Playlist" -msgstr "Renomear Playlist" - -#: monophony/frontend/windows/main_window.py:201 -msgid "Saved" -msgstr "Salvo" - -#: monophony/frontend/windows/rename_window.py:15 -msgid "Enter Name..." -msgstr "Insira o nome..." - -#: monophony/frontend/windows/rename_window.py:19 -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "Ok" - -#: monophony/frontend/widgets/player.py:33 -#: monophony/frontend/widgets/player.py:61 -#: monophony/frontend/widgets/player.py:62 -msgid "Toggle pause" -msgstr "Alternar Pausa" - -#: monophony/frontend/widgets/player.py:37 -#: monophony/frontend/widgets/player.py:65 -#: monophony/frontend/widgets/player.py:66 -msgid "Next song" -msgstr "Próxima música" - -#: monophony/frontend/widgets/player.py:42 -#: monophony/frontend/widgets/player.py:70 -#: monophony/frontend/widgets/player.py:71 -msgid "Previous song" -msgstr "Música anterior" - -#: monophony/frontend/widgets/player.py:47 -#: monophony/frontend/widgets/player.py:75 -#: monophony/frontend/widgets/player.py:76 -msgid "Add to playlist" -msgstr "Adicionar à playlist" - -#: monophony/frontend/widgets/player.py:53 -#: monophony/frontend/widgets/player.py:81 -#: monophony/frontend/widgets/player.py:82 -msgid "Remove From Queue" -msgstr "Remover Da Fila" - -#: monophony/frontend/widgets/player.py:59 -#: monophony/frontend/widgets/player.py:87 -#: monophony/frontend/widgets/player.py:94 -msgid "Volume" -msgstr "Volume" - -#: monophony/frontend/widgets/player.py:82 -#: monophony/frontend/widgets/player.py:110 -#: monophony/frontend/widgets/player.py:117 -msgid "Radio Mode" -msgstr "Modo de rádio" - -#: monophony/frontend/widgets/player.py:92 -#: monophony/frontend/widgets/player.py:120 -#: monophony/frontend/widgets/player.py:127 -msgid "Loop" -msgstr "Loop" - -#: monophony/frontend/widgets/player.py:99 -#: monophony/frontend/widgets/player.py:127 -#: monophony/frontend/widgets/player.py:134 -msgid "Shuffle" -msgstr "Aleatório" - -#: monophony/frontend/widgets/player.py:115 -#: monophony/frontend/widgets/song_row.py:41 -#: monophony/frontend/widgets/group_row.py:34 -#: monophony/frontend/widgets/player.py:143 -#: monophony/frontend/widgets/group_row.py:41 -#: monophony/frontend/widgets/player.py:150 -msgid "More actions" -msgstr "Mais Ações" - -#: monophony/frontend/widgets/song_row.py:21 -#: monophony/frontend/widgets/group_row.py:33 -msgid "Play" -msgstr "Tocar" - -#: monophony/frontend/widgets/song_popover.py:23 -msgid "Move Up" -msgstr "Mover para Cima" - -#: monophony/frontend/widgets/song_popover.py:24 -msgid "Move Down" -msgstr "Mover para Baixo" - -#: monophony/frontend/widgets/song_popover.py:39 -msgid "Remove From Downloads" -msgstr "Remover Dos Downloads" - -#: monophony/frontend/widgets/song_popover.py:46 -msgid "Download to Music Folder" -msgstr "Baixar para a Pasta de Música" - -#: monophony/frontend/widgets/song_popover.py:53 -msgid "Add to Queue" -msgstr "Adicionar à Fila" - -#: monophony/frontend/widgets/song_popover.py:59 -msgid "New Playlist..." -msgstr "Nova Playlist..." - -#: monophony/frontend/widgets/group_row.py:45 -#: monophony/frontend/widgets/group_row.py:52 -msgid "Add to library" -msgstr "Adicionar à Biblioteca" - -#: monophony/frontend/widgets/group_row.py:66 -#: monophony/frontend/widgets/group_row.py:85 -msgid "Rename..." -msgstr "Renomear..." - -#: monophony/frontend/pages/search_page.py:136 -#: monophony/frontend/pages/search_page.py:142 -#: monophony/frontend/pages/search_page.py:143 -msgid "Artists" -msgstr "Artistas" - -#: monophony/frontend/windows/main_window.py:198 -#: monophony/frontend/windows/main_window.py:207 -#: monophony/frontend/windows/main_window.py:212 -msgid "Added" -msgstr "Adicionardo" - -#: monophony/frontend/widgets/artist_row.py:14 -msgid "View Artist" -msgstr "Ver Artista" - -#: monophony/frontend/pages/search_page.py:138 -#: monophony/frontend/pages/search_page.py:139 -msgid "Top Result" -msgstr "Principais Resultados" - -#: monophony/frontend/widgets/player.py:88 -msgid "Show Artist" -msgstr "Exibir Artista" diff --git a/source/locales/ru/LC_MESSAGES/all.mo b/source/locales/ru/LC_MESSAGES/all.mo deleted file mode 100644 index e589a0246767740fb7aac9955607237ec986aa3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4883 zcmca7#4?qEfq}t|fq_AWfq}t?iGd-Jfq|ih1tiMAz`(}9AjH7Hz{tkHAk4tPAi~DL zz{=1voutVHG9V)+&oq>Uefq`KqI|G9V z0|Uc0sQhVmh`m?Y85sB(7#JR~GcYhQFfe>zhlIyxb_NDeko{zbxbq)70|PGu0|Pq; z1A{mN1A{0B#2#G^h`2ciM4u}M0|Pe$1A{jQ#Qbm$i1|rS@gfe0d+RtL{%YlbxN9~C z1A`y~1H%TW{2{3MC!zW-L&fiNK-}|;1LFVpPYa^N^@~S>=);R_(O>kVy`+U z#C>{D+6pS}#>v3I$H2hg%Lxg`7)}NTCq@Q_d`<=iLk0$hO z2NiBeJeflIKHLyjmU|_K1gTzlMA0!@H`5@|M@j?8(1Im8| zr8)Q^=}(Oxl3qgiA?AhiL)?+V&%mI_z`#(?4+*b@{0t243=9mX`61ydEWp4Z&%nT- zCIIoLn*b!AgbF~yHD3S{F0BF(^VUMuoe*GPP+?$TxGuoJpvJ(!@JE1wL5YEZL0J%@ z&P@>F?nEeEF9?bMxq^`NxFNKcm;9o86a^5N`Ig@Gy(}SFfcekX&q2{fU-dp zD4&7KBoNz-fq}t}fq}ss#9@HM4T!1D04Xm(Y%6Ga4@%DrjtZecJ_?z|3Pq`frI|&k zDS8Z!3VDgSsc2$J`K2WcjwvZ1tWc7#r>6&ErKT`A<|LKo7Be^&m1Gu|DEQ@WghPXT5TYfi6(tJ!MG6Ip#U-f< ziJ-s(NxCIw=A@=5l;kTErIr+Brk16`9j%a(SdyraR+OKsP@Gy+mRh98;GVCLl$e~& z;0g8~l!ChhWFABY#PZ2ZDoQM>Wbn-|N>xZqF3HT#D`xOZEr%bFB|}hRN@l)-FUYW< z)a2B>k{n2kr=~Cjr6%X+=BDPQfZ4hEWvL87sUS+h4HQPOs4GShh1rZE2)2*`nrI*t zD8zzN3sMtH6oNq^5uBP>l$^m3oRMFy;Fy!c5S&q(mX?#s0Fo?b2(HXa&M3;y%dARG zVF<}jPtQqJC`c?VPGtzmFMtLiLug(~K0{b$N@{*FLs({NxdJ4mL)eMnRL>BWpHrHf z$`F}fibzhy3?)U0dBr)2CHY0V$wjFtnI)ib%g;(pF46T&(G5#2D$dN$vr^y+@YfAW zEz1P)bX^ilQmquYd=iUGbVK0!!6JF-rHSdOx*@5Fxk!8~g`(2 zT+aDEOx*?SXsa6W0gsWeWlbD%jt&p6NSX7)^VjCLbrfY#v2eK|LwMf@BFF8LY zGcVmr!6GTMgexGYv?wu0*Db#&x7bP{uK>&~wl%g^fG}+}@>C5CY;6q{)YKF}Oanz* zLqi371p@^uFyGGB2qtA?YXV||br{$f8Y$G&fRz~78JcJ+*ee(+SSc83T60C{x~Jx) z7J}Y-PAG z<-)!T8!v3Qu<61MkOde53Kuqk5+0Je-50iA*voKX!iC)zdoFCeuZoo3%f7u1(|eVD@a=5!rlv86)w!Zu;Iea3tJ%iuqeE+gW&^?iA;K5C6)r5iun*+)i=7v?f>mw;X`BnPZt;aJ7dBkj zePQ3l#)~})7v^8s3JNVyIDlMmVc&&KkicZP08)Oj8CCg?3wuBwx!3@<9TZ+r zvf;umh!;Sq2^2{n9h0E$+;Oo96jY#?0XcmGDAqs%XaRj;132k7 z<pzVeiFOa01(V0aOU@0JAq;0F`H;!eJ-EkX;vcU+hB^Ap0+D00q{? zW`+y1K?Tdj4p831l1V_d5v*#0l-?I6UDyFG>i0ns4=9!I0p;$C4GLK6!G0|2K=lZ? zR0DYhTrlhgW!1@G5A3=2 && " -"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" -"X-Generator: Poedit 3.4.2\n" - -#: monophony/frontend/popovers/local_song_popover.py:17 -msgid "Remove From Playlist" -msgstr "Удалить из Плейлиста" - -#: monophony/frontend/popovers/queue_song_popover.py:18 -msgid "Remove From Queue" -msgstr "Удалить из Очереди" - -#: monophony/frontend/popovers/song_popover.py:22 -msgid "Remove From Downloads" -msgstr "Удалить из Загрузок" - -#: monophony/frontend/popovers/song_popover.py:29 -#: monophony/frontend/rows/external_group_row.py:44 -#: monophony/frontend/rows/importable_group_row.py:28 -#: monophony/frontend/rows/local_group_row.py:42 -msgid "Download" -msgstr "Скачать" - -#: monophony/frontend/popovers/song_popover.py:36 -#: monophony/frontend/windows/add_window.py:19 -msgid "Add to..." -msgstr "Добавить в..." - -#: monophony/frontend/popovers/song_popover.py:42 -msgid "View Artist" -msgstr "Просмотр Исполнителя" - -#: monophony/frontend/rows/external_group_row.py:24 -#: monophony/frontend/rows/importable_group_row.py:17 -#: monophony/frontend/rows/local_group_row.py:23 -#: monophony/frontend/rows/song_row.py:45 -msgid "More actions" -msgstr "Больше действий" - -#: monophony/frontend/rows/external_group_row.py:31 -#: monophony/frontend/windows/import_window.py:33 -msgid "Synchronized" -msgstr "Синхронизированный" - -#: monophony/frontend/rows/external_group_row.py:38 -#: monophony/frontend/rows/local_group_row.py:36 -msgid "Delete" -msgstr "Удалить" - -#: monophony/frontend/rows/external_group_row.py:50 -#: monophony/frontend/rows/local_group_row.py:54 -msgid "Rename..." -msgstr "Переименовать..." - -#: monophony/frontend/rows/external_group_row.py:67 -#: monophony/frontend/rows/local_group_row.py:71 -msgid "Rename" -msgstr "Переименовать" - -#: monophony/frontend/rows/external_group_row.py:96 -#: monophony/frontend/rows/local_group_row.py:97 -msgid "Could not Rename" -msgstr "Не Удалось Переименовать" - -#: monophony/frontend/rows/external_group_row.py:97 -#: monophony/frontend/rows/local_group_row.py:98 -msgid "Playlist already exists" -msgstr "Список воспроизведения уже существует" - -#: monophony/frontend/rows/importable_group_row.py:34 -msgid "Import..." -msgstr "Импорт..." - -#: monophony/frontend/rows/local_group_row.py:48 -msgid "Duplicate" -msgstr "Дублировать" - -#: monophony/frontend/rows/artist_row.py:14 -#: monophony/frontend/rows/artist_row.py:23 -msgid "View artist" -msgstr "Просмотр исполнителя" - -#: monophony/frontend/rows/group_row.py:23 -#: monophony/frontend/rows/song_row.py:18 -msgid "Play" -msgstr "Воспроизвести" - -#: monophony/frontend/rows/song_row.py:36 -msgid "Downloaded" -msgstr "Загруженный" - -#: monophony/frontend/widgets/recent_searches.py:36 -msgid "Remove" -msgstr "Удалить" - -#: monophony/frontend/widgets/player.py:70 -msgid "Volume" -msgstr "Громкость" - -#: monophony/frontend/widgets/player.py:91 -msgid "Toggle pause" -msgstr "Переключить паузу" - -#: monophony/frontend/widgets/player.py:103 -msgid "Next song" -msgstr "Следующая песня" - -#: monophony/frontend/widgets/player.py:108 -msgid "Previous song" -msgstr "Предыдущая песня" - -#: monophony/frontend/widgets/player.py:115 -msgid "Playback mode" -msgstr "Режим воспроизведения" - -#: monophony/frontend/widgets/player.py:200 -msgid "Normal Playback" -msgstr "Обычное Воспроизведение" - -#: monophony/frontend/widgets/player.py:207 -msgid "Radio Mode" -msgstr "Режим Радио" - -#: monophony/frontend/widgets/player.py:217 -msgid "Repeat Song" -msgstr "Повторять Песню" - -#: monophony/frontend/widgets/player.py:227 -msgid "Shuffle" -msgstr "Перемешать" - -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "Ok" - -#: monophony/frontend/windows/main_window.py:37 -msgid "Library" -msgstr "Библиотека" - -#: monophony/frontend/windows/main_window.py:43 -msgid "Search" -msgstr "Поиск" - -#: monophony/frontend/windows/main_window.py:49 -#: monophony/frontend/windows/add_window.py:42 -msgid "Queue" -msgstr "Очередь" - -#: monophony/frontend/windows/main_window.py:58 -msgid "About" -msgstr "О Программе" - -#: monophony/frontend/windows/main_window.py:145 -msgid "translator-credits" -msgstr "Alex Kryuchkov https://github.com/alexkdeveloper" - -#: monophony/frontend/windows/main_window.py:188 -#, python-brace-format -msgid "Deleted \"{playlist_name}\"" -msgstr "\"{playlist_name}\" удалено" - -#: monophony/frontend/windows/main_window.py:191 -msgid "Undo" -msgstr "Отменить" - -#: monophony/frontend/windows/main_window.py:219 -msgid "Added" -msgstr "Добавлен" - -#: monophony/frontend/windows/add_window.py:25 -#: monophony/frontend/windows/import_window.py:53 -msgid "Cancel" -msgstr "Отмена" - -#: monophony/frontend/windows/add_window.py:27 -msgid "Add" -msgstr "Добавить" - -#: monophony/frontend/windows/add_window.py:48 -#: monophony/frontend/tabs/library_tab.py:73 -msgid "Your Playlists" -msgstr "Ваши Плейлисты" - -#: monophony/frontend/windows/add_window.py:54 -msgid "New Playlist Name..." -msgstr "Новое Название Плейлиста..." - -#: monophony/frontend/windows/add_window.py:56 -msgid "Create" -msgstr "Создать" - -#: monophony/frontend/windows/import_window.py:21 -msgid "Enter Playlist Name..." -msgstr "Введите Название Плейлиста..." - -#: monophony/frontend/windows/import_window.py:28 -msgid "Enter Playlist URL..." -msgstr "Введите URL Плейлиста..." - -#: monophony/frontend/windows/import_window.py:34 -msgid "Editable" -msgstr "Редактируемый" - -#: monophony/frontend/windows/import_window.py:55 -#: monophony/frontend/tabs/library_tab.py:55 -msgid "Import" -msgstr "Импорт" - -#: monophony/frontend/windows/import_window.py:77 -msgid "Import Playlist..." -msgstr "Импорт списка воспроизведения..." - -#: monophony/frontend/windows/import_window.py:105 -#: monophony/frontend/windows/import_window.py:123 -#: monophony/frontend/windows/import_window.py:129 -msgid "Could not import playlist" -msgstr "Не удалось импортировать список воспроизведения" - -#: monophony/frontend/windows/import_window.py:106 -msgid "Failed to retrieve playlist data from server." -msgstr "Не удалось получить данные плейлиста с сервера." - -#: monophony/frontend/windows/import_window.py:123 -msgid "A name is required." -msgstr "Требуется указать имя." - -#: monophony/frontend/windows/import_window.py:129 -msgid "A URL is required." -msgstr "Требуется указать URL-адрес." - -#: monophony/frontend/pages/artist_page.py:20 -#: monophony/frontend/pages/results_page.py:21 -msgid "No Results" -msgstr "Нет Результатов" - -#: monophony/frontend/pages/artist_page.py:55 -msgid "Other" -msgstr "Прочее" - -#: monophony/frontend/pages/artist_page.py:56 -#: monophony/frontend/pages/results_page.py:66 -msgid "Albums" -msgstr "Альбомы" - -#: monophony/frontend/pages/artist_page.py:57 -msgid "Playlists" -msgstr "Плейлисты" - -#: monophony/frontend/pages/artist_page.py:80 -#: monophony/frontend/pages/results_page.py:65 -msgid "Songs" -msgstr "Песни" - -#: monophony/frontend/pages/artist_page.py:86 -#: monophony/frontend/pages/results_page.py:67 -msgid "Videos" -msgstr "Видео" - -#: monophony/frontend/pages/artist_page.py:92 -msgid "Artist Not Found" -msgstr "Исполнитель не Найден" - -#: monophony/frontend/pages/results_page.py:64 -msgid "Top Result" -msgstr "Лучший Результат" - -#: monophony/frontend/pages/results_page.py:68 -msgid "Community Playlists" -msgstr "Плейлисты Сообщества" - -#: monophony/frontend/pages/results_page.py:69 -msgid "Artists" -msgstr "Исполнители" - -#: monophony/frontend/pages/results_page.py:74 -#: monophony/frontend/pages/results_page.py:83 -#: monophony/frontend/pages/results_page.py:92 -#: monophony/frontend/pages/results_page.py:101 -#: monophony/frontend/pages/results_page.py:110 -msgid "Show All" -msgstr "Показать Всё" - -#: monophony/frontend/tabs/queue_tab.py:24 -msgid "Queue Empty" -msgstr "Очередь Пуста" - -#: monophony/frontend/tabs/library_tab.py:51 -msgid "Recommended" -msgstr "Рекомендуемый" - -#: monophony/frontend/tabs/library_tab.py:64 -msgid "Play all" -msgstr "Воспроизвести всё" - -#: monophony/frontend/tabs/library_tab.py:78 -msgid "Clear" -msgstr "Очистить" - -#: monophony/frontend/tabs/library_tab.py:85 -msgid "Recently Played" -msgstr "Последние Запущенные" - -#: monophony/frontend/tabs/search_tab.py:18 -msgid "Enter text or paste a URL..." -msgstr "Введите текст или вставьте URL-адрес..." - -#: monophony/frontend/tabs/search_tab.py:24 -msgid "Go back" -msgstr "Вернуться назад" - -#, fuzzy -#~| msgid "Searching..." -#~ msgid "Search..." -#~ msgstr "Поиск..." - -#~ msgid "All Songs" -#~ msgstr "Все Песни" - -#~ msgid "All Videos" -#~ msgstr "Все Видео" - -#~ msgid "Import playlist" -#~ msgstr "Импорт списка воспроизведения" - -#~ msgid "More" -#~ msgstr "Больше" - -#~ msgid "Your Library is Empty" -#~ msgstr "Ваша Библиотека Пуста" - -#~ msgid "Find songs to play using the search bar above" -#~ msgstr "Найдите песни для воспроизведения, используя строку поиска выше" - -#~ msgid "Search for Content..." -#~ msgstr "Поиск Контента..." - -#~ msgid "Show Artist" -#~ msgstr "Показать Исполнителя" - -#~ msgid "Show Queue" -#~ msgstr "Показать Очередь" - -#~ msgid "Add to library" -#~ msgstr "Добавить в библиотеку" - -#~ msgid "Loading..." -#~ msgstr "Загрузка..." - -#~ msgid "Loading Library..." -#~ msgstr "Загрузка Библиотеки..." - -#~ msgid "About Monophony" -#~ msgstr "О Monophony" - -#~ msgid "External Playlist" -#~ msgstr "Внешний Плейлист" - -#~ msgid "Local Playlist" -#~ msgstr "Локальный Плейлист" - -#~ msgid "External" -#~ msgstr "Внешний" - -#~ msgid "Parsing Results..." -#~ msgstr "Анализ Результатов..." - -#~ msgid "As Synchronized Playlist" -#~ msgstr "Как Синхронизированный Список Воспроизведения" - -#~ msgid "As Editable Playlist" -#~ msgstr "Как Редактируемый Список Воспроизведения" - -#~ msgid "Primary Menu" -#~ msgstr "Основное Меню" - -#~ msgid "Donate" -#~ msgstr "Пожертвовать" - -#~ msgid "New Playlist" -#~ msgstr "Новый Плейлист" - -#~ msgid "Saved" -#~ msgstr "Сохранено" - -#~ msgid "Enter Name..." -#~ msgstr "Введите Название..." - -#~ msgid "Add to playlist" -#~ msgstr "Добавить в плейлист" - -#~ msgid "Loop" -#~ msgstr "Зациклить" - -#, fuzzy -#~| msgid "Move up" -#~ msgid "Move Up" -#~ msgstr "Наверх" - -#, fuzzy -#~| msgid "Move down" -#~ msgid "Move Down" -#~ msgstr "Вниз" - -#~ msgid "Download to Music Folder" -#~ msgstr "Загрузить в Папку \"Музыка\"" - -#~ msgid "Add to Queue" -#~ msgstr "Добавить в Очередь" - -#~ msgid "Enter new Name..." -#~ msgstr "Введите Новое Название..." - -#~ msgid "No playlists found" -#~ msgstr "Плейлисты не найдены" - -#~ msgid "Patrons" -#~ msgstr "Покровители" diff --git a/source/locales/tr/LC_MESSAGES/all.mo b/source/locales/tr/LC_MESSAGES/all.mo deleted file mode 100644 index 656be8733919261089685f16337ab9c209418e80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2510 zcmca7#4?qEfq}t*fq_AWfq`KTBLjmoh|LTVWnj=?VPIfoU|`T>VPN24U|`T^VPN26 zU|_IeVPN2AU|{fu%15&>Ft9N&FeI`tFt9T)Fl0da1uP5>R)~9)SQ!{N zLGEIO*lP(DcVvaQ&krge$I8IK1#%ZF0|OHS14A*?oHAAh1`Y-WhH6#@25|-kh89+c z{d1t|SFtiMh%zuRY-MF&5M*FrI1M%TF4Ww&tdQ{e4psLT>JDKxh`2Nx#GQ(4knquC zV_@KBU|=wX@*UYA=6kR~!ZnVKfkB9YfgzWTfkA_TfuVs7;=h$pb2dZe53n&Xh%qoQ zTxNs#`xR8(C#bqVQ1dz285pz~7#IZD85pEM@y5=;0E*ghb_NDb1_p*`c8LF5*&*)e zV~4n72|ELWI|Bp5W_F1EzoF__IUwQ42c;!BAmOaS0nul|!N4HGz`)?l0SV6tsC)rb zeFFz1T)Lt3L=H%JPv?N7zok(1hoI^&aX`|=T@Fb4`NqM(pv=I)z|IM=N1GGk9tTcH zI`ig)gl{}lJev~|?q!@1f7WtB;<1er5)Lz<`sP5*+W=K}l#_vh5t0SK=}?Vet~kqX%oZ-0I@;&2$UBT7#J92K?0!s1xmxv zd>gAM}&gB}9|gFFKyeqiDt z8kEjKVF=2vpfsih5@djc3n+~!F+kD+NEnn>KaguyW-MWG~LA+R*HG!@3l z$xJFrEUJWY3vv=Gb25v;YEx4f9CMONbBh@qi%K$!ONtqs6Z4W&a~PcSb8}1cGD|8I z0-(x^8JzP=b5a!Y@=FwgQu7jXQyE-Rb5cuS6kMS_gG+v1B1k;HJTE6d5$qt}(&Efy z1-Ja1l++>y*SwO{A_c$1+*Cb1JqEYTycC7v{JivHkjp`?Qz$LY%u81&$w*ZwPE9OI z&QM57EK*2J$}dZ0aL-prN=(jX@X60FVDQZ^N(B=NiOD6I`FX_*zWHUT3NHEOc@S1; z0fS#^IoxR|9FX09sTCz)yBYlQ6@pTWOLIz!8T_*uK%NE@3XVBBUP>do9i6BTgBAf@| zgB%u|SeBZ?5Df8sT7Hp&bADb)YF-IgE~7LpEhm*B7!+mNN2*VYs5=&C^6mnC`l1ob%4o}NVPR-7Q`H&$fF{LtJAvZsz zl;Ow*Q21r$q~_(M7J*_aJ23|oPmm;(l9;Ce6TpnCO7zGDdkJI+Ji0;t@lLEvNlnSj zNy%h5vf=RZ)S}F6SO7z$i$D=ua%5u>!_j%LC}3~_C8{Dwb^;|ncuK51vQZ)Z@U|j` k;M8K|w4Ik(rI3_=bUtcOfKp*zW)(wTMOJ=sYC#4A0Ol-aqW}N^ diff --git a/source/locales/tr/LC_MESSAGES/all.po b/source/locales/tr/LC_MESSAGES/all.po deleted file mode 100644 index 525c45b..0000000 --- a/source/locales/tr/LC_MESSAGES/all.po +++ /dev/null @@ -1,215 +0,0 @@ -msgid "" -msgstr "Content-Type: text/plain; charset=UTF-8\n" - -#: monophony/frontend/pages/search_page.py:24 -#: monophony/frontend/pages/search_page.py:96 -#: monophony/frontend/pages/search_page.py:20 -#: monophony/frontend/pages/search_page.py:123 -msgid "No Results" -msgstr "Sonuç yok" - -#: monophony/frontend/pages/search_page.py:108 -#: monophony/frontend/pages/search_page.py:133 -msgid "Songs" -msgstr "Şarkılar" - -#: monophony/frontend/pages/search_page.py:109 -#: monophony/frontend/pages/search_page.py:118 -#: monophony/frontend/pages/search_page.py:127 -#: monophony/frontend/pages/search_page.py:136 -#: monophony/frontend/pages/search_page.py:139 -#: monophony/frontend/pages/search_page.py:147 -#: monophony/frontend/pages/search_page.py:156 -#: monophony/frontend/pages/search_page.py:164 -#: monophony/frontend/pages/search_page.py:172 -msgid "More" -msgstr "Daha fazla" - -#: monophony/frontend/pages/search_page.py:117 -#: monophony/frontend/pages/search_page.py:134 -msgid "Albums" -msgstr "Albümler" - -#: monophony/frontend/pages/search_page.py:126 -#: monophony/frontend/pages/search_page.py:155 -msgid "Community Playlists" -msgstr "Topluluğun oluşturduğu oynatma listeleri" - -#: monophony/frontend/pages/search_page.py:135 -msgid "Videos" -msgstr "Videolar" - -#: monophony/frontend/pages/library_page.py:25 -msgid "Your Library is Empty" -msgstr "Kütüphaneniz boş" - -#: monophony/frontend/pages/library_page.py:26 -msgid "Find songs to play using the search bar above" -msgstr "Yukarıdaki arama çubuğunu kullanarak oynatılacak şarkılar arayın" - -#: monophony/frontend/pages/library_page.py:29 -msgid "Play All" -msgstr "Hepsini oynat" - -#: monophony/frontend/pages/library_page.py:33 -msgid "Your Playlists" -msgstr "Oynatma listeleriniz" - -#: monophony/frontend/windows/delete_window.py:15 -msgid "Delete Playlist?" -msgstr "Oynatma listesini silmek istiyor musunuz?" - -#: monophony/frontend/windows/delete_window.py:16 -#: monophony/frontend/windows/rename_window.py:18 -msgid "Cancel" -msgstr "İptal" - -#: monophony/frontend/windows/delete_window.py:17 -#: monophony/frontend/widgets/group_row.py:60 -msgid "Delete" -msgstr "Sil" - -#: monophony/frontend/windows/main_window.py:41 -msgid "Go back" -msgstr "Geri dön" - -#: monophony/frontend/windows/main_window.py:46 -msgid "About" -msgstr "Hakkında" - -#: monophony/frontend/windows/main_window.py:51 -msgid "Search for Content..." -msgstr "İçerik arayın..." - -#: monophony/frontend/windows/main_window.py:141 -#: monophony/frontend/windows/main_window.py:138 -msgid "translator-credits" -msgstr "nxjoseph" - -#: monophony/frontend/windows/main_window.py:143 -#: monophony/frontend/windows/main_window.py:140 -msgid "Donate" -msgstr "Bağış" - -#: monophony/frontend/windows/main_window.py:174 -#: monophony/frontend/windows/main_window.py:171 -msgid "New Playlist" -msgstr "Yeni oynatma listesi" - -#: monophony/frontend/windows/main_window.py:191 -#: monophony/frontend/windows/main_window.py:188 -msgid "Could not Rename" -msgstr "Yeniden adlandırılamadı" - -#: monophony/frontend/windows/main_window.py:192 -#: monophony/frontend/windows/main_window.py:189 -msgid "Playlist already exists" -msgstr "Oynatma listesi zaten mevcut" - -#: monophony/frontend/windows/main_window.py:196 -#: monophony/frontend/windows/main_window.py:193 -msgid "Rename Playlist" -msgstr "Oynatma listesini yeniden adlandır" - -#: monophony/frontend/windows/main_window.py:201 -msgid "Saved" -msgstr "Kaydedildi" - -#: monophony/frontend/windows/rename_window.py:15 -msgid "Enter Name..." -msgstr "İsim girin..." - -#: monophony/frontend/windows/rename_window.py:19 -#: monophony/frontend/windows/message_window.py:13 -msgid "Ok" -msgstr "Tamam" - -#: monophony/frontend/widgets/player.py:33 -msgid "Toggle pause" -msgstr "Durdur" - -#: monophony/frontend/widgets/player.py:37 -msgid "Next song" -msgstr "Sonraki şarkı" - -#: monophony/frontend/widgets/player.py:42 -msgid "Previous song" -msgstr "Önceki şarkı" - -#: monophony/frontend/widgets/player.py:47 -msgid "Add to playlist" -msgstr "Oynatma listesine ekle" - -#: monophony/frontend/widgets/player.py:53 -msgid "Remove From Queue" -msgstr "Sıradan kaldır" - -#: monophony/frontend/widgets/player.py:59 -msgid "Volume" -msgstr "Ses" - -#: monophony/frontend/widgets/player.py:82 -msgid "Radio Mode" -msgstr "Radyo modu" - -#: monophony/frontend/widgets/player.py:92 -msgid "Loop" -msgstr "Tekrarla" - -#: monophony/frontend/widgets/player.py:99 -msgid "Shuffle" -msgstr "Karıştır" - -#: monophony/frontend/widgets/player.py:115 -#: monophony/frontend/widgets/song_row.py:41 -#: monophony/frontend/widgets/group_row.py:34 -msgid "More actions" -msgstr "Daha fazla" - -#: monophony/frontend/widgets/song_row.py:21 -msgid "Play" -msgstr "Oynat" - -#: monophony/frontend/widgets/song_popover.py:23 -msgid "Move Up" -msgstr "Yukarı taşı" - -#: monophony/frontend/widgets/song_popover.py:24 -msgid "Move Down" -msgstr "Aşağı taşı" - -#: monophony/frontend/widgets/song_popover.py:39 -msgid "Remove From Downloads" -msgstr "İndirilenlerden kaldır" - -#: monophony/frontend/widgets/song_popover.py:46 -msgid "Download to Music Folder" -msgstr "Müzik dizinine indir" - -#: monophony/frontend/widgets/song_popover.py:53 -msgid "Add to Queue" -msgstr "Sıraya ekle" - -#: monophony/frontend/widgets/song_popover.py:59 -msgid "New Playlist..." -msgstr "Yeni oynatma listesi..." - -#: monophony/frontend/widgets/group_row.py:45 -msgid "Add to library" -msgstr "Kütüphaneye ekle" - -#: monophony/frontend/widgets/group_row.py:66 -msgid "Rename..." -msgstr "Yeniden adlandır..." - -#: monophony/frontend/pages/search_page.py:136 -msgid "Artists" -msgstr "Sanatçılar" - -#: monophony/frontend/windows/main_window.py:198 -msgid "Added" -msgstr "Eklendi" - -#: monophony/frontend/widgets/artist_row.py:14 -msgid "View Artist" -msgstr "Sanatçıyı gör" diff --git a/source/monophony/__init__.py b/source/monophony/__init__.py index 70ae03b..8a0cd28 100644 --- a/source/monophony/__init__.py +++ b/source/monophony/__init__.py @@ -1,2 +1,6 @@ __version__ = '3.4.3' -APP_ID = 'io.gitlab.zehkira.Monophony' +ID = 'io.gitlab.zehkira.Monophony' # Full ID per Freedesktop standards +NAME = 'monophony' # Use for app executable, directories and so on +DISPLAY_NAME = 'Monophony' # For window titles and such - do not use in logic +GRESOURCES_PATH = '/io/gitlab/zehkira/Monophony' +MIN_WIDTH = 360 # Same as in metainfo.xml diff --git a/source/monophony/app.py b/source/monophony/app.py new file mode 100644 index 0000000..a926de0 --- /dev/null +++ b/source/monophony/app.py @@ -0,0 +1,47 @@ +from monophony import ID +from monophony.ui.windows.main_window import MainWindow + +from gi.repository import Adw, Gio + + +class Application(Adw.Application): + __gtype_name__ = __qualname__ + + def __init__(self): + super().__init__( + application_id=ID, + flags=Gio.ApplicationFlags.DEFAULT_FLAGS + ) + self._window = None + + def do_activate(self): + windows = self.get_windows() + + if len(windows) > 0: + windows[0].props.visible = True + else: + quit_action = Gio.SimpleAction.new('quit', None) + quit_action.connect('activate', self._on_quit) + self.add_action(quit_action) + self.set_accels_for_action('app.quit', ['q']) + + close_window_action = Gio.SimpleAction.new('close-window', None) + close_window_action.connect('activate', self._on_close_window) + self.add_action(close_window_action) + self.set_accels_for_action('app.close-window', ['w']) + + self._window = MainWindow(application=self) + self._window.present() + + self.set_accels_for_action('win.focus-search', ['f']) + + def _on_close_window(self, _action, _param): + windows = self.get_windows() + if windows: + windows[0].close() + + def _on_quit(self, _action, _param): + if self._window: + self._window.close() + + self.quit() diff --git a/source/monophony/asynchronous.py b/source/monophony/asynchronous.py new file mode 100644 index 0000000..0f26d8c --- /dev/null +++ b/source/monophony/asynchronous.py @@ -0,0 +1,62 @@ +import threading +from collections.abc import Callable +from typing import Any + +from monophony.debug import MemoryDebugger + +from gi.repository import GLib + + +class Task(MemoryDebugger): + def __init__( + self, + progress_callback: Callable | None=None, + callback: Callable | None=None, + callback_args: tuple | None=None, + callback_kwargs: dict | None=None, + args: tuple | None=None, + kwargs: dict | None=None + ): + super().__init__() + + self.__args = args or () + self.__kwargs = kwargs or {} + self.__callback = callback + self.__callback_args = callback_args or () + self.__callback_kwargs = callback_kwargs or {} + self.__progress_callback = progress_callback + self.extra_data = None + self.result = None + self._canceled = False + self._thread = threading.Thread( + target=self.__perform, args=self.__args, kwargs=self.__kwargs + ) + self._thread.daemon = True + + def __perform(self, *args, **kwargs): + self.result = self._function(*args, **kwargs) + if self.__callback: + GLib.idle_add( + self.__callback, self, *self.__callback_args, **self.__callback_kwargs + ) + + def _function(self, *args, **kwargs) -> Any: + ... + + def _update_progress(self, *args, **kwargs): + if self.__progress_callback: + GLib.idle_add(self.__progress_callback, self, *args, **kwargs) + + def is_canceled(self) -> bool: + return self._canceled + + def is_running(self) -> bool: + return self._thread.is_alive() and not self._canceled + + def cancel(self): + self._canceled = True + + def start(self): + if not self.is_running(): + self._thread.start() + diff --git a/source/monophony/backend/cache.py b/source/monophony/backend/cache.py deleted file mode 100644 index 3200c1e..0000000 --- a/source/monophony/backend/cache.py +++ /dev/null @@ -1,122 +0,0 @@ -import contextlib, glob, json, os, subprocess - - -### --- CACHE FUNCTIONS --- ### - - -def is_song_being_cached(video_id: str) -> bool: - has_temp = False - has_result = False - for file in os.listdir(get_cache_directory()): - parts = file.split('.') - if parts[0] == video_id: - if parts[-1] == 'monophony': - has_temp = True - elif parts[-1] == video_id: - has_result = True - - if not has_result: - return has_temp - - return False - - -def is_song_cached(video_id: str) -> bool: - if video_id is None: - return False - - return os.path.exists(get_cache_directory() + video_id) - - -def get_song_uri(video_id: str) -> str: - if video_id is None: - return '' - - local_path = get_cache_directory() + video_id - if os.path.exists(local_path): - return 'file://' + local_path - - return '' - - -def cache_songs(songs: list): - path = get_cache_directory() - needed_ids = [] - for song in songs: - if not is_song_cached(song['id']): - needed_ids.append(song['id']) - open(f'{path}{song["id"]}.monophony', 'w').close() - new_songs = read_songs() - new_songs.append(song) - write_songs(new_songs) - - - subprocess.Popen( - 'yt-dlp -x ' - '--no-cache-dir --audio-quality 0 --add-metadata ' - f'-o "{path}%(id)s.%(ext)s" https://music.youtube.com/watch?v=' + - (' https://music.youtube.com/watch?v='.join(needed_ids)), - shell = True, - stdout = subprocess.PIPE - ).communicate() - - for video_id in needed_ids: - with contextlib.suppress(OSError, FileNotFoundError): - os.remove(f'{path}{video_id}.monophony') - - # rename id.* files to id - for file in glob.glob(path + '*.*'): - os.rename(file, '.'.join(file.split('.')[:-1])) - - -def uncache_song(song: dict): - write_songs([s for s in read_songs() if s['id'] != song['id']]) - - with contextlib.suppress(OSError, FileNotFoundError): - os.remove(get_cache_directory() + song['id']) - - -def clean_up(): - path = get_cache_directory() - for file in os.listdir(path): - if file.endswith(('.part', '.monophony')): - os.remove(path + file) - - write_songs([s for s in read_songs() if is_song_cached(s['id'])]) - - -### --- UTILITY FUNCTIONS --- ### - - -def get_cache_directory() -> str: - path = os.getenv( - 'XDG_DATA_HOME', os.path.expanduser('~/.local/share') - ) + '/monophony/' - os.makedirs(path, exist_ok=True) - return path - - -def write_songs(songs: list): - dir_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony' - downloads_path = dir_path + '/downloads.json' - - try: - with open(str(downloads_path), 'w') as downloads_file: - json.dump(songs, downloads_file, indent='\t') - except FileNotFoundError: - os.makedirs(str(dir_path)) - write_songs(songs) - - -def read_songs() -> list: - songs_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony/downloads.json' - - try: - with open(songs_path) as songs_file: - return json.load(songs_file) - except (OSError, json.decoder.JSONDecodeError): - return [] diff --git a/source/monophony/backend/history.py b/source/monophony/backend/history.py deleted file mode 100644 index da0135b..0000000 --- a/source/monophony/backend/history.py +++ /dev/null @@ -1,95 +0,0 @@ -import json, os - - -### --- HISTORY FUNCTIONS --- ### - - -def add_search(query: str) -> bool: - new_searches = read_searches() - if query not in new_searches: - new_searches.insert(0, query) - if len(new_searches) > 3: - new_searches = new_searches[:-1] - - write_searches(new_searches) - return True - - return False - - -def remove_search(query: str): - new_searches = read_searches() - new_searches.remove(query) - write_searches(new_searches) - - -def add_song(song: dict): - new_songs = read_songs() - if song not in new_songs: - new_songs.append(song) - if len(new_songs) > 15: - new_songs = new_songs[1:] - else: - new_songs.remove(song) - new_songs.append(song) - - write_songs(new_songs) - - -def clear_songs(): - write_songs([]) - - -### --- UTILITY FUNCTIONS --- ### - - -def write_searches(searches: list): - dir_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony' - recents_path = dir_path + '/recent_searches.json' - - try: - with open(str(recents_path), 'w') as recents_file: - json.dump(searches, recents_file, indent='\t') - except FileNotFoundError: - os.makedirs(str(dir_path)) - write_songs(searches) - - -def read_searches() -> list: - recents_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony/recent_searches.json' - - try: - with open(recents_path) as recents_file: - return json.load(recents_file) - except (OSError, json.decoder.JSONDecodeError): - return [] - - -def write_songs(songs: list): - dir_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony' - recents_path = dir_path + '/recent_songs.json' - - try: - with open(str(recents_path), 'w') as recents_file: - json.dump(songs, recents_file, indent='\t') - except FileNotFoundError: - os.makedirs(str(dir_path)) - write_songs(songs) - - -def read_songs() -> list: - songs_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony/recent_songs.json' - - try: - with open(songs_path) as songs_file: - return json.load(songs_file) - except (OSError, json.decoder.JSONDecodeError): - return [] diff --git a/source/monophony/backend/mpris.py b/source/monophony/backend/mpris.py deleted file mode 100644 index cbb90dd..0000000 --- a/source/monophony/backend/mpris.py +++ /dev/null @@ -1,103 +0,0 @@ -from monophony import APP_ID -from monophony.backend.player import PlaybackMode - -from gi.repository import GLib -from mpris_server.adapters import PlayState, MprisAdapter -from mpris_server.server import Server -from mpris_server.events import PlayerEventAdapter - - -class Adapter(MprisAdapter): - def __init__(self, monophony_player: object): - super().__init__() - self.monophony_player = monophony_player - - def get_desktop_entry(self) -> str: - return APP_ID - - def can_quit(self) -> bool: - return False - - def get_current_position(self) -> float: - return self.monophony_player.get_position_ns() / 1000 - - def next(self): - GLib.Thread.new(None, self.monophony_player.next_song, True) - - def previous(self): - GLib.Thread.new(None, self.monophony_player.previous_song) - - def pause(self): - self.monophony_player.set_pause(True) - - def resume(self): - self.monophony_player.set_pause(False) - - def stop(self): - self.monophony_player.clear_queue() - - def get_playstate(self) -> PlayState: - if self.monophony_player.is_paused(): - return PlayState.PAUSED - return PlayState.PLAYING - - def is_repeating(self) -> bool: - return self.monophony_player.mode == PlaybackMode.LOOP_SONG - - def get_shuffle(self) -> bool: - return False - - def get_volume(self): - return self.monophony_player.get_volume() - - def set_volume(self, volume: float): - self.monophony_player.set_volume(volume, True) - - def is_mute(self) -> bool: - return False - - def can_go_next(self) -> bool: - return True - - def can_go_previous(self) -> bool: - return True - - def can_play(self) -> bool: - return bool(self.monophony_player.get_current_song()) - - def can_pause(self) -> bool: - return bool(self.monophony_player.get_current_song()) - - def can_seek(self) -> bool: - return False - - def can_control(self) -> bool: - return True - - def can_raise(self) -> bool: - return True - - def set_raise(self, val: bool): - if val: - self.monophony_player.raise_callback() - - def metadata(self) -> dict: - song = self.monophony_player.get_current_song() - if song: - duration_ns = self.monophony_player.get_duration_ns() - return { - 'mpris:trackid': '/track/1', - 'mpris:artUrl': song.get('thumbnail', ''), - 'mpris:length': duration_ns / 1000 if duration_ns > 0 else None, - 'xesam:title': song.get('title', ''), - 'xesam:artist': [song['author']] if 'author' in song else [] - } - - return {'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'} - - -def init(player: object): - mpris = Server('Monophony', adapter=Adapter(player)) - player.mpris_adapter = PlayerEventAdapter(root=mpris.root, player=mpris.player) - player.mpris_server = mpris - player.mpris_server.loop() diff --git a/source/monophony/backend/player.py b/source/monophony/backend/player.py deleted file mode 100644 index adeca0a..0000000 --- a/source/monophony/backend/player.py +++ /dev/null @@ -1,495 +0,0 @@ -import contextlib, random, time - -import monophony.backend.cache -import monophony.backend.history -import monophony.backend.settings -import monophony.backend.yt - -import gi -gi.require_version('Gst', '1.0') -gi.require_version('GstAudio', '1.0') -from gi.repository import GLib, Gst, GstAudio - - -class PlaybackMode: - NORMAL = 0 - LOOP_SONG = 1 - LOOP_QUEUE = 2 - RADIO = 3 - - -class Player: - def __init__(self): - Gst.init([]) - self.lock = GLib.Mutex() - self.interrupt = False - self.paused = False - self.buffering = False - self.index = 0 - self.queue = [] - self.recent_songs = [] - self.next_fetch_lock = GLib.Mutex() - self.next_stream_url = '' - self.next_expected_id = '' - self.next_fetch_time = 0 - self.last_progress = 0 - self.mode = int( - monophony.backend.settings.get_value('mode', PlaybackMode.NORMAL) - ) - self.mpris_adapter = None - self.mpris_server = None - self.ui_update_callback = None - self.queue_change_callback = None - self.queue_end_callback = None - self.raise_callback = None - self.playbin = Gst.ElementFactory.make('playbin3', 'playbin3') - self.playbin.set_state(Gst.State.READY) - self.playbin.get_bus().add_signal_watch() - self.playbin.get_bus().connect('message::error', self._on_bus_error) - self.playbin.get_bus().connect('message::stream-start', self._on_song_start) - self.playbin.get_bus().connect('message::buffering', self._on_buffering) - self.playbin.get_bus().connect( - 'message::eos', lambda *_: GLib.Thread.new(None, self._on_song_end) - ) - - ### --- UTILITY METHODS --- ### - - def terminate(self): - self.lock.lock() - self.playbin.set_state(Gst.State.NULL) - self.mpris_server.unpublish() - - def is_busy(self) -> bool: - if not self.lock.trylock(): - return True - - self.lock.unlock() - return self.buffering - - def is_paused(self) -> bool: - return self.paused - - def get_current_song(self, lock: bool=True) -> dict: - if lock and not self.lock.trylock(): - try: - return self.queue[self.index] - except IndexError: - return {} - - state = self.playbin.get_state(Gst.CLOCK_TIME_NONE)[1] - acceptable_states = {Gst.State.PAUSED, Gst.State.PLAYING} - result = {} - - if state in acceptable_states: - with contextlib.suppress(IndexError): - result = self.queue[self.index] - - if lock: - self.lock.unlock() - return result - - def get_duration_ns(self) -> float: - return self.playbin.query_duration(Gst.Format.TIME)[1] - - def get_position_ns(self) -> float: - return self.playbin.query_position(Gst.Format.TIME)[1] - - def get_progress(self) -> float: - duration = self.get_duration_ns() - position = self.get_position_ns() - return (position / duration) if duration > 0 else 0.0 - - def set_volume(self, volume: float, from_mpris: bool): - self.lock.lock() - monophony.backend.settings.set_value('volume', volume) - self.playbin.set_property('volume', self.playbin.convert_volume( - GstAudio.StreamVolumeFormat.CUBIC, - GstAudio.StreamVolumeFormat.LINEAR, - volume - )) - self.lock.unlock() - - if not from_mpris: - self.mpris_adapter.on_volume() - - def get_volume(self) -> float: - return self.playbin.convert_volume( - GstAudio.StreamVolumeFormat.LINEAR, - GstAudio.StreamVolumeFormat.CUBIC, - self.playbin.get_property('volume') - ) - - ### --- EVENT HANDLERS --- ### - - def _on_buffering(self, _bus, msg): - self.lock.lock() - percent = msg.parse_buffering() - if not self.buffering and percent < 100: - print('Buffering...') - self.buffering = True - self.playbin.set_state(Gst.State.PAUSED) - GLib.idle_add( - self.ui_update_callback, - self.get_current_song(), - True, - self.paused, - False - ) - elif percent >= 100: - print('Done buffering') - self.buffering = False - if not self.paused: - self.playbin.set_state(Gst.State.PLAYING) - GLib.idle_add( - self.ui_update_callback, - self.get_current_song(), - False, - self.paused, - False - ) - - self.lock.unlock() - - def _on_bus_error(self, _bus, err): - print('Playback error:', err.parse_error().gerror.message) - self.last_progress = self.playbin.query_position(Gst.Format.TIME)[1] - print('Failed at', self.last_progress) - GLib.Thread.new( - None, - self.play_song, - self.queue[self.index], - True, - True - ) - - def _on_song_start(self, _bus, _msg): - self.lock.lock() - if not self.buffering: - print('No buffering occured at start of stream') - self.playbin.set_state(Gst.State.PLAYING) - GLib.idle_add( - self.ui_update_callback, self.get_current_song(), False, False, True - ) - - if self.last_progress > 0: - print('Seeking to', self.last_progress) - self.playbin.seek_simple( - Gst.Format.TIME, - Gst.SeekFlags.FLUSH | Gst.SeekFlags.ACCURATE, - self.last_progress - ) - - self.last_progress = 0 - self.lock.unlock() - - def _on_song_end(self): - self.lock.lock() - if self.playbin.get_bus().have_pending(): - self.lock.unlock() - return - - print('Song has ended') - self.next_song(lock=False) - self.lock.unlock() - - ### --- MISC --- ### - - def fetch_next_song_url(self): - self.lock.lock() - i = None - if self.mode == PlaybackMode.LOOP_SONG: - i = self.index - elif self.index < len(self.queue) - 1: - i = self.index + 1 - elif self.mode == PlaybackMode.LOOP_QUEUE: - i = 0 - if i is None: - self.lock.unlock() - return - song_id = self.queue[i]['id'] - print(f'Fetching stream URL for predicted song {song_id}...') - self.lock.unlock() - - self.next_fetch_lock.lock() - url = monophony.backend.yt.get_song_uri(song_id) - if url: - self.next_expected_id = song_id - self.next_stream_url = url - self.next_fetch_time = time.time() - - print('Done fetching') - self.next_fetch_lock.unlock() - - ### --- PLAYBACK CONTROLS --- ### - - def play_song(self, song: dict, lock: bool=False, resume: bool=False): - if lock: - self.lock.lock() - - GLib.idle_add(self.ui_update_callback, song, True, False, True) - GLib.idle_add(self.queue_change_callback) - if not resume: - print('Playing', song['id']) - self.last_progress = 0 - else: - print('Resuming', song['id']) - - if song['id'] not in self.recent_songs: - self.recent_songs.append(song['id']) - monophony.backend.history.add_song(song) - self.playbin.set_state(Gst.State.READY) - - print('Attempting to get song from cache...') - uri = monophony.backend.cache.get_song_uri(song['id']) - - if not uri: - print('Attempting to use prepared stream URL...') - if self.next_fetch_lock.trylock(): - if self.next_stream_url: - if time.time() - self.next_fetch_time < 60 * 5: - if song['id'] == self.next_expected_id: - uri = self.next_stream_url - print('Using prepared stream URL for predicted song') - else: - print('Predicted song ID is incorrect!') - else: - print('Predicted song stream URL is too old!') - else: - print('No stream URL prepeared!') - - self.next_fetch_lock.unlock() - else: - print('Predicted song stream URL is not yet ready!') - - if not uri: - print('Fetching stream from YT...') - while True: - uri = monophony.backend.yt.get_song_uri(song['id']) - if uri is not None: - break - if self.interrupt: - if lock: - self.lock.unlock() - return - - print('Starting playback') - self.playbin.set_property('uri', uri) - self.paused = False - # buffering expected, so don't actually start yet (unless offline) - self.playbin.set_state(Gst.State.PAUSED) - self.mpris_server.unpublish() - self.mpris_server.publish() - self.mpris_adapter.emit_all() - self.mpris_adapter.on_playback() - GLib.idle_add(self.ui_update_callback, song, True, False, True) - - GLib.Thread.new(None, self.fetch_next_song_url) - - if lock: - self.lock.unlock() - - def play_radio_song(self): - id_queue = [s['id'] for s in self.queue] - random.shuffle(id_queue) - - song = None - for id_ in id_queue: - song = monophony.backend.yt.get_similar_song(id_, ignore=id_queue) - if song and song['id']: - break - - if song: - self.queue.append(song) - self.index += 1 - self.play_song(song) - - def set_pause(self, pause: bool): - if not self.lock.trylock(): - return - if self.buffering: - self.lock.unlock() - return - - if pause: - self.playbin.set_state(Gst.State.PAUSED) - self.paused = True - else: - self.playbin.set_state(Gst.State.PLAYING) - self.paused = False - - self.mpris_adapter.on_playpause() - GLib.idle_add( - self.ui_update_callback, - self.get_current_song(), - False, - self.paused, - False - ) - self.lock.unlock() - - def toggle_pause(self): - self.set_pause(not self.paused) - - def next_song(self, ignore_loop: bool=False, lock: bool=True): - if lock and not self.lock.trylock(): - return - - queue_length = len(self.queue) - song = None - if self.mode == PlaybackMode.LOOP_SONG and not ignore_loop: - song = self.queue[self.index] - elif queue_length - 1 > self.index: - self.index += 1 - song = self.queue[self.index] - elif self.mode == PlaybackMode.LOOP_QUEUE: - self.index = 0 - song = self.queue[self.index] - - if song: - self.play_song(song) - elif self.mode == PlaybackMode.RADIO: - self.play_radio_song() - else: - self.clear_queue(lock=False) - - if lock: - self.lock.unlock() - - def previous_song(self): - if not self.lock.trylock(): - return - self.playbin.set_state(Gst.State.READY) - - self.index = max(self.index - 1, 0) - if len(self.recent_songs) > 1: - recent = self.recent_songs[-2] - for i, queue_song in enumerate(self.queue): - if queue_song['id'] == recent: - self.index = i - self.recent_songs.pop(-1) - break - - if len(self.queue) > 0: - song = self.queue[self.index] - self.play_song(song) - - self.lock.unlock() - - def play_queue(self, queue: list, index: int): - if not self.lock.trylock(): - self.interrupt = True - self.lock.lock() - self.interrupt = False - - if len(queue) == 0: - self.lock.unlock() - return - - self.recent_songs = [] - self.queue = queue - self.index = index - song = queue[index] - self.play_song(song) - self.lock.unlock() - - def clear_queue(self, lock: bool=True): - if lock: - self.lock.lock() - - self.playbin.set_state(Gst.State.NULL) - self.playbin.set_property('uri', '') - self.queue = [] - self.index = 0 - self.mpris_server.unpublish() - GLib.idle_add(self.queue_change_callback) - GLib.idle_add(self.ui_update_callback, None, False, False, False) - GLib.idle_add(self.queue_end_callback) - - if lock: - self.lock.unlock() - - def shuffle_queue(self): - self.lock.lock() - song = self.get_current_song(lock=False) - random.shuffle(self.queue) - if song: - self.queue.remove(song) - self.queue.insert(0, song) - self.index = 0 - - GLib.Thread.new(None, self.fetch_next_song_url) - GLib.idle_add(self.queue_change_callback) - - self.lock.unlock() - - - def unqueue_song(self, song_id: str): - self.lock.lock() - if not self.queue: - self.lock.unlock() - return - - for i, song in enumerate(self.queue): - if song['id'] == song_id: - old_index = self.index - self.queue.pop(i) - if old_index < i: - break - self.index -= 1 - if old_index == i: - self.next_song(True, lock=False) - break - - GLib.Thread.new(None, self.fetch_next_song_url) - GLib.idle_add(self.queue_change_callback) - - self.lock.unlock() - - def move_song(self, from_i: int, to_i: int): - if not self.lock.trylock(): - return - - to_song = self.queue[to_i] - from_song = self.queue.pop(from_i) - self.queue.insert(self.queue.index(to_song), from_song) - if from_i == self.index: - if to_i > self.index: - self.index = to_i - 1 - else: - self.index = to_i - elif from_i < self.index and to_i > self.index: - self.index -= 1 - elif from_i > self.index and to_i <= self.index: - self.index += 1 - - GLib.Thread.new(None, self.fetch_next_song_url) - GLib.idle_add(self.queue_change_callback) - - self.lock.unlock() - - def queue_song(self, song: dict): - self.lock.lock() - - if not self.queue: - self.play_song(song) - - self.queue.append(song) - - GLib.Thread.new(None, self.fetch_next_song_url) - GLib.idle_add(self.queue_change_callback) - - self.lock.unlock() - - def seek(self, target: float): - if not self.lock.trylock(): - return - - duration = self.get_duration_ns() - if duration > 0 and self.get_current_song(lock=False): - self.playbin.seek_simple( - Gst.Format.TIME, - Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, - round(duration * target) - ) - - self.lock.unlock() diff --git a/source/monophony/backend/playlists.py b/source/monophony/backend/playlists.py deleted file mode 100644 index 9dfcb3a..0000000 --- a/source/monophony/backend/playlists.py +++ /dev/null @@ -1,280 +0,0 @@ -import json, os - -import monophony.backend.yt - -import ytmusicapi - - -# playlists = { -# 'my playlist': [ -# {'id': 'ASvGDFQwe', 'title': 'Cool song'} -# ] -# } - -# external_playlists = [ -# { -# 'title': 'my playlist', -# 'url': 'ASDFfghjklZXbcnmhjklzxbcn', -# 'contents': [ -# {'id': 'ASvGDFQwe', 'title': 'Cool song'} -# ] -# } -# ] - - -### --- PLAYLIST FUNCTIONS --- ### - - -def add_playlist(name: str, songs: list | None = None): - new_lists = read_playlists() - name = get_unique_name(name) - - songs = songs if songs else [] - new_lists[name] = [] - for song in songs: - if song not in new_lists[name]: - new_lists[name].append(song) - - write_playlists(playlists=new_lists) - - -def add_external_playlist(playlist: dict): - lists = read_external_playlists() - lists.append(playlist) - write_playlists(ext_playlists=lists) - - -def rename_playlist(name: str, new_name: str, local: bool=True) -> bool: - if name == new_name: - return True - if get_unique_name(new_name) != new_name: - return False - - if local: - new_lists = read_playlists() - new_lists[new_name] = new_lists.pop(name) - write_playlists(playlists=new_lists) - return True - - new_lists = read_external_playlists() - for i, playlist in enumerate(new_lists): - if playlist['title'] == name: - new_lists[i]['title'] = new_name - write_playlists(ext_playlists=new_lists) - return True - - return False - - -def import_playlist(name: str, url: str, local: bool, overwrite: bool=False) -> bool: - new_lists = read_playlists() - new_ext_lists = read_external_playlists() - songs = [] - playlist_id = url.split('list=')[-1].split('&')[0] - is_album = playlist_id.startswith('MPREb_') - - try: - yt = ytmusicapi.YTMusic() - if is_album: - album = yt.get_album(playlist_id) - data = album['tracks'] - else: - data = yt.get_playlist(playlist_id, limit=None)['tracks'] - except: - print('Could not get playlist') - return False - - for item in data: - if not item['videoId']: - continue - - parsed_song = { - 'title': item['title'], - 'author': item['artists'][0]['name'], - 'author_id': item['artists'][0]['id'], - 'length': item.get('duration', ''), - 'id': item['videoId'] - } - if is_album: - parsed_song['thumbnail'] = album['thumbnails'][0]['url'] - else: - parsed_song['thumbnail'] = item['thumbnails'][0]['url'] - - if parsed_song not in songs: - songs.append(parsed_song) - - name = get_unique_name(name) if not overwrite else name - if local: - new_lists[name] = songs - write_playlists(playlists=new_lists) - else: - new_ext_lists = [l for l in new_ext_lists if l['title'] != name] - new_ext_lists.append({'title': name, 'id': playlist_id, 'contents': songs}) - write_playlists(ext_playlists=new_ext_lists) - - return True - - -def remove_playlist(name: str): - new_lists = read_playlists() - new_lists.pop(name) - write_playlists(playlists=new_lists) - - -def remove_external_playlist(name: str): - write_playlists( - ext_playlists=[ - l for l in read_external_playlists() if l['title'] != name - ] - ) - - -def update_external_playlists(): - lists = read_external_playlists() - for playlist in lists: - import_playlist(playlist['title'], playlist['id'], False, True) - - clean_up_playlists() - - -def clean_up_playlists(): - lists = read_playlists() - ext_lists = read_external_playlists() - - new_lists = {} - for k, l in lists.items(): - new_lists[k] = [s for s in l if s['id']] - - new_ext_lists = [] - for l in ext_lists: - new_l = l.copy() - new_l['contents'] = [s for s in new_l['contents'] if s['id']] - new_ext_lists.append(new_l) - - write_playlists(playlists=new_lists, ext_playlists=new_ext_lists) - - -### --- SONG FUNCTIONS --- ### - - -def add_song(song: dict, playlist: str): - new_lists = read_playlists() - if song not in new_lists[playlist]: - new_lists[playlist].append(song) - write_playlists(playlists=new_lists) - - -def rename_song(index: int, playlist: str, new_name: str): - new_lists = read_playlists() - new_lists[playlist][index]['title'] = new_name - write_playlists(playlists=new_lists) - - -def swap_songs(p_name: str, i: int, j: int): - lists = read_playlists() - i = 0 if i >= len(lists[p_name]) else i - j = 0 if j >= len(lists[p_name]) else j - lists[p_name][i], lists[p_name][j] = lists[p_name][j], lists[p_name][i] - write_playlists(playlists=lists) - - -def move_song(p_name: str, from_i: int, to_i: int): - lists = read_playlists() - - to_song = lists[p_name][to_i] - from_song = lists[p_name].pop(from_i) - lists[p_name].insert(lists[p_name].index(to_song), from_song) - - write_playlists(playlists=lists) - - -def remove_song(id_: str, playlist: str): - new_lists = read_playlists() - new_lists[playlist] = [s for s in new_lists[playlist] if s['id'] != id_] - write_playlists(playlists=new_lists) - - -### --- UTILITY FUNCTIONS --- ### - - -def get_unique_name(base: str) -> str: - taken_names = ( - list(read_playlists().keys()) + - [p['title'] for p in read_external_playlists()] - ) - name = base - - if name in taken_names: - i = 1 - while f'{name} ({i})' in taken_names: - i += 1 - - name = f'{name} ({i})' - - return name - - -def write_playlists(playlists: dict | None = None, ext_playlists: list | None = None): - dir_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony' - lists_path = dir_path + '/playlists.json' - ext_lists_path = dir_path + '/external-playlists.json' - - try: - if playlists is not None: - with open(str(lists_path), 'w') as lists_file: - json.dump(playlists, lists_file, indent='\t') - if ext_playlists is not None: - with open(str(ext_lists_path), 'w') as ext_lists_file: - json.dump(ext_playlists, ext_lists_file, indent='\t') - except FileNotFoundError: - os.makedirs(str(dir_path)) - write_playlists(playlists=playlists, ext_playlists=ext_playlists) - - -def read_playlists() -> dict: - lists_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony/playlists.json' - - lists = {} - try: - with open(lists_path) as lists_file: - lists = json.load(lists_file) - except (OSError, json.decoder.JSONDecodeError): - return {} - - # backwards compatibility - updated = False - for name, playlist in lists.items(): - for i, song in enumerate(playlist): - if 'author_id' not in song: - print(f'Updating song {song["id"]}...') - song_details = monophony.backend.yt.get_song(song['id']) - if song_details: - lists[name][i]['author_id'] = song_details['author_id'] - print('Updated song', song['id']) - else: - print('Failed to update song', song['id']) - updated = True - - if updated: - write_playlists(playlists=lists) - - return lists - - -def read_external_playlists() -> list: - lists_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony/external-playlists.json' - - lists = [] - try: - with open(lists_path) as lists_file: - lists = json.load(lists_file) - except (OSError, json.decoder.JSONDecodeError): - return [] - - return lists diff --git a/source/monophony/backend/settings.py b/source/monophony/backend/settings.py deleted file mode 100644 index 3d01de3..0000000 --- a/source/monophony/backend/settings.py +++ /dev/null @@ -1,37 +0,0 @@ -import json, os - - -def set_value(key: str, value): - settings = read_settings() - settings[key] = str(value) - write_settings(settings) - - -def get_value(key: str, default='') -> str: - return read_settings().get(key, default) - - -def write_settings(settings: dict): - dir_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony' - sets_path = dir_path + '/settings.json' - - try: - with open(str(sets_path), 'w') as sets_file: - json.dump(settings, sets_file, indent='\t') - except FileNotFoundError: - os.makedirs(dir_path) - write_settings(settings) - - -def read_settings() -> dict: - sets_path = os.getenv( - 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') - ) + '/monophony/settings.json' - - try: - with open(sets_path) as sets_file: - return json.load(sets_file) - except (OSError, json.decoder.JSONDecodeError): - return {} diff --git a/source/monophony/backend/utils.py b/source/monophony/backend/utils.py deleted file mode 100644 index c2939fd..0000000 --- a/source/monophony/backend/utils.py +++ /dev/null @@ -1,32 +0,0 @@ -import contextlib, datetime - - -def time_str_to_sec(string: str) -> int: - if not string: - return 0 - - seconds = 0 - parts = string.split(':') - if len(parts) > 0: - with contextlib.suppress(ValueError): - seconds += int(parts[-1]) - if len(parts) > 1: - with contextlib.suppress(ValueError): - seconds += int(parts[-2]) * 60 - if len(parts) > 2: - with contextlib.suppress(ValueError): - seconds += int(parts[-3]) * 60 * 60 - - return seconds - - -def sec_to_time_str(seconds: int) -> str: - return str(datetime.timedelta(seconds=seconds)) - - -def sanitize_str(string: str) -> str: - bad_unicode = ['\u3011'] - for symbol in bad_unicode: - string = string.replace(symbol, '') - - return string diff --git a/source/monophony/backend/yt.py b/source/monophony/backend/yt.py deleted file mode 100644 index 927c263..0000000 --- a/source/monophony/backend/yt.py +++ /dev/null @@ -1,346 +0,0 @@ -import random, subprocess, traceback - -import ytmusicapi - - -def _connect() -> ytmusicapi.YTMusic | None: - try: - return ytmusicapi.YTMusic() - except: - print('Failed to connect to YTM:\033[0;33m') - traceback.print_exc() - print('\033[0m') - - return None - - -def _get_artist_names(artists: list) -> list: - return [artist['name'] for artist in artists if artist['id']] - - -def _get_artist_id(artists: list) -> str: - a_id = '' - for i in range(len(artists)): - a_id = artists[i]['id'] - if a_id: - break - - return a_id - - -def _parse_single_result(yt: ytmusicapi.YTMusic, result: dict) -> dict | None: - if result['resultType'] == 'single': - result['resultType'] = 'album' - - item = {'type': result['resultType'], 'top': False} - if 'category' in result: - item['top'] = (result['category'] == 'Top result') - if result['category'] in ['Profiles', 'Episodes']: - return None - - if result['resultType'] == 'artist': - try: - if result['category'] == 'Top result': - item['author'] = ', '.join( - _get_artist_names(result['artists']) - ) - item['id'] = _get_artist_id(result['artists']) - else: - item['author'] = result['artist'] - item['id'] = result['browseId'] - except: - print('Failed to parse artist result:\033[0;33m') - traceback.print_exc() - print('\033[0m') - return None - elif result['resultType'] == 'album': - try: - item['author'] = result['artists'][0]['name'] - item['id'] = result['browseId'] - item['title'] = result['title'] - - album = ( - yt.get_playlist(result['playlistId']) if - result['playlistId'] else - yt.get_album(result['browseId']) - ) - item['contents'] = [ - { - 'id': str(s['videoId']), - 'title': s['title'], - 'type': 'song', - 'author': ', '.join(_get_artist_names(s['artists'])), - 'author_id': _get_artist_id(s['artists']), - 'length': s['duration'], - 'thumbnail': result['thumbnails'][0]['url'] - } for s in album['tracks'] if s['videoId'] - ] - except: - print('Failed to parse album result:\033[0;33m') - traceback.print_exc() - print('\033[0m') - return None - elif result['resultType'] == 'playlist': - try: - album = yt.get_playlist(result['browseId'], limit=None) - if 'author' in result: - item['author'] = result['author'] - else: - item['author'] = ', '.join( - _get_artist_names(result['artists']) - ) - item['id'] = result['browseId'] - item['title'] = result['title'] - item['contents'] = [ - { - 'id': str(s['videoId']), - 'title': s['title'], - 'type': 'song', - 'author': ', '.join(_get_artist_names(s['artists'])), - 'author_id': _get_artist_id(s['artists']), - 'length': s['duration'], - 'thumbnail': s['thumbnails'][0]['url'] - } for s in album['tracks'] if s['videoId'] - ] - except: - print('Failed to parse playlist result:\033[0;33m') - traceback.print_exc() - print('\033[0m') - return None - elif result['resultType'] in {'song', 'video'}: - try: - if not result['videoId']: - return None - item['id'] = str(result['videoId']) - item['title'] = result['title'] - item['author'] = ', '.join( - _get_artist_names(result['artists']) - ) - item['author_id'] = _get_artist_id(result['artists']) - if 'duration' in result: - item['length'] = result['duration'] - item['thumbnail'] = result['thumbnails'][0]['url'] - - # ytm sometimes returns videos as song results when filtered - if 'category' in result and result['category'] == 'Songs': - item['type'] = 'song' - except: - print('Failed to parse song/video result:\033[0;33m') - traceback.print_exc() - print('\033[0m') - return None - - return item - - -def _parse_results(data: list) -> list: - yt = _connect() - if yt is None: - return [] - - exp_types = {'album', 'song', 'video', 'playlist', 'artist', 'single'} - results = [ - _parse_single_result(yt, item) - for item in data if item.get('resultType', '') in exp_types - ] - - return [r for r in results if r] - - -def get_song_uri(video_id: str) -> str | None: - out, err = subprocess.Popen( - f'yt-dlp -g -x --no-warnings https://music.youtube.com/watch?v={video_id}', - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ).communicate() - if err: - print(err.decode()) - return None - - return out.decode().split('\n')[0] - - -def get_similar_song(video_id: str, ignore: list | None = None) -> dict: - yt = _connect() - if yt is None: - return {} - - ignore = ignore if ignore else [] - try: - data = yt.get_watch_playlist(video_id, radio=True)['tracks'] - except: - return {} - - acceptable_tracks = [] - for item in data: - track = { - 'title': item['title'], - 'author': ', '.join(_get_artist_names(item['artists'])), - 'author_id': _get_artist_id(item['artists']), - 'length': item['length'], - 'id': item['videoId'], - 'thumbnail': item['thumbnail'][0]['url'] - } - if not track['id']: - continue - - for id_ in ignore: - if id_ == track['id']: - break - else: # nobreak - acceptable_tracks.append(track) - - if acceptable_tracks: - return random.choice(acceptable_tracks) - return {} - - -def get_recommendations() -> dict: - yt = _connect() - if yt is None: - return {} - - try: - data = yt.get_home() - except: - return {} - - categories = {} - for group in data: - songs = [ - { - 'title': item['title'], - 'author': ', '.join(_get_artist_names(item['artists'])), - 'author_id': _get_artist_id(item['artists']), - 'id': item['videoId'], - } for item in group['contents'] if item.get('videoId') - ] - - if songs: - categories[group['title']] = songs - - return categories - - -def get_song(id_: str) -> dict | None: - yt = _connect() - if yt is None: - return None - - try: - result = yt.get_song(id_)['videoDetails'] - except: - return None - - seconds = int(result['lengthSeconds']) - minutes = seconds // 60 - seconds %= 60 - return { - 'top': True, - 'type': 'video', - 'id': result['videoId'], - 'title': result['title'], - 'author': result['author'], - 'author_id': result['channelId'], - 'length': f'{minutes}:{seconds:02}', - 'thumbnail': result['thumbnail']['thumbnails'][0] - } - - -def get_artist(browse_id: str) -> list: - yt = _connect() - if yt is None: - return [] - - try: - metadata = yt.get_artist(browse_id) - artist = {'name': metadata['name']} - for group in ['albums', 'singles']: - artist[group] = {} - artist[group]['results'] = [] - if group not in metadata: - continue - - if 'params' in metadata[group]: - artist[group]['results'] = yt.get_artist_albums( - metadata[group]['browseId'], metadata[group]['params'] - ) - else: - artist[group]['results'] = metadata[group]['results'] - - for group in ['songs', 'videos', 'playlists']: - if group in metadata: - artist[group] = metadata[group] - else: - print('Artist has no', group) - except: - try: - artist = yt.get_user(browse_id) - except: - print('Could not get artist:\033[0;31m') - traceback.print_exc() - print('\033[0m') - return [] - - data = [] - for group in ['songs', 'albums', 'singles', 'videos', 'playlists']: - content = [] - if group in artist: - if group in {'songs', 'videos'}: - try: - yt.get_playlist( - artist[group]['browseId'], limit=None - )['tracks'] - except: - content = artist[group].get('results', []) - else: - content = [] - for alb in artist[group]['results']: - if not ('browseId' in alb or 'playlistId' in alb): - print(f'Failed to get artist {group}:\033[0;33m') - print('browseId/playlistId missing') - print('\033[0m') - continue - - content.append({ - 'title': alb['title'], - 'browseId': ( - alb['browseId' if 'browseId' in alb else 'playlistId'] - ), - 'playlistId': alb.get( - 'audioPlaylistId', alb.get('playlistId', None) - ), - 'artists': [{'name': artist['name'], 'id': browse_id}], - 'thumbnails': alb['thumbnails'] - }) - - for item in content: - item['resultType'] = group[:-1] - - data.extend(content) - - return _parse_results(data) - - -def search(query: str, filter_: str='') -> list: - yt = _connect() - if yt is None: - return [] - - try: - if '?v=' in query and '/' in query: - song = get_song(query.split('?v=')[-1].split('&')[0]) - return [song] if song else [] - if 'youtu.be/' in query: - song = get_song(query.split('youtu.be/')[-1].split('?')[0]) - return [song] if song else [] - - data = ( - yt.search(query, filter=filter_, limit=100) if filter_ - else yt.search(query) - ) - except: - return [] - - return _parse_results(data) diff --git a/source/monophony/data.py b/source/monophony/data.py new file mode 100644 index 0000000..6f6f92b --- /dev/null +++ b/source/monophony/data.py @@ -0,0 +1,146 @@ +import contextlib +import datetime +from typing import Any + +from monophony import logging + + +class YTItem: + def __eq__(self, other: Any): + if isinstance(other, YTItem): + return self.yt_id == other.yt_id + + return False + + def __hash__(self): + return hash(self.yt_id) + + def __init__(self, yt_id: str=''): + self.yt_id = yt_id or '' + + def serialize(self) -> dict: + return {'id': self.yt_id} + + +class Artist(YTItem): + def __init__(self, name: str='', yt_id: str=''): + super().__init__(yt_id) + self.name = name or '' + + def serialize(self) -> dict: + return {'name': self.name, 'id': self.yt_id} + + +class Song(YTItem): + def __init__( + self, + title: str='', + author: Artist | None=None, + length: str='', + thumbnail: str='', + yt_id: str='' + ): + super().__init__(yt_id) + self.title = title or '' + self.author = author if author else Artist() + self.length = length or '' + self.thumbnail = thumbnail or '' + + def serialize(self) -> dict: + return { + 'title': self.title, + 'author': self.author.name, + 'author_id': self.author.yt_id, + 'length': self.length, + 'thumbnail': self.thumbnail, + 'id': self.yt_id + } + + +class Group(YTItem): + def __init__( + self, + title: str='', + author: Artist | None=None, + songs: list[Song] | None=None, + yt_id: str='' + ): + super().__init__(yt_id) + + self._songs = [] + self.title = title or '' + self.author = author if author else Artist() + self.songs = songs if songs else [] + + @property + def songs(self) -> list: + return self._songs + + @songs.setter + def songs(self, song_list: list[Song]): + ids = [] + unique_songs = [] + for song in song_list: + if not song.yt_id or song.yt_id in ids: + continue + + unique_songs.append(song) + ids.append(song.yt_id) + + self._songs = unique_songs + + def serialize(self) -> dict: + return { + 'title': self.title, + 'author': self.author.name, + 'author_id': self.author.yt_id, + 'contents': [s.serialize() for s in self.songs], + 'id': self.yt_id + } + + +class TimeString: + SECONDS_POS = 0 + MINUTES_POS = 1 + HOURS_POS = 2 + + def __init__(self, string: str='', seconds: int=0): + if string: + self._string = string + else: + self._string = str(datetime.timedelta(seconds=abs(seconds))) + + def as_seconds(self) -> int: + seconds = 0 + parts = self._string.split(':') + if len(parts) > TimeString.SECONDS_POS: + with contextlib.suppress(ValueError): + seconds += int(parts[-1]) + if len(parts) > TimeString.MINUTES_POS: + with contextlib.suppress(ValueError): + seconds += int(parts[-2]) * 60 + if len(parts) > TimeString.HOURS_POS: + with contextlib.suppress(ValueError): + seconds += int(parts[-3]) * 60 * 60 + if len(parts) > TimeString.HOURS_POS + 1: + logging.warning( + __name__, f'TimeString "{self._string}" has too many parts' + ) + + return abs(seconds) + + def as_string(self) -> str: + return self._string + + +class PlaybackMode: + NORMAL = 0 + LOOP_SONG = 1 + LOOP_QUEUE = 2 + RADIO = 3 + + +class PlaybackState: + NONE = 0 + PLAYING = 1 + LOADING = 2 diff --git a/source/monophony/debug.py b/source/monophony/debug.py new file mode 100644 index 0000000..9926d7c --- /dev/null +++ b/source/monophony/debug.py @@ -0,0 +1,48 @@ +import gc +import os + +from monophony import logging + +from gi.repository import GLib, GObject + + +_DEBUG_VARIABLE = 'DEBUG' + + +def is_active() -> bool: + return bool(os.getenv(_DEBUG_VARIABLE)) + + +def log_memory_status() -> bool: + gobject_count = 0 + other_count = 0 + gc.collect() + for obj in gc.get_objects(): + if isinstance(obj, GObject.Object): + gobject_count += 1 + else: + other_count += 1 + + logging.info( + __name__, + f'{gobject_count} GObjects and {other_count} other objects in memory' + ) + return True + + +# Use with multiple inheriance: class Class(MemoryDebugger, ...) +class MemoryDebugger: + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if is_active(): + logging.info(__name__, f'Initialized {self.__class__.__name__}') + + def __del__(self): + if is_active(): + logging.info(__name__, f'Collected {self.__class__.__name__}') + + +if is_active(): + logging.warning(__name__, 'Debug mode enabled, expect low performance') + GLib.timeout_add_seconds(2, log_memory_status) diff --git a/source/monophony/downloads.py b/source/monophony/downloads.py new file mode 100644 index 0000000..5b97cdb --- /dev/null +++ b/source/monophony/downloads.py @@ -0,0 +1,292 @@ +import glob +import json +import os +import subprocess +import traceback + +from monophony import NAME, logging +from monophony.asynchronous import Task +from monophony.data import Artist, Group, Song + +from gi.repository import GLib + + +def get_directory() -> str: + return os.getenv( + 'XDG_DATA_HOME', os.path.expanduser('~/.local/share') + ) + f'/{NAME}/' + + +def get_temp_directory() -> str: + return os.getenv('XDG_RUNTIME_DIR', '/var/tmp') + f'/{NAME}/downloads/' + + +def get_file(song: Song) -> str | None: + files = [ + file for file in glob.glob(get_directory() + '*' + song.yt_id + '*') + if not file.endswith('.' + NAME) + ] + + if files: + if len(files) > 1: + logging.warning( + __name__, + f'Multiple song files match id "{song.yt_id}"', '\n'.join(files) + ) + return files[0] + + return None + + +def is_being_downloaded(song: Song) -> bool: + return os.path.exists(get_directory() + song.yt_id + '.' + NAME) + + +def is_downloaded(song: Song) -> bool: + return ( + song.yt_id and get_file(song) and not is_being_downloaded(song) + ) + + +class DownloadTask(Task): + def _function(self, downloader: '_Downloader', group: Group) -> bool: + downloader.lock.lock() + logging.info(__name__, f'Downloading {len(group.songs)} songs...') + + path = get_directory() + needed_ids = [] + new_group = Group() + for song in group.songs: + if not song.yt_id: + logging.error( + __name__, f'Failed to download song "{song.title}" - no id' + ) + continue + + if is_downloaded(song) or is_being_downloaded(song): + logging.info( + __name__, + f'Skipped download of song "{song.yt_id}" - already taken care of' + ) + continue + + downloader.create_lock_file(song.yt_id) + needed_ids.append(song.yt_id) + new_group.songs.append(song) + + # *.NAME files act as locks for this part + self._update_progress() + downloader.lock.unlock() + + if not needed_ids: + logging.info( + __name__, 'Canceled download as there are no songs to download' + ) + return True + + song_urls = [ + f'https://music.youtube.com/watch?v={yt_id}' for yt_id in needed_ids + ] + ytdlp = subprocess.Popen( + [ + 'yt-dlp', + '--extract-audio', + '--no-cache-dir', + '--audio-quality', + '0', + '--add-metadata', + '--paths', + 'home:' + path, + '--paths', + 'temp:' + get_temp_directory(), + '--restrict-filenames', + '--output', + '%(title)s_-_%(creators)s_%(id)s.%(ext)s', + *song_urls + ], + text=True, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE + ) + return_code = ytdlp.wait() + + downloader.lock.lock() + for yt_id in needed_ids: + downloader.delete_lock_file(yt_id) + + if return_code != 0: + logging.error(__name__, 'Failed to download songs', ytdlp.stdout.read()) + downloader.lock.unlock() + return False + + new_group.songs = [song for song in new_group.songs if is_downloaded(song)] + logging.info( + __name__, f'Downloaded {len(new_group.songs)}/{len(group.songs)} songs' + ) + logging.info(__name__, 'Saving data about newly downloaded songs...') + downloader.write(Group(songs=new_group.songs + downloader.read().songs)) + logging.info(__name__, 'Saved newly downloaded song data') + + downloader.lock.unlock() + return True + + +class _Downloader: + def __init__(self): + self.lock = GLib.Mutex() + + self.lock.lock() + logging.info(__name__, 'Cleaning up downloads...') + downloads_group = self.read() + path = get_directory() + os.makedirs(path, exist_ok=True) + for file in os.listdir(path): + if file.endswith(('.part', '.' + NAME)): + os.remove(path + file) + logging.info(__name__, f'Removed abandoned temp file "{file}"') + continue + + for song in downloads_group.songs: + if path + file == get_file(song): + break + else: + os.remove(path + file) + logging.warning(__name__, f'Removed unexpected file "{file}"') + + self.write( + Group(songs=[song for song in self.read().songs if is_downloaded(song)]) + ) + logging.info(__name__, 'Cleaned up downloads') + self.lock.unlock() + + def create_lock_file(self, name: str): + logging.info(__name__, f'Creating lock file "{name}.{NAME}"...') + + # Always lock self.lock before calling this to prevent race conditions + if self.lock.trylock(): + logging.warning( + __name__, 'Creating lock file while self.lock is unlocked' + ) + self.lock.unlock() + + try: + open(f'{get_directory()}{name}.{NAME}', 'w').close() + except OSError: + logging.error( + __name__, 'Failed to create lock file', traceback.format_exc() + ) + return + + logging.info(__name__, 'Created lock file') + + def delete_lock_file(self, name: str): + logging.info(__name__, f'Deleting lock file "{name}.{NAME}"...') + + # Always lock self.lock before calling this to prevent race conditions + if self.lock.trylock(): + logging.warning( + __name__, 'Deleting lock file while self.lock is unlocked' + ) + self.lock.unlock() + + try: + os.remove(f'{get_directory()}{name}.{NAME}') + except (OSError, FileNotFoundError): + logging.error( + __name__, + f'Failed to remove lock file "{name}.{NAME}"', + traceback.format_exc() + ) + + logging.info(__name__, 'Deleted lock file') + + def read(self) -> Group: + # Always lock self.lock before calling this to prevent race conditions + if self.lock.trylock(): + logging.warning(__name__, 'Reading downloads while self.lock is unlocked') + self.lock.unlock() + + songs_path = os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + f'/{NAME}/downloads.json' + + try: + with open(songs_path) as songs_file: + return Group( + songs=[ + Song( + title=item.get('title', ''), + author=Artist( + name=item.get('author', ''), + yt_id=item.get('author_id', '') + ), + length=item.get('length', ''), + thumbnail=item.get('thumbnail', ''), + yt_id=item.get('id', '') + ) for item in json.load(songs_file) + ] + ) + except (OSError, json.decoder.JSONDecodeError): + return Group() + + def write(self, group: Group): + logging.info(__name__, f'Writing {len(group.songs)} songs to downloads...') + + # Always lock self.lock before calling this to prevent race conditions + if self.lock.trylock(): + logging.warning(__name__, 'Writing downloads while self.lock is unlocked') + self.lock.unlock() + + dir_path = os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + '/' + NAME + downloads_path = dir_path + '/downloads.json' + + os.makedirs(dir_path, exist_ok=True) + with open(downloads_path, 'w') as downloads_file: + json.dump(group.serialize()['contents'], downloads_file, indent='\t') + + logging.info(__name__, 'Done writing to downloads') + + def get_downloads(self) -> Group: + self.lock.lock() + downloads = self.read() + self.lock.unlock() + return downloads + + def remove(self, song: Song): + self.lock.lock() + logging.info(__name__, f'Removing song "{song.yt_id}" from downloads...') + + # No race conditions here as long as lock files are only created and + # deleted while holding the lock + if not is_downloaded(song): + logging.error( + __name__, 'Failed remove song from downloads - not downloaded' + ) + self.lock.unlock() + return + if is_being_downloaded(song): + logging.error( + __name__, 'Failed remove song from downloads - download in progress' + ) + self.lock.unlock() + return + + self.write( + Group(songs=[s for s in self.read().songs if s.yt_id != song.yt_id]) + ) + + file = get_file(song) + if not file: + logging.error( + __name__, 'Failed remove song from downloads - file not found' + ) + os.remove(file) + + logging.info(__name__, 'Removed song from downloads') + self.lock.unlock() + + +# Singleton for thread safety +downloader = _Downloader() diff --git a/source/monophony/frontend/app.py b/source/monophony/frontend/app.py deleted file mode 100644 index 5f97a4a..0000000 --- a/source/monophony/frontend/app.py +++ /dev/null @@ -1,24 +0,0 @@ -from monophony import APP_ID -from monophony.frontend.windows.main_window import MonophonyMainWindow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gio - - -class MonophonyApplication(Adw.Application): - def __init__(self): - super().__init__( - application_id=APP_ID, - flags=Gio.ApplicationFlags.DEFAULT_FLAGS - ) - - def do_activate(self): - windows = self.get_windows() - - if len(windows) > 0: - windows[0].set_visible(True) - else: - self.window = MonophonyMainWindow(application=self) - self.window.present() diff --git a/source/monophony/frontend/pages/artist_page.py b/source/monophony/frontend/pages/artist_page.py deleted file mode 100644 index 59839d6..0000000 --- a/source/monophony/frontend/pages/artist_page.py +++ /dev/null @@ -1,99 +0,0 @@ -import monophony.backend.yt -from monophony.frontend.rows.importable_group_row import MonophonyImportableGroupRow -from monophony.frontend.rows.locked_group_row import MonophonyLockedGroupRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib, Gtk - - -class MonophonyArtistPage(Gtk.Box): - def __init__(self, player: object, artist: str): - super().__init__(orientation=Gtk.Orientation.VERTICAL) - - self.pge_status = Adw.StatusPage() - self.pge_status.set_vexpand(True) - self.pge_status.set_valign(Gtk.Align.FILL) - self.pge_status.set_icon_name('system-search-symbolic') - self.pge_status.set_title(_('No Results')) - self.pge_status.set_visible(False) - self.append(self.pge_status) - - self.pge_results = Adw.PreferencesPage.new() - self.pge_results.set_vexpand(True) - self.pge_results.set_valign(Gtk.Align.FILL) - self.pge_results.set_visible(False) - self.append(self.pge_results) - - spn_big = Adw.Spinner() - spn_big.set_hexpand(True) - spn_big.set_vexpand(True) - - self.box_loading = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.box_loading.set_margin_bottom(10) - self.box_loading.append(spn_big) - self.box_loading.set_visible(True) - self.append(self.box_loading) - - self.set_vexpand(True) - self.artist = artist - self.results = [] - self.player = player - - GLib.Thread.new(None, self.do_get_artist) - - def do_get_artist(self): - self.results = monophony.backend.yt.get_artist(self.artist) - GLib.idle_add(self.present_results) - - def present_results(self) -> bool: - self.box_loading.set_visible(False) - self.pge_status.set_visible(len(self.results) == 0) - if self.results: - self.pge_results.set_visible(True) - box_other = Adw.PreferencesGroup.new() - box_albums = Adw.PreferencesGroup.new() - box_playlists = Adw.PreferencesGroup.new() - box_other.set_title(_('Other')) - box_albums.set_title(_('Albums')) - box_playlists.set_title(_('Playlists')) - - songs = [] - videos = [] - non_empty = [] - for item in self.results: - if item['type'] == 'song': - songs.append(item) - elif item['type'] == 'video': - videos.append(item) - elif item['type'] == 'album': - box_albums.add(MonophonyImportableGroupRow(item, self.player)) - if box_albums not in non_empty: - non_empty.append(box_albums) - elif item['type'] == 'playlist': - box_playlists.add(MonophonyImportableGroupRow(item, self.player)) - if box_playlists not in non_empty: - non_empty.append(box_playlists) - if songs or videos: - non_empty.append(box_other) - if songs: - box_other.add( - MonophonyLockedGroupRow( - {'title': _('Songs'), 'contents': songs}, self.player - ) - ) - if videos: - box_other.add( - MonophonyLockedGroupRow( - {'title': _('Videos'), 'contents': videos}, self.player - ) - ) - for box in non_empty: - self.pge_results.add(box) - else: - self.pge_status.set_title(_('Artist Not Found')) - self.box_loading.set_visible(False) - self.pge_status.set_visible(True) - - return False diff --git a/source/monophony/frontend/pages/results_page.py b/source/monophony/frontend/pages/results_page.py deleted file mode 100644 index b6a0d37..0000000 --- a/source/monophony/frontend/pages/results_page.py +++ /dev/null @@ -1,144 +0,0 @@ -import weakref - -import monophony.backend.yt -from monophony.frontend.rows.importable_group_row import MonophonyImportableGroupRow -from monophony.frontend.rows.song_row import MonophonySongRow -from monophony.frontend.rows.artist_row import MonophonyArtistRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib, Gtk - - -class MonophonyResultsPage(Gtk.Box): - def __init__(self, player: object, query: str='', filter_: str=''): - super().__init__(orientation=Gtk.Orientation.VERTICAL) - - self.pge_status = Adw.StatusPage() - self.pge_status.set_vexpand(True) - self.pge_status.set_valign(Gtk.Align.FILL) - self.pge_status.set_icon_name('system-search-symbolic') - self.pge_status.set_title(_('No Results')) - self.pge_status.set_visible(False) - self.append(self.pge_status) - - self.pge_results = Adw.PreferencesPage.new() - self.pge_results.set_vexpand(True) - self.pge_results.set_valign(Gtk.Align.FILL) - self.pge_results.set_visible(False) - self.append(self.pge_results) - - spn_big = Adw.Spinner() - spn_big.set_hexpand(True) - spn_big.set_vexpand(True) - - self.box_loading = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.box_loading.set_margin_bottom(10) - self.box_loading.append(spn_big) - self.box_loading.set_visible(bool(query)) - self.append(self.box_loading) - - self.set_vexpand(True) - self.query = query - self.filter = filter_ - self.player = player - - if query: - GLib.Thread.new('search', self.do_search) - else: - self.pge_status.set_visible(True) - self.pge_status.set_title('') - - def do_search(self): - results = monophony.backend.yt.search(self.query, self.filter) - GLib.idle_add(self.show_results, results) - - def show_results(self, results: list) -> bool: - def create_result_box(query: str, result_type: str, filtered: bool): - box = Adw.PreferencesGroup.new() - if not filtered: - img_icon = Gtk.Image.new_from_icon_name('go-next-symbolic') - lbl_text = Gtk.Label.new(_('Show All')) - box_btn = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_btn.set_spacing(8) - box_btn.append(lbl_text) - box_btn.append(img_icon) - btn_more = Gtk.Button() - btn_more.add_css_class('suggested-action') - btn_more.set_child(box_btn) - btn_more.connect( - 'clicked', - lambda _b, f: ref_window()._on_show_more(query, f), - result_type - ) - box.set_header_suffix(btn_more) - return box - - self.box_loading.set_visible(False) - self.pge_status.set_visible(len(results) == 0) - if results: - self.pge_results.set_visible(True) - box_top = Adw.PreferencesGroup.new() - filtered = self.filter != '' - box_songs = create_result_box(self.query, 'songs', filtered) - box_videos = create_result_box(self.query, 'videos', filtered) - box_albums = create_result_box(self.query, 'albums', filtered) - box_playlists = create_result_box(self.query, 'playlists', filtered) - box_artists = create_result_box(self.query, 'artists', filtered) - box_top.set_title(_('Top Result')) - box_songs.set_title(_('Songs')) - box_albums.set_title(_('Albums')) - box_videos.set_title(_('Videos')) - box_playlists.set_title(_('Community Playlists')) - box_artists.set_title(_('Artists')) - ref_window = weakref.ref(self.get_ancestor(Gtk.Window)) - - non_empty = [] - for item in results: - if item['type'] == 'song': - if item['top']: - box_top.add(MonophonySongRow(item, self.player)) - non_empty.append(box_top) - continue - box_songs.add(MonophonySongRow(item, self.player)) - if box_songs not in non_empty: - non_empty.append(box_songs) - elif item['type'] == 'video': - if item['top']: - box_top.add(MonophonySongRow(item, self.player)) - non_empty.append(box_top) - continue - box_videos.add(MonophonySongRow(item, self.player)) - if box_videos not in non_empty: - non_empty.append(box_videos) - elif item['type'] == 'album': - if item['top']: - box_top.add(MonophonyImportableGroupRow(item, self.player)) - non_empty.append(box_top) - continue - box_albums.add(MonophonyImportableGroupRow(item, self.player)) - if box_albums not in non_empty: - non_empty.append(box_albums) - elif item['type'] == 'playlist': - if item['top']: - box_top.add(MonophonyImportableGroupRow(item, self.player)) - non_empty.append(box_top) - continue - box_playlists.add(MonophonyImportableGroupRow(item, self.player)) - if box_playlists not in non_empty: - non_empty.append(box_playlists) - elif item['type'] == 'artist': - if item['top']: - box_top.add(MonophonyArtistRow(item)) - non_empty.append(box_top) - continue - box_artists.add(MonophonyArtistRow(item)) - if box_artists not in non_empty: - non_empty.append(box_artists) - for box in non_empty: - self.pge_results.add(box) - - non_empty.clear() - results.clear() - return False diff --git a/source/monophony/frontend/popovers/importable_group_popover.py b/source/monophony/frontend/popovers/importable_group_popover.py deleted file mode 100644 index b385f30..0000000 --- a/source/monophony/frontend/popovers/importable_group_popover.py +++ /dev/null @@ -1,25 +0,0 @@ -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import Gio, Gtk - - -class MonophonyImportableGroupPopover(Gtk.PopoverMenu): - def __init__(self, btn: Gtk.MenuButton, group: dict): - super().__init__() - - window = btn.get_ancestor(Gtk.Window) - mnu_actions = Gio.Menu() - mnu_actions.append(_('Download'), 'cache-playlist') - window.install_action( - 'cache-playlist', - None, - lambda w, *_: w._on_cache_playlist(group['contents']) - ) - mnu_actions.append(_('Import...'), 'import-playlist') - window.install_action( - 'import-playlist', - None, - lambda w, *_: w._on_import_clicked(group=group) - ) - self.set_menu_model(mnu_actions) - btn.set_popover(self) diff --git a/source/monophony/frontend/popovers/local_song_popover.py b/source/monophony/frontend/popovers/local_song_popover.py deleted file mode 100644 index cce663a..0000000 --- a/source/monophony/frontend/popovers/local_song_popover.py +++ /dev/null @@ -1,18 +0,0 @@ -from monophony.frontend.popovers.song_popover import MonophonySongPopover - -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import Gtk - - -class MonophonyLocalSongPopover(MonophonySongPopover): - def __init__(self, btn: Gtk.MenuButton, song: dict, group: list): - super().__init__(btn, song) - - btn.get_ancestor(Gtk.Window).install_action( - 'remove-song', - None, - lambda w, *_: w._on_remove_song(song['id'], group['title']) - ) - self.get_menu_model().append(_('Remove From Playlist'), 'remove-song') - diff --git a/source/monophony/frontend/popovers/queue_song_popover.py b/source/monophony/frontend/popovers/queue_song_popover.py deleted file mode 100644 index 5d21aed..0000000 --- a/source/monophony/frontend/popovers/queue_song_popover.py +++ /dev/null @@ -1,21 +0,0 @@ -from monophony.frontend.popovers.song_popover import MonophonySongPopover - -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import Gtk - - -class MonophonyQueueSongPopover(MonophonySongPopover): - def __init__(self, btn: Gtk.MenuButton, song: dict, player: object): - super().__init__(btn, song) - - self.player = player - self.install_action( - 'unqueue-song', - None, - lambda p, *_: p._on_unqueue_song() - ) - self.get_menu_model().append(_('Remove From Queue'), 'unqueue-song') - - def _on_unqueue_song(self): - self.player.unqueue_song(self.song['id']) diff --git a/source/monophony/frontend/popovers/song_popover.py b/source/monophony/frontend/popovers/song_popover.py deleted file mode 100644 index 5820581..0000000 --- a/source/monophony/frontend/popovers/song_popover.py +++ /dev/null @@ -1,53 +0,0 @@ -import monophony.backend.cache -import monophony.backend.playlists - -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import Gio, GLib, Gtk - - -class MonophonySongPopover(Gtk.PopoverMenu): - def __init__(self, btn: Gtk.MenuButton, song: dict): - super().__init__() - - self.song = song - menu = Gio.Menu() - btn.set_popover(self) - window = self.get_ancestor(Gtk.Window) - - if monophony.backend.cache.is_song_being_cached(song['id']): - pass - elif monophony.backend.cache.is_song_cached(song['id']): - menu.append(_('Remove From Downloads'), 'uncache-song') - window.install_action( - 'uncache-song', None, lambda *_: self._on_uncache(song) - ) - else: - menu.append(_('Download'), 'cache-song') - window.install_action( - 'cache-song', None, lambda *_: self._on_cache(song) - ) - - menu.append(_('Add to...'), 'add-song-to') - window.install_action( - 'add-song-to', None, lambda w, *_: w._on_add_clicked(song) - ) - menu.append(_('View Artist'), 'view-artist') - window.install_action( - 'view-artist', None, lambda w, *_: w._on_show_artist(song['author_id']) - ) - self.set_menu_model(menu) - - def _on_cache(self, song): - window = self.get_ancestor(Gtk.Window) - row = self.get_ancestor(Gtk.ListBoxRow) - window._on_cache_song(song) - row.spinner.set_visible(True) - GLib.timeout_add_seconds(1, row.update_download_status) - - def _on_uncache(self, song): - window = self.get_ancestor(Gtk.Window) - row = self.get_ancestor(Gtk.ListBoxRow) - window._on_uncache_song(song) - row.spinner.set_visible(False) - row.checkmark.set_visible(False) diff --git a/source/monophony/frontend/rows/artist_row.py b/source/monophony/frontend/rows/artist_row.py deleted file mode 100644 index 3bb356c..0000000 --- a/source/monophony/frontend/rows/artist_row.py +++ /dev/null @@ -1,33 +0,0 @@ -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib, Gtk - - -class MonophonyArtistRow(Adw.ActionRow): - def __init__(self, artist: dict): - super().__init__() - - self.artist = artist - artist_id = self.artist['id'] - - btn_view = Gtk.Button.new_from_icon_name('go-next-symbolic') - btn_view.set_tooltip_text(_('View artist')) - btn_view.set_vexpand(False) - btn_view.set_valign(Gtk.Align.CENTER) - btn_view.set_has_frame(False) - btn_view.connect( - 'clicked', - lambda b: b.get_ancestor(Gtk.Window)._on_show_artist(artist_id), - ) - self.add_suffix(btn_view) - self.set_tooltip_text(_('View artist')) - self.set_property('activatable', True) - self.connect( - 'activated', - lambda b: b.get_ancestor(Gtk.Window)._on_show_artist(artist_id), - ) - - self.set_title(GLib.markup_escape_text( - artist.get('author', '') or '', -1 - )) diff --git a/source/monophony/frontend/rows/external_group_row.py b/source/monophony/frontend/rows/external_group_row.py deleted file mode 100644 index 85406c7..0000000 --- a/source/monophony/frontend/rows/external_group_row.py +++ /dev/null @@ -1,110 +0,0 @@ -import monophony.backend.playlists -from monophony.backend.utils import sanitize_str - -from monophony.frontend.rows.group_row import MonophonyGroupRow -from monophony.frontend.rows.song_row import MonophonySongRow -from monophony.frontend.windows.message_window import MonophonyMessageWindow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gtk, Gio - - -class MonophonyExternalGroupRow(MonophonyGroupRow): - def __init__(self, group: dict, player: object): - super().__init__(group, player) - - btn_more = Gtk.MenuButton() - btn_more.set_tooltip_text(_('More actions')) - btn_more.set_icon_name('view-more-symbolic') - btn_more.set_has_frame(False) - btn_more.set_vexpand(False) - btn_more.set_valign(Gtk.Align.CENTER) - btn_more.set_create_popup_func(self._on_show_actions) - self.add_action(btn_more) - self.set_enable_expansion(False) - - playlists = monophony.backend.playlists.read_external_playlists() - for playlist in playlists: - if playlist['title'] == self.group['title']: - self.group['contents'] = playlist['contents'] - break - for song in self.group['contents']: - self.add_row(MonophonySongRow(song, self.player, self.group)) - self.set_enable_expansion(True) - - self.set_expanded(False) - self.update() - - def _on_show_actions(self, btn: Gtk.MenuButton): - window = self.get_ancestor(Gtk.Window) - mnu_actions = Gio.Menu() - mnu_actions.append(_('Delete'), 'delete-playlist') - window.install_action( - 'delete-playlist', None, lambda *_: self._on_delete() - ) - mnu_actions.append(_('Download'), 'cache-playlist') - window.install_action( - 'cache-playlist', - None, - lambda w, *_: w._on_cache_playlist(self.group['contents']) - ) - mnu_actions.append(_('Rename...'), 'rename-playlist') - window.install_action( - 'rename-playlist', - None, - lambda *_: self._on_open_rename_menu(btn) - ) - pop_menu = Gtk.PopoverMenu() - pop_menu.set_menu_model(mnu_actions) - btn.set_popover(pop_menu) - - def _on_open_rename_menu(self, btn: Gtk.MenuButton): - pop_rename = Gtk.Popover.new() - ent_name = Gtk.Entry.new() - ent_name.set_text(self.group['title']) - ent_name.connect( - 'activate', lambda e: self._on_rename(e.get_text(), pop_rename) - ) - btn_rename = Gtk.Button.new_with_label(_('Rename')) - btn_rename.add_css_class('suggested-action') - btn_rename.connect( - 'clicked', lambda _b: self._on_rename(ent_name.get_text(), pop_rename) - ) - box_rename = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_rename.set_spacing(5) - box_rename.set_margin_top(5) - box_rename.set_margin_bottom(5) - box_rename.set_margin_start(5) - box_rename.set_margin_end(5) - box_rename.append(ent_name) - box_rename.append(btn_rename) - pop_rename.set_child(box_rename) - pop_rename.set_parent(btn) - btn.popdown() - pop_rename.popup() - - def _on_delete(self): - self.get_ancestor(Gtk.Window)._on_delete_playlist(self, local=False) - self.get_ancestor(Adw.PreferencesGroup).remove(self) - - def _on_rename(self, name: str, pop: Gtk.Popover): - pop.popdown() - success = monophony.backend.playlists.rename_playlist( - self.group['title'], name, False - ) - if success: - self.group['title'] = name - self.set_title(sanitize_str(name)) - else: - MonophonyMessageWindow( - self.get_ancestor(Gtk.Window), - _('Could not Rename'), - _('Playlist already exists') - ).present() - - def update(self): - super().update() - self.set_subtitle(self.get_subtitle() + ' ' + _('(Synchronized)')) - diff --git a/source/monophony/frontend/rows/group_row.py b/source/monophony/frontend/rows/group_row.py deleted file mode 100644 index 3378bc8..0000000 --- a/source/monophony/frontend/rows/group_row.py +++ /dev/null @@ -1,32 +0,0 @@ -from monophony.backend.utils import sanitize_str, sec_to_time_str, time_str_to_sec - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib - - -class MonophonyGroupRow(Adw.ExpanderRow): - def __init__(self, group: dict, player: object): - super().__init__() - - self.player = player - self.group = group - - self.set_title(GLib.markup_escape_text( - sanitize_str(group.get('title', '') or ''), -1 - )) - self.set_expanded(False) - - def update(self): - total_seconds = 0 - for song in self.group.get('contents', []): - total_seconds += time_str_to_sec(song.get('length', '0')) - - self.set_subtitle( - sec_to_time_str(total_seconds) + - ' ' + - GLib.markup_escape_text( - sanitize_str(self.group.get('author', '') or ''), -1 - ) - ) diff --git a/source/monophony/frontend/rows/importable_group_row.py b/source/monophony/frontend/rows/importable_group_row.py deleted file mode 100644 index d16d1bb..0000000 --- a/source/monophony/frontend/rows/importable_group_row.py +++ /dev/null @@ -1,27 +0,0 @@ -from monophony.frontend.popovers.importable_group_popover import \ - MonophonyImportableGroupPopover -from monophony.frontend.rows.group_row import MonophonyGroupRow -from monophony.frontend.rows.song_row import MonophonySongRow - -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import Gtk - - -class MonophonyImportableGroupRow(MonophonyGroupRow): - def __init__(self, group: dict, player: object): - super().__init__(group, player) - - for item in group['contents']: - self.add_row(MonophonySongRow(item, player, group)) - - btn_more = Gtk.MenuButton() - btn_more.set_tooltip_text(_('More actions')) - btn_more.set_icon_name('view-more-symbolic') - btn_more.set_has_frame(False) - btn_more.set_vexpand(False) - btn_more.set_valign(Gtk.Align.CENTER) - group = self.group.copy() - btn_more.set_create_popup_func(MonophonyImportableGroupPopover, group) - self.add_action(btn_more) - super().update() diff --git a/source/monophony/frontend/rows/local_group_row.py b/source/monophony/frontend/rows/local_group_row.py deleted file mode 100644 index 3d84d91..0000000 --- a/source/monophony/frontend/rows/local_group_row.py +++ /dev/null @@ -1,126 +0,0 @@ -import monophony.backend.playlists -from monophony.frontend.windows.message_window import MonophonyMessageWindow -from monophony.frontend.rows.group_row import MonophonyGroupRow -from monophony.frontend.rows.local_song_row import MonophonyLocalSongRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gio, Gtk - - -class MonophonyLocalGroupRow(MonophonyGroupRow): - def __init__(self, group: dict, player: object): - super().__init__(group, player) - - self.song_widgets = [] - for item in group['contents']: - row = MonophonyLocalSongRow(item, player, group) - self.add_row(row) - self.song_widgets.append(row) - - btn_more = Gtk.MenuButton() - btn_more.set_tooltip_text(_('More actions')) - btn_more.set_icon_name('view-more-symbolic') - btn_more.set_has_frame(False) - btn_more.set_vexpand(False) - btn_more.set_valign(Gtk.Align.CENTER) - btn_more.set_create_popup_func(self._on_show_actions) - self.add_action(btn_more) - super().update() - - def _on_show_actions(self, btn: Gtk.MenuButton): - window = self.get_ancestor(Gtk.Window) - mnu_actions = Gio.Menu() - mnu_actions.append(_('Delete'), 'delete-playlist') - window.install_action('delete-playlist', None, lambda *_: self._on_delete()) - mnu_actions.append(_('Download'), 'cache-playlist') - window.install_action( - 'cache-playlist', - None, - lambda w, *_: w._on_cache_playlist(self.group['contents']) - ) - mnu_actions.append(_('Duplicate'), 'duplicate-playlist') - window.install_action( - 'duplicate-playlist', - None, - lambda w, *_: w._on_duplicate_playlist(self) - ) - mnu_actions.append(_('Rename...'), 'rename-playlist') - window.install_action( - 'rename-playlist', - None, - lambda *_: self._on_open_rename_menu(btn) - ) - pop_menu = Gtk.PopoverMenu() - pop_menu.set_menu_model(mnu_actions) - btn.set_popover(pop_menu) - - def _on_open_rename_menu(self, btn: Gtk.MenuButton): - pop_rename = Gtk.Popover.new() - ent_name = Gtk.Entry.new() - ent_name.set_text(self.group['title']) - ent_name.connect( - 'activate', lambda e: self._on_rename(e.get_text(), pop_rename) - ) - btn_rename = Gtk.Button.new_with_label(_('Rename')) - btn_rename.add_css_class('suggested-action') - btn_rename.connect( - 'clicked', lambda _b: self._on_rename(ent_name.get_text(), pop_rename) - ) - box_rename = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_rename.set_spacing(5) - box_rename.set_margin_top(5) - box_rename.set_margin_bottom(5) - box_rename.set_margin_start(5) - box_rename.set_margin_end(5) - box_rename.append(ent_name) - box_rename.append(btn_rename) - pop_rename.set_child(box_rename) - pop_rename.set_parent(btn) - btn.popdown() - pop_rename.popup() - - def _on_delete(self): - self.get_ancestor(Gtk.Window)._on_delete_playlist(self, local=True) - self.get_ancestor(Adw.PreferencesGroup).remove(self) - - def _on_rename(self, name: str, pop: Gtk.Popover): - pop.popdown() - success = monophony.backend.playlists.rename_playlist( - self.group['title'], name - ) - if not success: - MonophonyMessageWindow( - self.get_ancestor(Gtk.Window), - _('Could not Rename'), - _('Playlist already exists') - ).present() - return - - self.get_ancestor(Adw.PreferencesGroup).remove(self) - - def update(self) -> bool: - super().update() - - self.set_enable_expansion(self.song_widgets != []) - playlists = monophony.backend.playlists.read_playlists() - if self.group['title'] not in playlists: - self.get_ancestor(Adw.PreferencesGroup).remove(self) - return False - - if self.group['contents'] == playlists[self.group['title']]: - return True - - for widget in self.song_widgets: - self.remove(widget) - - self.song_widgets = [] - self.group['contents'] = playlists[self.group['title']] - for song in self.group['contents']: - row = MonophonyLocalSongRow(song, self.player, self.group) - self.add_row(row) - self.song_widgets.append(row) - self.set_enable_expansion(True) - - return True diff --git a/source/monophony/frontend/rows/local_song_row.py b/source/monophony/frontend/rows/local_song_row.py deleted file mode 100644 index efec8a7..0000000 --- a/source/monophony/frontend/rows/local_song_row.py +++ /dev/null @@ -1,79 +0,0 @@ -import monophony.backend.playlists -from monophony.frontend.popovers.local_song_popover import MonophonyLocalSongPopover -from monophony.frontend.rows.song_row import MonophonySongRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gdk, Gtk - - -class MonophonyLocalSongRow(MonophonySongRow): - def __init__(self, song: dict, player: object, group: dict | None = None): - super().__init__(song, player, group) - - img_handle = Gtk.Image.new_from_icon_name('list-drag-handle-symbolic') - img_handle.add_css_class('drag-handle') - css = Gtk.CssProvider.new() - css.load_from_data(''' - .drag-handle { - opacity: 0.5; - } - - .dnd-item { - background-color: #00000022; - } - ''', -1) - Gtk.StyleContext.add_provider_for_display( - Gdk.Display.get_default(), - css, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - ) - self.add_prefix(img_handle) - self.drg_handle = Gtk.DragSource.new() - self.drg_handle.set_actions(Gdk.DragAction.MOVE) - self.drg_handle.connect('prepare', self._on_dnd_prepare) - self.drg_handle.connect('drag-end', self._on_dnd_cancel_or_end) - self.drg_handle.connect('drag-cancel', self._on_dnd_cancel_or_end) - img_handle.add_controller(self.drg_handle) - self.drp_target = Gtk.DropTarget.new(self.__gtype__, Gdk.DragAction.MOVE) - self.drp_target.connect('drop', self._on_dnd_drop) - self.drp_target.connect('enter', self._on_dnd_enter) - self.add_controller(self.drp_target) - - self.btn_more.set_create_popup_func( - MonophonyLocalSongPopover, self.song, self.group - ) - - def _on_dnd_drop(self, _t, song_row: Adw.ActionRow, _x: float, _y: float) -> bool: - monophony.backend.playlists.move_song( - self.group['title'], - self.group['contents'].index(song_row.song), - self.group['contents'].index(self.song), - ) - self.get_ancestor(Gtk.Window).library_tab.update_playlists() - return True - - def _on_dnd_prepare(self, *_) -> Gdk.ContentProvider: - self.add_css_class('dnd-item') - self.drg_handle.set_icon(Gtk.WidgetPaintable.new(self), 0, 0) - return Gdk.ContentProvider.new_for_value(self) - - def _on_dnd_enter(self, *_) -> int: - if self.drg_handle.get_drag(): - self.drp_target.reject() - return 0 - - # reject if no drag was started in the local group - child_index = 0 - while True: - child = self.get_parent().get_row_at_index(child_index) - child_index += 1 - if not child: - self.drp_target.reject() - return 0 - if child.drg_handle.get_drag(): - return Gdk.DragAction.MOVE - - def _on_dnd_cancel_or_end(self, *_): - self.remove_css_class('dnd-item') diff --git a/source/monophony/frontend/rows/locked_group_row.py b/source/monophony/frontend/rows/locked_group_row.py deleted file mode 100644 index b254761..0000000 --- a/source/monophony/frontend/rows/locked_group_row.py +++ /dev/null @@ -1,16 +0,0 @@ -from monophony.frontend.rows.group_row import MonophonyGroupRow -from monophony.frontend.rows.song_row import MonophonySongRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') - - -class MonophonyLockedGroupRow(MonophonyGroupRow): - def __init__(self, group: dict, player: object): - super().__init__(group, player) - - for item in group['contents']: - self.add_row(MonophonySongRow(item, player, group)) - - super().update() diff --git a/source/monophony/frontend/rows/queue_song_row.py b/source/monophony/frontend/rows/queue_song_row.py deleted file mode 100644 index b08333f..0000000 --- a/source/monophony/frontend/rows/queue_song_row.py +++ /dev/null @@ -1,80 +0,0 @@ -from monophony.frontend.popovers.queue_song_popover import MonophonyQueueSongPopover -from monophony.frontend.rows.song_row import MonophonySongRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gdk, Gtk - - -class MonophonyQueueSongRow(MonophonySongRow): - def __init__(self, song: dict, player: object, queue: list): - super().__init__(song, player, queue) - self.btn_more.set_create_popup_func( - MonophonyQueueSongPopover, self.song, self.player - ) - - img_handle = Gtk.Image.new_from_icon_name('list-drag-handle-symbolic') - img_handle.add_css_class('drag-handle') - css = Gtk.CssProvider.new() - css.load_from_data(''' - .drag-handle { - opacity: 0.5; - } - - .dnd-item { - background-color: #00000022; - } - - .current-queue-item { - color: @accent_color; - } - ''', -1) - Gtk.StyleContext.add_provider_for_display( - Gdk.Display.get_default(), - css, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - ) - self.add_prefix(img_handle) - self.drg_handle = Gtk.DragSource.new() - self.drg_handle.set_actions(Gdk.DragAction.MOVE) - self.drg_handle.connect('prepare', self._on_dnd_prepare) - self.drg_handle.connect('drag-end', self._on_dnd_cancel_or_end) - self.drg_handle.connect('drag-cancel', self._on_dnd_cancel_or_end) - img_handle.add_controller(self.drg_handle) - self.drp_target = Gtk.DropTarget.new(self.__gtype__, Gdk.DragAction.MOVE) - self.drp_target.connect('drop', self._on_dnd_drop) - self.drp_target.connect('enter', self._on_dnd_enter) - self.add_controller(self.drp_target) - - def _on_dnd_drop(self, _t, song_row: Adw.ActionRow, *_) -> bool: - queue = self.player.queue.copy() - self.player.move_song( - queue.index(song_row.song), - queue.index(self.song), - ) - return True - - def _on_dnd_prepare(self, *_) -> Gdk.ContentProvider: - self.add_css_class('dnd-item') - self.drg_handle.set_icon(Gtk.WidgetPaintable.new(self), 0, 0) - return Gdk.ContentProvider.new_for_value(self) - - def _on_dnd_enter(self, *_) -> int: - if self.drg_handle.get_drag(): - self.drp_target.reject() - return 0 - - # reject if no drag was started in the local group - child_index = 0 - while True: - child = self.get_parent().get_row_at_index(child_index) - child_index += 1 - if not child: - self.drp_target.reject() - return 0 - if child.drg_handle.get_drag(): - return Gdk.DragAction.MOVE - - def _on_dnd_cancel_or_end(self, *_): - self.remove_css_class('dnd-item') diff --git a/source/monophony/frontend/rows/song_row.py b/source/monophony/frontend/rows/song_row.py deleted file mode 100644 index ca57c35..0000000 --- a/source/monophony/frontend/rows/song_row.py +++ /dev/null @@ -1,70 +0,0 @@ -import monophony.backend.cache -from monophony.backend.utils import sanitize_str -from monophony.frontend.popovers.song_popover import MonophonySongPopover - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib, GObject, Gtk - - -class MonophonySongRow(Adw.ActionRow, GObject.Object): - def __init__(self, song: dict, player: object, group: dict | None = None): - super().__init__() - - self.player = player - self.song = song - self.group = group - - self.set_tooltip_text(_('Play')) - self.set_property('activatable', True) - self.connect('activated', lambda row: row._on_play_clicked()) - - title = GLib.markup_escape_text(song.get('title', '') or '', -1) - length = GLib.markup_escape_text(song.get('length', '0:00') or '0:00', -1) - author = GLib.markup_escape_text(song.get('author', '') or '', -1) - subtitle = sanitize_str(author) - if length: - subtitle = length + ' ' + subtitle - - self.checkmark = Gtk.Image.new_from_icon_name('folder-download-symbolic') - self.checkmark.set_tooltip_text(_('Downloaded')) - self.checkmark.set_visible(False) - self.add_suffix(self.checkmark) - self.spinner = Adw.Spinner() - self.spinner.set_visible(False) - self.add_suffix(self.spinner) - self.set_title(sanitize_str(title)) - self.set_subtitle(sanitize_str(subtitle)) - - self.btn_more = Gtk.MenuButton() - self.btn_more.set_tooltip_text(_('More actions')) - self.btn_more.set_icon_name('view-more-symbolic') - self.btn_more.set_has_frame(False) - self.btn_more.set_vexpand(False) - self.btn_more.set_valign(Gtk.Align.CENTER) - self.btn_more.set_create_popup_func(MonophonySongPopover, self.song) - self.add_suffix(self.btn_more) - - self.update_download_status() - - def _on_play_clicked(self): - queue = [self.song] - if self.group: - queue = self.group['contents'] - - GLib.Thread.new( - None, self.player.play_queue, queue, queue.index(self.song) - ) - - def update_download_status(self) -> bool: - if monophony.backend.cache.is_song_being_cached(self.song['id']): - self.spinner.set_visible(True) - return True - - self.spinner.set_visible(False) - self.checkmark.set_visible( - monophony.backend.cache.is_song_cached(self.song['id']) - ) - - return False diff --git a/source/monophony/frontend/tabs/library_tab.py b/source/monophony/frontend/tabs/library_tab.py deleted file mode 100644 index b257696..0000000 --- a/source/monophony/frontend/tabs/library_tab.py +++ /dev/null @@ -1,231 +0,0 @@ -import monophony.backend.cache -import monophony.backend.history -import monophony.backend.playlists -import monophony.backend.yt -from monophony.frontend.rows.external_group_row import MonophonyExternalGroupRow -from monophony.frontend.rows.local_group_row import MonophonyLocalGroupRow -from monophony.frontend.rows.locked_group_row import MonophonyLockedGroupRow -from monophony.frontend.rows.song_row import MonophonySongRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gio, GLib, GObject, Gtk - - -class MonophonyLibraryTab(Gtk.Box): - def __init__(self, player: object): - super().__init__(orientation=Gtk.Orientation.VERTICAL) - - self.player = player - self.playlist_widgets = [] - self.downloads_widgets = [] - self.old_downloads = [] - self.recents_widgets = [] - self.old_recents = [] - self.recommendations = {} - self.loading_lock = GLib.Mutex() - self.set_vexpand(True) - - self.box_meta = Adw.PreferencesPage.new() - self.box_meta.set_vexpand(True) - self.box_meta.set_visible(False) - self.box_meta.set_valign(Gtk.Align.FILL) - self.append(self.box_meta) - - spn_big = Adw.Spinner() - spn_big.set_hexpand(True) - spn_big.set_vexpand(True) - - self.box_loading = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.box_loading.set_margin_bottom(10) - self.box_loading.append(spn_big) - self.box_loading.set_visible(True) - self.append(self.box_loading) - - self.box_meta.bind_property( - 'visible', - self.box_loading, - 'visible', - GObject.BindingFlags.SYNC_CREATE | - GObject.BindingFlags.INVERT_BOOLEAN | - GObject.BindingFlags.BIDIRECTIONAL - ) - - self.box_recommendations = Adw.PreferencesGroup() - self.box_recommendations.set_visible(False) - self.box_recommendations.set_title(_('Recommended')) - self.box_meta.add(self.box_recommendations) - - con_import = Adw.ButtonContent.new() - con_import.set_label(_('Import')) - con_import.set_icon_name('list-add-symbolic') - btn_import = Gtk.Button.new() - btn_import.set_child(con_import) - btn_import.add_css_class('suggested-action') - btn_import.connect( - 'clicked', lambda _b: self.get_ancestor(Gtk.Window)._on_import_clicked() - ) - - self.btn_play = Gtk.Button.new_from_icon_name('media-playback-start-symbolic') - self.btn_play.add_css_class('suggested-action') - self.btn_play.set_tooltip_text(_('Play all')) - self.btn_play.connect('clicked', self._on_play_all) - - box_suffix = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_suffix.set_spacing(5) - box_suffix.append(btn_import) - box_suffix.append(self.btn_play) - - self.box_playlists = Adw.PreferencesGroup() - self.box_playlists.set_title(_('Your Playlists')) - self.box_playlists.set_header_suffix(box_suffix) - self.box_meta.add(self.box_playlists) - - btn_downloads = Gtk.Button.new_from_icon_name('folder-symbolic') - btn_downloads.set_tooltip_text(_('Open downloads directory')) - btn_downloads.connect('clicked', lambda _b: self._on_open_downloads()) - - self.box_downloads = Adw.PreferencesGroup() - self.box_downloads.set_visible(False) - self.box_downloads.set_title(_('Downloads')) - self.box_downloads.set_header_suffix(btn_downloads) - self.box_meta.add(self.box_downloads) - - btn_clear = Gtk.Button.new_from_icon_name('edit-clear-all-symbolic') - btn_clear.add_css_class('destructive-action') - btn_clear.set_tooltip_text(_('Clear')) - btn_clear.connect( - 'clicked', lambda _b: monophony.backend.history.clear_songs() - ) - - self.box_recents = Adw.PreferencesGroup() - self.box_recents.set_visible(False) - self.box_recents.set_title(_('Recently Played')) - self.box_recents.set_header_suffix(btn_clear) - self.box_meta.add(self.box_recents) - - GLib.Thread.new('library-load', self.load) - GLib.timeout_add(200, self.update) - - def _on_open_downloads(self): - Gio.AppInfo.launch_default_for_uri( - 'file://' + monophony.backend.cache.get_cache_directory(), None - ) - - def _on_play_all(self, _b): - all_songs = [] - for content in monophony.backend.playlists.read_playlists().values(): - all_songs.extend(content) - for playlist in monophony.backend.playlists.read_external_playlists(): - all_songs.extend(playlist['contents']) - - GLib.Thread.new(None, self.player.play_queue, all_songs, 0) - - def load(self): - self.loading_lock.lock() - monophony.backend.playlists.update_external_playlists() - self.recommendations = monophony.backend.yt.get_recommendations() - self.loading_lock.unlock() - - def update_playlists(self): - for w in self.playlist_widgets: - if isinstance(w, MonophonyLocalGroupRow): - w.update() - - def update(self) -> bool: - if not self.get_ancestor(Gtk.Window).get_visible(): - return True - - if not self.loading_lock.trylock(): - return True - self.loading_lock.unlock() - self.box_loading.set_visible(False) - - new_playlists = monophony.backend.playlists.read_playlists() - new_ext_lists = monophony.backend.playlists.read_external_playlists() - self.btn_play.set_visible(new_playlists or new_ext_lists) - - remaining_widgets = [ - w for w in self.playlist_widgets if w.is_ancestor(self.box_meta) - ] - self.playlist_widgets = remaining_widgets - - for title in new_playlists: - for widget in self.playlist_widgets: - if widget.get_title() == GLib.markup_escape_text(title, -1): - for song_row in widget.song_widgets: - song_row.update_download_status() - break - else: # nobreak - new_widget = MonophonyLocalGroupRow( - {'title': title, 'contents': new_playlists[title]}, self.player - ) - self.playlist_widgets.append(new_widget) - self.box_playlists.add(new_widget) - break # one per update call - - for playlist in new_ext_lists: - title = GLib.markup_escape_text(playlist['title'], -1) - for widget in self.playlist_widgets: - if widget.get_title() == title: - break - else: # nobreak - new_widget = MonophonyExternalGroupRow(playlist, self.player) - self.playlist_widgets.append(new_widget) - self.box_playlists.add(new_widget) - break # one per update call - - if self.recommendations: - self.box_recommendations.set_visible(True) - for group in self.recommendations: - widget = MonophonyLockedGroupRow( - {'title': group, 'contents': self.recommendations[group]}, - self.player - ) - self.box_recommendations.add(widget) - - self.recommendations = {} - - new_downloads = monophony.backend.cache.read_songs() - if new_downloads != self.old_downloads: - self.box_downloads.set_visible(True) - - for widget in self.downloads_widgets: - self.box_downloads.remove(widget) - - self.downloads_widgets = [] - self.old_downloads = new_downloads - for song in new_downloads: - widget = MonophonySongRow(song, self.player) - self.box_downloads.add(widget) - self.downloads_widgets.append(widget) - else: - for widget in self.downloads_widgets: - if widget.spinner.get_visible(): - widget.update_download_status() - - self.box_downloads.set_visible(bool(new_downloads)) - - # player could be adding to recents at this moment - if self.player.is_busy(): - return True - - new_recents = monophony.backend.history.read_songs() - new_recents.reverse() - if new_recents != self.old_recents: - self.box_recents.set_visible(True) - - for widget in self.recents_widgets: - self.box_recents.remove(widget) - - self.recents_widgets = [] - self.old_recents = new_recents - for song in new_recents: - widget = MonophonySongRow(song, self.player) - self.box_recents.add(widget) - self.recents_widgets.append(widget) - - self.box_recents.set_visible(bool(new_recents)) - - return True diff --git a/source/monophony/frontend/tabs/queue_tab.py b/source/monophony/frontend/tabs/queue_tab.py deleted file mode 100644 index bb22b01..0000000 --- a/source/monophony/frontend/tabs/queue_tab.py +++ /dev/null @@ -1,91 +0,0 @@ -from monophony.backend.utils import time_str_to_sec, sec_to_time_str -from monophony.frontend.rows.queue_song_row import MonophonyQueueSongRow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gtk - - -class MonophonyQueueTab(Gtk.Box): - def __init__(self, player: object): - super().__init__(orientation=Gtk.Orientation.VERTICAL) - - self.player = player - self.player.queue_change_callback = self.update - self.old_queue = [] - self.old_index = -1 - self.queue_widgets = [] - self.set_vexpand(True) - - self.pge_status = Adw.StatusPage() - self.pge_status.set_vexpand(True) - self.pge_status.set_valign(Gtk.Align.FILL) - self.pge_status.set_icon_name('view-list-symbolic') - self.pge_status.set_title(_('Queue Empty')) - self.pge_status.set_visible(True) - - btn_clear = Gtk.Button.new_from_icon_name('edit-clear-all-symbolic') - btn_clear.add_css_class('destructive-action') - btn_clear.set_tooltip_text(_('Clear')) - btn_clear.connect('clicked', lambda _b: player.clear_queue()) - - btn_shuffle = Gtk.Button.new_from_icon_name('media-playlist-shuffle-symbolic') - btn_shuffle.set_tooltip_text(_('Shuffle')) - btn_shuffle.connect('clicked', lambda _b: self.player.shuffle_queue()) - - box_actions = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_actions.set_spacing(5) - box_actions.append(btn_clear) - box_actions.append(btn_shuffle) - - self.box_queue = Adw.PreferencesGroup() - self.box_queue.set_header_suffix(box_actions) - self.box_queue.set_title(_('Queue')) - - self.box_meta = Adw.PreferencesPage.new() - self.box_meta.set_visible(False) - self.box_meta.set_vexpand(True) - self.box_meta.set_valign(Gtk.Align.FILL) - self.box_meta.add(self.box_queue) - - self.append(self.box_meta) - self.append(self.pge_status) - - def update(self) -> bool: - new_queue = self.player.queue.copy() - new_index = self.player.index - - if new_queue != self.old_queue: - for widget in self.queue_widgets: - self.box_queue.remove(widget) - - self.box_meta.set_visible(bool(new_queue)) - self.pge_status.set_visible(not bool(new_queue)) - - self.queue_widgets = [] - self.old_queue = new_queue.copy() - self.old_index = new_index - total_seconds = 0 - for i, song in enumerate(new_queue): - widget = MonophonyQueueSongRow( - song, - self.player, - {'title': '', 'contents': new_queue} - ) - total_seconds += time_str_to_sec(song.get('length', '0')) - if i == new_index: - widget.add_css_class('current-queue-item') - self.box_queue.add(widget) - self.queue_widgets.append(widget) - - self.box_queue.set_description(sec_to_time_str(total_seconds)) - elif new_index != self.old_index: - self.old_index = new_index - for i, widget in enumerate(self.queue_widgets): - if i == new_index: - widget.add_css_class('current-queue-item') - else: - widget.remove_css_class('current-queue-item') - - return False diff --git a/source/monophony/frontend/tabs/search_tab.py b/source/monophony/frontend/tabs/search_tab.py deleted file mode 100644 index 4f614e7..0000000 --- a/source/monophony/frontend/tabs/search_tab.py +++ /dev/null @@ -1,109 +0,0 @@ -import monophony.backend.history - -from monophony.frontend.pages.artist_page import MonophonyArtistPage -from monophony.frontend.pages.results_page import MonophonyResultsPage -from monophony.frontend.widgets.recent_searches import MonophonyRecentSearches - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gtk - - -class MonophonySearchTab(Gtk.Box): - def __init__(self, player: object): - super().__init__(orientation=Gtk.Orientation.VERTICAL) - - self.ent_search = Gtk.SearchEntry() - self.ent_search.set_placeholder_text(_('Enter text or paste a URL...')) - self.ent_search.set_hexpand(True) - self.ent_search.set_halign(Gtk.Align.FILL) - self.ent_search.connect('activate', lambda e: self._on_search(e.get_text())) - - self.btn_back = Gtk.Button.new_from_icon_name('go-previous-symbolic') - self.btn_back.set_tooltip_text(_('Go back')) - self.btn_back.set_visible(False) - self.btn_back.connect('clicked', lambda _b: self._on_back_clicked()) - - box_search = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_search.set_spacing(5) - box_search.append(self.btn_back) - box_search.append(self.ent_search) - - self.box_recents = MonophonyRecentSearches(self._on_search) - - box_search_meta = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - box_search_meta.set_spacing(8) - box_search_meta.append(box_search) - box_search_meta.append(self.box_recents) - - clm_search = Adw.Clamp() - clm_search.set_child(box_search_meta) - - search_bar = Gtk.SearchBar() - search_bar.add_css_class('inline') - search_bar.set_show_close_button(False) - search_bar.set_search_mode(True) - search_bar.set_child(clm_search) - search_bar.set_key_capture_widget(self.ent_search) - self.append(search_bar) - - self.pge_results = MonophonyResultsPage(player) - self.pge_results.set_vexpand(True) - self.pge_results.set_valign(Gtk.Align.FILL) - self.append(self.pge_results) - self.pge_detail_results = None - - self.set_vexpand(True) - self.player = player - - def _on_back_clicked(self): - self.btn_back.set_visible(False) - self.pge_results.set_visible(True) - if self.pge_detail_results: - self.remove(self.pge_detail_results) - self.pge_detail_results = None - - def show_artist(self, artist: str): - self.btn_back.set_visible(True) - self.pge_results.set_visible(False) - if self.pge_detail_results: - self.remove(self.pge_detail_results) - self.pge_detail_results = None - - self.pge_detail_results = MonophonyArtistPage(self.player, artist) - self.pge_detail_results.set_vexpand(True) - self.pge_detail_results.set_valign(Gtk.Align.FILL) - self.append(self.pge_detail_results) - - def show_more(self, query: str, filter_: str): - self.btn_back.set_visible(True) - self.pge_results.set_visible(False) - if self.pge_detail_results: - self.remove(self.pge_detail_results) - self.pge_detail_results = None - - self.pge_detail_results = MonophonyResultsPage(self.player, query, filter_) - self.pge_detail_results.set_vexpand(True) - self.pge_detail_results.set_valign(Gtk.Align.FILL) - self.append(self.pge_detail_results) - - def _on_search(self, text: str): - self.ent_search.set_text(text) - if not text: - return - - if monophony.backend.history.add_search(text): - self.box_recents.add_search(text) - self.btn_back.set_visible(False) - if self.pge_results: - self.remove(self.pge_results) - self.pge_results = None - if self.pge_detail_results: - self.remove(self.pge_detail_results) - self.pge_detail_results = None - - self.pge_results = MonophonyResultsPage(self.player, text) - self.pge_results.set_vexpand(True) - self.pge_results.set_valign(Gtk.Align.FILL) - self.append(self.pge_results) diff --git a/source/monophony/frontend/widgets/__init__.py b/source/monophony/frontend/widgets/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/source/monophony/frontend/widgets/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/source/monophony/frontend/widgets/player.py b/source/monophony/frontend/widgets/player.py deleted file mode 100644 index 4b36991..0000000 --- a/source/monophony/frontend/widgets/player.py +++ /dev/null @@ -1,335 +0,0 @@ -from typing import ClassVar - -import monophony.backend.player -import monophony.backend.settings - -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gdk, Gio, GLib, GObject, Gtk, Pango - - -class MonophonyPlayer(Gtk.Box): - playback_icons: ClassVar = { - monophony.backend.player.PlaybackMode.NORMAL: - 'media-playlist-consecutive-symbolic', - monophony.backend.player.PlaybackMode.LOOP_SONG: - 'media-playlist-repeat-song-symbolic', - monophony.backend.player.PlaybackMode.LOOP_QUEUE: - 'media-playlist-repeat-symbolic', - monophony.backend.player.PlaybackMode.RADIO: - 'io.gitlab.zehkira.Monophony-symbolic', - } - - def __init__(self, window: Gtk.Window, player: object): - super().__init__(orientation=Gtk.Orientation.VERTICAL) - volume = float(monophony.backend.settings.get_value('volume', 1)) - - self.window = window - self.player = player - self.player.set_volume(volume, False) - self.player.ui_update_callback = self.update - self.inhibit_cookie = None - - box_info = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - box_info.set_margin_start(11) - box_info.set_spacing(5) - box_info.set_margin_end(5) - box_info.set_halign(Gtk.Align.START) - box_info.set_valign(Gtk.Align.CENTER) - - self.lnk_title = Gtk.LinkButton.new_with_label('', '') - self.lnk_title.set_margin_bottom(2) - self.lnk_title.set_margin_top(4) - - self.lnk_title.set_halign(Gtk.Align.START) - self.lnk_title.get_child().set_ellipsize(Pango.EllipsizeMode.END) - self.lnk_title.add_css_class('title-link') - - self.lbl_author = Gtk.Label(label='') - self.lbl_author.set_margin_top(2) - self.lbl_author.set_halign(Gtk.Align.START) - self.lbl_author.add_css_class('caption') - self.lbl_author.add_css_class('dim-label') - self.lbl_author.set_ellipsize(Pango.EllipsizeMode.END) - - self.box_sng_info = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.box_sng_info.append(self.lnk_title) - self.box_sng_info.append(self.lbl_author) - box_info.append(self.box_sng_info) - - self.spn_loading = Adw.Spinner() - self.spn_loading.set_halign(Gtk.Align.CENTER) - self.spn_loading.set_margin_start(9) - self.spn_loading.set_margin_end(9) - self.spn_loading.set_visible(False) - self.btn_pause = Gtk.Button.new_from_icon_name('media-playback-start-symbolic') - self.btn_pause.set_valign(Gtk.Align.CENTER) - self.btn_pause.set_tooltip_text(_('Toggle pause')) - self.btn_pause.bind_property( - 'visible', - self.spn_loading, - 'visible', - GObject.BindingFlags.BIDIRECTIONAL | - GObject.BindingFlags.SYNC_CREATE | - GObject.BindingFlags.INVERT_BOOLEAN - ) - self.btn_pause.connect('clicked', self._on_pause_clicked) - btn_next = Gtk.Button.new_from_icon_name('media-skip-forward-symbolic') - btn_next.set_valign(Gtk.Align.CENTER) - btn_next.set_tooltip_text(_('Next song')) - btn_next.connect('clicked', self._on_next_clicked) - btn_next.set_has_frame(False) - btn_prev = Gtk.Button.new_from_icon_name('media-skip-backward-symbolic') - btn_prev.set_valign(Gtk.Align.CENTER) - btn_prev.set_tooltip_text(_('Previous song')) - btn_prev.connect('clicked', self._on_previous_clicked) - btn_prev.set_has_frame(False) - - self.btn_vol = Gtk.ScaleButton.new(0, 1, 0.02, [ - 'audio-volume-muted-symbolic', - 'audio-volume-high-symbolic', - 'audio-volume-low-symbolic', - 'audio-volume-medium-symbolic', - 'audio-volume-high-symbolic' - ]) - self.btn_vol.set_value(volume) - self.btn_vol.set_tooltip_text(_('Change volume')) - self.btn_vol.connect('value-changed', self._on_volume_changed) - - self.btn_mode = Gtk.MenuButton() - self.btn_mode.set_valign(Gtk.Align.CENTER) - self.btn_mode.set_icon_name(MonophonyPlayer.playback_icons[player.mode]) - self.btn_mode.set_tooltip_text(_('Playback mode')) - self.btn_mode.set_create_popup_func(self.build_menu_popup) - self.btn_mode.set_has_frame(False) - - box_controls = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_controls.set_spacing(2) - box_controls.set_valign(Gtk.Align.CENTER) - box_controls.set_halign(Gtk.Align.END) - box_controls.set_hexpand(True) - box_controls.append(self.btn_vol) - box_controls.append(btn_prev) - box_controls.append(self.btn_pause) - box_controls.append(self.spn_loading) - box_controls.append(btn_next) - box_controls.append(self.btn_mode) - - box_meta = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_meta.set_margin_top(10) - box_meta.set_margin_bottom(10) - box_meta.set_margin_start(5) - box_meta.set_margin_end(8) - box_meta.set_valign(Gtk.Align.END) - box_meta.set_halign(Gtk.Align.FILL) - box_meta.set_hexpand(True) - box_meta.append(box_info) - box_meta.append(box_controls) - - self.scl_progress = Gtk.Scale.new_with_range( - Gtk.Orientation.HORIZONTAL, 0, 1, 0.01 - ) - self.scl_progress.add_css_class('seekbar') - self.scl_progress.set_draw_value(False) - self.scl_progress.set_halign(Gtk.Align.FILL) - self.scl_progress.set_valign(Gtk.Align.END) - self.scl_progress.connect('change-value', self._on_seek_performed) - - self.set_hexpand(True) - self.append(self.scl_progress) - self.append(box_meta) - self.add_css_class('playerbar') - self.add_css_class('toolbar') - - css = Gtk.CssProvider.new() - css.load_from_data(''' - .seekbar { - margin-bottom: -6px; - padding: 0px; - min-height: 10px; - } - - .seekbar trough, .seekbar highlight { - border-radius: 0px; - border-left: none; - border-right: none; - min-height: 10px; - } - - .seekbar highlight { - border-left: none; - border-right: none; - } - - .title-link { - padding-top: 0px; - padding-bottom: 0px; - padding-left: 0px; - padding-right: 0px; - margin-bottom: -8px; - margin-top: -8px; - } - - .playerbar { - padding: 0px; - } - ''', -1) - Gtk.StyleContext.add_provider_for_display( - Gdk.Display.get_default(), - css, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - ) - - GLib.timeout_add(1000, self.update_progress) - - def build_menu_popup(self, btn: Gtk.MenuButton): - mnu_more = Gio.Menu() - chk_normal = Gtk.CheckButton.new_with_label(_('Normal Playback')) - chk_normal.set_active( - self.player.mode == monophony.backend.player.PlaybackMode.NORMAL - ) - chk_normal.connect('toggled', self._on_normal_toggled) - itm_normal = Gio.MenuItem() - itm_normal.set_attribute_value('custom', GLib.Variant.new_string('normal')) - chk_autoplay = Gtk.CheckButton.new_with_label(_('Radio Mode')) - chk_autoplay.set_group(chk_normal) - chk_autoplay.set_active( - self.player.mode == monophony.backend.player.PlaybackMode.RADIO - ) - chk_autoplay.connect('toggled', self._on_radio_toggled) - itm_autoplay = Gio.MenuItem() - itm_autoplay.set_attribute_value( - 'custom', GLib.Variant.new_string('autoplay') - ) - chk_loop = Gtk.CheckButton.new_with_label(_('Repeat Song')) - chk_loop.set_group(chk_normal) - chk_loop.connect('toggled', self._on_loop_toggled) - chk_loop.set_active( - self.player.mode == monophony.backend.player.PlaybackMode.LOOP_SONG - ) - itm_loop = Gio.MenuItem() - itm_loop.set_attribute_value( - 'custom', GLib.Variant.new_string('loop') - ) - chk_loop_q = Gtk.CheckButton.new_with_label(_('Repeat Queue')) - chk_loop_q.set_group(chk_normal) - chk_loop_q.set_active( - self.player.mode == monophony.backend.player.PlaybackMode.LOOP_QUEUE - ) - chk_loop_q.connect('toggled', self._on_loop_q_toggled) - itm_loop_q = Gio.MenuItem() - itm_loop_q.set_attribute_value( - 'custom', GLib.Variant.new_string('loop_q') - ) - mnu_more.append_item(itm_normal) - mnu_more.append_item(itm_loop) - mnu_more.append_item(itm_loop_q) - mnu_more.append_item(itm_autoplay) - pop_menu = Gtk.PopoverMenu() - pop_menu.set_menu_model(mnu_more) - pop_menu.add_child(chk_normal, 'normal') - pop_menu.add_child(chk_loop, 'loop') - pop_menu.add_child(chk_loop_q, 'loop_q') - pop_menu.add_child(chk_autoplay, 'autoplay') - btn.set_popover(pop_menu) - - def _on_volume_changed(self, _b, volume): - self.player.set_volume(volume, False) - - def _on_seek_performed(self, _s, _t, target: float): - GLib.Thread.new(None, self.player.seek, target) - - def _on_pause_clicked(self, _b): - self.player.toggle_pause() - - def _on_next_clicked(self, _b): - GLib.Thread.new(None, self.player.next_song, True) - - def _on_previous_clicked(self, _b): - GLib.Thread.new(None, self.player.previous_song) - - def _on_loop_q_toggled(self, btn: Gtk.CheckButton): - if btn.get_active(): - mode = monophony.backend.player.PlaybackMode.LOOP_QUEUE - self.player.mode = mode - monophony.backend.settings.set_value('mode', mode) - self.btn_mode.set_icon_name( - MonophonyPlayer.playback_icons[self.player.mode] - ) - - def _on_loop_toggled(self, btn: Gtk.CheckButton): - if btn.get_active(): - mode = monophony.backend.player.PlaybackMode.LOOP_SONG - self.player.mode = mode - monophony.backend.settings.set_value('mode', mode) - self.btn_mode.set_icon_name( - MonophonyPlayer.playback_icons[self.player.mode] - ) - - def _on_normal_toggled(self, btn: Gtk.CheckButton): - if btn.get_active(): - mode = monophony.backend.player.PlaybackMode.NORMAL - self.player.mode = mode - monophony.backend.settings.set_value('mode', mode) - self.btn_mode.set_icon_name( - MonophonyPlayer.playback_icons[self.player.mode] - ) - - def _on_radio_toggled(self, btn: Gtk.CheckButton): - if btn.get_active(): - mode = monophony.backend.player.PlaybackMode.RADIO - self.player.mode = mode - monophony.backend.settings.set_value('mode', mode) - self.btn_mode.set_icon_name( - MonophonyPlayer.playback_icons[self.player.mode] - ) - - def _on_show_artist_clicked(self): - song = self.player.get_current_song() - if song: - self.window._on_show_artist(song['author_id']) - - def update_progress(self) -> bool: - if self.player.buffering or not self.lnk_title.get_label(): - return True - - progress = self.player.get_progress() - if progress: - self.scl_progress.set_value(progress) - - return True - - def update(self, song: dict, busy: bool, paused: bool, starting: bool) -> bool: - if starting: - self.scl_progress.set_value(0) - - if song: - self.lnk_title.set_label(song['title']) - self.lnk_title.get_child().set_ellipsize(Pango.EllipsizeMode.END) - self.lnk_title.set_uri( - 'https://music.youtube.com/watch?v=' + song['id'] - ) - self.lbl_author.set_label(song['author']) - self.window.toolbar_view.set_reveal_bottom_bars(True) - if self.inhibit_cookie is None and not paused: - self.inhibit_cookie = self.window.get_application().inhibit( - self.window, Gtk.ApplicationInhibitFlags.SUSPEND, None - ) - else: - self.lnk_title.set_label('') - self.lnk_title.set_uri('') - self.lbl_author.set_label('') - self.window.toolbar_view.set_reveal_bottom_bars(False) - - if (paused or not song) and self.inhibit_cookie is not None: - self.window.get_application().uninhibit(self.inhibit_cookie) - self.inhibit_cookie = None - - self.scl_progress.set_sensitive(not busy) - self.btn_pause.set_visible(not busy) - self.btn_pause.set_icon_name( - 'media-playback-start-symbolic' if paused else - 'media-playback-pause-symbolic' - ) - - return False diff --git a/source/monophony/frontend/widgets/recent_searches.py b/source/monophony/frontend/widgets/recent_searches.py deleted file mode 100644 index 124c80f..0000000 --- a/source/monophony/frontend/widgets/recent_searches.py +++ /dev/null @@ -1,63 +0,0 @@ -import monophony.backend.history - -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import Gtk, Pango - - -class MonophonyRecentSearches(Gtk.Box): - def __init__(self, search_callback: callable): - super().__init__(orientation=Gtk.Orientation.HORIZONTAL) - - self.children = [] - self.set_visible(False) - self.set_spacing(8) - self.set_hexpand(True) - self.set_halign(Gtk.Align.FILL) - self.search_callback = search_callback - - searches = monophony.backend.history.read_searches() - searches.reverse() - for query in searches: - self.add_search(query) - - def add_search(self, query: str): - self.set_visible(True) - lbl_query = Gtk.Label.new(query) - lbl_query.set_ellipsize(Pango.EllipsizeMode.END) - btn_search = Gtk.Button.new() - btn_search.set_child(lbl_query) - btn_search.set_hexpand(True) - btn_search.set_halign(Gtk.Align.FILL) - btn_search.connect( - 'clicked', lambda b: self._on_search(b.get_child().get_label()) - ) - btn_remove = Gtk.Button.new_from_icon_name('edit-delete-symbolic') - btn_remove.set_tooltip_text(_('Remove')) - btn_remove.connect( - 'clicked', - lambda b, q: self._on_remove_search(b, q), - query - ) - box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box.set_hexpand(True) - box.set_halign(Gtk.Align.FILL) - box.append(btn_search) - box.append(btn_remove) - box.add_css_class('linked') - - if len(self.children) > 2: - self.remove(self.children[0]) - self.children.pop(0) - self.children.append(box) - self.prepend(box) - - def _on_search(self, query: str): - self.search_callback(query) - - def _on_remove_search(self, btn: Gtk.Button, query: str): - monophony.backend.history.remove_search(query) - self.children.remove(btn.get_parent()) - self.remove(btn.get_parent()) - if not monophony.backend.history.read_searches(): - self.set_visible(False) diff --git a/source/monophony/frontend/windows/__init__.py b/source/monophony/frontend/windows/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/source/monophony/frontend/windows/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/source/monophony/frontend/windows/add_window.py b/source/monophony/frontend/windows/add_window.py deleted file mode 100644 index 526563f..0000000 --- a/source/monophony/frontend/windows/add_window.py +++ /dev/null @@ -1,143 +0,0 @@ -import monophony.backend.playlists - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib, Gtk - - -class MonophonyAddWindow(Adw.Window): - def __init__(self, song: dict, player, callback): - super().__init__() - - self.song = song - self.player = player - self.callback = callback - self.add_to_queue = False - self.add_to_playlists = [] - self.playlists = [] - - self.set_title(_('Add to...')) - self.set_modal(True) - - btn_cancel = Gtk.Button.new_with_label(_('Cancel')) - btn_cancel.connect('clicked', lambda _b: self.destroy()) - btn_add = Gtk.Button.new_with_label(_('Add')) - btn_add.add_css_class('suggested-action') - btn_add.connect('clicked', self._on_submit) - headerbar = Adw.HeaderBar.new() - headerbar.set_decoration_layout('') - headerbar.pack_start(btn_cancel) - headerbar.pack_end(btn_add) - - box_queue = Adw.PreferencesGroup() - self.chk_queue = Gtk.CheckButton() - self.row_queue = Adw.ActionRow() - self.row_queue.set_title(_('Queue')) - self.row_queue.add_suffix(self.chk_queue) - self.row_queue.set_property('activatable-widget', self.chk_queue) - self.chk_queue.connect('toggled', self._on_add_to_queue_toggled) - box_queue.add(self.row_queue) - - self.box_list = Adw.PreferencesGroup() - self.box_list.set_title(_('Your Playlists')) - - ent_name = Gtk.Entry.new() - ent_name.connect('activate', self._on_create) - ent_name.set_hexpand(True) - ent_name.set_halign(Gtk.Align.FILL) - ent_name.set_placeholder_text(_('New Playlist Name...')) - - btn_create = Gtk.Button.new_with_label(_('Create')) - btn_create.connect('clicked', lambda _b: self._on_create(ent_name)) - - box_create = Gtk.Box() - box_create.set_spacing(6) - box_create.set_margin_start(6) - box_create.set_margin_end(6) - box_create.set_margin_top(6) - box_create.set_margin_bottom(6) - box_create.set_hexpand(True) - box_create.append(ent_name) - box_create.append(btn_create) - - clm_create = Adw.Clamp() - clm_create.set_hexpand(True) - clm_create.set_child(box_create) - - pge_list = Adw.PreferencesPage() - pge_list.set_vexpand(True) - pge_list.add(box_queue) - pge_list.add(self.box_list) - - toolbar_view = Adw.ToolbarView() - toolbar_view.add_top_bar(headerbar) - toolbar_view.set_content(pge_list) - toolbar_view.add_bottom_bar(clm_create) - - self.add_shortcut(Gtk.Shortcut.new( - Gtk.ShortcutTrigger.parse_string('Escape'), - Gtk.CallbackAction.new((lambda w, _: w.close())) - )) - self.set_content(toolbar_view) - self.update_groups() - - def update_groups(self): - for playlist in self.playlists: - self.box_list.remove(playlist) - - self.playlists.clear() - - for queue_song in self.player.queue.copy(): - if queue_song['id'] == self.song['id']: - self.add_to_queue = False - self.chk_queue.set_active(True) - self.chk_queue.set_sensitive(False) - self.row_queue.set_sensitive(False) - break - - for playlist, contents in monophony.backend.playlists.read_playlists().items(): - chk_list = Gtk.CheckButton.new() - row_list = Adw.ActionRow() - row_list.set_title(playlist) - row_list.add_suffix(chk_list) - row_list.set_property('activatable-widget', chk_list) - self.box_list.add(row_list) - self.playlists.append(row_list) - - for check_song in contents: - if check_song['id'] == self.song['id']: - chk_list.set_active(True) - chk_list.set_sensitive(False) - row_list.set_sensitive(False) - break - - chk_list.connect('toggled', self._on_add_to_playlist_toggled) - - def _on_add_to_queue_toggled(self, btn: Gtk.CheckButton): - self.add_to_queue = btn.get_active() - - def _on_add_to_playlist_toggled(self, chk: Gtk.CheckButton): - toggled_list = chk.get_ancestor(Gtk.ListBoxRow).get_title() - if chk.get_active(): - self.add_to_playlists.append(toggled_list) - else: - self.add_to_playlists.remove(toggled_list) - - def _on_submit(self, _btn: Gtk.CheckButton): - for playlist in self.add_to_playlists: - monophony.backend.playlists.add_song(self.song, playlist) - - if self.add_to_queue: - GLib.Thread.new(None, self.player.queue_song, self.song) - - self.destroy() - self.callback() - - def _on_create(self, ent: Gtk.Entry): - text = ent.get_text() - ent.set_text('') - - if text.strip(): - monophony.backend.playlists.add_playlist(text) - self.update_groups() diff --git a/source/monophony/frontend/windows/import_window.py b/source/monophony/frontend/windows/import_window.py deleted file mode 100644 index 8600bf8..0000000 --- a/source/monophony/frontend/windows/import_window.py +++ /dev/null @@ -1,132 +0,0 @@ -import monophony.backend.playlists -from monophony.frontend.windows.message_window import MonophonyMessageWindow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib, Gtk - - -class MonophonyImportWindow(Adw.Window): - def __init__(self, url: str='', group: list | None = None): - super().__init__() - - self.import_lock = GLib.Mutex() - self.error = False - self.group = group - - self.ent_name = Gtk.Entry.new() - self.ent_name.set_text(group['title'] if group else '') - self.ent_name.set_placeholder_text(_('Enter Playlist Name...')) - self.ent_name.set_hexpand(True) - self.ent_name.set_margin_start(10) - self.ent_name.set_margin_end(10) - self.ent_name.set_halign(Gtk.Align.FILL) - self.ent_url = Gtk.Entry.new() - self.ent_url.set_text(url) - self.ent_url.set_placeholder_text(_('Enter Playlist URL...')) - self.ent_url.set_hexpand(True) - self.ent_url.set_halign(Gtk.Align.FILL) - self.ent_url.set_margin_start(10) - self.ent_url.set_margin_end(10) - chk_sync = Gtk.CheckButton.new_with_label(_('Synchronized')) - self.chk_local = Gtk.CheckButton.new_with_label(_('Editable')) - self.chk_local.set_group(chk_sync) - self.chk_local.set_active(True) - box_type = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - box_type.set_spacing(10) - box_type.set_margin_start(10) - box_type.set_margin_end(10) - box_type.append(self.chk_local) - box_type.append(chk_sync) - - self.spn_import = Adw.Spinner() - self.spn_import.set_margin_end(5) - self.spn_import.set_visible(False) - btn_cancel = Gtk.Button.new_with_label(_('Cancel')) - btn_cancel.connect('clicked', lambda _b: self.destroy()) - self.btn_import = Gtk.Button.new_with_label(_('Import')) - self.btn_import.add_css_class('suggested-action') - self.btn_import.connect('clicked', lambda _b: self._on_submit()) - headerbar = Adw.HeaderBar.new() - headerbar.set_decoration_layout('') - headerbar.pack_start(btn_cancel) - headerbar.pack_end(self.btn_import) - headerbar.pack_end(self.spn_import) - - self.box_content = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.box_content.set_spacing(10) - self.box_content.set_margin_bottom(10) - self.box_content.set_margin_top(10) - self.box_content.append(self.ent_name) - if not self.group: - self.box_content.append(self.ent_url) - self.box_content.append(box_type) - - toolbar_view = Adw.ToolbarView() - toolbar_view.add_top_bar(headerbar) - toolbar_view.set_content(self.box_content) - - self.set_title(_('Import Playlist...')) - self.set_modal(True) - self.set_resizable(False) - self.props.height_request = 100 - self.set_content(toolbar_view) - self.connect('close-request', lambda w: not w.box_content.get_sensitive()) - self.add_shortcut(Gtk.Shortcut.new( - Gtk.ShortcutTrigger.parse_string('Escape'), - Gtk.CallbackAction.new((lambda w, _: w.close())) - )) - - def do_import(self, name: str, url: str, local: bool): - self.import_lock.lock() - if self.group and local: - monophony.backend.playlists.add_playlist(name, self.group['contents']) - elif not monophony.backend.playlists.import_playlist(name, url, local): - self.error = True - self.import_lock.unlock() - - def await_import(self) -> bool: - if self.import_lock.trylock(): - self.import_lock.unlock() - if self.error: - self.error = False - self.box_content.set_sensitive(True) - self.spn_import.set_visible(False) - self.btn_import.set_visible(True) - MonophonyMessageWindow( - self, - _('Could not import playlist'), - _('Failed to retrieve playlist data from server.') - ).present() - else: - self.destroy() - - return False - return True - - def _on_submit(self): - name = self.ent_name.get_text() - url = ( - 'https://www.youtube.com/playlist?list=' + self.group['id'] - ) if self.group else self.ent_url.get_text() - local = self.chk_local.get_active() - - if not name: - MonophonyMessageWindow( - self, _('Could not import playlist'), _('A name is required.') - ).present() - return - - if not url and not self.group: - MonophonyMessageWindow( - self, _('Could not import playlist'), _('A URL is required.') - ).present() - return - - self.box_content.set_sensitive(False) - self.spn_import.set_visible(True) - self.btn_import.set_visible(False) - - GLib.Thread.new(None, self.do_import, name, url, local) - GLib.timeout_add(1000, self.await_import) diff --git a/source/monophony/frontend/windows/main_window.py b/source/monophony/frontend/windows/main_window.py deleted file mode 100644 index f0c4a4e..0000000 --- a/source/monophony/frontend/windows/main_window.py +++ /dev/null @@ -1,277 +0,0 @@ -import monophony.backend.cache -import monophony.backend.mpris -import monophony.backend.player -import monophony.backend.playlists -import monophony.backend.settings -from monophony import __version__, APP_ID -from monophony.frontend.tabs.library_tab import MonophonyLibraryTab -from monophony.frontend.tabs.queue_tab import MonophonyQueueTab -from monophony.frontend.tabs.search_tab import MonophonySearchTab -from monophony.frontend.widgets.player import MonophonyPlayer -from monophony.frontend.windows.add_window import MonophonyAddWindow -from monophony.frontend.windows.import_window import MonophonyImportWindow - -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, GLib, Gtk - - -class MonophonyMainWindow(Adw.ApplicationWindow): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.set_default_size( - int(monophony.backend.settings.get_value('window-width', 600)), - int(monophony.backend.settings.get_value('window-height', 500)) - ) - self.set_title('Monophony') - self.set_icon_name(APP_ID) - self.player = monophony.backend.player.Player() - self.player.queue_end_callback = self._on_queue_end - self.player.raise_callback = self.present - self.deleted_playlists = [] - GLib.Thread.new(None, monophony.backend.mpris.init, self.player) - - self.stack = Adw.ViewStack() - self.library_tab = MonophonyLibraryTab(self.player) - self.stack.add_titled_with_icon( - self.library_tab, 'library', _('Library'), 'audio-x-generic-symbolic' - ) - self.stack.add_titled_with_icon( - MonophonySearchTab(self.player), - 'search', - _('Search'), - 'system-search-symbolic' - ) - self.stack.add_titled_with_icon( - MonophonyQueueTab(self.player), - 'queue', - _('Queue'), - 'view-list-symbolic' - ) - self.stack.set_visible_child_name('library') - - self.toaster = Adw.ToastOverlay.new() - self.toaster.set_child(self.stack) - - btn_about = Gtk.Button.new_from_icon_name('help-about-symbolic') - btn_about.set_tooltip_text(_('About')) - btn_about.connect('clicked', lambda _b: self._on_about_clicked()) - - switcher = Adw.ViewSwitcher() - switcher.set_stack(self.stack) - - header_bar = Adw.HeaderBar() - header_bar.set_title_widget(switcher) - header_bar.pack_start(btn_about) - - self.toolbar_view = Adw.ToolbarView() - self.toolbar_view.add_top_bar(header_bar) - self.toolbar_view.add_bottom_bar(MonophonyPlayer(self, self.player)) - self.toolbar_view.set_content(self.toaster) - self.toolbar_view.set_reveal_bottom_bars(False) - self.set_content(self.toolbar_view) - - self.install_action( - 'quit-app', None, (lambda w, *_: w._on_quit()) - ) - self.install_action( - 'focus-library', - None, - (lambda w, *_: w.stack.set_visible_child_name('library')) - ) - self.install_action( - 'focus-search', - None, - (lambda w, *_: w._on_search()) - ) - self.install_action( - 'focus-queue', - None, - (lambda w, *_: w.stack.set_visible_child_name('queue')) - ) - self.install_action( - 'playlist-delete-undo', None, (lambda w, *_: w._on_undo_delete()) - ) - self.get_application().set_accels_for_action( - 'quit-app', ['w', 'q'] - ) - self.get_application().set_accels_for_action('focus-library', ['1']) - self.get_application().set_accels_for_action( - 'focus-search', ['f', '2'] - ) - self.get_application().set_accels_for_action('focus-queue', ['3']) - self.connect('close-request', MonophonyMainWindow.run_background) - - def append_page(self, widget: Gtk.Widget): - while child := self.stack.get_adjacent_child(Adw.NavigationDirection.FORWARD): - self.stack.remove(child) - - self.stack.append(widget) - self.stack.navigate(Adw.NavigationDirection.FORWARD) - - def run_background(self) -> bool: - if self.player.get_current_song(): - self.set_visible(False) - return True - - self._on_quit() - return False - - def _on_quit(self): - self.player.terminate() - size = self.get_default_size() - monophony.backend.settings.set_value('window-width', size.width) - monophony.backend.settings.set_value('window-height', size.height) - self.get_application().quit() - - def _on_search(self): - self.stack.set_visible_child_name('search') - self.stack.get_visible_child().ent_search.grab_focus() - - def _on_show_more(self, query: str, filter_: str): - self.stack.set_visible_child_name('search') - self.stack.get_visible_child().show_more(query, filter_) - - def _on_show_artist(self, artist: str): - self.stack.set_visible_child_name('search') - self.stack.get_visible_child().show_artist(artist) - - def _on_about_clicked(self): - win_about = Adw.AboutWindow.new() - win_about.set_application_icon(APP_ID) - win_about.set_application_name('Monophony') - win_about.set_version(__version__) - win_about.set_copyright('Copyright © Zehkira and contributors') - win_about.set_license_type(Gtk.License.GPL_2_0) - win_about.add_legal_section( - 'ytmusicapi', 'Copyright © 2024 sigma67', Gtk.License.MIT_X11 - ) - win_about.add_legal_section( - 'mpris_server', 'Copyright © Alex DeLorenzo', Gtk.License.LGPL_3_0 - ) - win_about.add_legal_section( - 'StrEnum', 'Copyright © 2019 James C Sinclair', Gtk.License.MIT_X11 - ) - win_about.add_legal_section( - 'Unidecode', 'Copyright © 2024, Tomaž Šolc', Gtk.License.GPL_2_0 - ) - win_about.add_legal_section( - 'emoji', - 'Copyright © 2014-2024, Taehoon Kim, Kevin Wurster', - Gtk.License.BSD_3 - ) - win_about.add_legal_section('pycairo', '', Gtk.License.LGPL_2_1_ONLY) - win_about.add_legal_section( - 'pydbus', - 'Copyright © 2014, 2015, 2016 Linus Lewandowski', - Gtk.License.LGPL_2_1 - ) - win_about.add_legal_section('certifi', '', Gtk.License.MPL_2_0) - win_about.add_legal_section( - 'charset_normalizer', - 'Copyright © 2025 TAHRI Ahmed R.', - Gtk.License.MIT_X11 - ) - win_about.add_legal_section( - 'idna', - 'Copyright © 2013-2024, Kim Davies and contributors', - Gtk.License.BSD_3 - ) - win_about.add_legal_section( - 'requests', - 'Copyright © 2019 Kenneth Reitz', - Gtk.License.APACHE_2_0 - ) - win_about.add_legal_section( - 'urllib3', - 'Copyright © 2008-2020 Andrey Petrov and contributors', - Gtk.License.MIT_X11 - ) - win_about.add_legal_section('PyGObject', '', Gtk.License.LGPL_2_1) - win_about.set_translator_credits(_('translator-credits')) - win_about.set_issue_url('https://gitlab.com/zehkira/monophony/-/issues') - win_about.set_website('https://gitlab.com/zehkira/monophony') - win_about.set_transient_for(self) - win_about.present() - - def _on_import_clicked(self, url: str='', group: list | None = None): - popup = MonophonyImportWindow(url=url, group=group) - popup.set_transient_for(self) - popup.present() - - def _on_add_clicked(self, song: dict): - popup = MonophonyAddWindow( - song, self.player, self.library_tab.update_playlists - ) - popup.set_transient_for(self) - popup.present() - - def _on_remove_song(self, song: str, playlist: str): - monophony.backend.playlists.remove_song(song, playlist) - self.library_tab.update_playlists() - - def _on_move_song(self, song: dict, group: dict, direction: int): - index = group['contents'].index(song) - monophony.backend.playlists.swap_songs( - group['title'], index, index + direction - ) - self.library_tab.update_playlists() - - def _on_uncache_song(self, song: dict): - monophony.backend.cache.uncache_song(song) - - def _on_cache_song(self, song: dict): - GLib.Thread.new( - None, monophony.backend.cache.cache_songs, [song] - ) - - def _on_cache_playlist(self, songs: list): - GLib.Thread.new(None, monophony.backend.cache.cache_songs, songs) - - def _on_delete_playlist(self, widget: object, local: bool=True): - group = widget.group.copy() - group['local'] = local - - tst_undo = Adw.Toast.new( - _('Deleted playlist "{playlist_name}"').format( - playlist_name=group['title'] - ) - ) - tst_undo.set_priority(Adw.ToastPriority.HIGH) - tst_undo.set_button_label(_('Undo')) - tst_undo.set_action_name('playlist-delete-undo') - tst_undo.connect('dismissed', self._on_toast_dismissed) - self.toaster.add_toast(tst_undo) - self.deleted_playlists.append(group) - - if local: - monophony.backend.playlists.remove_playlist(group['title']) - else: - monophony.backend.playlists.remove_external_playlist(group['title']) - - def _on_duplicate_playlist(self, widget: object): - monophony.backend.playlists.add_playlist( - widget.group['title'], widget.group['contents'] - ) - - def _on_save_playlist(self, name: str, contents: list): - monophony.backend.playlists.add_playlist(name, contents) - self.toaster.add_toast(Adw.Toast.new(_('Added'))) - - def _on_queue_end(self): - if not self.is_visible(): - self._on_quit() - - def _on_toast_dismissed(self, _t: Adw.Toast): - self.deleted_playlists.pop() - - def _on_undo_delete(self): - playlist = self.deleted_playlists[-1] - if playlist['local']: - monophony.backend.playlists.add_playlist( - playlist['title'], playlist['contents'] - ) - return - - monophony.backend.playlists.add_external_playlist(playlist) diff --git a/source/monophony/frontend/windows/message_window.py b/source/monophony/frontend/windows/message_window.py deleted file mode 100644 index 5f51cd3..0000000 --- a/source/monophony/frontend/windows/message_window.py +++ /dev/null @@ -1,23 +0,0 @@ -import gi -gi.require_version('Adw', '1') -gi.require_version('Gtk', '4.0') -from gi.repository import Adw, Gtk - - -class MonophonyMessageWindow(Adw.MessageDialog): - def __init__(self, parent: Adw.Window, title: str, text: str): - super().__init__() - - self.set_heading(title) - self.set_body(text) - self.add_response('ok', _('Ok')) - self.set_transient_for(parent) - self.set_modal(True) - self.connect('response', self._on_response) - self.add_shortcut(Gtk.Shortcut.new( - Gtk.ShortcutTrigger.parse_string('Escape'), - Gtk.CallbackAction.new((lambda w, _: w.close())) - )) - - def _on_response(self, _w, _response: str): - self.destroy() diff --git a/source/monophony/logging.py b/source/monophony/logging.py new file mode 100644 index 0000000..925da27 --- /dev/null +++ b/source/monophony/logging.py @@ -0,0 +1,133 @@ +import os +import platform +import sys +import threading +import time +import traceback +from typing import Any + +from monophony import NAME, __version__ + +from gi.repository import GLib + + +_LOG_LEVELS_VARIABLE = 'LOG_LEVELS' +_DEFAULT_LOG_LEVELS = 'INFO,WARN,ERRO' + + +class LogLevel: + name = '' + color = '' + + +class InfoLevel(LogLevel): + name = 'INFO' + color = '\033[0m' + + +class WarningLevel(LogLevel): + name = 'WARN' + color = '\033[1;33m' + + +class ErrorLevel(LogLevel): + name = 'ERRO' + color = '\033[1;31m' + + +def get_log() -> str: + if threading.current_thread() is not threading.main_thread(): + warning(__name__, 'Reading log from non-main thread') + + try: + with open(_get_directory() + '/log') as log_file: + return log_file.read() + except OSError: + error(__name__, 'Failed to read log file', traceback.format_exc()) + + return '' + + +# For status updates +def info(source: str, text: Any, details: Any=''): + _log(InfoLevel, source, str(text), str(details)) + + +# For unusual but possibly acceptable things +def warning(source: str, text: Any, details: Any=''): + _log(WarningLevel, source, str(text), str(details)) + + +# For failures of all sorts +def error(source: str, text: Any, details: Any=''): + _log(ErrorLevel, source, str(text), str(details)) + + +def _log(level: type, source: str, text: str, details: str): + if threading.current_thread() is not threading.main_thread(): + GLib.idle_add(_log, level, source, text, details) + return + + if level.name not in os.getenv( + _LOG_LEVELS_VARIABLE, _DEFAULT_LOG_LEVELS + ).split(','): + return + + text = text.strip() + details = details.strip() + line = f'{time.strftime("%H:%M")} [{level.name}] {source}: {text}' + sys.stdout.write(f'{level.color}{line}\n') + if details: + sys.stdout.write(details + '\n') + sys.stdout.write('\033[0m') + + log_directory = _get_directory() + os.makedirs(log_directory, exist_ok=True) + try: + with open(f'{log_directory}/log', 'a+') as log: + log.write(line + '\n') + if details: + log.write(details + '\n') + except (OSError, ValueError): + sys.stdout.write( + f'{ErrorLevel.color}[{ErrorLevel.name}] {__name__}: ' + 'Failed to write to log file\033[0m\n' + f'{traceback.format_exc()}\n' + ) + + +def _get_directory() -> str: + return os.getenv('XDG_RUNTIME_DIR', '/var/tmp') + '/' + NAME + + +log_directory = _get_directory() +os.makedirs(log_directory, exist_ok=True) +open(f'{log_directory}/log', 'a+').close() + +max_log_lines = 1000 +with open(f'{log_directory}/log', 'r+') as log_file: + lines = log_file.readlines() + if len(lines) > max_log_lines: + lines = lines[-max_log_lines:] + log_file.seek(0) + log_file.truncate() + log_file.writelines(lines) + +info(__name__, f'Logging initiated. Logs will be written to {log_directory}/log') + +os_release = {} +try: + os_release = platform.freedesktop_os_release() +except OSError: + warning(__name__, 'Could not read OS release file') + +os_info_string = '' +for key in ('PRETTY_NAME', 'NAME', 'ID', 'ID_LIKE', 'VERSION', 'VERSION_ID'): + if value := os_release.get(key): + os_info_string += f'{key}: {value}\n' + +info( + __name__, + f'{NAME} {__version__} on {platform.platform(aliased=True)}', + os_info_string.strip('\n') +) diff --git a/source/monophony/mpris.py b/source/monophony/mpris.py new file mode 100644 index 0000000..a92ad84 --- /dev/null +++ b/source/monophony/mpris.py @@ -0,0 +1,112 @@ +from monophony import ID, logging +from monophony.data import PlaybackMode, PlaybackState + +from mprisify.adapters import MprisAdapter +from mprisify.adapters import PlayState as MprisPlayState +from mprisify.events import PlayerEventAdapter as MprisPlayerEventAdapter +from mprisify.server import Server as MprisServer + + +class EventHandler(MprisAdapter): + def __init__(self, player: object): + super().__init__() + self._player = player + + def get_desktop_entry(self) -> str: + return ID + + def can_quit(self) -> bool: + return False + + def get_current_position(self) -> float: + return self._player.get_position_ns() / 1000 + + def next(self): + self._player.next(from_user=True) + + def previous(self): + self._player.previous() + + def pause(self): + self._player.set_pause(True) + + def resume(self): + self._player.set_pause(False) + + def stop(self): + self._player.stop() + + def get_playstate(self) -> MprisPlayState: + playstate = MprisPlayState.PLAYING + if self._player.paused: + playstate = MprisPlayState.PAUSED + elif self._player.state == PlaybackState.NONE: + playstate = MprisPlayState.STOPPED + + logging.info(__name__, f'Reported playstate as "{playstate}"') + return playstate + + def is_repeating(self) -> bool: + return self._player.mode == PlaybackMode.LOOP_SONG + + def get_shuffle(self) -> bool: + return False + + def get_volume(self): + return self._player.get_volume() + + def set_volume(self, volume: float): + self._player.set_volume(volume, notify_mpris=False) + + def is_mute(self) -> bool: + return False + + def can_go_next(self) -> bool: + return True + + def can_go_previous(self) -> bool: + return True + + def can_play(self) -> bool: + return bool(self._player.get_current_song()) + + def can_pause(self) -> bool: + return bool(self._player.get_current_song()) + + def can_seek(self) -> bool: + return False + + def can_control(self) -> bool: + return True + + def can_raise(self) -> bool: + return True + + def set_raise(self, value: bool): + if value: + self._player.emit('raise') + + def metadata(self) -> dict: + metadata = {'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'} + if song := self._player.get_current_song(): + duration_ns = self._player.get_duration_ns() + metadata = { + 'mpris:trackid': '/track/1', + 'mpris:artUrl': song.thumbnail, + 'mpris:length': duration_ns / 1000, + 'xesam:title': song.title, + 'xesam:artist': [song.author.name] + } + + logging.info(__name__, 'Reported metadata for current song', metadata) + return metadata + + +class Server(MprisServer): + def __init__(self, id_: str, event_handler: EventHandler): + super().__init__(id_, adapter=event_handler) + + +class EventSender(MprisPlayerEventAdapter): + def __init__(self, server: Server): + super().__init__(root=server.root, player=server.player) diff --git a/source/monophony/player.py b/source/monophony/player.py new file mode 100644 index 0000000..916ff93 --- /dev/null +++ b/source/monophony/player.py @@ -0,0 +1,503 @@ +import copy +import random +import time + +from monophony import DISPLAY_NAME, ID, downloads, logging, recents, settings, yt +from monophony.asynchronous import Task +from monophony.data import Group, PlaybackMode, PlaybackState, Song +from monophony.mpris import EventHandler, EventSender, Server + +from gi.repository import GObject, Gst + + +class ReportProgressTask(Task): + def _function(self): + logging.info(__name__, 'Progress reporting started') + while not self.is_canceled(): + self._update_progress() + time.sleep(1) + + logging.info(__name__, 'Progress reporting stopped') + + +class FindRadioSongsTask(Task): + def _function(self, from_song: Song, ignore_songs: Group) -> Group | None: + return yt.get_similar_songs(from_song, ignore_songs) + + +class FindURITask(Task): + def _function(self, song: Song, known_uris: dict) -> str | None: + logging.info( + __name__, f'Looking for "{song.yt_id}" song URI locally and online...' + ) + if uri := known_uris.get(song.yt_id): + logging.info(__name__, 'Found already known song URI') + return uri + + if downloads.is_downloaded(song): + song_path = downloads.get_file(song) + if song_path: + logging.info(__name__, 'Found local song URI') + return 'file://' + song_path + + if self.is_canceled(): + logging.info(__name__, 'Canceled URI lookup') + return None + + if uri := yt.get_song_uri(song): + logging.info(__name__, 'Found online song URI') + return uri + + logging.error(__name__, 'Failed to find song URI') + return None + + +class Player(GObject.Object): + def __init__(self): + super().__init__() + + Gst.init([]) + + self._find_uri_task = Task() + self._progress_task = Task() + self._radio_task = Task() + self._last_known_position = 0 + self._start_position = 0 + self._song_uris = {} + self._queue = Group() + self._queue_index = 0 + self.mode = PlaybackMode.NORMAL + self.state = PlaybackState.NONE + self.paused = False + self.buffering = False + + pulse_sink = Gst.ElementFactory.make('pulsesink', None) + pulse_sink.props.client_name = DISPLAY_NAME + pulse_sink.props.stream_properties = Gst.Structure.new_from_string( + 'props,' + f'application.name={DISPLAY_NAME},' + f'application.id={ID},' + f'application.icon_name={ID},' + 'media.role=music,' + ) + + self._playbin = Gst.ElementFactory.make('playbin3', None) + self._playbin.props.audio_sink = pulse_sink + self._playbin.set_state(Gst.State.READY) + self._playbin.get_bus().add_signal_watch() + self._playbin.get_bus().connect('message::error', self._on_bus_error) + self._playbin.get_bus().connect('message::latency', self._on_latency) + self._playbin.get_bus().connect('message::state-changed', self._on_state_change) + self._playbin.get_bus().connect('message::stream-start', self._on_stream_start) + self._playbin.get_bus().connect('message::buffering', self._on_buffering) + self._playbin.get_bus().connect('message::eos', self._on_stream_end) + + # Last part of ID and not DISPLAY_NAME as that can differ + self._mpris_server = Server(ID.split('.')[-1], EventHandler(self)) + self._mpris_event_sender = EventSender(self._mpris_server) + self._mpris_server.publish() + + self.set_mode(int(settings.load('mode', PlaybackMode.NORMAL))) + self.set_volume(float(settings.load('volume', 1.0))) + + @GObject.Signal(name='queue-changed') + def _queue_changed(self, _queue: object, _index: int): + return + + @GObject.Signal(name='recents-changed') + def _recents_changed(self): + return + + @GObject.Signal(name='progress-changed') + def _progress_changed(self, _progress: float): + return + + @GObject.Signal(name='buffering-changed') + def _buffering_changed(self, _progress: float): + return + + @GObject.Signal(name='state-changed') + def _state_changed(self, _state: int): + return + + @GObject.Signal(name='volume-changed') + def _volume_changed(self, _volume: float): + return + + @GObject.Signal(name='mode-changed') + def _mode_changed(self, _mode: int): + return + + @GObject.Signal(name='pause-changed') + def _pause_changed(self, _pause: bool): + return + + @GObject.Signal(name='raise') + def _raise(self): + return + + def _on_background_uri_search_done(self, task: FindURITask): + if task.result: + self._save_uri(task.extra_data.yt_id, task.result) + + if self._find_uri_task.is_running(): + return + + for song in self._queue.songs: + if song.yt_id not in self._song_uris: + self._find_uri_task = FindURITask( + callback=self._on_background_uri_search_done, + args=(song, self._song_uris) + ) + self._find_uri_task.extra_data = song + self._find_uri_task.start() + return + + logging.info(__name__, 'Found all song URIs for current queue') + + def _on_buffering(self, _bus: Gst.Bus, message: Gst.Message): + percentage = message.parse_buffering() + self.emit('buffering-changed', percentage / 100.0) + if percentage < 100: # noqa: PLR2004 - 100% + if not self.buffering: + logging.info(__name__, 'Buffering...') + self._playbin.set_state(Gst.State.PAUSED) + self.buffering = True + + return + + logging.info(__name__, 'Done buffering') + self.buffering = False + if self.state != PlaybackState.NONE: + self._playbin.set_state(Gst.State.PLAYING) + self.state = PlaybackState.PLAYING + self.emit('state-changed', self.state) + if self._start_position > 0: + logging.info(__name__, f'Seeking to {self._start_position}ns') + self._playbin.seek_simple( + Gst.Format.TIME, + Gst.SeekFlags.FLUSH | Gst.SeekFlags.ACCURATE, + self._start_position + ) + self._start_position = 0 + return + + logging.info( + __name__, 'Ignoring end of buffering as state is already NONE' + ) + + def _on_bus_error(self, _bus: Gst.Bus, message: Gst.Message): + logging.error(__name__, 'Bus error', message.parse_error().gerror.message) + self.pop_uri(self._queue.songs[self._queue_index].yt_id) + self.play( + self._queue.songs[self._queue_index], + self._queue, + self._last_known_position + ) + + def _on_latency(self, _bus: Gst.Bus, _message: Gst.Message): + if self._playbin.recalculate_latency(): + logging.info(__name__, 'Recalculated latency') + else: + logging.error(__name__, 'Failed to recalculate latency') + + def _on_radio_songs_found(self, task: FindRadioSongsTask): + if task.is_canceled() or self._radio_task is not task: + return + + if not task.result: + logging.warning(__name__, 'No radio songs found') + self.play(self._queue.songs[self._queue_index], self._queue) + return + + self.play(task.result.songs[0], task.result) + + def _on_state_change(self, _bus: Gst.Bus, _message: Gst.Message): + success, state, _d = self._playbin.get_state(1) + if success != Gst.StateChangeReturn.SUCCESS: + return + if state == Gst.State.PLAYING and self.paused: + logging.info(__name__, 'Adjusted state to paused after change') + self._playbin.set_state(Gst.State.PAUSED) + + def _on_stream_start(self, _bus: Gst.Bus, _message: Gst.Message): + if self.state == PlaybackState.LOADING and not self.buffering: + logging.info(__name__, 'Stream started') + self._playbin.set_state(Gst.State.PLAYING) + self.state = PlaybackState.PLAYING + self.emit('state-changed', self.state) + if self._start_position: + logging.info(__name__, f'Seeking to {self._start_position}ns') + self._playbin.seek_simple( + Gst.Format.TIME, + Gst.SeekFlags.FLUSH | Gst.SeekFlags.ACCURATE, + self._start_position + ) + self._start_position = 0 + + def _on_stream_end(self, _bus: Gst.Bus, _message): + logging.info(__name__, 'Stream has ended') + self.next() + + def _report_progress(self, _task: ReportProgressTask): + duration = self.get_duration_ns() + position = self.get_position_ns() + if duration > 0 and not self.buffering: + self.emit('progress-changed', position / duration) + if position > 0: + self._last_known_position = position + + def _save_uri(self, yt_id: str, uri: str): + while len(self._song_uris) > len(self._queue.songs): + self._song_uris.pop(next(iter(self._song_uris.keys()))) + + self._song_uris[yt_id] = uri + logging.info(__name__, f'Added URI for song "{yt_id}" to known') + + def _start_playback(self, task: FindURITask, position: int=0): + if task.is_canceled() or self._find_uri_task is not task: + logging.info(__name__, 'Ignoring callback from canceled URI lookup task') + return + + song = self._queue.songs[self._queue_index] + uri = task.result + if not uri: + logging.error(__name__, f'Failed to find URI for song "{song.yt_id}"') + self.play(song, self._queue, position) + return + + self._save_uri(song.yt_id, uri) + self._find_uri_task = FindURITask( + callback=self._on_background_uri_search_done, + args=(song, self._song_uris) + ) + self._find_uri_task.extra_data = song + self._find_uri_task.start() + + self._progress_task = ReportProgressTask( + progress_callback=self._report_progress + ) + self._progress_task.start() + + # Don't actually start yet - wait for messages on the bus + self._playbin.props.uri = uri + self._playbin.set_state(Gst.State.PAUSED) + logging.info(__name__, 'Started playback') + + def add_to_queue(self, group: Group): + if self._queue.songs: + self._queue.songs += group.songs + self.emit('queue-changed', self._queue, self._queue_index) + return + + self.play(group.songs[0], group) + + def get_current_song(self) -> Song | None: + if self._queue.songs: + return self._queue.songs[self._queue_index] + + return None + + def get_duration_ns(self) -> float: + return self._playbin.query_duration(Gst.Format.TIME)[1] + + def get_position_ns(self) -> float: + return self._playbin.query_position(Gst.Format.TIME)[1] + + def get_queue(self) -> Group: + return self._queue + + def get_volume(self) -> float: + return self._playbin.props.volume + + def move_song(self, song: Song, target: Song): + logging.info( + __name__, f'Moving song "{song.yt_id}" to "{target.yt_id}" in queue...' + ) + current_song = self._queue.songs[self._queue_index] + from_index = self._queue.songs.index(song) + to_index = self._queue.songs.index(target) + if abs(from_index - to_index) > 1: + self._queue.songs.pop(from_index) + self._queue.songs.insert(self._queue.songs.index(target), song) + else: + self._queue.songs[from_index], self._queue.songs[to_index] = ( + self._queue.songs[to_index], self._queue.songs[from_index] + ) + + self._queue_index = self._queue.songs.index(current_song) + self.emit('queue-changed', self._queue, self._queue_index) + logging.info(__name__, 'Moved song in queue') + + def next(self, from_user: bool=False): + if self.mode == PlaybackMode.RADIO: + if len(self._queue.songs) > self._queue_index + 1: + self.play(self._queue.songs[self._queue_index + 1], self._queue) + return + + self.state = PlaybackState.LOADING + self.emit('state-changed', self.state) + self._radio_task = FindRadioSongsTask( + callback=self._on_radio_songs_found, + args=(self._queue.songs[self._queue_index], self._queue) + ) + self._radio_task.start() + return + + if self.mode == PlaybackMode.LOOP_QUEUE: + if len(self._queue.songs) > self._queue_index + 1: + self.play(self._queue.songs[self._queue_index + 1], self._queue) + return + + self.play(self._queue.songs[0], self._queue) + return + + if self.mode == PlaybackMode.LOOP_SONG: + if from_user: + if len(self._queue.songs) > self._queue_index + 1: + self.play(self._queue.songs[self._queue_index + 1], self._queue) + return + + self.stop() + return + + self.play(self._queue.songs[self._queue_index], self._queue) + return + + if len(self._queue.songs) > self._queue_index + 1: + self.play(self._queue.songs[self._queue_index + 1], self._queue) + return + + self.stop() + + def play(self, song: Song, group: Group, position: int=0): + logging.info( + __name__, f'Playback of song "{song.yt_id}" at {position}ns requested' + ) + + recents.add(song) + self.emit('recents-changed') + self.state = PlaybackState.LOADING + self.emit('state-changed', self.state) + self._queue = copy.deepcopy(group) + self._queue_index = self._queue.songs.index(song) + self.emit('queue-changed', self._queue, self._queue_index) + self._playbin.set_state(Gst.State.NULL) + self._playbin.props.uri = '' + self.buffering = False + self.paused = False + if position <= 0: + self._start_position = 0 + self.emit('progress-changed', 0) + else: + self._start_position = position + self.emit('buffering-changed', 0) + + self._mpris_event_sender.emit_all() + + self._find_uri_task.cancel() + self._progress_task.cancel() + self._radio_task.cancel() + self._find_uri_task = FindURITask( + callback=self._start_playback, + callback_args=(position,), + args=(song, self._song_uris) + ) + self._find_uri_task.extra_data = song + self._find_uri_task.start() + + def pop_uri(self, yt_id: str) -> str | None: + if yt_id in self._song_uris: + logging.info(__name__, f'Popped known URI for song "{yt_id}"') + return self._song_uris.pop(yt_id) + + return None + + def previous(self): + if self._queue_index > 0: + self.play(self._queue.songs[self._queue_index - 1], self._queue) + return + + self.seek(0) + + def remove_from_queue(self, song: Song): + if len(self._queue.songs) > 1: + current_song = self._queue.songs[self._queue_index] + self._queue.songs.remove(song) + self._queue_index = self._queue.songs.index(current_song) + self.emit('queue-changed', self._queue, self._queue_index) + return + + self.stop() + + def seek(self, value: float): + seek_position = round(self.get_duration_ns() * value) + self._playbin.seek_simple( + Gst.Format.TIME, Gst.SeekFlags.FLUSH, max(seek_position, 0) + ) + self._mpris_event_sender.on_seek(value) + + def set_pause(self, pause: bool): + logging.info(__name__, f'Setting pause to "{pause}"...') + self.paused = pause + + if not self.buffering and self.state != PlaybackState.LOADING: + self._playbin.set_state( + Gst.State.PAUSED if self.paused else Gst.State.PLAYING + ) + + self.emit('pause-changed', pause) + self._mpris_event_sender.on_playpause() + logging.info(__name__, f'Set pause to "{pause}"') + + def set_volume( + self, volume: float, notify_frontend: bool=True, notify_mpris: bool=True + ): + settings.save({'volume': volume}) + self._playbin.props.volume = volume + if notify_mpris: + self._mpris_event_sender.on_volume() + if notify_frontend: + self.emit('volume-changed', volume) + + def set_mode(self, mode: int): + self.mode = mode + settings.save({'mode': mode}) + self.emit('mode-changed', self.mode) + + def shuffle(self): + logging.info(__name__, 'Shuffling songs...') + back_part = self._queue.songs[:self._queue_index] + front_part = self._queue.songs[self._queue_index + 1:] + + shuffled = [] + if len(back_part) > 1 or len(front_part) > 1: + while True: + shuffled = ( + random.sample(back_part, k=len(back_part)) + + [self._queue.songs[self._queue_index]] + + random.sample(front_part, k=len(front_part)) + ) + if shuffled != self._queue.songs: + self._queue.songs = shuffled + self.emit('queue-changed', self._queue, self._queue_index) + break + + logging.info(__name__, 'Shuffled songs') + + def stop(self): + logging.info(__name__, 'Stopping playback...') + self._find_uri_task.cancel() + self._progress_task.cancel() + self._radio_task.cancel() + self._playbin.set_state(Gst.State.NULL) + self._playbin.props.uri = '' + self.state = PlaybackState.NONE + self.emit('state-changed', self.state) + self.paused = False + self.buffering = False + self._queue = Group() + self._queue_index = 0 + self.emit('queue-changed', self._queue, self._queue_index) + self._mpris_event_sender.emit_all() + logging.info(__name__, 'Stopped playback') diff --git a/source/monophony/playlists.py b/source/monophony/playlists.py new file mode 100644 index 0000000..467d759 --- /dev/null +++ b/source/monophony/playlists.py @@ -0,0 +1,320 @@ +import json +import os +import time + +from monophony import NAME, logging, yt +from monophony.asynchronous import Task +from monophony.data import Artist, Group, Song + +from gi.repository import GLib + + +def _get_directory() -> str: + return os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + '/' + NAME + + +def _get_file_path() -> str: + return _get_directory() + '/playlists.json' + + +def _get_external_file_path() -> str: + return _get_directory() + '/external-playlists.json' + + +def add(playlist: Group) -> str: + logging.info(__name__, f'Adding playlist "{playlist.title}"...') + new_lists = read() + old_title = playlist.title + playlist.title = make_unique_name(playlist.title) + + song_ids = [] + unique_songs = [] + for song in playlist.songs: + if song.yt_id not in song_ids: + unique_songs.append(song) + song_ids.append(song.yt_id) + + playlist.songs = unique_songs + new_lists.append(playlist) + _write(playlists=new_lists) + logging.info(__name__, f'Added playlist "{old_title}" as "{playlist.title}"') + return playlist.title + + +def add_external(playlist: Group): + logging.info(__name__, f'Adding external playlist "{playlist.yt_id}"...') + lists = read_external() + + song_ids = [] + unique_songs = [] + for song in playlist.songs: + if song.yt_id not in song_ids: + unique_songs.append(song) + song_ids.append(song.yt_id) + + playlist.songs = unique_songs + lists.append(playlist) + _write(ext_playlists=lists) + logging.info(__name__, 'Added external playlist') + + +def rename(name: str, new_name: str) -> str: + new_name = make_unique_name(new_name) + logging.info(__name__, f'Renaming playlist "{name}" to "{new_name}"...') + + new_lists = read() + for playlist in new_lists: + if playlist.title == name: + playlist.title = new_name + _write(playlists=new_lists) + logging.info(__name__, 'Renamed playlist') + return new_name + + logging.error(__name__, 'Failed to rename: playlist not found') + return name + + +def delete(playlist_name: str): + logging.info(__name__, f'Deleting playlist "{playlist_name}"...') + _write( + playlists=[playlist for playlist in read() if playlist.title != playlist_name] + ) + logging.info(__name__, 'Deleted playlist') + + +def delete_external(playlist_name: str): + logging.info(__name__, f'Deleting external playlist "{playlist_name}"...') + _write( + ext_playlists=[ + playlist for playlist in read_external() if playlist.title != playlist_name + ] + ) + logging.info(__name__, 'Deleted external playlist') + + +def add_songs(songs: Group, playlist_name: str): + logging.info( + __name__, f'Adding {len(songs.songs)} songs to playlist "{playlist_name}"...' + ) + new_lists = read() + for song in songs.songs: + for playlist in new_lists: + if playlist.title == playlist_name: + for existing_song in playlist.songs: + if song.yt_id == existing_song.yt_id: + return + playlist.songs.append(song) + break + + _write(playlists=new_lists) + logging.info(__name__, 'Added songs to playlist') + + +def swap_songs(playlist_name: str, i: int, j: int): + logging.info( + __name__, f'Swapping songs #{i} and #{j} in playlist "{playlist_name}"...' + ) + new_lists = read() + for playlist in new_lists: + if playlist.title == playlist_name: + i = 0 if i >= len(playlist.songs) else i + j = 0 if j >= len(playlist.songs) else j + playlist.songs[i], playlist.songs[j] = playlist.songs[j], playlist.songs[i] + break + + _write(playlists=new_lists) + logging.info(__name__, 'Swapped songs') + + +def move_song(playlist_name: str, from_i: int, to_i: int): + logging.info( + __name__, + f'Moving song from #{from_i} to #{to_i} in playlist "{playlist_name}"...' + ) + new_lists = read() + for playlist in new_lists: + if playlist.title == playlist_name: + to_song = playlist.songs[to_i] + from_song = playlist.songs.pop(from_i) + playlist.songs.insert(playlist.songs.index(to_song), from_song) + break + + _write(playlists=new_lists) + logging.info(__name__, 'Moved song') + + +def remove_song(song: Song, playlist_name: str): + logging.info( + __name__, f'Removing song "{song.yt_id}" from playlist "{playlist_name}"...' + ) + new_lists = read() + for playlist in new_lists: + if playlist.title == playlist_name: + playlist.songs = [s for s in playlist.songs if s.yt_id != song.yt_id] + break + + _write(playlists=new_lists) + logging.info(__name__, 'Removed song') + + +def make_unique_name(name: str) -> str: + taken_names = ( + [playlist.title for playlist in read()] + + [playlist.title for playlist in read_external()] + ) + new_name = name or _('Playlist') + + i = 1 + while new_name in taken_names: + new_name = f'{name} ({i})' + i += 1 + + return new_name + + +def _write(playlists: list[Group] | None=None, ext_playlists: list[Group] | None=None): + lock.lock() + logging.info( + __name__, + f'Writing {len(playlists) if playlists else "no"} playlists and ' + f'{len(ext_playlists) if ext_playlists else "no"} external playlists...' + ) + lists_path = _get_file_path() + ext_lists_path = _get_external_file_path() + os.makedirs(_get_directory(), exist_ok=True) + + if playlists is not None: + serialized_playlists = {} + for playlist in playlists: + serialized_playlists[playlist.title] = playlist.serialize()['contents'] + with open(lists_path, 'w') as lists_file: + json.dump(serialized_playlists, lists_file, indent='\t') + + if ext_playlists is not None: + with open(ext_lists_path, 'w') as ext_lists_file: + json.dump( + [playlist.serialize() for playlist in ext_playlists], + ext_lists_file, + indent='\t' + ) + + logging.info(__name__, 'Done writing playlist and external playlists') + lock.unlock() + + +def read() -> list[Group]: + lock.lock() + try: + with open(_get_file_path()) as lists_file: + result = [ + Group( + title=name, + songs=[ + Song( + title=song.get('title', ''), + author=Artist( + name=song.get('author', ''), + yt_id=song.get('author_id', '') + ), + length=song.get('length', ''), + thumbnail=song.get('thumbnail', ''), + yt_id=song.get('id', '') + ) for song in songs + ] + ) for name, songs in json.load(lists_file).items() + ] + lock.unlock() + return result + except (OSError, json.decoder.JSONDecodeError): + lock.unlock() + return [] + + +def read_external() -> list[Group]: + lock.lock() + try: + with open(_get_external_file_path()) as lists_file: + result = [ + Group( + title=playlist.get('title', ''), + yt_id=playlist.get('id', ''), + songs=[ + Song( + title=song.get('title', ''), + author=Artist( + name=song.get('author', ''), + yt_id=song.get('author_id', '') + ), + length=song.get('length', ''), + thumbnail=song.get('thumbnail', ''), + yt_id=song.get('id', '') + ) for song in playlist.get('contents', []) + ] + ) for playlist in json.load(lists_file) + ] + lock.unlock() + return result + except (OSError, json.decoder.JSONDecodeError): + lock.unlock() + return [] + + +class ImportTask(Task): + def _function( + self, name: str, url: str, local: bool, overwrite: bool=False + ) -> bool: + logging.info(__name__, f'Importing playlist "{url}"...') + new_lists = [playlist for playlist in read() if playlist.title != name] + new_ext_lists = [ + playlist for playlist in read_external() if playlist.title != name + ] + + if not ( + playlist := yt.get_album_or_playlist(url.split('list=')[-1].split('&')[0]) + ): + logging.error(__name__, 'Failed to import playlist') + return False + + playlist.title = make_unique_name( + name if local else playlist.title + ) if not overwrite else name + + if local: + new_lists.append(playlist) + _write(playlists=new_lists) + else: + new_ext_lists.append(playlist) + _write(ext_playlists=new_ext_lists) + + logging.info(__name__, 'Imported playlist') + return True + + +class UpdateExternalTask(Task): + def _function(self): + logging.info(__name__, 'Updating external playlists...') + tasks = [] + external = read_external() + for i, playlist in enumerate(external): + task = ImportTask(args=(playlist.title, playlist.yt_id, False, True)) + task.start() + tasks.append(task) + + # Rate limit + if i % 4 == 0: + self._update_progress((i / len(external)) / 2) + while task.is_running(): + time.sleep(0.5) + + for i, task in enumerate(tasks): + while task.is_running(): + self._update_progress((i / len(tasks)) / 2 + 0.5) + time.sleep(0.5) + + logging.info(__name__, 'Updated external playlists') + + +# Signleton +lock = GLib.Mutex() diff --git a/source/monophony/recents.py b/source/monophony/recents.py new file mode 100644 index 0000000..b16b721 --- /dev/null +++ b/source/monophony/recents.py @@ -0,0 +1,68 @@ +import json +import os +import traceback + +from monophony import NAME, logging +from monophony.data import Artist, Group, Song + + +MAX_SONGS = 15 + + +def _get_directory() -> str: + return os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + f'/{NAME}' + + +def _get_file_path() -> str: + return _get_directory() + '/recent-songs.json' + + +def _write(group: Group): + if len(group.songs) > MAX_SONGS: + group.songs = group.songs[:MAX_SONGS] + + logging.info(__name__, f'Writing {len(group.songs)} songs to recents...') + os.makedirs(_get_directory(), exist_ok=True) + try: + with open(_get_file_path(), 'w') as recents_file: + json.dump(group.serialize()['contents'], recents_file, indent='\t') + except OSError: + logging.error(__name__, 'Failed to write to recents', traceback.format_exc()) + return + + logging.info(__name__, 'Done writing to recents') + + +def add(song: Song): + logging.info(__name__, f'Adding song "{song.yt_id}" to recents...') + group = read() + group.songs = [song, *group.songs] + _write(group) + logging.info(__name__, 'Added song to recents') + + +def clear(): + _write(Group()) + + +def read() -> Group: + try: + with open(_get_file_path()) as recents_file: + return Group( + songs=[ + Song( + title=item.get('title', ''), + author=Artist( + name=item.get('author', ''), + yt_id=item.get('author_id', '') + ), + length=item.get('length', ''), + thumbnail=item.get('thumbnail', ''), + yt_id=item.get('id', '') + ) for item in json.load(recents_file) + ] + ) + except (OSError, json.decoder.JSONDecodeError): + return Group() diff --git a/source/monophony/recommendations.py b/source/monophony/recommendations.py new file mode 100644 index 0000000..b63146a --- /dev/null +++ b/source/monophony/recommendations.py @@ -0,0 +1,54 @@ +import json +import os + +from monophony import NAME, logging +from monophony.data import Artist, Group, Song + + +def _get_directory() -> str: + return os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + '/' + NAME + + +def _get_file_path() -> str: + return _get_directory() + '/recommendations.json' + + +def write(recommendations: list[Group]): + logging.info(__name__, f'Writing {len(recommendations)} recommendations...') + recommendations_path = _get_file_path() + os.makedirs(_get_directory(), exist_ok=True) + + serialized_recommendations = {} + for group in recommendations: + serialized_recommendations[group.title] = group.serialize()['contents'] + with open(recommendations_path, 'w') as recommendations_file: + json.dump(serialized_recommendations, recommendations_file, indent='\t') + + logging.info(__name__, 'Done writing recommendations') + + +def read() -> list[Group]: + try: + with open(_get_file_path()) as recommendations_file: + return [ + Group( + title=name, + songs=[ + Song( + title=song.get('title', ''), + author=Artist( + name=song.get('author', ''), + yt_id=song.get('author_id', '') + ), + length=song.get('length', ''), + thumbnail=song.get('thumbnail', ''), + yt_id=song.get('id', '') + ) for song in songs + ] + ) for name, songs in json.load(recommendations_file).items() + ] + except (OSError, json.decoder.JSONDecodeError): + return [] + diff --git a/source/monophony/settings.py b/source/monophony/settings.py new file mode 100644 index 0000000..ab7e26d --- /dev/null +++ b/source/monophony/settings.py @@ -0,0 +1,46 @@ +import json +import os +from typing import Any + +from monophony import NAME, logging + + +def save(values: dict): + logging.info(__name__, f'Saving settings "{values}"...') + + settings = _read() + for key, value in values.items(): + settings[key] = value + + _write(settings) + logging.info(__name__, 'Saved settings') + + +def load(key: str, default: Any=None) -> Any: + return _read().get(key, default) + + +def _write(settings: dict): + logging.info(__name__, 'Writing settings...') + directory = os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + '/' + NAME + settings_path = directory + '/settings.json' + + os.makedirs(directory, exist_ok=True) + with open(settings_path, 'w') as settings_file: + json.dump(settings, settings_file, indent='\t') + + logging.info(__name__, 'Done writing settings') + + +def _read() -> dict: + settings_path = os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + f'/{NAME}/settings.json' + + try: + with open(settings_path) as settings_file: + return json.load(settings_file) + except (OSError, json.decoder.JSONDecodeError): + return {} diff --git a/source/monophony/backend/__init__.py b/source/monophony/ui/__init__.py similarity index 100% rename from source/monophony/backend/__init__.py rename to source/monophony/ui/__init__.py diff --git a/source/monophony/frontend/pages/__init__.py b/source/monophony/ui/bars/__init__.py similarity index 100% rename from source/monophony/frontend/pages/__init__.py rename to source/monophony/ui/bars/__init__.py diff --git a/source/monophony/ui/bars/header_bar.py b/source/monophony/ui/bars/header_bar.py new file mode 100644 index 0000000..90bfdac --- /dev/null +++ b/source/monophony/ui/bars/header_bar.py @@ -0,0 +1,29 @@ +import weakref + +from monophony.debug import MemoryDebugger + +from gi.repository import Adw, GObject, Gtk + + +class HeaderBar(MemoryDebugger, Adw.Bin): + __gtype_name__ = __qualname__ + + def __init__(self): + super().__init__() + + about_button = Gtk.Button.new_from_icon_name('help-about-symbolic') + about_button.props.tooltip_text = _('About') + about_button.connect( + 'clicked', + lambda _button, ref: ref().emit('show-about'), + weakref.ref(self) + ) + + header_bar = Adw.HeaderBar() + header_bar.pack_end(about_button) + + self.props.child = header_bar + + @GObject.Signal(name='show-about') + def _show_about(self): + return diff --git a/source/monophony/ui/bars/player_bar.py b/source/monophony/ui/bars/player_bar.py new file mode 100644 index 0000000..ab38be9 --- /dev/null +++ b/source/monophony/ui/bars/player_bar.py @@ -0,0 +1,302 @@ +import weakref + +from monophony import ID +from monophony.data import PlaybackMode, PlaybackState, Song + +from gi.repository import Adw, Gdk, Gio, GLib, GObject, GstAudio, Gtk, Pango + + +class PlayerBar(Gtk.Box): + __gtype_name__ = __qualname__ + + def __init__(self): + super().__init__(orientation=Gtk.Orientation.VERTICAL) + + self._buffer_bar = Gtk.ProgressBar() + self._buffer_bar.add_css_class('buffbar') + + self._progress_bar = Gtk.Scale.new_with_range( + Gtk.Orientation.HORIZONTAL, 0, 1, 0.01 + ) + self._progress_bar.add_css_class('seekbar') + self._progress_bar.props.draw_value = False + self._progress_bar.props.halign = Gtk.Align.FILL + self._progress_bar.props.valign = Gtk.Align.END + self._progress_bar.connect( + 'change-value', + lambda _bar, _scroll, value, ref: ref().emit('seek', value), + weakref.ref(self) + ) + + css = Gtk.CssProvider() + css.load_from_data(''' + .title-link { + padding-top: 0px; + padding-bottom: 0px; + padding-left: 0px; + padding-right: 0px; + margin-bottom: -8px; + margin-top: -8px; + } + + .buffbar { + min-height: 10px; + margin-bottom: -6px; + margin-top: 0px; + } + + .buffbar trough { + border-radius: 0px; + min-height: 10px; + } + + .buffbar progress { + border-radius: 0px; + min-height: 10px; + background-color: var(--sidebar-fg-color); + opacity: 0.5; + } + + .seekbar { + margin-top: -10px; + margin-bottom: 0px; + padding: 0px; + min-height: 10px; + } + + .seekbar trough, .seekbar highlight { + border-radius: 0px; + border-left: none; + border-right: none; + min-height: 10px; + } + + .seekbar highlight { + border-left: none; + border-right: none; + } + + .player { + padding: 0px; + background-color: var(--headerbar-bg-color); + } + ''', -1) + Gtk.StyleContext.add_provider_for_display( + Gdk.Display.get_default(), + css, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + + self.queue_button = Gtk.ToggleButton() + self.queue_button.props.icon_name = 'view-list-symbolic' + self.queue_button.props.valign = Gtk.Align.CENTER + self.queue_button.props.tooltip_text = _('Queue') + + self._title_link = Gtk.LinkButton.new_with_label('', '') + self._title_link.props.margin_bottom = 2 + self._title_link.props.margin_top = 6 + self._title_link.props.halign = Gtk.Align.START + self._title_link.props.child.props.ellipsize = Pango.EllipsizeMode.END + self._title_link.add_css_class('title-link') + + self._artist_label = Gtk.Label() + self._artist_label.props.halign = Gtk.Align.START + self._artist_label.props.ellipsize = Pango.EllipsizeMode.END + self._artist_label.add_css_class('caption') + self._artist_label.add_css_class('dim-label') + + info_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + info_box.props.spacing = 4 + info_box.props.margin_start = 6 + info_box.props.margin_end = 6 + info_box.props.halign = Gtk.Align.START + info_box.props.valign = Gtk.Align.CENTER + info_box.props.hexpand = True + info_box.append(self._title_link) + info_box.append(self._artist_label) + + self._mode_button = Gtk.MenuButton() + self._mode_button.props.icon_name = 'media-playlist-repeat-song-symbolic' + self._mode_button.props.tooltip_text = _('Playback Mode') + self._mode_button.props.valign = Gtk.Align.CENTER + self._mode_button.props.halign = Gtk.Align.END + self._mode_button.props.hexpand = True + self._mode_button.set_create_popup_func(self._on_create_mode_popup) + + previous_button = Gtk.Button.new_from_icon_name('media-skip-backward-symbolic') + previous_button.props.tooltip_text = _('Previous') + previous_button.props.valign = Gtk.Align.CENTER + previous_button.connect( + 'clicked', + lambda _button, ref: ref().emit('previous-song'), + weakref.ref(self) + ) + + self._spinner = Adw.Spinner() + self._spinner.props.margin_start = 9 + self._spinner.props.margin_end = 9 + self._spinner.props.visible = False + + self._pause_button = Gtk.Button.new_from_icon_name( + 'media-playback-pause-symbolic' + ) + self._pause_button.props.tooltip_text = _('Pause') + self._pause_button.props.valign = Gtk.Align.CENTER + self._pause_button.connect( + 'clicked', + lambda _button, ref: ref().emit('pause'), + weakref.ref(self) + ) + + next_button = Gtk.Button.new_from_icon_name('media-skip-forward-symbolic') + next_button.props.tooltip_text = _('Next') + next_button.props.valign = Gtk.Align.CENTER + next_button.connect( + 'clicked', lambda _button, ref: ref().emit('next-song'), weakref.ref(self) + ) + + self._volume_button = Gtk.ScaleButton.new(0, 1, 0.02, [ + 'audio-volume-muted-symbolic', + 'audio-volume-high-symbolic', + 'audio-volume-low-symbolic', + 'audio-volume-medium-symbolic', + 'audio-volume-high-symbolic' + ]) + self._volume_button.props.tooltip_text = _('Volume') + self._volume_button.props.valign = Gtk.Align.CENTER + self._volume_button.props.halign = Gtk.Align.END + self._volume_button.connect( + 'value-changed', + lambda _button, value, ref: ref().emit( + 'volume-changed', + GstAudio.stream_volume_convert_volume( + GstAudio.StreamVolumeFormat.CUBIC, + GstAudio.StreamVolumeFormat.LINEAR, + value + ) + ), + weakref.ref(self) + ) + + controls_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + controls_box.props.spacing = 6 + controls_box.props.margin_top = 2 + controls_box.props.margin_bottom = 8 + controls_box.props.margin_start = 8 + controls_box.props.margin_end = 8 + controls_box.props.halign = Gtk.Align.FILL + controls_box.props.hexpand = True + controls_box.append(self.queue_button) + controls_box.append(info_box) + controls_box.append(self._mode_button) + controls_box.append(previous_button) + controls_box.append(self._spinner) + controls_box.append(self._pause_button) + controls_box.append(next_button) + controls_box.append(self._volume_button) + + self._mode = PlaybackMode.NORMAL + self.add_css_class('toolbar') + self.add_css_class('player') + self.append(self._buffer_bar) + self.append(self._progress_bar) + self.append(controls_box) + + @GObject.Signal(name='mode-changed') + def _mode_changed(self, _mode: int): + return + + @GObject.Signal(name='next-song') + def _next_song(self): + return + + @GObject.Signal(name='pause') + def _pause(self): + return + + @GObject.Signal(name='previous-song') + def _previous_song(self): + return + + @GObject.Signal(name='seek') + def _seek(self, _value: float): + return + + @GObject.Signal(name='volume-changed') + def _volume_changed(self, _volume: float): + return + + def _on_create_mode_popup(self, button: Gtk.MenuButton): + menu = Gio.Menu() + popover_menu = Gtk.PopoverMenu() + popover_menu.props.menu_model = menu + + button_group = None + for mode, label in { + PlaybackMode.NORMAL: _('Normal Playback'), + PlaybackMode.LOOP_SONG: _('Repeat Song'), + PlaybackMode.LOOP_QUEUE: _('Repeat Queue'), + PlaybackMode.RADIO: _('Autoplay Similar') + }.items(): + check_button = Gtk.CheckButton.new_with_label(label) + check_button.props.margin_bottom = 6 + check_button.props.margin_start = 6 + check_button.props.margin_end = 6 + check_button.props.active = self._mode == mode + check_button.connect('toggled', self._on_mode_toggled, mode) + if button_group: + check_button.props.group = button_group + else: + check_button.props.margin_top = 6 + button_group = check_button + + item = Gio.MenuItem() + item.set_attribute_value('custom', GLib.Variant.new_string(str(mode))) + menu.append_item(item) + popover_menu.add_child(check_button, str(mode)) + + button.props.popover = popover_menu + + def _on_mode_toggled(self, _button: Gtk.CheckButton, mode: int): + self.emit('mode-changed', mode) + + def update_song(self, song: Song): + # Simply using .props.label would create a new child label and discard + # previously set properties + self._title_link.props.child.props.label = song.title + self._title_link.props.uri = ( + 'https://music.youtube.com/watch?v=' + song.yt_id + ) + self._artist_label.props.label = song.author.name + + def update_pause(self, pause: bool): + self._pause_button.props.icon_name = ( + f'media-playback-{"start" if pause else "pause"}-symbolic' + ) + + def update_progress(self, progress: float): + self._progress_bar.set_value(progress) + + def update_state(self, state: int): + self._spinner.props.visible = state == PlaybackState.LOADING + self._pause_button.props.visible = not self._spinner.props.visible + self._progress_bar.props.sensitive = state != PlaybackState.LOADING + + def update_buffering(self, progress: float): + self._buffer_bar.props.fraction = progress + + def update_volume(self, volume: float): + self._volume_button.props.value = GstAudio.stream_volume_convert_volume( + GstAudio.StreamVolumeFormat.LINEAR, + GstAudio.StreamVolumeFormat.CUBIC, + volume + ) + + def update_mode(self, mode: int): + self._mode = mode + self._mode_button.props.icon_name = { + PlaybackMode.NORMAL: 'media-playlist-consecutive-symbolic', + PlaybackMode.LOOP_SONG: 'media-playlist-repeat-song-symbolic', + PlaybackMode.LOOP_QUEUE: 'media-playlist-repeat-symbolic', + PlaybackMode.RADIO: ID + '-symbolic', + }[self._mode] diff --git a/source/monophony/ui/bars/search_bar.py b/source/monophony/ui/bars/search_bar.py new file mode 100644 index 0000000..955984d --- /dev/null +++ b/source/monophony/ui/bars/search_bar.py @@ -0,0 +1,37 @@ +from gi.repository import Adw, GObject, Gtk + + +class SearchBar(Adw.Bin): + __gtype_name__ = __qualname__ + + def __init__(self): + super().__init__() + + self._search_entry = Gtk.SearchEntry() + self._search_entry.props.placeholder_text = _('Search...') + self._search_entry.props.margin_start = 18 + self._search_entry.props.margin_end = 18 + self._search_entry.props.hexpand = True + self._search_entry.props.halign = Gtk.Align.FILL + self._search_entry.connect( + 'activate', lambda entry: self.emit('search', entry.props.text, '') + ) + + search_clamp = Adw.Clamp() + search_clamp.props.maximum_size = 590 + search_clamp.props.child = self._search_entry + + search_bar = Adw.HeaderBar() + search_bar.props.title_widget = search_clamp + search_bar.props.show_back_button = False + search_bar.props.show_start_title_buttons = False + search_bar.props.show_end_title_buttons = False + + self.props.child = search_bar + + @GObject.Signal(name='search', arg_types=(str, str)) + def _search(self, _query: str, _filter: str): + return + + def focus_search(self): + self._search_entry.grab_focus() diff --git a/source/monophony/frontend/popovers/__init__.py b/source/monophony/ui/pages/__init__.py similarity index 100% rename from source/monophony/frontend/popovers/__init__.py rename to source/monophony/ui/pages/__init__.py diff --git a/source/monophony/ui/pages/artist_page.py b/source/monophony/ui/pages/artist_page.py new file mode 100644 index 0000000..25cfe84 --- /dev/null +++ b/source/monophony/ui/pages/artist_page.py @@ -0,0 +1,11 @@ +from monophony.ui.pages.results_page import ResultsPage +from monophony.yt import SearchResult + + +class ArtistPage(ResultsPage): + __gtype_name__ = __qualname__ + + def __init__(self, results: list[SearchResult], filter_: str | None): + super().__init__(results, filter_) + + self.props.title = _('Artist Page') diff --git a/source/monophony/ui/pages/home_page.py b/source/monophony/ui/pages/home_page.py new file mode 100644 index 0000000..bc880f5 --- /dev/null +++ b/source/monophony/ui/pages/home_page.py @@ -0,0 +1,493 @@ +import weakref + +from monophony import downloads, playlists, recents, recommendations +from monophony.data import Artist, Group, Song +from monophony.ui.bars.search_bar import SearchBar +from monophony.ui.pages.page import Page +from monophony.ui.row_groups.editable_group_row_group import EditableGroupRowGroup +from monophony.ui.row_groups.group_row_group import GroupRowGroup +from monophony.ui.row_groups.queueable_row_group import QueueableRowGroup +from monophony.ui.row_groups.synchronized_group_row_group import ( + SynchronizedGroupRowGroup, +) + +from gi.repository import Adw, Gdk, Gio, GLib, GObject, Gtk + + +class HomePage(Page): + __gtype_name__ = __qualname__ + + def __init__(self): + super().__init__() + + self._deleted_playlists = [] + + self._recommended_group = GroupRowGroup() + self._recommended_group.props.title = _('Recommended') + self._recommended_group.props.margin_start = 12 + self._recommended_group.props.margin_end = 12 + self._recommended_group.connect( + 'play', + lambda _group, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + self._recommended_group.connect( + 'queue-song', + lambda _group, song, ref: ref().emit('queue-song', song), + weakref.ref(self) + ) + self._recommended_group.connect( + 'add-song-to', + lambda _group, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + self._recommended_group.connect( + 'view-artist', + lambda _group, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + self._recommended_group.connect( + 'undownload-song', + lambda _group, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + self._recommended_group.connect( + 'download-song', + lambda _group, song, ref: ref().emit('download-song', song), + weakref.ref(self) + ) + self._recommended_group.connect( + 'queue-group', + lambda _group, group, ref: ref().emit('queue-group', group), + weakref.ref(self) + ) + self._recommended_group.connect( + 'add-group-to', + lambda _group, group, ref: ref().emit('add-group-to', group), + weakref.ref(self) + ) + self._recommended_group.connect( + 'download-group', + lambda _group, group, ref: ref().emit('download-group', group), + weakref.ref(self) + ) + + self._playlists_group = EditableGroupRowGroup() + self._playlists_group.props.title = _('Your Playlists') + self._playlists_group.props.margin_start = 12 + self._playlists_group.props.margin_end = 12 + self._playlists_group.connect( + 'play', + lambda _group, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + self._playlists_group.connect( + 'queue-song', + lambda _group, song, ref: ref().emit('queue-song', song), + weakref.ref(self) + ) + self._playlists_group.connect( + 'add-song-to', + lambda _group, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + self._playlists_group.connect( + 'view-artist', + lambda _group, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + self._playlists_group.connect( + 'undownload-song', + lambda _group, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + self._playlists_group.connect( + 'download-song', + lambda _group, song, ref: ref().emit('download-song', song), + weakref.ref(self) + ) + self._playlists_group.connect( + 'queue-group', + lambda _group, group, ref: ref().emit('queue-group', group), + weakref.ref(self) + ) + self._playlists_group.connect( + 'add-group-to', + lambda _group, group, ref: ref().emit('add-group-to', group), + weakref.ref(self) + ) + self._playlists_group.connect( + 'download-group', + lambda _group, group, ref: ref().emit('download-group', group), + weakref.ref(self) + ) + self._playlists_group.connect( + 'delete-playlist', + lambda _group, playlist, ref: HomePage._on_delete_playlist(ref(), playlist), + weakref.ref(self) + ) + + no_playlists_group = Adw.PreferencesGroup() + no_playlists_group.props.title = _('Your Playlists') + no_playlists_group.props.description = _( + 'Playlists you create will appear here' + ) + no_playlists_group.props.margin_start = 12 + no_playlists_group.props.margin_end = 12 + no_playlists_group.bind_property( + 'visible', + self._playlists_group, + 'visible', + GObject.BindingFlags.BIDIRECTIONAL | + GObject.BindingFlags.SYNC_CREATE | + GObject.BindingFlags.INVERT_BOOLEAN + ) + + import_button = Adw.ButtonRow() + import_button.props.start_icon_name = 'list-add-symbolic' + import_button.props.title = _('Import') + import_button.connect( + 'activated', + lambda _button, ref: ref().emit('import-group', Group()), + weakref.ref(self) + ) + + import_group = Adw.PreferencesGroup() + import_group.props.margin_start = 12 + import_group.props.margin_end = 12 + import_group.add(import_button) + + self._external_playlists_group = SynchronizedGroupRowGroup() + self._external_playlists_group.props.title = _('Synchronized Playlists') + self._external_playlists_group.props.margin_start = 12 + self._external_playlists_group.props.margin_end = 12 + self._external_playlists_group.connect( + 'play', + lambda _group, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'queue-song', + lambda _group, song, ref: ref().emit('queue-song', song), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'add-song-to', + lambda _group, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'view-artist', + lambda _group, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'undownload-song', + lambda _group, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'download-song', + lambda _group, song, ref: ref().emit('download-song', song), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'queue-group', + lambda _group, group, ref: ref().emit('queue-group', group), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'add-group-to', + lambda _group, group, ref: ref().emit('add-group-to', group), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'download-group', + lambda _group, group, ref: ref().emit('download-group', group), + weakref.ref(self) + ) + self._external_playlists_group.connect( + 'delete-playlist', + lambda _group, playlist, ref: + HomePage._on_delete_external_playlist(ref(), playlist), + weakref.ref(self) + ) + + open_dir_button = Gtk.Button.new_from_icon_name('folder-symbolic') + open_dir_button.props.tooltip_text = _('Downloads Directory') + open_dir_button.connect( + 'clicked', + lambda _button: + Gio.AppInfo.launch_default_for_uri( + 'file://' + downloads.get_directory() + ) + ) + + self._downloads_group = QueueableRowGroup() + self._downloads_group.props.title = _('Downloads') + self._downloads_group.props.header_suffix = open_dir_button + self._downloads_group.props.margin_start = 12 + self._downloads_group.props.margin_end = 12 + self._downloads_group.connect( + 'play', + lambda _group, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + self._downloads_group.connect( + 'queue-song', + lambda _group, song, ref: ref().emit('queue-song', song), + weakref.ref(self) + ) + self._downloads_group.connect( + 'add-song-to', + lambda _group, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + self._downloads_group.connect( + 'view-artist', + lambda _group, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + self._downloads_group.connect( + 'undownload-song', + lambda _group, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + + clear_button = Gtk.Button.new_from_icon_name('edit-clear-all-symbolic') + clear_button.add_css_class('destructive-action') + clear_button.props.tooltip_text = _('Clear') + clear_button.connect( + 'clicked', + lambda _button, ref: HomePage._on_clear_history(ref()), + weakref.ref(self) + ) + + self._history_group = QueueableRowGroup() + self._history_group.props.title = _('Recently Played') + self._history_group.props.header_suffix = clear_button + self._history_group.props.margin_start = 12 + self._history_group.props.margin_end = 12 + self._history_group.connect( + 'play', + lambda _group, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + self._history_group.connect( + 'queue-song', + lambda _group, song, ref: ref().emit('queue-song', song), + weakref.ref(self) + ) + self._history_group.connect( + 'add-song-to', + lambda _group, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + self._history_group.connect( + 'view-artist', + lambda _group, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + self._history_group.connect( + 'undownload-song', + lambda _group, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + self._history_group.connect( + 'download-song', + lambda _group, song, ref: ref().emit('download-song', song), + weakref.ref(self) + ) + + donate_button = Adw.ButtonRow() + donate_button.props.start_icon_name = 'emote-love-symbolic' + donate_button.props.title = _('Donate') + donate_button.add_css_class('donate-button') + donate_button.connect( + 'activated', + lambda _button, ref: HomePage._on_donate(ref()), + weakref.ref(self) + ) + + css = Gtk.CssProvider() + css.load_from_data(''' + .donate-button { + background-color: var(--accent-red); + color: var(--light-1); + } + ''', -1) + Gtk.StyleContext.add_provider_for_display( + Gdk.Display.get_default(), + css, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + + donate_group = Adw.PreferencesGroup() + donate_group.props.margin_start = 12 + donate_group.props.margin_end = 12 + donate_group.add(donate_button) + + self._page.add(self._recommended_group) + self._page.add(self._playlists_group) + self._page.add(no_playlists_group) + self._page.add(import_group) + self._page.add(self._external_playlists_group) + self._page.add(self._downloads_group) + self._page.add(self._history_group) + self._page.add(donate_group) + + self._search_bar = SearchBar() + self._search_bar.connect( + 'search', + lambda _bar, query, filter_, ref: ref().emit('search', query, filter_), + weakref.ref(self) + ) + + self._toolbar_view.add_top_bar(self._search_bar) + + self.props.title = _('Home') + self.update_playlists() + self.update_recommendations() + self.update_external_playlists() + self.update_history() + + @GObject.Signal(name='play', arg_types=(object, object)) + def _play(self, _song: Song, _group: Group): + return + + @GObject.Signal(name='search', arg_types=(str, str)) + def _search(self, _query: str, _filter: str): + return + + @GObject.Signal(name='queue-song', arg_types=(object,)) + def _queue_song(self, _song: Song): + return + + @GObject.Signal(name='add-song-to', arg_types=(object,)) + def _add_song_to(self, _song: Song): + return + + @GObject.Signal(name='view-artist', arg_types=(object,)) + def _view_artist(self, _artist: Artist): + return + + @GObject.Signal(name='undownload-song', arg_types=(object,)) + def _undownload_song(self, _song: Song): + return + + @GObject.Signal(name='download-song', arg_types=(object,)) + def _download_song(self, _song: Song): + return + + @GObject.Signal(name='queue-group', arg_types=(object,)) + def _queue_group(self, _group: Group): + return + + @GObject.Signal(name='add-group-to', arg_types=(object,)) + def _add_group_to(self, _group: Group): + return + + @GObject.Signal(name='download-group', arg_types=(object,)) + def _download_group(self, _group: Group): + return + + @GObject.Signal(name='import-group', arg_types=(object,)) + def _import_group(self, _group: Group): + return + + def _on_clear_history(self): + recents.clear() + self.update_history() + + def _on_delete_external_playlist(self, playlist: Group): + playlists.delete_external(playlist.title) + self._deleted_playlists.append(playlist) + + toast = Adw.Toast() + toast.props.title = _('Deleted playlist "{name}"').format( + name=GLib.markup_escape_text(playlist.title, -1) + ) + toast.props.priority = Adw.ToastPriority.HIGH + toast.props.button_label = _('Undo') + toast.connect( + 'button-clicked', + lambda _toast, ref: HomePage._on_delete_toast_undo(ref(), True), + weakref.ref(self) + ) + toast.connect( + 'dismissed', + lambda _toast, ref: HomePage._on_delete_toast_dismissed(ref()), + weakref.ref(self) + ) + self._toast_overlay.add_toast(toast) + self.update_external_playlists() + + def _on_delete_toast_dismissed(self): + self._deleted_playlists.pop() + self.update_playlists() + self.update_external_playlists() + self._toast_overlay.props.sensitive = True + + def _on_delete_playlist(self, playlist: Group): + playlists.delete(playlist.title) + self._deleted_playlists.append(playlist) + + toast = Adw.Toast() + toast.props.title = _('Deleted playlist "{name}"').format( + name=GLib.markup_escape_text(playlist.title, -1) + ) + toast.props.priority = Adw.ToastPriority.HIGH + toast.props.button_label = _('Undo') + toast.connect( + 'button-clicked', + lambda _toast, ref: HomePage._on_delete_toast_undo(ref(), False), + weakref.ref(self) + ) + toast.connect( + 'dismissed', + lambda _toast, ref: HomePage._on_delete_toast_dismissed(ref()), + weakref.ref(self) + ) + self._toast_overlay.add_toast(toast) + self.update_playlists() + + def _on_delete_toast_undo(self, external_playlist: bool): + self._toast_overlay.props.sensitive = False + if external_playlist: + playlists.add_external(self._deleted_playlists[-1]) + else: + playlists.add(self._deleted_playlists[-1]) + + def _on_donate(self): + launcher = Gtk.UriLauncher() + launcher.props.uri = 'https://zeh-kira.itch.io/monophony/purchase' + launcher.launch() + + def focus_search(self): + self._search_bar.focus_search() + + def update_external_playlists(self): + self._external_playlists_group.update_contents(playlists.read_external()) + + def update_downloads(self, downloads: Group): + self._downloads_group.update_contents(downloads.songs) + + def update_download_status(self): + for group in ( + self._recommended_group, + self._playlists_group, + self._external_playlists_group, + self._downloads_group, + self._history_group + ): + group.update_download_status() + + def update_history(self): + new_contents = recents.read().songs + self._history_group.update_contents(new_contents) + + def update_playlists(self): + self._playlists_group.update_contents(playlists.read()) + + def update_recommendations(self): + self._recommended_group.update_contents(recommendations.read()) diff --git a/source/monophony/ui/pages/loading_page.py b/source/monophony/ui/pages/loading_page.py new file mode 100644 index 0000000..9b8a638 --- /dev/null +++ b/source/monophony/ui/pages/loading_page.py @@ -0,0 +1,24 @@ +from monophony.ui.pages.page import Page + +from gi.repository import Adw + + +class LoadingPage(Page): + __gtype_name__ = __qualname__ + + def __init__(self): + super().__init__() + + spinner = Adw.SpinnerPaintable() + + self._status_page = Adw.StatusPage() + self._status_page.props.paintable = spinner + self._status_page.props.description = '0%' + spinner.props.widget = self._status_page + + self._toolbar_view.props.content = self._status_page + + self.props.title = _('Loading...') + + def update_progress(self, progress: float): + self._status_page.props.description = f'{int(progress * 100)}%' diff --git a/source/monophony/ui/pages/page.py b/source/monophony/ui/pages/page.py new file mode 100644 index 0000000..fe716d0 --- /dev/null +++ b/source/monophony/ui/pages/page.py @@ -0,0 +1,38 @@ +import weakref + +from monophony.debug import MemoryDebugger +from monophony.ui.bars.header_bar import HeaderBar + +from gi.repository import Adw, GObject + + +class Page(MemoryDebugger, Adw.NavigationPage): + __gtype_name__ = __qualname__ + + def __init__(self): + super().__init__() + + # Must hold reference, otherwise the bar's weakref to self fails + self._header_bar = HeaderBar() + self._header_bar.connect( + 'show-about', + lambda _bar, ref: ref().emit('show-about'), + weakref.ref(self) + ) + + self._page = Adw.PreferencesPage() + + self._toolbar_view = Adw.ToolbarView() + self._toolbar_view.props.top_bar_style = Adw.ToolbarStyle.RAISED + self._toolbar_view.props.bottom_bar_style = Adw.ToolbarStyle.RAISED + self._toolbar_view.props.content = self._page + self._toolbar_view.add_top_bar(self._header_bar) + + self._toast_overlay = Adw.ToastOverlay() + self._toast_overlay.props.child = self._toolbar_view + + self.props.child = self._toast_overlay + + @GObject.Signal(name='show-about') + def _show_about(self): + return diff --git a/source/monophony/ui/pages/results_page.py b/source/monophony/ui/pages/results_page.py new file mode 100644 index 0000000..0abe7fa --- /dev/null +++ b/source/monophony/ui/pages/results_page.py @@ -0,0 +1,315 @@ +import weakref + +from monophony.data import Artist, Group, Song +from monophony.ui.pages.page import Page +from monophony.ui.row_groups.importable_group_row_group import ImportableGroupRowGroup +from monophony.ui.row_groups.playable_row_group import PlayableRowGroup +from monophony.ui.row_groups.queueable_row_group import QueueableRowGroup +from monophony.ui.row_groups.row_group import RowGroup +from monophony.ui.rows.artist_row import ArtistRow +from monophony.ui.rows.importable_group_row import ImportableGroupRow +from monophony.ui.rows.song_row import SongRow +from monophony.yt import SearchResult + +from gi.repository import Adw, GLib, GObject + + +class ResultsPage(Page): + __gtype_name__ = __qualname__ + + def __init__(self, results: list[SearchResult], filter_: str | None): + super().__init__() + + self._results = results + + songs_button = Adw.ButtonRow() + songs_button.props.end_icon_name = 'go-next-symbolic' + songs_button.props.title = _('Show All') + if filter_ == 'songs': + songs_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', ''), + weakref.ref(self) + ) + else: + songs_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', 'songs'), + weakref.ref(self) + ) + + self._songs_button_group = Adw.PreferencesGroup() + self._songs_button_group.props.visible = False + self._songs_button_group.add(songs_button) + + videos_button = Adw.ButtonRow() + videos_button.props.end_icon_name = 'go-next-symbolic' + videos_button.props.title = _('Show All') + if filter_ == 'videos': + videos_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', ''), + weakref.ref(self) + ) + else: + videos_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', 'videos'), + weakref.ref(self) + ) + + self._videos_button_group = Adw.PreferencesGroup() + self._videos_button_group.props.visible = False + self._videos_button_group.add(videos_button) + + albums_button = Adw.ButtonRow() + albums_button.props.end_icon_name = 'go-next-symbolic' + albums_button.props.title = _('Show All') + if filter_ == 'albums': + albums_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', ''), + weakref.ref(self) + ) + else: + albums_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', 'albums'), + weakref.ref(self) + ) + + self._albums_button_group = Adw.PreferencesGroup() + self._albums_button_group.props.visible = False + self._albums_button_group.add(albums_button) + + playlists_button = Adw.ButtonRow() + playlists_button.props.end_icon_name = 'go-next-symbolic' + playlists_button.props.title = _('Show All') + if filter_ == 'playlists': + playlists_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', ''), + weakref.ref(self) + ) + else: + playlists_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', 'playlists'), + weakref.ref(self) + ) + + self._playlists_button_group = Adw.PreferencesGroup() + self._playlists_button_group.props.visible = False + self._playlists_button_group.add(playlists_button) + + artists_button = Adw.ButtonRow() + artists_button.props.end_icon_name = 'go-next-symbolic' + artists_button.props.title = _('Show All') + if filter_ == 'artists': + artists_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', ''), + weakref.ref(self) + ) + else: + artists_button.connect( + 'activated', + lambda _button, ref: ref().emit('filter-results', 'artists'), + weakref.ref(self) + ) + + self._artists_button_group = Adw.PreferencesGroup() + self._artists_button_group.props.visible = False + self._artists_button_group.add(artists_button) + + self._songs_group = QueueableRowGroup() + self._songs_group.props.title = _('Songs') + + self._videos_group = QueueableRowGroup() + self._videos_group.props.title = _('Videos') + + self._albums_group = ImportableGroupRowGroup() + self._albums_group.props.title = _('Albums and Singles') + + self._playlists_group = ImportableGroupRowGroup() + self._playlists_group.props.title = _('Playlists') + + self._artists_group = RowGroup() + self._artists_group.props.title = _('Artists') + + self._top_group = QueueableRowGroup() + for result in self._results: + if result.top: + if result.type == 'artist': + self._top_group = RowGroup() + elif result.type in ('album', 'playlist'): + self._top_group = ImportableGroupRowGroup() + self._top_group.props.title = _('Top Result') + if self._top_group.props.header_suffix: + self._top_group.props.header_suffix.props.visible = False + + for group, button_group in { + self._top_group: None, + self._songs_group: self._songs_button_group, + self._videos_group: self._videos_button_group, + self._albums_group: self._albums_button_group, + self._playlists_group: self._playlists_button_group, + self._artists_group: self._artists_button_group + }.items(): + group.props.margin_start = 12 + group.props.margin_end = 12 + group.connect( + 'view-artist', + lambda _group, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + if isinstance(group, QueueableRowGroup): + group.connect( + 'queue-song', + lambda _group, song, ref: ref().emit('queue-song', song), + weakref.ref(self) + ) + if isinstance(group, PlayableRowGroup): + group.connect( + 'play', + lambda _group, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + group.connect( + 'add-song-to', + lambda _group, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + group.connect( + 'undownload-song', + lambda _group, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + group.connect( + 'download-song', + lambda _group, song, ref: ref().emit('download-song', song), + weakref.ref(self) + ) + if isinstance(group, ImportableGroupRowGroup): + group.connect( + 'import-group', + lambda _group, group, ref: ref().emit('import-group', group), + weakref.ref(self) + ) + group.connect( + 'queue-group', + lambda _group, group, ref: ref().emit('queue-group', group), + weakref.ref(self) + ) + group.connect( + 'add-group-to', + lambda _group, group, ref: ref().emit('add-group-to', group), + weakref.ref(self) + ) + group.connect( + 'download-group', + lambda _group, group, ref: ref().emit('download-group', group), + weakref.ref(self) + ) + + self._page.add(group) + if button_group and not filter_: + button_group.props.margin_start = 12 + button_group.props.margin_end = 12 + self._page.add(button_group) + + self.props.title = _('Search Results') + self.props.sensitive = False + + GLib.idle_add(self._load) + + @GObject.Signal(name='add-group-to', arg_types=(object,)) + def _add_group_to(self, _group: Group): + return + + @GObject.Signal(name='add-song-to', arg_types=(object,)) + def _add_song_to(self, _song: Song): + return + + @GObject.Signal(name='download-group', arg_types=(object,)) + def _download_group(self, _group: Group): + return + + @GObject.Signal(name='download-song', arg_types=(object,)) + def _download_song(self, _song: Song): + return + + @GObject.Signal(name='filter-results', arg_types=(str,)) + def _filter_results(self, _filter: str): + return + + @GObject.Signal(name='import-group', arg_types=(object,)) + def _import_group(self, _group: Group): + return + + @GObject.Signal(name='play', arg_types=(object, object)) + def _play(self, _song: Song, _group: Group): + return + + @GObject.Signal(name='queue-group', arg_types=(object,)) + def _queue_group(self, _group: Group): + return + + @GObject.Signal(name='queue-song', arg_types=(object,)) + def _queue_song(self, _song: Song): + return + + @GObject.Signal(name='undownload-song', arg_types=(object,)) + def _undownload_song(self, _song: Song): + return + + @GObject.Signal(name='view-artist', arg_types=(object,)) + def _view_artist(self, _artist: Artist): + return + + def _load(self) -> bool: + if self._results: + result = self._results.pop(0) + row = None + + if isinstance(result.item, Group): + row = ImportableGroupRow(result.item) + elif isinstance(result.item, Song): + row = SongRow(result.item) + else: + row = ArtistRow(result.item) + + if result.top: + self._top_group.add(row) + elif result.type == 'artist': + self._artists_button_group.props.visible = True + self._artists_group.add(row) + elif result.type == 'album': + self._albums_button_group.props.visible = True + self._albums_group.add(row) + elif result.type == 'playlist': + self._playlists_button_group.props.visible = True + self._playlists_group.add(row) + elif result.type == 'song': + self._songs_button_group.props.visible = True + self._songs_group.add(row) + else: + self._videos_button_group.props.visible = True + self._videos_group.add(row) + + return True + + self.props.sensitive = True + return False + + def update_download_status(self): + for group in ( + self._top_group, + self._songs_group, + self._videos_group, + self._albums_group, + self._playlists_group, + ): + # Top group is non-playable when top result is an artist + if isinstance(group, PlayableRowGroup): + group.update_download_status() diff --git a/source/monophony/ui/pages/status_page.py b/source/monophony/ui/pages/status_page.py new file mode 100644 index 0000000..01dd994 --- /dev/null +++ b/source/monophony/ui/pages/status_page.py @@ -0,0 +1,19 @@ +from monophony.ui.pages.page import Page + +from gi.repository import Adw + + +class StatusPage(Page): + __gtype_name__ = __qualname__ + + def __init__(self, title: str, details: str, icon: str): + super().__init__() + + status_page = Adw.StatusPage() + status_page.props.title = title + status_page.props.description = details + status_page.props.icon_name = icon + + self._toolbar_view.props.content = status_page + + self.props.title = title diff --git a/source/monophony/frontend/__init__.py b/source/monophony/ui/popovers/__init__.py similarity index 100% rename from source/monophony/frontend/__init__.py rename to source/monophony/ui/popovers/__init__.py diff --git a/source/monophony/ui/popovers/editable_group_row_popover.py b/source/monophony/ui/popovers/editable_group_row_popover.py new file mode 100644 index 0000000..a3d2aa0 --- /dev/null +++ b/source/monophony/ui/popovers/editable_group_row_popover.py @@ -0,0 +1,26 @@ +from monophony.ui.popovers.group_row_popover import GroupRowPopover + +from gi.repository import GObject + + +class EditableGroupRowPopover(GroupRowPopover): + __gtype_name__ = __qualname__ + actions = (*GroupRowPopover.actions, 'rename-playlist', 'delete-playlist') + + def __init__(self): + super().__init__(False) + + self.props.menu_model.append( + _('Rename...'), self.__gtype_name__ + '.rename-playlist' + ) + self.props.menu_model.append( + _('Delete'), self.__gtype_name__ + '.delete-playlist' + ) + + @GObject.Signal(name='rename-playlist') + def _rename_playlist(self): + return + + @GObject.Signal(name='delete-playlist') + def _delete_playlist(self): + return diff --git a/source/monophony/ui/popovers/editable_song_row_popover.py b/source/monophony/ui/popovers/editable_song_row_popover.py new file mode 100644 index 0000000..7f619bc --- /dev/null +++ b/source/monophony/ui/popovers/editable_song_row_popover.py @@ -0,0 +1,19 @@ +from monophony.ui.popovers.song_row_popover import SongRowPopover + +from gi.repository import GObject + + +class EditableSongRowPopover(SongRowPopover): + __gtype_name__ = __qualname__ + actions = (*SongRowPopover.actions, 'remove-song') + + def __init__(self, downloaded: bool, being_downloaded: bool): + super().__init__(downloaded, being_downloaded) + + self.props.menu_model.append( + _('Remove From Playlist'), self.__gtype_name__ + '.remove-song' + ) + + @GObject.Signal(name='remove-song') + def _remove_song(self): + return diff --git a/source/monophony/ui/popovers/group_row_popover.py b/source/monophony/ui/popovers/group_row_popover.py new file mode 100644 index 0000000..29d08f1 --- /dev/null +++ b/source/monophony/ui/popovers/group_row_popover.py @@ -0,0 +1,42 @@ +from monophony.ui.popovers.row_popover import RowPopover + +from gi.repository import Gio, GObject + + +class GroupRowPopover(RowPopover): + __gtype_name__ = __qualname__ + actions = ( + *RowPopover.actions, + 'queue-group', + 'add-group-to', + 'view-artist', + 'download-group' + ) + + def __init__(self, viewable_artist: bool): + super().__init__() + + menu = Gio.Menu() + menu.append(_('Add to Queue'), self.__gtype_name__ + '.queue-group') + menu.append(_('Add to...'), self.__gtype_name__ + '.add-group-to') + if viewable_artist: + menu.append(_('View Artist'), self.__gtype_name__ + '.view-artist') + menu.append(_('Download'), self.__gtype_name__ + '.download-group') + + self.props.menu_model = menu + + @GObject.Signal(name='queue-group') + def _queue_group(self): + return + + @GObject.Signal(name='add-group-to') + def _add_group_to(self): + return + + @GObject.Signal(name='view-artist') + def _view_artist(self): + return + + @GObject.Signal(name='download-group') + def _download_group(self): + return diff --git a/source/monophony/ui/popovers/importable_group_row_popover.py b/source/monophony/ui/popovers/importable_group_row_popover.py new file mode 100644 index 0000000..bf01709 --- /dev/null +++ b/source/monophony/ui/popovers/importable_group_row_popover.py @@ -0,0 +1,20 @@ +from monophony.ui.popovers.group_row_popover import GroupRowPopover + +from gi.repository import GObject + + +class ImportableGroupRowPopover(GroupRowPopover): + __gtype_name__ = __qualname__ + actions = (*GroupRowPopover.actions, 'import-group') + + def __init__(self, viewable_artist: bool): + super().__init__(viewable_artist) + + self.props.menu_model.append( + _('Import...'), self.__gtype_name__ + '.import-group' + ) + + @GObject.Signal(name='import-group') + def _import_group(self): + return + diff --git a/source/monophony/ui/popovers/queue_song_row_popover.py b/source/monophony/ui/popovers/queue_song_row_popover.py new file mode 100644 index 0000000..70e0a17 --- /dev/null +++ b/source/monophony/ui/popovers/queue_song_row_popover.py @@ -0,0 +1,20 @@ +from monophony.ui.popovers.song_row_popover import SongRowPopover + +from gi.repository import GObject + + +class QueueSongRowPopover(SongRowPopover): + __gtype_name__ = __qualname__ + actions = (*SongRowPopover.actions, 'unqueue-song') + + def __init__(self, downloaded: bool, being_downloaded: bool): + super().__init__(downloaded, being_downloaded) + + self.props.menu_model.remove(0) # queue-song + self.props.menu_model.append( + _('Remove From Queue'), self.__gtype_name__ + '.unqueue-song' + ) + + @GObject.Signal(name='unqueue-song') + def _unqueue_song(self): + return diff --git a/source/monophony/ui/popovers/row_popover.py b/source/monophony/ui/popovers/row_popover.py new file mode 100644 index 0000000..110bf02 --- /dev/null +++ b/source/monophony/ui/popovers/row_popover.py @@ -0,0 +1,19 @@ +from monophony.debug import MemoryDebugger + +from gi.repository import Gtk + + +class RowPopover(MemoryDebugger, Gtk.PopoverMenu): + __gtype_name__ = __qualname__ + actions = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + + for action in cls.actions: + cls.install_action( + cls.__gtype_name__ + '.' + action, None, cls.emit_signal_from_action + ) + + def emit_signal_from_action(self, action: str, _property: None): + self.emit(action.split('.')[-1]) diff --git a/source/monophony/ui/popovers/song_row_popover.py b/source/monophony/ui/popovers/song_row_popover.py new file mode 100644 index 0000000..eaaa1f0 --- /dev/null +++ b/source/monophony/ui/popovers/song_row_popover.py @@ -0,0 +1,51 @@ +from monophony.ui.popovers.row_popover import RowPopover + +from gi.repository import Gio, GObject + + +class SongRowPopover(RowPopover): + __gtype_name__ = __qualname__ + actions = ( + *RowPopover.actions, + 'queue-song', + 'add-song-to', + 'view-artist', + 'undownload-song', + 'download-song' + ) + + def __init__(self, downloaded: bool, being_downloaded: bool): + super().__init__() + + menu = Gio.Menu() + menu.append(_('Add to Queue'), self.__gtype_name__ + '.queue-song') + menu.append(_('Add to...'), self.__gtype_name__ + '.add-song-to') + menu.append(_('View Artist'), self.__gtype_name__ + '.view-artist') + if downloaded: + menu.append( + _('Remove From Downloads'), self.__gtype_name__ + '.undownload-song' + ) + elif not being_downloaded: + menu.append(_('Download'), self.__gtype_name__ + '.download-song') + + self.props.menu_model = menu + + @GObject.Signal(name='queue-song') + def _queue_song(self): + return + + @GObject.Signal(name='add-song-to') + def _add_song_to(self): + return + + @GObject.Signal(name='view-artist') + def _view_artist(self): + return + + @GObject.Signal(name='undownload-song') + def _undownload_song(self): + return + + @GObject.Signal(name='download-song') + def _download_song(self): + return diff --git a/source/monophony/ui/popovers/synchronized_group_row_popover.py b/source/monophony/ui/popovers/synchronized_group_row_popover.py new file mode 100644 index 0000000..67e71fd --- /dev/null +++ b/source/monophony/ui/popovers/synchronized_group_row_popover.py @@ -0,0 +1,20 @@ +from monophony.ui.popovers.group_row_popover import GroupRowPopover + +from gi.repository import GObject + + +class SynchronizedGroupRowPopover(GroupRowPopover): + __gtype_name__ = __qualname__ + actions = (*GroupRowPopover.actions, 'delete-playlist') + + def __init__(self): + super().__init__(False) + + self.props.menu_model.append( + _('Delete'), self.__gtype_name__ + '.delete-playlist' + ) + + @GObject.Signal(name='delete-playlist') + def _delete_playlist(self): + return + diff --git a/source/monophony/ui/queue_sidebar.py b/source/monophony/ui/queue_sidebar.py new file mode 100644 index 0000000..ec74327 --- /dev/null +++ b/source/monophony/ui/queue_sidebar.py @@ -0,0 +1,206 @@ +import weakref + +from monophony.data import Artist, Group, Song, TimeString +from monophony.ui.row_groups.queue_row_group import QueueRowGroup +from monophony.ui.rows.queue_song_row import QueueSongRow + +from gi.repository import Adw, GObject, Gtk + + +class QueueSidebar(Adw.Bin): + __gtype_name__ = __qualname__ + _min_songs_for_shuffle = 3 + + def __init__(self): + super().__init__() + + clear_button = Gtk.Button.new_from_icon_name('media-playback-stop-symbolic') + clear_button.props.tooltip_text = _('Stop') + clear_button.props.halign = Gtk.Align.FILL + clear_button.props.hexpand = True + clear_button.add_css_class('destructive-action') + clear_button.connect( + 'clicked', + lambda _button, ref: ref().emit('clear-queue'), + weakref.ref(self) + ) + + self._shuffle_button = Gtk.Button.new_from_icon_name( + 'media-playlist-shuffle-symbolic' + ) + self._shuffle_button.props.tooltip_text = _('Shuffle') + self._shuffle_button.props.halign = Gtk.Align.FILL + self._shuffle_button.props.hexpand = True + self._shuffle_button.connect( + 'clicked', + lambda _button, ref: ref().emit('shuffle-queue'), + weakref.ref(self) + ) + + button_content = Adw.ButtonContent() + button_content.props.label = _('Add to...') + button_content.props.icon_name = 'list-add-symbolic' + add_button = Gtk.Button() + add_button.props.child = button_content + add_button.connect( + 'clicked', + lambda _button, ref: ref().emit('add-group-to', Group()), + weakref.ref(self) + ) + + internal_buttons_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + internal_buttons_box.props.spacing = 6 + internal_buttons_box.props.halign = Gtk.Align.FILL + internal_buttons_box.props.hexpand = True + internal_buttons_box.append(clear_button) + internal_buttons_box.append(self._shuffle_button) + + buttons_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + buttons_box.props.spacing = 6 + buttons_box.props.margin_start = 6 + buttons_box.append(internal_buttons_box) + buttons_box.append(add_button) + + self._queue_group = QueueRowGroup() + self._queue_group.props.title = _('Currently Playing') + self._queue_group.props.description = '00:00:00' + self._queue_group.props.margin_start = 12 + self._queue_group.props.margin_end = 12 + self._queue_group.props.header_suffix = buttons_box + self._queue_group.connect( + 'play', + lambda _group, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + self._queue_group.connect( + 'add-song-to', + lambda _group, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + self._queue_group.connect( + 'view-artist', + lambda _group, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + self._queue_group.connect( + 'undownload-song', + lambda _group, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + self._queue_group.connect( + 'download-song', + lambda _group, song, ref: ref().emit('download-song', song), + weakref.ref(self) + ) + self._queue_group.connect( + 'move-song', + lambda _group, from_s, to_s, ref: + ref().emit('move-song', from_s, to_s), + weakref.ref(self) + ) + self._queue_group.connect( + 'unqueue-song', + lambda _group, song, ref: ref().emit('unqueue-song', song), + weakref.ref(self) + ) + + queue_page = Adw.PreferencesPage() + queue_page.props.valign = Gtk.Align.FILL + queue_page.props.vexpand = True + queue_page.props.visible = False + queue_page.add(self._queue_group) + + self._status_page = Adw.StatusPage() + self._status_page.props.valign = Gtk.Align.FILL + self._status_page.props.vexpand = True + self._status_page.props.title = _('Queue Empty') + self._status_page.props.description = _('Nothing is playing right now') + self._status_page.props.icon_name = 'view-list-symbolic' + self._status_page.bind_property( + 'visible', + queue_page, + 'visible', + GObject.BindingFlags.BIDIRECTIONAL | + GObject.BindingFlags.INVERT_BOOLEAN | + GObject.BindingFlags.SYNC_CREATE + ) + + pages_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + pages_box.props.valign = Gtk.Align.FILL + pages_box.props.vexpand = True + pages_box.append(queue_page) + pages_box.append(self._status_page) + + self.hide_button = Gtk.Button.new_from_icon_name('go-previous-symbolic') + self.hide_button.props.tooltip_text = _('Back') + + header_bar = Adw.HeaderBar() + header_bar.props.title_widget = Adw.WindowTitle(title=_('Queue')) + header_bar.pack_start(self.hide_button) + + toolbar_view = Adw.ToolbarView() + toolbar_view.props.content = pages_box + toolbar_view.add_top_bar(header_bar) + + self.props.child = toolbar_view + + @GObject.Signal(name='play', arg_types=(object, object)) + def _play(self, _song: Song, _group: Group): + return + + @GObject.Signal(name='add-song-to', arg_types=(object,)) + def _add_song_to(self, _song: Song): + return + + @GObject.Signal(name='undownload-song', arg_types=(object,)) + def _undownload_song(self, _song: Song): + return + + @GObject.Signal(name='download-song', arg_types=(object,)) + def _download_song(self, _song: Song): + return + + @GObject.Signal(name='move-song', arg_types=(object, object)) + def _move_song(self, _from: Song, _to: Song): + return + + @GObject.Signal(name='unqueue-song', arg_types=(object,)) + def _unqueue_song(self, _song: Song): + return + + @GObject.Signal(name='add-group-to', arg_types=(object,)) + def _add_group_to(self, _group: Group): + return + + @GObject.Signal(name='view-artist', arg_types=(object,)) + def _view_artist(self, _artist: Artist): + return + + @GObject.Signal(name='shuffle-queue') + def _shuffle_queue(self): + return + + @GObject.Signal(name='clear-queue') + def _clear_queue(self): + return + + def add_song_row(self, song: Song): + self._queue_group.add(QueueSongRow(song)) + + def update_contents(self, group: Group, song_index: int): + self._queue_group.update_contents(group.songs, song_index) + self._status_page.props.visible = not bool(group.songs) + self._shuffle_button.props.sensitive = ( + len(group.songs) >= self._min_songs_for_shuffle + ) + + total_seconds = 0 + for song in group.songs: + total_seconds += TimeString(string=song.length).as_seconds() + + self._queue_group.props.description = TimeString( + seconds=total_seconds + ).as_string() + + def update_download_status(self): + self._queue_group.update_download_status() diff --git a/source/monophony/frontend/rows/__init__.py b/source/monophony/ui/row_groups/__init__.py similarity index 100% rename from source/monophony/frontend/rows/__init__.py rename to source/monophony/ui/row_groups/__init__.py diff --git a/source/monophony/ui/row_groups/editable_group_row_group.py b/source/monophony/ui/row_groups/editable_group_row_group.py new file mode 100644 index 0000000..fd9057c --- /dev/null +++ b/source/monophony/ui/row_groups/editable_group_row_group.py @@ -0,0 +1,26 @@ +import weakref + +from monophony.data import Group +from monophony.ui.row_groups.group_row_group import GroupRowGroup +from monophony.ui.rows.editable_group_row import EditableGroupRow + +from gi.repository import GObject + + +class EditableGroupRowGroup(GroupRowGroup): + __gtype_name__ = __qualname__ + _row_type = EditableGroupRow + + @GObject.Signal(name='delete-playlist', arg_types=(object,)) + def _delete_playlist(self, _playlist: Group): + return + + def add(self, row: _row_type): + super().add(row) + + row.connect( + 'delete-playlist', + lambda _row, playlist, ref: ref().emit('delete-playlist', playlist), + weakref.ref(self) + ) + diff --git a/source/monophony/ui/row_groups/group_row_group.py b/source/monophony/ui/row_groups/group_row_group.py new file mode 100644 index 0000000..84aafd0 --- /dev/null +++ b/source/monophony/ui/row_groups/group_row_group.py @@ -0,0 +1,64 @@ +import weakref + +from monophony.data import Group +from monophony.ui.row_groups.queueable_row_group import QueueableRowGroup +from monophony.ui.rows.group_row import GroupRow + +from gi.repository import GObject + + +class GroupRowGroup(QueueableRowGroup): + __gtype_name__ = __qualname__ + _row_type = GroupRow + + @GObject.Signal(name='queue-group', arg_types=(object,)) + def _queue_group(self, _group: Group): + return + + @GObject.Signal(name='add-group-to', arg_types=(object,)) + def _add_group_to(self, _group: Group): + return + + @GObject.Signal(name='download-group', arg_types=(object,)) + def _download_group(self, _group: Group): + return + + def add(self, row: _row_type): + super().add(row) + + row.connect( + 'queue-group', + lambda _row, group, ref: ref().emit('queue-group', group), + weakref.ref(self) + ) + row.connect( + 'add-group-to', + lambda _row, group, ref: ref().emit('add-group-to', group), + weakref.ref(self) + ) + row.connect( + 'download-group', + lambda _row, group, ref: ref().emit('download-group', group), + weakref.ref(self) + ) + + def on_play_all(self): + group = Group() + for row_ref in self._rows: + group.songs += row_ref().group.songs + + self.emit('play', group.songs[0], group) + + def update_contents(self, new_groups: list[Group]): + new_titles = [group.title for group in new_groups] + existing_titles = [] + for row in self._rows.copy(): + if row().group.title in new_titles and row().props.expanded: + row().update_contents() + existing_titles.append(row().group.title) + else: + self.remove(row()) + + for group in new_groups: + if group.title not in existing_titles: + self.add(self._row_type(group)) diff --git a/source/monophony/ui/row_groups/importable_group_row_group.py b/source/monophony/ui/row_groups/importable_group_row_group.py new file mode 100644 index 0000000..2ebc924 --- /dev/null +++ b/source/monophony/ui/row_groups/importable_group_row_group.py @@ -0,0 +1,25 @@ +import weakref + +from monophony.data import Group +from monophony.ui.row_groups.group_row_group import GroupRowGroup +from monophony.ui.rows.importable_group_row import ImportableGroupRow + +from gi.repository import GObject + + +class ImportableGroupRowGroup(GroupRowGroup): + __gtype_name__ = __qualname__ + _row_type = ImportableGroupRow + + @GObject.Signal(name='import-group', arg_types=(object,)) + def _import_group(self, _group: Group): + return + + def add(self, row: _row_type): + super().add(row) + + row.connect( + 'import-group', + lambda _row, group, ref: ref().emit('import-group', group), + weakref.ref(self) + ) diff --git a/source/monophony/ui/row_groups/playable_row_group.py b/source/monophony/ui/row_groups/playable_row_group.py new file mode 100644 index 0000000..e403b8f --- /dev/null +++ b/source/monophony/ui/row_groups/playable_row_group.py @@ -0,0 +1,55 @@ +import weakref + +from monophony.data import Group, Song +from monophony.ui.row_groups.row_group import RowGroup + +from gi.repository import GObject, Gtk + + +class PlayableRowGroup(RowGroup): + __gtype_name__ = __qualname__ + _row_type = Gtk.ListBoxRow # SongRow | GroupRow + + @GObject.Signal(name='play', arg_types=(object, object)) + def _play(self, _song: Song, _group: Group): + return + + @GObject.Signal(name='add-song-to', arg_types=(object,)) + def _add_song_to(self, _song: Song): + return + + @GObject.Signal(name='undownload-song', arg_types=(object,)) + def _undownload_song(self, _song: Song): + return + + @GObject.Signal(name='download-song', arg_types=(object,)) + def _download_song(self, _song: Song): + return + + def add(self, row: _row_type): + super().add(row) + + row.connect( + 'play', + lambda _row, song, group, ref: ref().emit('play', song, group), + weakref.ref(self) + ) + row.connect( + 'add-song-to', + lambda _row, song, ref: ref().emit('add-song-to', song), + weakref.ref(self) + ) + row.connect( + 'undownload-song', + lambda _row, song, ref: ref().emit('undownload-song', song), + weakref.ref(self) + ) + row.connect( + 'download-song', + lambda _row, song, ref: ref().emit('download-song', song), + weakref.ref(self) + ) + + def update_download_status(self): + for row in self._rows: + row().update_download_status() diff --git a/source/monophony/ui/row_groups/queue_row_group.py b/source/monophony/ui/row_groups/queue_row_group.py new file mode 100644 index 0000000..0b75ac0 --- /dev/null +++ b/source/monophony/ui/row_groups/queue_row_group.py @@ -0,0 +1,41 @@ +import weakref + +from monophony.data import Song +from monophony.ui.row_groups.playable_row_group import PlayableRowGroup +from monophony.ui.rows.queue_song_row import QueueSongRow + +from gi.repository import GObject + + +class QueueRowGroup(PlayableRowGroup): + __gtype_name__ = __qualname__ + _row_type = QueueSongRow + + @GObject.Signal(name='move-song', arg_types=(object, object)) + def _move_song(self, _from: Song, _to: Song): + return + + @GObject.Signal(name='unqueue-song', arg_types=(object,)) + def _unqueue_song(self, _song: Song): + return + + def add(self, row: QueueSongRow): + super().add(row) + + row.connect( + 'move-song', + lambda _row, from_s, to_s, ref: ref().emit('move-song', from_s, to_s), + weakref.ref(self) + ) + row.connect( + 'unqueue-song', + lambda _row, song, ref: ref().emit('unqueue-song', song), + weakref.ref(self) + ) + + def update_contents(self, new_songs: list[Song], song_index: int): + super().update_contents(new_songs) + + for i, row_ref in enumerate(self._rows): + if i == song_index: + row_ref().add_css_class('accent') diff --git a/source/monophony/ui/row_groups/queueable_row_group.py b/source/monophony/ui/row_groups/queueable_row_group.py new file mode 100644 index 0000000..d1aa64e --- /dev/null +++ b/source/monophony/ui/row_groups/queueable_row_group.py @@ -0,0 +1,45 @@ +import weakref + +from monophony.data import Group, Song +from monophony.ui.row_groups.playable_row_group import PlayableRowGroup +from monophony.ui.rows.song_row import SongRow + +from gi.repository import GObject, Gtk + + +class QueueableRowGroup(PlayableRowGroup): + __gtype_name__ = __qualname__ + _row_type = SongRow + + def __init__(self): + super().__init__() + + play_button = Gtk.Button.new_from_icon_name( + 'media-playback-start-symbolic' + ) + play_button.props.tooltip_text = _('Play All') + play_button.connect( + 'clicked', + lambda _button, ref: ref().on_play_all(), + weakref.ref(self) + ) + + self.props.header_suffix = play_button + + @GObject.Signal(name='queue-song', arg_types=(object,)) + def _queue_song(self, _song: Song): + return + + def add(self, row: _row_type): + super().add(row) + + row.connect( + 'queue-song', + lambda _row, song, ref: ref().emit('queue-song', song), + weakref.ref(self) + ) + + def on_play_all(self): + group = Group(songs=[row_ref().song for row_ref in self._rows]) + self.emit('play', group.songs[0], group) + diff --git a/source/monophony/ui/row_groups/row_group.py b/source/monophony/ui/row_groups/row_group.py new file mode 100644 index 0000000..30070f9 --- /dev/null +++ b/source/monophony/ui/row_groups/row_group.py @@ -0,0 +1,53 @@ +import weakref + +from monophony.data import Artist, YTItem +from monophony.debug import MemoryDebugger + +from gi.repository import Adw, GObject, Gtk + + +class RowGroup(MemoryDebugger, Adw.PreferencesGroup): + __gtype_name__ = __qualname__ + _row_type = Gtk.ListBoxRow + + def __init__(self): + super().__init__() + + self.props.visible = False + self._rows = [] + + @GObject.Signal(name='view-artist', arg_types=(object,)) + def _view_artist(self, _artist: Artist): + return + + def add(self, row: _row_type): + super().add(row) + + self._rows.append(weakref.ref(row)) + + row.connect( + 'view-artist', + lambda _row, artist, ref: ref().emit('view-artist', artist), + weakref.ref(self) + ) + self.props.visible = True + + def clear(self): + for reference in self._rows: + super().remove(reference()) + + self._rows.clear() + self.props.visible = False + + def remove(self, row: _row_type): + super().remove(row) + + self._rows = [reference for reference in self._rows if reference() is not row] + self.props.visible = bool(self._rows) + + def update_contents(self, new_contents: list[YTItem]): + self.clear() + + for item in new_contents: + self.add(self._row_type(item)) + diff --git a/source/monophony/ui/row_groups/synchronized_group_row_group.py b/source/monophony/ui/row_groups/synchronized_group_row_group.py new file mode 100644 index 0000000..e7d3ab3 --- /dev/null +++ b/source/monophony/ui/row_groups/synchronized_group_row_group.py @@ -0,0 +1,8 @@ +from monophony.ui.row_groups.editable_group_row_group import EditableGroupRowGroup +from monophony.ui.rows.synchronized_group_row import SynchronizedGroupRow + + +class SynchronizedGroupRowGroup(EditableGroupRowGroup): + __gtype_name__ = __qualname__ + _row_type = SynchronizedGroupRow + diff --git a/source/monophony/ui/rows/__init__.py b/source/monophony/ui/rows/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/source/monophony/ui/rows/artist_row.py b/source/monophony/ui/rows/artist_row.py new file mode 100644 index 0000000..def4026 --- /dev/null +++ b/source/monophony/ui/rows/artist_row.py @@ -0,0 +1,29 @@ +from monophony.data import Artist +from monophony.debug import MemoryDebugger + +from gi.repository import Adw, GLib, GObject, Gtk + + +class ArtistRow(MemoryDebugger, Adw.ActionRow): + __gtype_name__ = __qualname__ + + def __init__(self, artist: Artist): + super().__init__() + + self.artist = artist + + self.props.tooltip_text = _('View Artist') + self.props.activatable = True + self.props.title = GLib.markup_escape_text(artist.name, -1) + + view_button = Gtk.Button.new_from_icon_name('go-next-symbolic') + view_button.props.tooltip_text = _('View Artist') + view_button.props.vexpand = False + view_button.props.valign = Gtk.Align.CENTER + view_button.props.has_frame = False + self.add_suffix(view_button) + self.connect('activated', lambda row: row.emit('view-artist', row.artist)) + + @GObject.Signal(name='view-artist', arg_types=(object,)) + def _view_artist(self, _artist: Artist): + return diff --git a/source/monophony/ui/rows/draggable_song_row.py b/source/monophony/ui/rows/draggable_song_row.py new file mode 100644 index 0000000..3c699fa --- /dev/null +++ b/source/monophony/ui/rows/draggable_song_row.py @@ -0,0 +1,80 @@ +import weakref + +from monophony.data import Song +from monophony.ui.rows.song_row import SongRow + +from gi.repository import Gdk, GObject, Gtk + + +class DraggableSongRow(SongRow): + __gtype_name__ = __qualname__ + + def __init__(self, song: Song): + super().__init__(song) + + self.drag_source = Gtk.DragSource() + self.drag_source.props.actions = Gdk.DragAction.MOVE + self.drag_source.set_icon(Gtk.WidgetPaintable.new(self), 0, 0) + self.drag_source.connect( + 'prepare', + lambda _source, _x, _y, row: DraggableSongRow._on_drag_prepare(row()), + weakref.ref(self) + ) + self.drag_source.connect( + 'drag-end', + lambda _source, _drag, _data, row: DraggableSongRow._on_drag_end(row()), + weakref.ref(self) + ) + self.drag_source.connect( + 'drag-cancel', + lambda _source, _drag, _data, row: DraggableSongRow._on_drag_end(row()), + weakref.ref(self) + ) + + handle_image = Gtk.Image.new_from_icon_name('list-drag-handle-symbolic') + handle_image.add_css_class('dimmed') + + drop_target = Gtk.DropTarget.new(self.__gtype__, Gdk.DragAction.MOVE) + drop_target.connect( + 'drop', + lambda _target, drop, _x, _y, row: DraggableSongRow._on_drop(row(), drop), + weakref.ref(self) + ) + drop_target.connect( + 'enter', + lambda target, _x, _y, row: DraggableSongRow._on_drag_enter(row(), target), + weakref.ref(self) + ) + + self.add_controller(self.drag_source) + self.add_controller(drop_target) + self.add_prefix(handle_image) + + @GObject.Signal(name='move-song', arg_types=(object, object)) + def _move_song(self, _from: Song, _to: Song): + return + + def _on_drag_prepare(self) -> Gdk.ContentProvider: + self.add_css_class('background') + return Gdk.ContentProvider.new_for_value(self) + + def _on_drag_enter(self, drop_target: Gtk.DropTarget) -> int: + if self.drag_source.get_drag(): + drop_target.reject() + return 0 + + i = 0 + while row := self.get_parent().get_row_at_index(i): + i += 1 + if row.drag_source.get_drag(): + return Gdk.DragAction.MOVE + + drop_target.reject() + return 0 + + def _on_drag_end(self): + self.remove_css_class('background') + + def _on_drop(self, dropped_row: SongRow) -> bool: + self.emit('move-song', dropped_row.song, self.song) + return True diff --git a/source/monophony/ui/rows/editable_group_row.py b/source/monophony/ui/rows/editable_group_row.py new file mode 100644 index 0000000..484faf1 --- /dev/null +++ b/source/monophony/ui/rows/editable_group_row.py @@ -0,0 +1,118 @@ +import weakref + +from monophony import logging, playlists +from monophony.data import Group, Song +from monophony.ui.popovers.editable_group_row_popover import EditableGroupRowPopover +from monophony.ui.rows.editable_song_row import EditableSongRow +from monophony.ui.rows.group_row import GroupRow +from monophony.ui.windows.rename_window import RenameWindow + +from gi.repository import GLib, GObject, Gtk + + +class EditableGroupRow(GroupRow): + __gtype_name__ = __qualname__ + _row_type = EditableSongRow + + def __init__(self, group: Group): + super().__init__(group) + + self._more_button.set_create_popup_func( + lambda button, ref: EditableGroupRow._on_show_more(ref(), button), + weakref.ref(self) + ) + + @GObject.Signal(name='delete-playlist', arg_types=(object,)) + def _delete_playlist(self, _playlist: Group): + return + + def _on_show_more(self, button: Gtk.MenuButton): + self._popover = EditableGroupRowPopover() + self._popover.connect( + 'queue-group', + lambda _popover, row: row().emit('queue-group', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'add-group-to', + lambda _popover, row: row().emit('add-group-to', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'download-group', + lambda _popover, row: row().emit('download-group', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'delete-playlist', + lambda _popover, row: row().emit('delete-playlist', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'rename-playlist', + lambda _popover, row: EditableGroupRow._on_rename_playlist(row()), + weakref.ref(self) + ) + button.set_popover(self._popover) + + def add_row(self, row: _row_type): + super().add_row(row) + row.connect( + 'move-song', + lambda _row, from_s, to_s, ref: + EditableGroupRow._on_move_song(ref(), from_s, to_s), + weakref.ref(self) + ) + row.connect( + 'remove-song', + lambda _row, song, ref: EditableGroupRow._on_remove_song(ref(), song), + weakref.ref(self) + ) + + def update_contents(self): + logging.info( + __name__, f'Updating row contents for playlist "{self.group.title}"...' + ) + + for playlist in playlists.read(): + if playlist.title == self.group.title: + self.group = playlist + break + else: + logging.error( + __name__, + 'Failed to update row contents - ' + f'playlist "{self.group.title}" does not exist' + ) + return + + super().update_contents() + logging.info(__name__, 'Updated row contents') + + def _on_move_song(self, from_song: Song, to_song: Song): + i = self.group.songs.index(from_song) + j = self.group.songs.index(to_song) + if abs(i - j) == 1: + playlists.swap_songs(self.group.title, i, j) + else: + playlists.move_song(self.group.title, i, j) + + self.update_contents() + + def _on_remove_song(self, song: Song): + playlists.remove_song(song, self.group.title) + self.update_contents() + + def _on_rename_confirmed(self, name: str): + self.group.title = playlists.rename(self.group.title, name) + self.props.title = GLib.markup_escape_text(self.group.title, -1) + + def _on_rename_playlist(self): + rename_window = RenameWindow(self.group.title) + rename_window.connect( + 'rename', + lambda _window, name, ref: + EditableGroupRow._on_rename_confirmed(ref(), name), + weakref.ref(self), + ) + rename_window.present(self) diff --git a/source/monophony/ui/rows/editable_song_row.py b/source/monophony/ui/rows/editable_song_row.py new file mode 100644 index 0000000..410e08c --- /dev/null +++ b/source/monophony/ui/rows/editable_song_row.py @@ -0,0 +1,59 @@ +import weakref + +from monophony import downloads +from monophony.data import Song +from monophony.ui.popovers.editable_song_row_popover import EditableSongRowPopover +from monophony.ui.rows.draggable_song_row import DraggableSongRow + +from gi.repository import GObject, Gtk + + +class EditableSongRow(DraggableSongRow): + __gtype_name__ = __qualname__ + + def __init__(self, song: Song): + super().__init__(song) + + self._more_button.set_create_popup_func( + lambda button, ref: EditableSongRow._on_show_more(ref(), button), + weakref.ref(self) + ) + + @GObject.Signal(name='remove-song', arg_types=(object,)) + def _remove_song(self, _song: Song): + return + + def _on_show_more(self, button: Gtk.MenuButton): + popover = EditableSongRowPopover( + downloads.is_downloaded(self.song), downloads.is_being_downloaded(self.song) + ) + popover.connect( + 'queue-song', + lambda _popover, row: row().emit('queue-song', row().song), + weakref.ref(self) + ) + popover.connect( + 'add-song-to', lambda _popover, row: row().emit('add-song-to', row().song), + weakref.ref(self) + ) + popover.connect( + 'view-artist', + lambda _popover, row: row().emit('view-artist', row().song.author), + weakref.ref(self) + ) + popover.connect( + 'undownload-song', + lambda _popover, row: row().emit('undownload-song', row().song), + weakref.ref(self) + ) + popover.connect( + 'download-song', + lambda _popover, row: row().emit('download-song', row().song), + weakref.ref(self) + ) + popover.connect( + 'remove-song', + lambda _popover, row: row().emit('remove-song', row().song), + weakref.ref(self) + ) + button.set_popover(popover) diff --git a/source/monophony/ui/rows/group_row.py b/source/monophony/ui/rows/group_row.py new file mode 100644 index 0000000..e64113a --- /dev/null +++ b/source/monophony/ui/rows/group_row.py @@ -0,0 +1,163 @@ +import weakref + +from monophony.data import Artist, Group, Song, TimeString +from monophony.debug import MemoryDebugger +from monophony.ui.popovers.group_row_popover import GroupRowPopover +from monophony.ui.rows.song_row import SongRow + +from gi.repository import Adw, GLib, GObject, Gtk + + +class GroupRow(MemoryDebugger, Adw.ExpanderRow): + __gtype_name__ = __qualname__ + _row_type = SongRow + + def __init__(self, group: Group): + super().__init__() + + self.group = group + self._rows = [] + + self._more_button = Gtk.MenuButton() + self._more_button.props.tooltip_text = _('More') + self._more_button.props.icon_name = 'view-more-symbolic' + self._more_button.props.has_frame = False + self._more_button.props.vexpand = False + self._more_button.props.valign = Gtk.Align.CENTER + self._more_button.set_create_popup_func( + lambda button, ref: GroupRow._on_show_more(ref(), button), + weakref.ref(self) + ) + + self.props.title = GLib.markup_escape_text(group.title, -1) + self.props.expanded = False + self.add_suffix(self._more_button) + self.connect('notify::expanded', GroupRow._on_expanded) + self.update_subtitle() + + @GObject.Signal(name='play', arg_types=(object, object)) + def _play(self, _song: Song, _group: Group): + return + + @GObject.Signal(name='queue-song', arg_types=(object,)) + def _queue_song(self, _song: Song): + return + + @GObject.Signal(name='add-song-to', arg_types=(object,)) + def _add_song_to(self, _song: Song): + return + + @GObject.Signal(name='view-artist', arg_types=(object,)) + def _view_artist(self, _artist: Artist): + return + + @GObject.Signal(name='undownload-song', arg_types=(object,)) + def _undownload_song(self, _song: Song): + return + + @GObject.Signal(name='download-song', arg_types=(object,)) + def _download_song(self, _song: Song): + return + + @GObject.Signal(name='queue-group', arg_types=(object,)) + def _queue_group(self, _group: Group): + return + + @GObject.Signal(name='add-group-to', arg_types=(object,)) + def _add_group_to(self, _group: Group): + return + + @GObject.Signal(name='download-group', arg_types=(object,)) + def _download_group(self, _group: Group): + return + + def _on_expanded(self, _param): + if self.props.expanded: + self.update_contents() + + def _on_show_more(self, button: Gtk.MenuButton): + self._popover = GroupRowPopover(bool(self.group.author.yt_id)) + self._popover.connect( + 'queue-group', + lambda _popover, row: row().emit('queue-group', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'add-group-to', + lambda _popover, row: row().emit('add-group-to', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'view-artist', + lambda _popover, row: row().emit('view-artist', row().group.author), + weakref.ref(self) + ) + self._popover.connect( + 'download-group', + lambda _popover, row: row().emit('download-group', row().group), + weakref.ref(self) + ) + button.set_popover(self._popover) + + def add_row(self, row: _row_type): + row.connect( + 'play', + lambda _row, song, _group, g_row: g_row().emit('play', song, g_row().group), + weakref.ref(self) + ) + row.connect( + 'queue-song', + lambda _row, song, g_row: g_row().emit('queue-song', song), + weakref.ref(self) + ) + row.connect( + 'add-song-to', + lambda _row, song, g_row: g_row().emit('add-song-to', song), + weakref.ref(self) + ) + row.connect( + 'view-artist', + lambda _row, artist, g_row: g_row().emit('view-artist', artist), + weakref.ref(self) + ) + row.connect( + 'download-song', + lambda _row, song, g_row: g_row().emit('download-song', song), + weakref.ref(self) + ) + row.connect( + 'undownload-song', + lambda _row, song, g_row: g_row().emit('undownload-song', song), + weakref.ref(self) + ) + self._rows.append(weakref.ref(row)) + super().add_row(row) + + def add_song(self, song: Song): + self.add_row(self._row_type(song)) + + def update_download_status(self): + for row_ref in self._rows: + row_ref().update_download_status() + + def update_contents(self): + for row_ref in self._rows: + self.remove(row_ref()) + + self._rows.clear() + + for song in self.group.songs: + self.add_song(song) + + self.update_subtitle() + + def update_subtitle(self): + total_seconds = 0 + for song in self.group.songs: + total_seconds += TimeString(string=song.length).as_seconds() + + self.props.subtitle = ( + TimeString(seconds=total_seconds).as_string() + + ' ' + + GLib.markup_escape_text(self.group.author.name, -1) + ) if total_seconds else '' diff --git a/source/monophony/ui/rows/importable_group_row.py b/source/monophony/ui/rows/importable_group_row.py new file mode 100644 index 0000000..8e837d3 --- /dev/null +++ b/source/monophony/ui/rows/importable_group_row.py @@ -0,0 +1,55 @@ +import weakref + +from monophony.data import Group +from monophony.ui.popovers.importable_group_row_popover import ImportableGroupRowPopover +from monophony.ui.rows.group_row import GroupRow +from monophony.ui.rows.song_row import SongRow + +from gi.repository import GObject, Gtk + + +class ImportableGroupRow(GroupRow): + __gtype_name__ = __qualname__ + _row_type = SongRow + + def __init__(self, group: Group): + super().__init__(group) + + self._more_button.set_create_popup_func( + lambda button, ref: ImportableGroupRow._on_show_more(ref(), button), + weakref.ref(self) + ) + + @GObject.Signal(name='import-group', arg_types=(object,)) + def _import_group(self, _group: Group): + return + + def _on_show_more(self, button: Gtk.MenuButton): + self._popover = ImportableGroupRowPopover(bool(self.group.author.yt_id)) + self._popover.connect( + 'queue-group', + lambda _popover, row: row().emit('queue-group', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'add-group-to', + lambda _popover, row: row().emit('add-group-to', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'view-artist', + lambda _popover, row: row().emit('view-artist', row().group.author), + weakref.ref(self) + ) + self._popover.connect( + 'download-group', + lambda _popover, row: row().emit('download-group', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'import-group', + lambda _popover, row: row().emit('import-group', row().group), + weakref.ref(self) + ) + button.set_popover(self._popover) + diff --git a/source/monophony/ui/rows/queue_song_row.py b/source/monophony/ui/rows/queue_song_row.py new file mode 100644 index 0000000..0d43126 --- /dev/null +++ b/source/monophony/ui/rows/queue_song_row.py @@ -0,0 +1,54 @@ +import weakref + +from monophony import downloads +from monophony.data import Song +from monophony.ui.popovers.queue_song_row_popover import QueueSongRowPopover +from monophony.ui.rows.draggable_song_row import DraggableSongRow + +from gi.repository import GObject, Gtk + + +class QueueSongRow(DraggableSongRow): + __gtype_name__ = __qualname__ + + def __init__(self, song: Song): + super().__init__(song) + + self._more_button.set_create_popup_func( + lambda button, ref: QueueSongRow._on_show_more(ref(), button), + weakref.ref(self) + ) + + @GObject.Signal(name='unqueue-song', arg_types=(object,)) + def _unqueue_song(self, _song: Song): + return + + def _on_show_more(self, button: Gtk.MenuButton): + popover = QueueSongRowPopover( + downloads.is_downloaded(self.song), downloads.is_being_downloaded(self.song) + ) + popover.connect( + 'add-song-to', lambda _popover, row: row().emit('add-song-to', row().song), + weakref.ref(self) + ) + popover.connect( + 'view-artist', + lambda _popover, row: row().emit('view-artist', row().song.author), + weakref.ref(self) + ) + popover.connect( + 'undownload-song', + lambda _popover, row: row().emit('undownload-song', row().song), + weakref.ref(self) + ) + popover.connect( + 'download-song', + lambda _popover, row: row().emit('download-song', row().song), + weakref.ref(self) + ) + popover.connect( + 'unqueue-song', + lambda _popover, row: row().emit('unqueue-song', row().song), + weakref.ref(self) + ) + button.set_popover(popover) diff --git a/source/monophony/ui/rows/song_row.py b/source/monophony/ui/rows/song_row.py new file mode 100644 index 0000000..c32ded2 --- /dev/null +++ b/source/monophony/ui/rows/song_row.py @@ -0,0 +1,113 @@ +import weakref + +from monophony import downloads +from monophony.data import Artist, Group, Song, TimeString +from monophony.debug import MemoryDebugger +from monophony.ui.popovers.song_row_popover import SongRowPopover + +from gi.repository import Adw, GLib, GObject, Gtk + + +class SongRow(MemoryDebugger, Adw.ActionRow): + __gtype_name__ = __qualname__ + + def __init__(self, song: Song): + super().__init__() + + self.song = song + + title = GLib.markup_escape_text(song.title, -1) + length = GLib.markup_escape_text(song.length, -1) + author = GLib.markup_escape_text(song.author.name, -1) + subtitle = ( + f'{length} {author}' if TimeString(string=length).as_seconds() else author + ) + + self._downloaded_image = Gtk.Image.new_from_icon_name( + 'folder-download-symbolic' + ) + self._downloaded_image.props.tooltip_text = _('Downloaded') + + self._spinner = Adw.Spinner() + + self._more_button = Gtk.MenuButton() + self._more_button.props.tooltip_text = _('More') + self._more_button.props.icon_name = 'view-more-symbolic' + self._more_button.props.has_frame = False + self._more_button.props.vexpand = False + self._more_button.props.valign = Gtk.Align.CENTER + self._more_button.set_create_popup_func( + lambda button, ref: SongRow._on_show_more(ref(), button), + weakref.ref(self) + ) + + self.props.title = title + self.props.subtitle = subtitle + self.props.tooltip_text = _('Play') + self.props.activatable = True + self.add_suffix(self._downloaded_image) + self.add_suffix(self._spinner) + self.add_suffix(self._more_button) + self.connect( + 'activated', lambda row: row.emit('play', row.song, Group(songs=[row.song])) + ) + self.update_download_status() + + @GObject.Signal(name='play', arg_types=(object, object)) + def _play(self, _song: Song, _group: Group): + return + + @GObject.Signal(name='queue-song', arg_types=(object,)) + def _queue_song(self, _song: Song): + return + + @GObject.Signal(name='add-song-to', arg_types=(object,)) + def _add_song_to(self, _song: Song): + return + + @GObject.Signal(name='view-artist', arg_types=(object,)) + def _view_artist(self, _artist: Artist): + return + + @GObject.Signal(name='undownload-song', arg_types=(object,)) + def _undownload_song(self, _song: Song): + return + + @GObject.Signal(name='download-song', arg_types=(object,)) + def _download_song(self, _song: Song): + return + + def _on_show_more(self, button: Gtk.MenuButton): + popover = SongRowPopover( + downloads.is_downloaded(self.song), downloads.is_being_downloaded(self.song) + ) + popover.connect( + 'queue-song', + lambda _popover, row: row().emit('queue-song', row().song), + weakref.ref(self) + ) + popover.connect( + 'add-song-to', + lambda _popover, row: row().emit('add-song-to', row().song), + weakref.ref(self) + ) + popover.connect( + 'view-artist', + lambda _popover, row: row().emit('view-artist', row().song.author), + weakref.ref(self) + ) + popover.connect( + 'undownload-song', + lambda _popover, row: row().emit('undownload-song', row().song), + weakref.ref(self) + ) + popover.connect( + 'download-song', + lambda _popover, row: row().emit('download-song', row().song), + weakref.ref(self) + ) + button.props.popover = popover + + def update_download_status(self): + self._spinner.props.visible = downloads.is_being_downloaded(self.song) + self._downloaded_image.props.visible = downloads.is_downloaded(self.song) diff --git a/source/monophony/ui/rows/synchronized_group_row.py b/source/monophony/ui/rows/synchronized_group_row.py new file mode 100644 index 0000000..7566a20 --- /dev/null +++ b/source/monophony/ui/rows/synchronized_group_row.py @@ -0,0 +1,73 @@ +import weakref + +from monophony import logging, playlists +from monophony.data import Group +from monophony.ui.popovers.synchronized_group_row_popover import ( + SynchronizedGroupRowPopover, +) +from monophony.ui.rows.group_row import GroupRow +from monophony.ui.rows.song_row import SongRow + +from gi.repository import GObject, Gtk + + +class SynchronizedGroupRow(GroupRow): + __gtype_name__ = __qualname__ + _row_type = SongRow + + def __init__(self, group: Group): + super().__init__(group) + + self._more_button.set_create_popup_func( + lambda button, ref: SynchronizedGroupRow._on_show_more(ref(), button), + weakref.ref(self) + ) + + @GObject.Signal(name='delete-playlist', arg_types=(object,)) + def _delete_playlist(self, _playlist: Group): + return + + def _on_show_more(self, button: Gtk.MenuButton): + self._popover = SynchronizedGroupRowPopover() + self._popover.connect( + 'queue-group', + lambda _popover, row: row().emit('queue-group', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'add-group-to', + lambda _popover, row: row().emit('add-group-to', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'download-group', + lambda _popover, row: row().emit('download-group', row().group), + weakref.ref(self) + ) + self._popover.connect( + 'delete-playlist', + lambda _popover, row: row().emit('delete-playlist', row().group), + weakref.ref(self) + ) + button.set_popover(self._popover) + + def update_contents(self): + logging.info( + __name__, + f'Updating row contents for external playlist "{self.group.title}"...' + ) + + for playlist in playlists.read_external(): + if playlist.title == self.group.title: + self.group = playlist + break + else: + logging.error( + __name__, + 'Failed to update row contents - ' + f'external playlist "{self.group.title}" does not exist' + ) + return + + super().update_contents() + logging.info(__name__, 'Updated row contents') diff --git a/source/monophony/frontend/tabs/__init__.py b/source/monophony/ui/windows/__init__.py similarity index 100% rename from source/monophony/frontend/tabs/__init__.py rename to source/monophony/ui/windows/__init__.py diff --git a/source/monophony/ui/windows/add_window.py b/source/monophony/ui/windows/add_window.py new file mode 100644 index 0000000..d6a9e65 --- /dev/null +++ b/source/monophony/ui/windows/add_window.py @@ -0,0 +1,100 @@ +import weakref + +from monophony import MIN_WIDTH, playlists +from monophony.data import Group +from monophony.debug import MemoryDebugger + +from gi.repository import Adw, Gtk + + +class AddWindow(MemoryDebugger, Adw.Dialog): + def __init__(self, group: Group): + super().__init__() + + self._selected_lists = [] + self._group = group + + self._playlists_group = Adw.PreferencesGroup() + self._playlists_group.props.visible = False + lists = playlists.read() + for playlist in lists: + self._add_playlist_row(playlist) + + new_playlist_row = Adw.EntryRow() + new_playlist_row.props.title = _('New Playlist') + new_playlist_row.props.show_apply_button = True + new_playlist_row.connect( + 'apply', + lambda row, ref: AddWindow._on_create_playlist(ref(), row), + weakref.ref(self) + ) + + new_playlist_group = Adw.PreferencesGroup() + new_playlist_group.add(new_playlist_row) + + playlists_page = Adw.PreferencesPage() + playlists_page.add(self._playlists_group) + playlists_page.add(new_playlist_group) + + self.add_button = Gtk.Button() + self.add_button.props.label = _('Add') + self.add_button.props.sensitive = False + self.add_button.add_css_class('suggested-action') + self.add_button.connect( + 'clicked', lambda _button, ref: AddWindow._on_add(ref()), weakref.ref(self) + ) + + add_bar = Gtk.ActionBar() + add_bar.pack_end(self.add_button) + + toolbar_view = Adw.ToolbarView() + toolbar_view.props.content = playlists_page + toolbar_view.add_top_bar(Adw.HeaderBar()) + toolbar_view.add_bottom_bar(add_bar) + + self.props.title = _('Add to Playlists...') + self.props.child = toolbar_view + self.props.follows_content_size = True + self.props.width_request = MIN_WIDTH + + def _add_playlist_row(self, playlist: Group, checked: bool=False): + check_button = Gtk.CheckButton() + check_button.connect( + 'toggled', + lambda button, ref, playlist: + AddWindow._on_check_playlist(ref(), button.props.active, playlist), + weakref.ref(self), + playlist + ) + check_button.props.active = checked + + check_row = Adw.ActionRow() + check_row.props.title = playlist.title + check_row.props.activatable_widget = check_button + check_row.add_prefix(check_button) + + self._playlists_group.props.visible = True + self._playlists_group.add(check_row) + + def _on_check_playlist(self, check: bool, playlist: Group): + if check: + self._selected_lists.append(playlist) + self.add_button.props.sensitive = True + else: + self._selected_lists.remove(playlist) + + def _on_create_playlist(self, entry_row: Adw.EntryRow): + if not entry_row.props.text: + return + + self._add_playlist_row( + Group(title=playlists.add(Group(title=entry_row.props.text))), True + ) + entry_row.props.text = '' + self.add_button.props.sensitive = True + + def _on_add(self): + for playlist in self._selected_lists: + playlists.add_songs(self._group, playlist.title) + + self.close() diff --git a/source/monophony/ui/windows/import_window.py b/source/monophony/ui/windows/import_window.py new file mode 100644 index 0000000..0737907 --- /dev/null +++ b/source/monophony/ui/windows/import_window.py @@ -0,0 +1,110 @@ +import weakref + +from monophony import MIN_WIDTH +from monophony.data import Group +from monophony.debug import MemoryDebugger +from monophony.playlists import ImportTask + +from gi.repository import Adw, GObject, Gtk + + +class ImportWindow(MemoryDebugger, Adw.Dialog): + def __init__(self, group: Group | None=None): + super().__init__() + + self._group = Group() if group is None else group + + self._url_entry = Adw.EntryRow() + self._url_entry.props.title = _('Playlist URL') + self._url_entry.props.text = ( + 'https://www.youtube.com/playlist?list=' + self._group.yt_id + ) if self._group.yt_id else '' + self._url_entry.connect( + 'changed', + lambda _entry, ref: ImportWindow._on_url_changed(ref()), + weakref.ref(self) + ) + + self._name_entry = Adw.EntryRow() + self._name_entry.props.title = _('Playlist Name') + self._name_entry.props.text = self._group.title + + self._sync_switch = Adw.SwitchRow() + self._sync_switch.props.title = _('Synchronized') + self._sync_switch.props.subtitle = _( + "Synchronized playlists are updated automatically and can't be edited" + ) + self._sync_switch.connect( + 'notify::active', + lambda _switch, _param, ref: ImportWindow._on_sync_switched(ref()), + weakref.ref(self) + ) + + row_group = Adw.PreferencesGroup() + row_group.add(self._url_entry) + row_group.add(self._name_entry) + row_group.add(self._sync_switch) + + page = Adw.PreferencesPage() + page.add(row_group) + + self._import_button = Gtk.Button() + self._import_button.props.label = _('Import') + self._import_button.props.sensitive = bool(self._group.yt_id) + self._import_button.add_css_class('suggested-action') + self._import_button.connect( + 'clicked', + lambda _button, ref: ImportWindow._on_import(ref()), + weakref.ref(self) + ) + + action_bar = Gtk.ActionBar() + action_bar.pack_end(self._import_button) + + toolbar_view = Adw.ToolbarView() + toolbar_view.props.content = page + toolbar_view.add_top_bar(Adw.HeaderBar()) + toolbar_view.add_bottom_bar(action_bar) + + self.props.title = _('Import Playlist...') + self.props.child = toolbar_view + self.props.content_width = MIN_WIDTH + + @GObject.Signal(name='import') + def _import(self): + return + + @GObject.Signal(name='import-failed') + def _import_failed(self): + return + + def _on_import(self): + if not self._url_entry.props.text: + return + + self.props.sensitive = False + self.props.can_close = False + self._import_button.props.child = Adw.Spinner() + ImportTask( + callback=self._on_import_finished, + args=( + self._name_entry.props.text, + self._url_entry.props.text, + not self._sync_switch.props.active + ) + ).start() + + def _on_import_finished(self, task: ImportTask): + self.props.can_close = True + self.close() + self.emit('import' if task.result else 'import-failed') + + def _on_sync_switched(self): + if self._sync_switch.props.active: + self._name_entry.props.text = self._group.title + self._name_entry.props.sensitive = False + else: + self._name_entry.props.sensitive = True + + def _on_url_changed(self): + self._import_button.props.sensitive = bool(self._url_entry.props.text) diff --git a/source/monophony/ui/windows/main_window.py b/source/monophony/ui/windows/main_window.py new file mode 100644 index 0000000..90ce160 --- /dev/null +++ b/source/monophony/ui/windows/main_window.py @@ -0,0 +1,925 @@ +import time +import weakref + +from monophony import ( + DISPLAY_NAME, + GRESOURCES_PATH, + ID, + MIN_WIDTH, + __version__, + downloads, + logging, + recommendations, + settings, +) +from monophony.asynchronous import Task +from monophony.data import Artist, Group, Song +from monophony.downloads import DownloadTask +from monophony.player import Player +from monophony.playlists import UpdateExternalTask +from monophony.ui.bars.player_bar import PlayerBar +from monophony.ui.pages.artist_page import ArtistPage +from monophony.ui.pages.home_page import HomePage +from monophony.ui.pages.loading_page import LoadingPage +from monophony.ui.pages.results_page import ResultsPage +from monophony.ui.pages.status_page import StatusPage +from monophony.ui.queue_sidebar import QueueSidebar +from monophony.ui.windows.add_window import AddWindow +from monophony.ui.windows.import_window import ImportWindow +from monophony.ui.windows.message_window import MessageWindow +from monophony.yt import GetArtistTask, GetRecommendationsTask, SearchTask + +from gi.repository import Adw, Gio, GObject, Gtk + + +class PrepareHomePageTask(Task): + def _on_progress_update(self, task: Task, progress: float): + if isinstance(task, GetRecommendationsTask): + self._recommendations_progress = progress + else: + self._externals_update_progress = progress + + self._update_progress( + (self._recommendations_progress + self._externals_update_progress) / 2 + ) + + def _function(self): + self._recommendations_progress = 0 + self._externals_update_progress = 0 + recommendations_task = GetRecommendationsTask( + progress_callback=self._on_progress_update + ) + update_task = UpdateExternalTask( + progress_callback=self._on_progress_update + ) + recommendations_task.start() + update_task.start() + + while recommendations_task.is_running() or update_task.is_running(): + time.sleep(0.5) + + result = recommendations_task.result + if result: + recommendations.write(result) + + +class MainWindow(Adw.ApplicationWindow): + __gtype_name__ = __qualname__ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self._downloader = downloads.downloader + self._inhibit_suspend_cookie = 0 + self._last_search_query = None + self._last_artist = None + self._current_browsing_task = SearchTask() + + self._application = self.props.application + self._application.hold() + + self._player = Player() + self._player.connect( + 'recents-changed', + lambda _player, ref: + MainWindow._on_recents_changed(ref()), + weakref.ref(self) + ) + self._player.connect( + 'queue-changed', + lambda _player, queue, i, ref: + MainWindow._on_queue_changed(ref(), queue, i), + weakref.ref(self) + ) + self._player.connect( + 'progress-changed', + lambda _player, progress, ref: + MainWindow._on_progress_changed(ref(), progress), + weakref.ref(self) + ) + self._player.connect( + 'buffering-changed', + lambda _player, progress, ref: + MainWindow._on_buffering_changed(ref(), progress), + weakref.ref(self) + ) + self._player.connect( + 'state-changed', + lambda _player, state, ref: + MainWindow._on_state_changed(ref(), state), + weakref.ref(self) + ) + self._player.connect( + 'volume-changed', + lambda _player, volume, ref: + MainWindow._on_volume_changed_in_backend(ref(), volume), + weakref.ref(self) + ) + self._player.connect( + 'mode-changed', + lambda _player, mode, ref: + MainWindow._on_mode_changed_in_backend(ref(), mode), + weakref.ref(self) + ) + self._player.connect( + 'pause-changed', + lambda _player, pause, ref: + MainWindow._on_pause_changed_in_backend(ref(), pause), + weakref.ref(self) + ) + self._player.connect( + 'raise', lambda _player, ref: ref().present(), weakref.ref(self) + ) + + self._queue_sidebar = QueueSidebar() + self._queue_sidebar.connect( + 'play', + lambda _sidebar, song, _group, ref, player_ref: + MainWindow._on_play(ref(), song, player_ref().get_queue()), + weakref.ref(self), + weakref.ref(self._player) + ) + self._queue_sidebar.connect( + 'add-group-to', + lambda _sidebar, _group, ref: MainWindow._on_add_queue_to(ref()), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'add-song-to', + lambda _sidebar, song, ref: MainWindow._on_add_song_to(ref(), song), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'undownload-song', + lambda _sidebar, song, ref: + MainWindow._on_remove_song_from_downloads(ref(), song), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'download-song', + lambda _sidebar, song, ref: + MainWindow._on_download_songs(ref(), Group(songs=[song])), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'move-song', + lambda _sidebar, from_s, to_s, ref: + MainWindow._on_queue_move_song(ref(), from_s, to_s), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'unqueue-song', + lambda _sidebar, song, ref: MainWindow._on_remove_from_queue(ref(), song), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'view-artist', + lambda _sidebar, artist, ref: + MainWindow._on_view_artist(ref(), artist, True), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'clear-queue', + lambda _sidebar, ref: MainWindow._on_clear_queue(ref()), + weakref.ref(self) + ) + self._queue_sidebar.connect( + 'shuffle-queue', + lambda _sidebar, ref: MainWindow._on_shuffle_queue(ref()), + weakref.ref(self) + ) + self._queue_sidebar.hide_button.connect( + 'clicked', + lambda _b, ref: MainWindow._on_hide_sidebar(ref()), + weakref.ref(self) + ) + + self._home_page = HomePage() + self._home_page.connect( + 'play', + lambda _page, song, group, ref: MainWindow._on_play(ref(), song, group), + weakref.ref(self) + ) + self._home_page.connect( + 'queue-group', + lambda _page, group, ref: MainWindow._on_add_to_queue(ref(), group), + weakref.ref(self) + ) + self._home_page.connect( + 'queue-song', + lambda _page, song, ref: + MainWindow._on_add_to_queue(ref(), Group(songs=[song])), + weakref.ref(self) + ) + self._home_page.connect( + 'add-song-to', + lambda _page, song, ref: MainWindow._on_add_song_to(ref(), song), + weakref.ref(self) + ) + self._home_page.connect( + 'view-artist', + lambda _page, artist, ref: MainWindow._on_view_artist(ref(), artist, True), + weakref.ref(self) + ) + self._home_page.connect( + 'undownload-song', + lambda _page, song, ref: + MainWindow._on_remove_song_from_downloads(ref(), song), + weakref.ref(self) + ) + self._home_page.connect( + 'download-song', + lambda _page, song, ref: + MainWindow._on_download_songs(ref(), Group(songs=[song])), + weakref.ref(self) + ) + self._home_page.connect( + 'add-group-to', + lambda _page, group, ref: MainWindow._on_add_group_to(ref(), group), + weakref.ref(self) + ) + self._home_page.connect( + 'download-group', + lambda _page, group, ref: MainWindow._on_download_songs(ref(), group), + weakref.ref(self) + ) + self._home_page.connect( + 'import-group', + lambda _page, group, ref: MainWindow._on_import_group(ref(), group), + weakref.ref(self) + ) + self._home_page.connect( + 'search', + lambda _page, query, filter_, ref: + MainWindow._on_search(ref(), query, filter_), + weakref.ref(self) + ) + self._home_page.connect( + 'show-about', + lambda _page, ref: MainWindow._on_show_about(ref()), + weakref.ref(self) + ) + self._home_page.update_downloads(self._downloader.get_downloads()) + + self._navigation_view = Adw.NavigationView() + self._navigation_view.add(LoadingPage()) + self._navigation_view.connect( + 'popped', + lambda _view, page, ref: MainWindow._on_page_popped(ref(), page), + weakref.ref(self) + ) + + self._player_bar = PlayerBar() + self._player_bar.connect( + 'mode-changed', + lambda _bar, mode, ref: + MainWindow._on_mode_changed_in_frontend(ref(), mode), + weakref.ref(self) + ) + self._player_bar.connect( + 'next-song', + lambda _bar, ref: MainWindow._on_next_song(ref()), + weakref.ref(self) + ) + self._player_bar.connect( + 'previous-song', + lambda _bar, ref: MainWindow._on_previous_song(ref()), + weakref.ref(self) + ) + self._player_bar.connect( + 'seek', + lambda _bar, value, ref: + MainWindow._on_seek(ref(), value), + weakref.ref(self) + ) + self._player_bar.connect( + 'volume-changed', + lambda _bar, volume, ref: + MainWindow._on_volume_changed_in_frontend(ref(), volume), + weakref.ref(self) + ) + self._player_bar.connect( + 'pause', + lambda _bar, ref: MainWindow._on_pause_changed_in_frontend(ref()), + weakref.ref(self) + ) + + self._toolbar_view = Adw.ToolbarView() + self._toolbar_view.props.content = self._navigation_view + self._toolbar_view.props.reveal_bottom_bars = False + self._toolbar_view.add_bottom_bar(self._player_bar) + + self._split_view = Adw.OverlaySplitView() + self._split_view.props.content = self._toolbar_view + self._split_view.props.sidebar = self._queue_sidebar + self._split_view.props.min_sidebar_width = MIN_WIDTH + self._split_view.bind_property( + 'show-sidebar', + self._player_bar.queue_button, + 'active', + GObject.BindingFlags.BIDIRECTIONAL | + GObject.BindingFlags.SYNC_CREATE + ) + self._split_view.bind_property( + 'collapsed', + self._player_bar.queue_button, + 'visible', + GObject.BindingFlags.BIDIRECTIONAL | + GObject.BindingFlags.SYNC_CREATE + ) + self._split_view.bind_property( + 'collapsed', + self._queue_sidebar.hide_button, + 'visible', + GObject.BindingFlags.BIDIRECTIONAL | + GObject.BindingFlags.SYNC_CREATE + ) + + view_breakpoint = Adw.Breakpoint() + view_breakpoint.props.condition = Adw.BreakpointCondition.parse( + 'max-width: 700' + ) + view_breakpoint.add_setter(self._split_view, 'collapsed', True) + + focus_search_action = Gio.SimpleAction.new('focus-search', None) + focus_search_action.connect('activate', self._on_focus_search) + self.add_action(focus_search_action) + + self.props.title = DISPLAY_NAME + self.props.icon_name = ID + self.props.content = self._split_view + self.props.width_request = MIN_WIDTH + self.props.height_request = MIN_WIDTH + self.set_default_size( + int(settings.load('window-width', 720)), + int(settings.load('window-height', 600)) + ) + self.connect('close-request', MainWindow._on_close) + self.add_breakpoint(view_breakpoint) + + self._current_browsing_task = PrepareHomePageTask( + progress_callback=self._on_loading_progress, + callback=self._on_home_page_prepared + ) + self._current_browsing_task.start() + self._on_volume_changed_in_backend(self._player.get_volume()) + self._on_mode_changed_in_backend(self._player.mode) + + def _inhibit_suspend(self): + self._uninhibit_suspend() + self._inhibit_suspend_cookie = self._application.inhibit( + self, + Gtk.ApplicationInhibitFlags.SUSPEND, + _('Playing') + ) + + def _uninhibit_suspend(self): + if self._inhibit_suspend_cookie != 0: + self._application.uninhibit(self._inhibit_suspend_cookie) + self._inhibit_suspend_cookie = 0 + + def _update_external_playlists(self): + self._home_page.update_external_playlists() + + def _update_playlists(self): + self._home_page.update_playlists() + + def _update_downloads(self): + self._queue_sidebar.update_download_status() + for page in self._navigation_view.props.navigation_stack: + if not isinstance(page, LoadingPage): + page.update_download_status() + + self._home_page.update_downloads(self._downloader.get_downloads()) + + def _on_add_group_to(self, group: Group): + add_window = AddWindow(group) + add_window.connect( + 'closed', + lambda _window, ref: MainWindow._update_playlists(ref()), + weakref.ref(self) + ) + add_window.present(self) + + def _on_add_song_to(self, song: Song): + add_window = AddWindow(Group(songs=[song])) + add_window.connect( + 'closed', + lambda _window, ref: MainWindow._update_playlists(ref()), + weakref.ref(self) + ) + add_window.present(self) + + def _on_add_queue_to(self): + self._on_add_group_to(self._player.get_queue()) + + def _on_add_to_queue(self, group: Group): + self._player.add_to_queue(group) + + def _on_buffering_changed(self, progress: float): + self._player_bar.update_buffering(progress) + + def _on_clear_queue(self): + self._player.stop() + + def _on_close(self) -> bool: + logging.info(__name__, 'Close requested') + if self._player.get_queue().songs: + logging.info(__name__, 'Still playing - hiding instead of closing') + self.props.visible = False + return True + + size = self.get_default_size() + settings.save({ + 'window-width': size.width, + 'window-height': size.height + }) + self._application.release() + return False + + def _on_download_songs(self, group: Group): + DownloadTask( + progress_callback=self._on_download_status_changed, + callback=self._on_download_finished, + args=( + self._downloader, + group + ) + ).start() + + def _on_download_finished(self, task: DownloadTask): + self._on_download_status_changed(task) + if not task.result: + MessageWindow( + _('Download Failed'), _('Some songs could not be downloaded') + ).present(self) + + def _on_download_status_changed(self, _task: DownloadTask | None=None): + # Never canceled + self._update_downloads() + + def _on_home_page_prepared(self, _task: PrepareHomePageTask): + self._navigation_view.replace([self._home_page]) + self._update_external_playlists() + self._home_page.update_recommendations() + + def _on_filter_artist(self, filter_: str): + if isinstance( + self._navigation_view.get_previous_page( + self._navigation_view.get_visible_page() + ), + ArtistPage + ): + self._navigation_view.pop() + self._on_view_artist(self._last_artist, False, filter_) + + def _on_filter_results(self, filter_: str): + if isinstance( + self._navigation_view.get_previous_page( + self._navigation_view.get_visible_page() + ), + ResultsPage + ): + self._navigation_view.pop() + self._on_search(self._last_search_query, filter_) + + def _on_focus_search(self, _action, _param): + self._home_page.focus_search() + + def _on_hide_sidebar(self): + self._split_view.props.show_sidebar = False + + def _on_import_group(self, group: Group): + import_window = ImportWindow(group) + import_window.connect( + 'import', + lambda _window, ref: MainWindow._on_import_success(ref()), + weakref.ref(self) + ) + import_window.connect( + 'import-failed', + lambda _window, ref: + MessageWindow( + _('Failed to Import'), + _('Check your internet connection and try again') + ).present(ref()), + weakref.ref(self) + ) + import_window.present(self) + + def _on_import_success(self): + self._update_playlists() + self._update_external_playlists() + + def _on_loading_progress(self, task: Task, progress: float): + if task.is_canceled() or task is not self._current_browsing_task: + logging.info( + __name__, f'Ignoring progress update "{progress}" from canceled task' + ) + return + + page = self._navigation_view.props.visible_page + if not isinstance(page, LoadingPage): + logging.info( + __name__, f'Ignoring progress update "{progress}" as loading is done' + ) + return + + page.update_progress(progress) + + def _on_mode_changed_in_backend(self, mode: int): + self._player_bar.update_mode(mode) + + def _on_mode_changed_in_frontend(self, mode: int): + self._player.set_mode(mode) + + def _on_next_song(self): + self._player.next(from_user=True) + + def _on_page_popped(self, page: Adw.NavigationPage): + if isinstance(page, LoadingPage): + self._current_browsing_task.cancel() + + if isinstance(self._navigation_view.get_visible_page(), LoadingPage): + self._navigation_view.pop() + + def _on_pause_changed_in_backend(self, pause: bool): + if pause: + self._uninhibit_suspend() + else: + self._inhibit_suspend() + + self._player_bar.update_pause(pause) + + def _on_pause_changed_in_frontend(self): + self._player.set_pause(not self._player.paused) + + def _on_play(self, song: Song, group: Group): + self._player.play(song, group) + + def _on_previous_song(self): + self._player.previous() + + def _on_queue_move_song(self, song: Song, to_song: Song): + self._player.move_song(song, to_song) + + def _on_recents_changed(self): + self._home_page.update_history() + + def _on_remove_from_queue(self, song: Song): + self._player.remove_from_queue(song) + + def _on_remove_song_from_downloads(self, song: Song): + self._downloader.remove(song) + self._update_downloads() + + def _on_search(self, query: str, filter_: str=''): + query = query.strip() + if not query: + return + + self._last_search_query = query + + loading_page = LoadingPage() + loading_page.connect( + 'show-about', + lambda _page, ref: MainWindow._on_show_about(ref()), + weakref.ref(self) + ) + self._navigation_view.push(loading_page) + + logging.info(__name__, f'Searching for "{query}" with filter "{filter_}"...') + + self._current_browsing_task.cancel() + self._current_browsing_task = SearchTask( + progress_callback=self._on_loading_progress, + callback=self._on_search_finished, + args=( + query, filter_, None if filter_ else 4 + ) + ) + self._current_browsing_task.extra_data = filter_ + self._current_browsing_task.start() + + def _on_search_finished(self, task: SearchTask): + if task.is_canceled() or self._current_browsing_task is not task: + logging.info(__name__, 'Ignoring callback from canceled search task') + return + + filter_ = task.extra_data + results = task.result + if isinstance(self._navigation_view.get_visible_page(), LoadingPage): + self._navigation_view.pop() + + page = None + if results is None: + logging.error(__name__, 'Failed to search') + page = StatusPage( + _('Failed to Search'), + _('Check your internet connection and try again'), + 'dialog-error-symbolic' + ) + elif results == []: + logging.warning(__name__, 'Done searching, no results') + page = StatusPage( + _('No Results'), + _('Try searching for something else'), + 'dialog-information-symbolic' + ) + else: + logging.info(__name__, 'Done searching') + page = ResultsPage(results, filter_) + page.connect( + 'play', + lambda _page, song, group, ref: MainWindow._on_play(ref(), song, group), + weakref.ref(self) + ) + page.connect( + 'filter-results', + lambda _page, filter_, ref: + MainWindow._on_filter_results(ref(), filter_), + weakref.ref(self) + ) + page.connect( + 'queue-song', + lambda _page, song, ref: + MainWindow._on_add_to_queue(ref(), Group(songs=[song])), + weakref.ref(self) + ) + page.connect( + 'add-song-to', + lambda _page, song, ref: MainWindow._on_add_song_to(ref(), song), + weakref.ref(self) + ) + page.connect( + 'undownload-song', + lambda _page, song, ref: + MainWindow._on_remove_song_from_downloads(ref(), song), + weakref.ref(self) + ) + page.connect( + 'download-song', + lambda _page, song, ref: + MainWindow._on_download_songs(ref(), Group(songs=[song])), + weakref.ref(self) + ) + page.connect( + 'queue-group', + lambda _page, group, ref: MainWindow._on_add_to_queue(ref(), group), + weakref.ref(self) + ) + page.connect( + 'add-group-to', + lambda _page, group, ref: MainWindow._on_add_group_to(ref(), group), + weakref.ref(self) + ) + page.connect( + 'download-group', + lambda _page, group, ref: MainWindow._on_download_songs(ref(), group), + weakref.ref(self) + ) + page.connect( + 'import-group', + lambda _page, group, ref: MainWindow._on_import_group(ref(), group), + weakref.ref(self) + ) + page.connect( + 'view-artist', + lambda _page, artist, ref: + MainWindow._on_view_artist(ref(), artist, True), + weakref.ref(self) + ) + + page.connect( + 'show-about', + lambda _page, ref: MainWindow._on_show_about(ref()), + weakref.ref(self) + ) + self._navigation_view.push(page) + + def _on_seek(self, value: float): + self._player.seek(value) + + def _on_show_about(self): + about_dialog = Adw.AboutDialog.new_from_appdata( + GRESOURCES_PATH + '/metainfo.xml', __version__ + ) + about_dialog.props.debug_info = logging.get_log() + about_dialog.props.debug_info_filename = 'log.txt' + about_dialog.props.translator_credits = _('translator-credits') + about_dialog.props.copyright = 'Copyright © Zehkira and contributors' + about_dialog.add_link( + _('Donate'), 'https://zeh-kira.itch.io/monophony/purchase' + ) + about_dialog.add_legal_section( + 'mprisify', + 'Copyright © Zehkira and contributors', + Gtk.License.LGPL_3_0_ONLY + ) + about_dialog.add_legal_section('Mopidy-MPRIS', '', Gtk.License.APACHE_2_0) + about_dialog.add_legal_section( + 'StrEnum', 'Copyright © 2019 James C Sinclair', Gtk.License.MIT_X11 + ) + about_dialog.add_legal_section('pycairo', '', Gtk.License.LGPL_2_1_ONLY) + about_dialog.add_legal_section('cairo', '', Gtk.License.LGPL_2_1_ONLY) + about_dialog.add_legal_section( + 'pydbus', + 'Copyright © 2014, 2015, 2016 Linus Lewandowski', + Gtk.License.LGPL_2_1 + ) + about_dialog.add_legal_section('GLib', '', Gtk.License.LGPL_2_1) + about_dialog.add_legal_section('PyGObject', '', Gtk.License.LGPL_2_1) + about_dialog.add_legal_section( + 'ytmusicapi', 'Copyright © 2024 sigma67', Gtk.License.MIT_X11 + ) + about_dialog.add_legal_section('certifi', '', Gtk.License.MPL_2_0) + about_dialog.add_legal_section( + 'charset_normalizer', + 'Copyright © 2025 TAHRI Ahmed R.', + Gtk.License.MIT_X11 + ) + about_dialog.add_legal_section( + 'idna', + 'Copyright © 2013-2025, Kim Davies and contributors', + Gtk.License.BSD_3 + ) + about_dialog.add_legal_section( + 'requests', + 'Copyright © 2019 Kenneth Reitz', + Gtk.License.APACHE_2_0 + ) + about_dialog.add_legal_section( + 'urllib3', + 'Copyright © 2008-2020 Andrey Petrov and contributors', + Gtk.License.MIT_X11 + ) + about_dialog.add_legal_section('GTK', '', Gtk.License.LGPL_2_1) + about_dialog.add_legal_section('GTK/roaring', '', Gtk.License.APACHE_2_0) + about_dialog.add_legal_section('GTK/timsort', '', Gtk.License.APACHE_2_0) + about_dialog.add_legal_section('libadwaita', '', Gtk.License.LGPL_2_1) + about_dialog.add_legal_section( + 'Adwaita Icon Theme', '', Gtk.License.LGPL_3_0_ONLY + ) + about_dialog.add_legal_section('GStreamer', '', Gtk.License.LGPL_2_1_ONLY) + + about_dialog.present(self) + + def _on_shuffle_queue(self): + self._player.shuffle() + + def _on_progress_changed(self, progress: float): + self._player_bar.update_progress(progress) + + def _on_queue_changed(self, queue: Group, song_index: int): + self._queue_sidebar.update_contents(queue, song_index) + if not queue.songs: + self._toolbar_view.props.reveal_bottom_bars = False + self._uninhibit_suspend() + if not self.props.visible: + logging.info(__name__, 'Playback ended while window hidden') + self.close() + return + + self._toolbar_view.props.reveal_bottom_bars = True + self._inhibit_suspend() + self._player_bar.update_song(queue.songs[song_index]) + + def _on_state_changed(self, state: int): + self._player_bar.update_state(state) + + def _on_view_artist(self, artist: Artist, replace_page: bool, filter_: str=''): + self._last_artist = artist + + if ( + replace_page and + isinstance( + self._navigation_view.get_visible_page(), ArtistPage | StatusPage + ) + ): + self._navigation_view.pop() + + loading_page = LoadingPage() + loading_page.connect( + 'show-about', + lambda _page, ref: MainWindow._on_show_about(ref()), + weakref.ref(self) + ) + self._navigation_view.push(loading_page) + + logging.info(__name__, f'Showing artist "{artist.yt_id}"...') + self._current_browsing_task.cancel() + self._current_browsing_task = GetArtistTask( + progress_callback=self._on_loading_progress, + callback=self._on_view_artist_finished, + args=( + artist.yt_id, filter_, None if filter_ else 4 + ) + ) + self._current_browsing_task.extra_data = filter_ + self._current_browsing_task.start() + + def _on_view_artist_finished(self, task: GetArtistTask): + if task.is_canceled() or self._current_browsing_task is not task: + logging.info(__name__, 'Ignoring callback from canceled view artist task') + return + + results = task.result + filter_ = task.extra_data + if isinstance(self._navigation_view.get_visible_page(), LoadingPage): + self._navigation_view.pop() + + page = None + if results is None: + logging.error(__name__, 'Failed to load artist page') + page = StatusPage( + _('Failed to Load Artist Page'), + _('Check your internet connection and try again'), + 'dialog-error-symbolic' + ) + elif results == []: + logging.warning(__name__, 'Loaded empty artist page') + page = StatusPage( + _('Empty Artist Page'), + _('No content found from this artist'), + 'dialog-information-symbolic' + ) + else: + logging.info(__name__, 'Loaded artist page') + page = ArtistPage(results, filter_) + page.connect( + 'play', + lambda _page, song, group, ref: MainWindow._on_play(ref(), song, group), + weakref.ref(self) + ) + page.connect( + 'filter-results', + lambda _page, filter_, ref: + MainWindow._on_filter_artist(ref(), filter_), + weakref.ref(self) + ) + page.connect( + 'queue-song', + lambda _page, song, ref: + MainWindow._on_add_to_queue(ref(), Group(songs=[song])), + weakref.ref(self) + ) + page.connect( + 'add-song-to', + lambda _page, song, ref: MainWindow._on_add_song_to(ref(), song), + weakref.ref(self) + ) + page.connect( + 'undownload-song', + lambda _page, song, ref: + MainWindow._on_remove_song_from_downloads(ref(), song), + weakref.ref(self) + ) + page.connect( + 'download-song', + lambda _page, song, ref: + MainWindow._on_download_songs(ref(), Group(songs=[song])), + weakref.ref(self) + ) + page.connect( + 'queue-group', + lambda _page, group, ref: MainWindow._on_add_to_queue(ref(), group), + weakref.ref(self) + ) + page.connect( + 'add-group-to', + lambda _page, group, ref: MainWindow._on_add_group_to(ref(), group), + weakref.ref(self) + ) + page.connect( + 'download-group', + lambda _page, group, ref: MainWindow._on_download_songs(ref(), group), + weakref.ref(self) + ) + page.connect( + 'import-group', + lambda _page, group, ref: MainWindow._on_import_group(ref(), group), + weakref.ref(self) + ) + page.connect( + 'view-artist', + lambda _page, artist, ref: + MainWindow._on_view_artist(ref(), artist, True), + weakref.ref(self) + ) + + page.connect( + 'show-about', + lambda _page, ref: MainWindow._on_show_about(ref()), + weakref.ref(self) + ) + self._navigation_view.push(page) + + def _on_volume_changed_in_backend(self, volume: float): + self._player_bar.update_volume(volume) + + def _on_volume_changed_in_frontend(self, volume: float): + self._player.set_volume(volume, notify_frontend=False) + + def present(self): + logging.info(__name__, 'Presenting window') + super().present() diff --git a/source/monophony/ui/windows/message_window.py b/source/monophony/ui/windows/message_window.py new file mode 100644 index 0000000..d8a2334 --- /dev/null +++ b/source/monophony/ui/windows/message_window.py @@ -0,0 +1,12 @@ +from monophony.debug import MemoryDebugger + +from gi.repository import Adw + + +class MessageWindow(MemoryDebugger, Adw.AlertDialog): + def __init__(self, title: str, details: str): + super().__init__() + + self.props.heading = title + self.props.body = details + self.add_response('dismiss', _('Ok')) diff --git a/source/monophony/ui/windows/rename_window.py b/source/monophony/ui/windows/rename_window.py new file mode 100644 index 0000000..86efc99 --- /dev/null +++ b/source/monophony/ui/windows/rename_window.py @@ -0,0 +1,45 @@ +import weakref + +from monophony.debug import MemoryDebugger + +from gi.repository import Adw, GObject + + +class RenameWindow(MemoryDebugger, Adw.Dialog): + def __init__(self, original_name: str): + super().__init__() + + self.original_name = original_name + + entry_row = Adw.EntryRow() + entry_row.props.title = _('Playlist Name') + entry_row.props.show_apply_button = True + entry_row.props.text = original_name + entry_row.connect( + 'apply', + lambda entry, ref: + RenameWindow._on_apply(ref(), entry.props.text), + weakref.ref(self) + ) + + group = Adw.PreferencesGroup() + group.add(entry_row) + + page = Adw.PreferencesPage() + page.add(group) + + toolbar_view = Adw.ToolbarView() + toolbar_view.props.content = page + toolbar_view.add_top_bar(Adw.HeaderBar()) + + self.props.title = _('Rename Playlist...') + self.props.child = toolbar_view + + @GObject.Signal(name='rename', arg_types=(str,)) + def _rename(self, _new_name: str): + return + + def _on_apply(self, text: str): + if text and text != self.original_name: + self.emit('rename', text) + self.close() diff --git a/source/monophony/yt.py b/source/monophony/yt.py new file mode 100644 index 0000000..ccd306f --- /dev/null +++ b/source/monophony/yt.py @@ -0,0 +1,553 @@ +import contextlib +import subprocess +import time +import traceback + +from monophony import logging +from monophony.asynchronous import Task +from monophony.data import Artist, Group, Song, TimeString, YTItem + +import requests +import ytmusicapi + + +# Exceptions raised by some (but not all) ytmusicapi functions in case of a data +# parsing error. They can be safely interpreted as a "not found" response from YTM. In +# the case of an internet connection error, requests.exceptions.ConnectionError is +# raised instead +YTMUSICAPI_PARSING_EXCEPTIONS = (AttributeError, KeyError, TypeError) + + +class SearchResult: + # Raises KeyError on unknown type + def __init__(self, type_: str, top: bool, item: YTItem | None=None): + if type_ == 'single': + type_ = 'album' + + self.item = item or { + 'album': Group, + 'artist': Artist, + 'playlist': Group, + 'song': Song, + 'video': Song + }[type_]() + self.top = top + self.type = type_ + + +def _get_artist_name(name_data: list[dict] | str) -> str: + if isinstance(name_data, str): + return name_data + if not isinstance(name_data, list): + logging.warning(__name__, 'Got unusual artist name data', name_data or None) + return '' + + return ', '.join( + [artist.get('name', '') for artist in name_data if 'id' in artist] + ) + + +def _get_artist_id(artists: list[dict] | str) -> str: + if isinstance(artists, str): + return '' + if not isinstance(artists, list): + logging.warning(__name__, 'Got unusual artist data', artists or None) + return '' + + a_id = '' + for artist in artists: + a_id = artist.get('id', '') + if a_id: + break + + return a_id + + +def _parse_single_result(yt: ytmusicapi.YTMusic, data: dict) -> SearchResult | None: + category = data.get('category', '') + type_ = data.get('resultType', '') + + if type_ in ('podcast', 'episode'): + logging.info(__name__, 'Discarded podcast result') + return None + + try: + result = SearchResult(type_, category == 'Top result') + except KeyError: + logging.error( + __name__, f'Failed to parse result of unexpected type "{type_}"', data + ) + return None + + if result.type == 'artist': + result.item.name = ( + _get_artist_name(data.get('artists', '')) or + data.get('artist', '') + ) + result.item.yt_id = ( + _get_artist_id(data.get('artists', '')) or + data.get('browseId', '') + ) + else: + result.item.yt_id = ( + data.get('videoId', '') or + data.get('browseId', '') or + data.get('playlistId', '') + ) + result.item.title = data.get('title', '') + result.item.author.yt_id = ( + _get_artist_id(data.get('artists', '')) or + _get_artist_id(data.get('author', '')) or + data.get('channelId', '') + ) + result.item.author.name = ( + _get_artist_name(data.get('artists', '')) or + _get_artist_name(data.get('author', '')) or + data.get('artist', '') + ) + result.item.length = ( + data.get('duration', '') or + TimeString(seconds=int(data.get('lengthSeconds', 0))).as_string() + ) + thumbnail_container = data.get('thumbnails') or data.get('thumbnail') + result.item.thumbnail = ( + thumbnail_container[0].get('url', '') + if isinstance(thumbnail_container, list) + else ( + thumbnail_container.get('thumbnails') or [{}] + )[0].get('url', '') + ) if thumbnail_container else '' + + if not result.item.yt_id: + result_name = ( + result.item.name if isinstance(result.item, Artist) else result.item.title + ) + logging.warning( + __name__, + f'Discarded id-less result "{result_name}" of type "{result.type}"' + ) + return None + + if result.type in ('album', 'playlist'): + try: + try: + playlist = yt.get_playlist(result.item.yt_id, limit=None) + except YTMUSICAPI_PARSING_EXCEPTIONS: + playlist = yt.get_album(result.item.yt_id) + except ( + *YTMUSICAPI_PARSING_EXCEPTIONS, + ytmusicapi.exceptions.YTMusicUserError, # Invalid ID + requests.exceptions.ConnectionError + ): + logging.error( + __name__, 'Failed to parse a result', traceback.format_exc() + ) + return None + + result.item.title = playlist.get('title', result.item.title) + for song_data in playlist.get('tracks', []): + song_data['resultType'] = 'song' + parsed_song_data = _parse_single_result(yt, song_data) + if parsed_song_data: + if result.item.thumbnail and not parsed_song_data.item.thumbnail: + parsed_song_data.item.thumbnail = result.item.thumbnail + result.item.songs.append(parsed_song_data.item) + + return result + + +def get_song_uri(song: Song) -> str | None: + logging.info(__name__, f'Getting URI for song "{song.yt_id}"...') + out, err = subprocess.Popen( + [ + 'yt-dlp', + '--get-url', + '--extract-audio', + '--quiet', + '--no-warnings', + f'https://music.youtube.com/watch?v={song.yt_id}' + ], + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ).communicate() + if err: + logging.error(__name__, 'Failed to get song URI', err) + return None + + logging.info(__name__, 'Got song URI') + return out.split('\n')[0] + + +def get_similar_songs(song: Song, ignore: Group | None=None) -> Group | None: + logging.info( + __name__, + f'Getting similar song to "{song.yt_id}" ignoring ' + f'{len(ignore.songs) if ignore else 0} songs...' + ) + yt = ytmusicapi.YTMusic() + ignore = ignore if ignore else Group() + + try: + data = yt.get_watch_playlist(song.yt_id, radio=True)['tracks'] + except (*YTMUSICAPI_PARSING_EXCEPTIONS, requests.exceptions.ConnectionError): + logging.error( + __name__, 'Failed to get similar song', traceback.format_exc() + ) + return None + + available_songs = [] + for item in data: + item['resultType'] = 'song' + parsed = _parse_single_result(yt, item) + if not parsed: + continue + + song = parsed.item + for ignore_song in ignore.songs: + if ignore_song.yt_id == song.yt_id: + break + else: + available_songs.append(song) + + if available_songs: + logging.info(__name__, f'Got {len(available_songs)} similar songs') + return Group(songs=available_songs) + + logging.error(__name__, 'Failed to get similar song - no songs available') + return Group() + + +def get_song(id_: str) -> Song | None: + logging.info(__name__, f'Getting song "{id_}"...') + yt = ytmusicapi.YTMusic() + + try: + result = yt.get_song(id_)['videoDetails'] + except (*YTMUSICAPI_PARSING_EXCEPTIONS, requests.exceptions.ConnectionError): + logging.error( + __name__, 'Failed to get song', traceback.format_exc() + ) + return None + + result['resultType'] = 'song' + if parsed := _parse_single_result(yt, result): + logging.info(__name__, 'Got song') + return parsed.item + + logging.error(__name__, 'Failed to get song') + return None + + +def get_album_or_playlist(yt_id: str) -> Group | None: + logging.info(__name__, f'Getting album/playlist "{yt_id}"...') + + if result := _parse_single_result( + ytmusicapi.YTMusic(), + { + 'resultType': 'playlist', + 'playlistId': yt_id + } + ): + logging.info(__name__, 'Got album/playlist') + return result.item + + logging.error(__name__, 'Failed to get album/playlist') + return None + + +class ParseResultsTask(Task): + def _function( + self, yt: ytmusicapi.YTMusic, data: list[dict], limit: int | None=None, + ) -> list[SearchResult] | None: + count_per_type = {} + + logging.info( + __name__, f'Parsing {len(data)} results with limit of {limit} per type...' + ) + results = [] + got_top_result = False + for i, item in enumerate(data): + if self.is_canceled(): + logging.info(__name__, 'Parsing canceled') + return None + + with contextlib.suppress(KeyError): + temp_result = SearchResult(item.get('resultType'), False) + if limit and count_per_type.get(temp_result.type, 0) >= limit: + continue + + parsed = _parse_single_result(yt, item) + self._update_progress(i / len(data)) + if parsed: + if parsed.top: + if got_top_result: + logging.warning(__name__, 'Multiple top results') + parsed.top = False + else: + logging.info(__name__, f'Top result type is "{parsed.type}"') + got_top_result = True + + count_per_type[parsed.type] = count_per_type.get(parsed.type, 0) + 1 + results.append(parsed) + + # Internal results (in albums, playlists) not counted + logging.info(__name__, f'Done parsing results, kept {len(results)}/{len(data)}') + return results + + +class GetArtistTask(Task): + def _on_parse_progress_update(self, task: ParseResultsTask, progress: float): + if not task.is_canceled(): + self._update_progress(0.5 + progress / 2) + + def _function( + self, browse_id: str, filter_: str, limit: int | None=None + ) -> list[SearchResult] | None: + logging.info( + __name__, + f'Getting artist "{browse_id}" with filter "{filter_}" and limit of ' + f'{limit} per type...' + ) + yt = ytmusicapi.YTMusic() + + logging.info(__name__, 'Fetching artist...') + try: + try: + data = yt.get_artist(browse_id) + logging.info(__name__, 'Fetched artist') + except YTMUSICAPI_PARSING_EXCEPTIONS: + logging.info(__name__, 'No such artist, fetching as user instead...') + data = yt.get_user(browse_id) + logging.info(__name__, 'Fetched artist as user') + except (*YTMUSICAPI_PARSING_EXCEPTIONS, requests.exceptions.ConnectionError): + logging.error( + __name__, + 'Failed to get artist - could not fetch', + traceback.format_exc() + ) + return None + + if self.is_canceled(): + logging.info(__name__, 'Canceled getting artist') + return None + + self._update_progress(0.1) + + to_parse = [] + for type_ in ('songs', 'videos'): + if filter_ and type_ != filter_: + continue + + if not (group := data.get(type_)): + logging.info(__name__, f'Artist has no {type_} list') + continue + + tracks = [] + logging.info( + __name__, f'Trying to get {type_} from artist with get_playlist...' + ) + try: + try: + tracks = yt.get_playlist(group.get('browseId', ''))['tracks'] + except YTMUSICAPI_PARSING_EXCEPTIONS: + logging.info( + __name__, + f'Got no {type_}, trying with get_user_videos instead...' + ) + # Does not raise YTMUSICAPI_PARSING_EXCEPTIONS, ever + tracks = yt.get_user_videos(browse_id, group.get('params', '')) + except requests.exceptions.ConnectionError: + logging.error(__name__, 'Failed to get artist', traceback.format_exc()) + return None + + if not tracks: + logging.info( + __name__, f'Got no {type_}, trying from "results" list instead...' + ) + if not (tracks := group.get('results')): + logging.info(__name__, f'Got no {type_} from artist') + continue + + logging.info(__name__, f'Got {len(tracks)} {type_} from artist') + for track in tracks: + track['resultType'] = type_[:-1] + to_parse.append(track) + + if self.is_canceled(): + logging.info(__name__, 'Canceled getting artist') + return None + + self._update_progress(0.2) + for type_ in ('albums', 'singles', 'playlists'): + if filter_ and ('albums' if type_ == 'singles' else type_) != filter_: + continue + + if not (group := data.get(type_)): + logging.info(__name__, f'Artist has no {type_} list') + continue + + lists = [] + logging.info( + __name__, f'Trying to get {type_} from artist with get_artist_albums...' + ) + try: + try: + lists = yt.get_artist_albums( + group.get('browseId', ''), group.get('params', '') + ) + except YTMUSICAPI_PARSING_EXCEPTIONS: + logging.info( + __name__, + f'Got no {type_}, trying with get_user_playlists instead...' + ) + # Does not raise YTMUSICAPI_PARSING_EXCEPTIONS, ever + lists = yt.get_user_playlists(browse_id, group.get('params', '')) + except requests.exceptions.ConnectionError: + logging.error(__name__, 'Failed to get artist', traceback.format_exc()) + return None + + if not lists: + logging.info( + __name__, f'Got no {type_}, trying from "results" list instead...' + ) + if not (lists := group.get('results')): + logging.info(__name__, f'Got no {type_} from artist') + continue + + logging.info(__name__, f'Got {len(lists)} {type_} from artist') + for list_ in lists: + list_['resultType'] = type_[:-1] + to_parse.append(list_) + + if self.is_canceled(): + logging.info(__name__, 'Canceled getting artist') + return None + + self._update_progress(0.5) + parse_task = ParseResultsTask( + progress_callback=self._on_parse_progress_update, + args=(yt, to_parse, limit) + ) + parse_task.start() + while parse_task.is_running(): + time.sleep(0.1) + if self.is_canceled(): + parse_task.cancel() + logging.info(__name__, 'Canceled getting artist') + return None + + if results := parse_task.result: + for result in results: + if not result.item.author.name: + result.item.author.name = data.get('name', '') + + time.sleep(0.1) + logging.info(__name__, 'Got artist') + return results + + logging.error(__name__, 'Failed to get artist') + return None + + +class GetRecommendationsTask(Task): + def _function(self) -> list[Group] | None: + logging.info(__name__, 'Getting recommendations...') + yt = ytmusicapi.YTMusic() + + try: + data = yt.get_home() + except (*YTMUSICAPI_PARSING_EXCEPTIONS, requests.exceptions.ConnectionError): + logging.error( + __name__, 'Failed to get recommendations', traceback.format_exc() + ) + return None + + recommendations = [] + for i, grouping in enumerate(data): + playlist = Group(title=grouping.get('title', '')) + for item in grouping.get('contents', []): + if not isinstance(item, dict): + logging.warning(__name__, 'Unexpected recommendation item', item) + continue + + if 'videoId' not in item: + continue + + item['resultType'] = 'song' + if parsed := _parse_single_result(yt, item): + playlist.songs.append(parsed.item) + + if playlist.songs: + recommendations.append(playlist) + self._update_progress(i / len(data)) + + logging.info(__name__, f'Got {len(recommendations)} groups of recommendations') + return recommendations + + +class SearchTask(Task): + def _on_parse_progress_update(self, task: ParseResultsTask, progress: float): + if not task.is_canceled(): + self._update_progress(0.5 + progress / 2) + + def _function( + self, query: str, filter_: str='', limit: int | None=None + ) -> list[SearchResult] | None: + logging.info(__name__, f'Searching for "{query}" with filter "{filter_}"...') + yt = ytmusicapi.YTMusic() + + self._update_progress(0.1) + try: + if '?v=' in query and '/' in query: + song = get_song(query.split('?v=')[-1].split('&')[0]) + if song: + logging.info(__name__, 'Done searching - got song from URL') + return [SearchResult('song', True, song)] + logging.error( + __name__, 'Failed to search - failed to get song from URL' + ) + return None + if 'youtu.be/' in query: + song = get_song(query.split('youtu.be/')[-1].split('?')[0]) + if song: + logging.info(__name__, 'Done searching - got song from URL') + return [SearchResult('song', True, song)] + logging.error( + __name__, 'Failed to search - failed to get song from URL' + ) + return None + + self._update_progress(0.2) + data = ( + yt.search(query, filter=filter_, limit=100) if filter_ + else yt.search(query) + ) + except (*YTMUSICAPI_PARSING_EXCEPTIONS, requests.exceptions.ConnectionError): + logging.error(__name__, 'Failed to search', traceback.format_exc()) + return None + + if self.is_canceled(): + logging.info(__name__, 'Canceled search') + return None + + self._update_progress(0.5) + parse_task = ParseResultsTask( + progress_callback=self._on_parse_progress_update, + args=(yt, data, limit) + ) + parse_task.start() + while parse_task.is_running(): + time.sleep(0.1) + if self.is_canceled(): + parse_task.cancel() + logging.info(__name__, 'Canceled search') + return None + + if parse_task.result is not None: + time.sleep(0.1) + logging.info(__name__, 'Done searching') + return parse_task.result + + logging.error(__name__, 'Failed to search') + return None diff --git a/source/pyproject.toml b/source/pyproject.toml index 74f8c8a..1cc2215 100644 --- a/source/pyproject.toml +++ b/source/pyproject.toml @@ -1,6 +1,6 @@ [project] name = 'monophony' -dynamic = ['dependencies', 'optional-dependencies', 'version'] +dynamic = ['version'] requires-python = '>=3.12' [build-system] @@ -8,8 +8,6 @@ requires = ['setuptools'] build-backend = 'setuptools.build_meta' [tool.setuptools.dynamic] -dependencies = {file=['requirements.txt']} -optional-dependencies = {dev={file=['requirements-dev.txt']}} version = {attr='monophony.__version__'} [tool.setuptools.packages.find] @@ -17,19 +15,50 @@ include = ['monophony*'] [tool.ruff] builtins = ['_'] -lint.extend-ignore = ['E401', 'E402', 'E722', 'E74'] -lint.extend-select = [ - 'ARG', 'BLE', 'E1', 'E5', 'FA', 'INP', 'ISC', 'PERF', 'PIE', 'PLC', 'Q', 'RET', - 'RUF', 'SIM', 'TRY', 'UP' -] line-length = 88 indent-width = 4 -[tool.ruff.format] -quote-style = 'single' -indent-style = 'tab' +[tool.ruff.lint] +select = [ + 'A', 'ARG', 'ASYNC', + 'B', 'BLE', + 'C4', 'COM', + 'DTZ', + 'E', 'EM', 'EXE', + 'F', 'FA', 'FIX', 'FLY', 'FURB', + 'G', + 'I', 'ICN', 'INP', 'INT', 'ISC', + 'LOG', + 'N', + 'PERF', 'PGH', 'PIE', 'PLC', 'PLE', 'PLR', 'PLW', 'PT', + 'Q', + 'RET', 'RSE', 'RUF', + 'S', 'SIM', 'SLF', 'SLOT', + 'T20', 'TID', 'TRY', + 'UP', + 'W' +] +ignore = [ + 'COM812', # missing-trailing-comma + 'PLR09', # too-many-* + 'S108', # hardcoded-temp-file + 'S311', # suspicious-non-cryptographic-random-usage + 'S603', # subprocess-without-shell-equals-true + 'S607', # start-process-with-partial-path + 'W191' # tab-indentation +] [tool.ruff.lint.flake8-quotes] inline-quotes = 'single' docstring-quotes = 'single' multiline-quotes = 'single' + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = 'all' + +[tool.ruff.lint.isort] +section-order = [ + 'future', 'standard-library', 'local-folder', 'first-party', 'third-party' +] +known-first-party = ['monophony'] +lines-after-imports = 2 diff --git a/source/requirements-dev.txt b/source/requirements-dev.txt deleted file mode 100644 index de62846..0000000 --- a/source/requirements-dev.txt +++ /dev/null @@ -1 +0,0 @@ -ruff==0.12.8 diff --git a/source/requirements.txt b/source/requirements.txt deleted file mode 100644 index 3eaa62e..0000000 --- a/source/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -mpris_server==0.9.0 -ytmusicapi==1.11.1 diff --git a/source/tests/tests.py b/source/tests/tests.py new file mode 100755 index 0000000..eae823b --- /dev/null +++ b/source/tests/tests.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +# ruff: noqa: S101 - Aseerts used in tests +# ruff: noqa: SLF001 - Private members used in tests + +import os +import shutil +import time +import unittest + +from monophony import ID, NAME, __version__, playlists, settings, yt +from monophony.data import Group, Song +from monophony.playlists import ImportTask +from monophony.yt import GetArtistTask, SearchTask + + +class BaseTestCase(unittest.TestCase): + def tearDown(self): + shutil.rmtree( + os.getenv( + 'XDG_CONFIG_HOME', os.path.expanduser('~/.config') + ) + '/' + NAME, + ignore_errors=True + ) + shutil.rmtree( + os.getenv( + 'XDG_DATA_HOME', os.path.expanduser('~/.local/share') + ) + '/' + NAME, + ignore_errors=True + ) + + +class MetadataTestCase(BaseTestCase): + def test_version(self): + for path in os.getenv('XDG_DATA_DIRS', '/usr/share/').split(':'): + file_path = ( + path if path.endswith('/') else path + '/' + ) + f'metainfo/{ID}.metainfo.xml' + + if not os.path.isfile(file_path): + continue + + with open(file_path) as metainfo: + version = metainfo.read().split(' Date: Wed, 24 Sep 2025 10:45:46 +0200 Subject: [PATCH 02/11] Fix RUF005 --- source/monophony/player.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/monophony/player.py b/source/monophony/player.py index 916ff93..f6e953f 100644 --- a/source/monophony/player.py +++ b/source/monophony/player.py @@ -473,11 +473,11 @@ class Player(GObject.Object): shuffled = [] if len(back_part) > 1 or len(front_part) > 1: while True: - shuffled = ( - random.sample(back_part, k=len(back_part)) + - [self._queue.songs[self._queue_index]] + - random.sample(front_part, k=len(front_part)) - ) + shuffled = [ + *random.sample(back_part, k=len(back_part)), + self._queue.songs[self._queue_index], + *random.sample(front_part, k=len(front_part)), + ] if shuffled != self._queue.songs: self._queue.songs = shuffled self.emit('queue-changed', self._queue, self._queue_index) -- GitLab From e1ad922575fcbb99003d83cb45182c551f7a6bf8 Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Wed, 24 Sep 2025 10:47:05 +0200 Subject: [PATCH 03/11] Update screenshots --- assets/screenshot1.png | Bin 70362 -> 70688 bytes assets/screenshot2.png | Bin 51963 -> 53402 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/screenshot1.png b/assets/screenshot1.png index f8e534bf3ab3141d13a63c93be278fc6ade9a358..fb4620fdfc6dd4bca198163ff313cd81f02f2a98 100644 GIT binary patch literal 70688 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

    O*8m)Yh!lt)-TH@^*{-tt&n#$jQYPeNE)! z+LiI)*P&MK88c@F?yV}#y1J^>D|OoOpKOnwJo$3s-vxhdD=W|%g^3d<9-L+Rn&+PO zi>=~|bRzcs;m^B!if7LE_x2}FPM&H0LQn4;L(G0Tw}OIa_uv2gEWV@gaoV-tdj2uj z-re0TFKu$;=FP&duTq_ynj&|VaH^@P&9QoVe46g(lS= zyBo54+E2^0tSqhr&(6+V%V%3v!oDDU{h0%xo$jpbe)q{*GyeE`z4gt_&-|B{`5rxW zs%yoH7d8DKKU(%gtzG6hdAddIFO}8d|6Mny>D}31KfkA^hiSTQOGQwBy+z*b`Q|s97jyf4zi|G}6fae&UbX&BTiDk3%h?`VmI6fbLYbMv%wXKJ!+%iryJ{>ylQiHEoM<4a4uudE21d*Hxl|AHkC?>7bgeDHt& z#K3#;$uD2B?v6b7&%C$08#IWtCVIQ#`SYd?b$@>ue>T21{@r5hKqGNxxpyRNt4wNteR=%r>jtLj`YjcEjvR6M z_aj-g{{P;+OI8Q1S#ngddvmd{kB?75SzgeBFPl_Uo_NVUf9vGt#&&&O?CI}2Hf%7E zGRw&*2!8t_Lfrc_``^sq%?rzYchb1beTeGg7`6%+{;>BG!mrJ&@ zn^{?#E2^qay}dnOP)y9t%S-FrxpQp4ckHkbmZ}n+>$iNq*!HBO?9B5w&DToP_W0i3 zq;X=azn|Z-q@!KGzKe3PhOQ3No$~4EV)P{t%g>zRQ+jn}C1@cGW5vs*(^rJ9UiRi@s}d&2J9y?fR# zuCJE|HJ@K!+rM$!wxla7w)4#U^7rrevR~idA6K7Wb7Y!sw4s?<+L;-FH*ViHuKHqe z;@79Vi;GydW?xq_GV-c_Ja26*VkM>a(Ph4IpoN?9b&(7HA1W_db9#p1;twA!edbz~ z&M-)nxp(j0ub2Mj`S;E&@f7y(^LzH|t3lMZ9LM=~duN(z7au5tEWu5FbcA!sGQA^5 zj!Xg7ZyM%mYHVx6+`GlrrKBve|5_!)y7L<&*HJ<92nNETcN;WQTHt#2vO=?~y>#i4Md_*a9_#uA^k-dO ze%_$I?%DJ6-Cj{Ln|}QIwJGOjSB(6IpG(D;m%hGsd1JD>-OneFl}_G2f4*OT-w&zs z?|y&2-CplA(`aVy?LCouDo!f*@2>rAW}IHzGyh-XzN4U#NEKDpjWs`mtV%NGua@EOU&q2S=j`U} z%Lkj=H*DFm#8=w9_SUM<)f+c&PChy5D5#&bE&u+>_xtrP@86GS4sfPvHlL*A%QLgh zr-x>09tY+MX#W6pa~^*m%k_WqimTue+%KfYY{pJDsEevW

    A7M4GIe3s3* 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∾^Be!OK4ZR*OfAXZ}vgeMDjxWAe{rs-G?o_FL=*m^6@?YpJ z>$2Gz)f=_e>|bG+Z`*mWX;+n|M#uqON z=G@;m^|)NM$ktnb{Qo{VS-vUrGFwuT($9zO?=x<0TKaOUUaS>p6-C|OUo&MtzgXPA zA^ZBdtkOgtnLT^+uPfP zgoRJeGPO6Z`l1nh6O`tEe_IP$;B$7ixqz(fLC{vc*=B1QXW0MejjR9rKjY>mRZyD$ z_cPr$cTLn+VMD`-fBt^gn^*B@qtx%%c&({j^?zS0Zp*z5TEejZ?>Fx4cZz;0%Pd{G zbjgw>7q;a_U;g>G_*Y~=fWpU*?HjglU%yqK-_~Sy?(J;?l9I>#?RC?Cd`JwhNP7kv z5j#0)eMeuPf=P)wzuk|9o3Bg%-WD@&m)0s+5prAp!K{B4pm+*;ynNUF^)8<`=iPnJ z!p?u>@ZrN##D$EQw6(p>a&Pr`Eq&Cj|8M@@-+ik${rPY4&#ImIgplijy{hHU7<`ZJ z4Kki=qFa9J-svlCSI_^NcK^znd`m6&)Tq4Tc4JZ3BPQbS);92@p0IlUy}h8x!I72k z#qXaLF>{O8{oZ%1_W0pqyXR8RwO?IZd-$~e{zDTK4}-3Bv;S)%&Ck!DJ@edLYe8Y* z!@~YNNk2c`|M~a3sD*{ag>|vfuPyK0i!;f8cl^;&?=_X@&Yj!y>D1|(FBktCW?T?B zH`n@n`F-EzF*}PGw&eU&h_AEkwExGkr2fD9&qv*zHb0**+^hTTn+;k+{qQjV@r?x! zmE`LGaEj^29lEh`@!I2{16c3Zzh54{eqQ9dzYp81H{{$jI+Iqs?KkLZpQPmEk6&}y zUS6Jf=gyrQd#$I;wQgS9*nBZ#N#pPFj%QviEE&ce|Uj75m=legHoxd!}YB z!gi;Ho;+zin6RMc=O-6;_q%2BaWx+++xX>AJ)iHtAbvmNn)v;V>-W!dIz5g5UfEp9 zHa^)y&(20mSQHp=i|ai)YaYKma`Uol`<_3|kA7EIp880Il{eRL)c$xlkA-dB^H*0_n`B>`(#9+O z;mu}!0jXW>%IeHU3_systD+cneX@IkDr{ZZdvt3!Z@v_ef@fUw&uniJ1UfvmDg%2 zDl+=}>&wW>u3ap9@Zjzjx3*gE|G)3iwtsSS=FE|>sSt>-G3=bG9bWk4gkaj)S!dVp z|98yze9xI;+jsBY?fG^~+rrZF<8k@M?RTduIZR_vgHH@v&cQd&9du9vZ#4 zKUYa_`O%Bh^;(^Ou|HYrJ^k|LbpB^&eohQp>UD8fsrJ0;cdQJruCE83jIlcG@4TgZ zR0A$@7MUgytK5VuWw=a`guB$n@mod|2cW`q>N3)g9&6DHi9;iFblQ*&c$wz!?$)=fG|Y&UiqTbzD9RXf}; z>q_*N%*XTPF4&xTcXwgrv(x{h11|8*tE*yRsH>}EVSUSzaJ)~pqPiM%IQA^l>|-Y< zFW*t|QE5qj#-$Y(|AG!mdv$d+=s>C-H)TFPzH3#Wqkn7u*zZ3tA}ZQ?GUd?0gN;j; zEXnz6QT0Wm{*T~^IhMvZx8+*zE^FP8cUS(%(YyEfkA$s{n=7R1B_Jd;N!4B@#k}AC z9{=KtGahU3ymZXio)^3M$=;90qRsdASH3*`|KIn}X|D@QOP}7@_;`D0dfNXu4Ov0K zytzhGukX*@`TMSL- zd~>^0Z@>SJ%-xQT)*T%k3!=B@y}2jrwKi&N#mlAPo99fv{30aJnl(yCa(2U?&5|ef zzy9(4^^P4D*Vg`So@ZOVA@g$DuJZTCj&x2gdRZ^JTs!QHLg;u~{r79Bd&_u;g@{oO5d??mW`#npZ-eRX}k z|LQQ^%uUvGH+_)K@++?mb_xN&on9CF%4u`}aHc z*8OD@ke*!{zHG{rE4SaJ$K8E*f7_kdcdu4@DJd!aeEH?&Wsr}qubT@xw$yu?&el2$ z`-%?&OTDHlX==_aets_ca_#rK?om;LZT8^9Ljwjr84HieYQ3AMWZv4sxpL*oYq3p-4)tBwvon5LvC*={DcdSO z3jO&TeR)-=w~VZ834SY&#)@B%D%Qn)yU{mTX$2w&g*v_dU|?y>g%1qy}y6{%S&SkyBe0+ zX1UAa_wU;t@xJtUzx?xCTeD5_=6u~}mU(H0b35Ptf-Q^>PE1smu_~EyxSjv=wdlnK zB_&T@F8@FC-joRw8fMRqp62>dlKV_?(WfV#lO|1C5wNgk+Wz&Qo@yr;-MoA4>(i@q z|Ky&&>)P1d(cxibZf^eS%F4N_f2*I*INCk^!-vECmzQ`ZPt%LdI?K;@@94d~zkmFC zt+^%R;vB2eEaR*z(LQs2#CuKGo11;RTU>vk=j3Bw-rStq$}OI1J#WH<11~NvuJ~|J z?8@r!>F4c!io$Vkz>c04647$ zfR+oMn5g{W<4V`{as27g9KOH4EM2ie<6hlw-LUm>lXJa4&tYMGtDvxezxw+-Wkp3r z@4DlCvYN5GwvF~blTJWufn{^A3kt+P1ig7_q+L# z!-t!zzP|c&*}quAV1fPBRiTSxcW*Pv`*R>S|NpERGfwo||2rd{zsGHRUhKz@D~~=n z*!<%9di~4G&z~r^y?F6r&F{A%Z9I}LQ?)`BYOBLG-#pVUU*~XlS82tIg?T*kb~@fW zckEbwr+hyDy?b#gDk=u)=lVg%IM{p%Ft4e}y14#+vd`>qyk}>b?q2))^lIzBx2Nqs zZfhCp>EfcIzBX#>lF-%DB0rs*V|mz_|L^CAzkV%x+|R*zb8hP1rkgp>K#l&d*W({= zK5w@)<>Vw#>?a;}EBN*0Wz226@^>=T-`_3HysWk+`n&tK$A*R*eP)@o_S?&~Z{Kd- z&L^96q+?=BdwhKJM$oNHwO>o;RlkdzSNlzJ(V|5&%=7IoFRxEuoBZ}x>7L*3e%t*0 z!ZEM@U*x}Eul4Qzd`M0^`%BR~TCBSjls+DH>x0fwc>eMpE9=__7u~Ck>;L6Peky;* zbL{HX*4p3yp1r!d`dYPV(h=hx`Tv5SpPzsF=H}*_pHIWDtf|btU7dGtPiyGvu+M+L z8%J!-GJU__zOVeAB0EBF4IW|n#?{K)a+A8%!^fAsX} z$xdPQir;U`^C~BPK5zfu>HfZc(2C^Ge?IFA$;lmD5xBVW@7M5`cPjaIm%mRt)bjt+ z*XzM+QuqC@Q%gR!$N6ZNsHEj5$z?u2XWh6F5m)!q_0)9z^GCbIe?FP~e?!jAPq+8& z+q2{H{@k6_PfTXMyIrRwYlDhCMM?R zhr|7rb$`xnzqUvCyxnh}ySvMie}1~ZIrpxX(swi8P$9vc?wPA5b$C47DZNZ9Gh9^= z1Wrz6Viy$DymV{J$LCkW-{bJ`VJh}`s4TZZKdU1x*)rPmY8N< zlBlk(o@J6bX?cy{#CmD-ceQ)=?$wOmrt^OP|8;A32j^%O2nbG8(p5<@KY3E~&Aq+W z{y(=r{`q{qs)51p^~(P9Y$opi|F64CR6FIQyiJ9`?QOX`7w z_~Vz^=4W@y@7GSziJWwG_4NnOpQoQSm+Cdiz18yec5}{^Xu+MKv(57tCLh-;d39y# z+UV^!cUd2u1`4sA&zCM;Dk!wc`t*sW`SbPN`{hz!Ut1gbDf{|9xd0Z{Sbmw9*w~*R z4)fpGlq!7ipyQbthEs2C&5l0*{_bzVYwKdqHZcCZxu=pjzRptfxZMmNwR;thP0jM} z9atA@{o_IN_YK>(>tA>+Cb(0SOUA`ziHG)@*s0s{`A-==dhqUC}#kEEN6i^}Ho^Gj-ee!8}6&mNo1 zo1Yxxs$Pm7>;G?Z{2S<^tM~WzO4|PmX5*3YC@C>H+{SxydbaS+>zUz8|13P<=;G3~ z<ow9y!}-`~ThYuBfQkwk<< z^Yp|-WuKX!OxgKl&S2PnBb4*OiHVCr$IQOI-LtFo^`l3R4xO8Okb#Bu?Sb>>#hVXK zNUe3=TUFZG(edQV%gfiQbi}w9E?jtJef;@Z;h??&0xRaZA`->zUH%?5z^L#M~aZ_};PO z$Jx!z&0lvgOXNjNKbmy1csHB#vBiqVYXo;1KR#A_gk645`_ZI}>%7Vxz_skI1q&3~ zc%^@>fAFBn;o-~3wt2R@i=UhjT(oErgGIpuhPb$SA*(_aPEI-h^%2jjNf)4$hw ztNYDSQB`HNx3~A3XOnvN{0uo&rE<08aMhj0vsS4nDNWMT$WRAWW1f@M-rV@;|3E-i z_UuGuchCh0x3~YFygxgfW8uQ;85SS+|M>YcXnUTl-M=5sr>3tzaA#+6{;jZ>7@4Ws z;mfkFY8^R#d~?D2zn@=!{~smOtScH9UW=k6I|fkNnrWO~SgwEa-QCFR z(x70!diCfoUhRgPInQ=Jk9+XoU~|pKqxCb5)5V^inworKLgF0j@_);6bt5(iq^GOb z{m!-f_U&8Ex0@_=f4`a^>ytgZr_%oM!-ogIyuAPP_SVijr?;#Q`gbDp+mrpDd1ihx znkQ{+o2Ih+ONy62UxRJHsr&n519IL>b=n~#D0p)9ix(1a-g2iMTLmkEi;kD>{q^ef zjoY^;PnmLNie~)NS^7$!@?T$HfB5!o=}Y?+UfkIkz3{uDo}OIu!G?=7{@=g7d-rZ( zDXBY59gIN@DGH{xOP8t_FH1T3WyVGY?a#$VPa`vrz>>bFi%Xtg_hMI2E<6)E)#b^% zC9zXKwS!6lLBXA?!TAK-K2=hhqzx)2m562J9@kP*`lR2?#=9hD=O&*SA6)y6zkU1m z<&~Anyi!-v&(7NV=I(C&<9p7?RW1?#e)sw0qjf z%B$Ph%rQZ1hTbOck5@;*C{XY&BHML{6 zzBTikEl=?*S5j7v+*u?V7#R5D$CX8ymzSxisReCL^WFEiOe<|OXh0`$saI-HP!J~< zSJM4``-|TMM@2>TIeNdmv=p=x@9F93>U!_AqPOKd{qXQGL&=K^a@Ez1ImA52KO zw&v!|Q?t)blX<;$`}XC<&;5S+AUoQctt5jNflpR_4Yh&zF3-t@o$cj}DJ>A{HGT9ZRxr zZ{ua>ms7H_QM$cxrg3`Ye0l3Ko7*4feEM|i@Zsincjc2#PFgz0zP@pqt~}@pg)?(3 zXA7x%J$U~7^sdtSf`5NlYiehMb}7{TOqF90)ecMe`AJkK{-1T)=9%B#-exo?eO2N$ zt>p2-_FaWP3M5XRJX!gw`(lQQ!h=9QIh!+!T)Q>H*U2z4_6rFMA71J`onb}XUa4*| zT}36O-?h6y0hnka_4C{9Obcsk;{=ELM~{UqN?r(jeQgffS^4tv{=)|kHmc8)IB;`w zx@GAr69&+gALr)CdwO^*NjocbWo2-?WpSN~u5PcK?XKW;F_wBUI|{nX<6QDu!TDlA z($Ox^nbDD((;h!~5U?Wm`r7FBW_JF_<(7YcJ`bB^lG(F7T~bL&X{K#8-;#Rm`nR`C zZEU=vPrJFrsp;sP`DLZ6%d4)g?lrAMSxs$PeBI3Y!)4K1vphdPtGjl6|NniHwrmOU zQLEmTd%Nvu(!(<|jo;k(IcnD zZr_8~$HivP17E3q?#9iV7Z*BLXI)w07!xBCzCP|~fBsB!d#_#Z?CkvU^yJiA{vjbEp{qhR?mL(J<<>``DUte*)l+R!UHIzXK zPf&0oXyo&FpX~O!*YoB-di?n2?sEC;YirJyt*QH4HAOqTPrh!3r9a^~09<{Rq%vYncyd-`$z_s54?KVRd2F;i7lweah!t2&j1h2e$e`cqZUA3m&n zyV88}tXZ?({<#9WkM;5l%i^-W($d%0T7744i?|-d!t&*0nO~Zs;%-jPo4u=7YwxYL zciNohdvl+y#`#*2iP6jN7YYbYtiE@1^Ya;YwQQTy&nJF+V|iw7b?x5+Z*CTMbal0T zxjSvo9vki@f0^0&f_8tay|N;ZdGbk@wNa*$HWdP8Wn~OEZb}vwY&d@OXsej+tovKm zwadL~+h3n8BO~MC?R_}OaPqWi)1L2}V_iP)$`z5Qty!HXQx*j+^)ku1(eU>-|DB7b z%a$$$op%7bFM0E3VLiQbU*7%Y;FU7jFz@k;7c=VqZe0hHQCoSl(xHr8vI4)y?uRE&AxSg-Fh?UV9vIblc)DsSnYRpci&v|lk3*DT<4WR zOFO!{k}faveJQ_&Ya6e$TTqaYqT=qAAuAv8zP`1u);e}q$wN?6TT`?1?d|3r z1rL+v{Z2BQ9l0SvFm`v@!Sm;*uMS_oDeLO18p+I`kNX|0%HAC45cHooZJJxejlkC& zhr2{qgEqCly}kYEkB`E0EDArheFgP9yF|5(a&8Fp9DBW6>+L<+s^fo!C(bs0qIiCS zrvBrdhL@AwHhKFm`|^b02J_NCe{Zv<{yQG-;*z6pWyMzgyXoh@6FFwd8zTxzOEo9_ za&_sNy<n)&dZt{^V3}V{qc`@SHcI(dCXVOFTAg-n_X~{M`&tfu?Ej z^wiX?rQ$9wT`e6Q9?|6w`#^oEj*gCwCtjdDL_3BEH^1GDrUwrcyrycUzFybSac459 z{ORcE@K}e!@Yr~DS+BkQUz7H=(dL_TZVJt-%93zn_FLvOBY%6_Cd=Y9 zM$jZeQ*-l+i;Jzf#m=Z36v#5zR)0&lxk5m8%Qae2D#^NsC)sU;p^`dnV{y8vB17Pd=Yl|8OIj zaf(jlr)jT#J)4~m8ofTNM{CNDaW$7!Cs&bs%w9QT0?lT9Tri&llNcbjXqm2t!N?aPCg`{_h)vzhv zzpSjRq$Zzi*}K>F7rwZV z7_~jGwYk|g`}(@wXSKxyH|q9UbaZ%3yu2aN`PsR@6DO(O-6U%~`>eTU=FWfdr+d$z zkTBd4eDdVUnx9Wa=2aw3F#Y=KtH$0c+f!FphciZO%dw2zRdOuDIT)1|3;%so^&($v`M#j$0E@Vx_$ywnGomhp0gx1QR z;+MArje?#Ede_J<2Wn0vAMazFFk?o?^7NTH&q1A@KO5@*GoG5NeZG}jd<|$fa?nz* z$mZEwv;QBsd2{Bovwt&gesc5;7CL!whT;1MPoK7K-)_Fxy?;|_`4crzlJ}Tc{OXD( zpFH2=wcGD4@SJ>XOXlPAA09eKZqJjQ_T^#AOyl&+Yoo(KX9KN`GJSbrp>6*DziQlK zI*(3G)n;gEdGqYw@Av5s4ydn;{cQ(Q_4(P^&rAD_)6Xd>JP2&#ll55}W%}`Z{@Eud z<1cK_Z}>W=rG-|TtUHTNHFx>%z1X!{Jxrz(Iie`yYw?N z%5QARG!|C(+ca-;ZM8uD{=a;nn@_*JnF*SX(TUhF!E>@&^m$I6nDhMhXOdoAIOsF` zn{MjqX{qn-?EHD@_}kmf6|dI@ZppvjcXM<4r+d}!g(P;&J8w68!qYver>6xk^%9+? z|NpMkmyQ$m$NK+I`S77YFLu`v_4$8}gW~2y=A!2$bffxtHs9CFzW^!=nv zcg=r!dHJQy&y`(VTyE^LR!UM96r3o*)26fEbaMQiyLUNxdGi>LFlag$O_(+B(xu6k z!JAU5&irswhV+|214|Q`9=v(e)9teVu(;lY@3`PlsYeLrWlv>eg7yxc`aJNoUa6r-6M z(bs0%)&6?$=FOD1J2r0?uKo38Va(1=7FJebQTw}$5F=b7tlHsv$4{T;Zs-=*|McPT zUhbu_;^N{=OursH44if1#A%(F9UVuG9Ql%bj)jHgNyVG8yS%|uO&%#IDJdy^pY!8K zg_vH9OGt>xym_e{950$DPY!Nm;VtKe^zD+2x7`WVw6mMX$}J`!S$R{+#^%?H#n%=j zUQ}Y`otas<1ym{AICE|7?H3mpPd|Ih-@_y2<0DlTmMl8lp&?P;DqT^w}&ik$5!P{d#8Xzu6`@l>1K$tQa&cy*ZXq{+cIwq}c0fBQ4_ z@lI9mX-lfVw`+v2)7hMMR#8ojZSqN%+uL$?ytk7N3J$g`d0`N}J@0UiS-N(xcwAi-`tkGUvtjeTtQ2~BdV2D`JwN|^xg29-Yy0u*SLOvNp5}6O zJJ{?0Zk_&q-|uCZB%UG3kY)akBxFT}1nz&v}%Kd%&J-xh?OiVzBQ)OLU z75U=3>yx}G8&?ONpVz;kwtB+a-OA<*L1P0!Pe6y=Oqg)s>gw>dmCw)3-QFkR;_m+W zZu$M_{-ei_r>_Quw3pYauccmITI<%Wv#_v8(UunzORIhN;DN*1sI3;2pHix9%a$xz zlCsiTRh5;WzrSX8oZ!Sq4xj`P^aRv-nKEULs=lTsCp-Ibo9l&-j(E;8|9<$~xjrFP zuM3-=Ht(DUK1U#s^PM28XN+n&~xFi|N7R;BK#}T6aI0c6NxAOM#lz zO6r!DlJ4&9I+cBWeJbkePj~k5N}GMU;C#2@-Oh9mU*FW%*K()mzVVb%fP@u z3v27g&z_~tT^F)3iS^K7Wd+-gjEjpb9UZMbyiQrpC@3sUyt(PA1P@zKP|%8ig-p}+ zI-Mk|j$71TM5M zcJCK^Br7Pm(H2xlOjJ@&{(YrS*80J#S6(+a-TU#&?D6~k|Je@QyvZpqFaP?{vpJw% zkF3>}y_s|D>{=Cnn~U?k zrfMY`&CIy9h11W^Z;oB&r zzyI`crR(c!YYR(DAO88ty(ap5`_~yA9&s)%E=dLw22!8@S67#pmpeH(M>7~Ya82M@ zvTXU5FDd7y=**PUeR(yd&Tp9wcyR0cibZl39UXUk-?bfZ44&!&0xIF)ktP=x8ZgwN z%N<-?-o;JVcfYxdTS;k>KRAOoY6c%Wd@WW$P*X@rNh$YktMxKaJ|lYQ4N{hqI$$?> zt+j%Z(xl0rllLtseI0i5Q~7(or!&*%ZT6g>vo`&_+@4>rPM5s8a`jrQ;Kb?);3nv% zZTb9R9S6zOnRm8mNiaCu<$iKV3h5-h&4U zRbO75YhwM($rF=q`|SPwS@Y{=9iCsGm-O&ZyrjVbdr(iH(|5L6;B zbfjasfmAPOwh}Z%%g`;RoAB(+`{FmZ+2!{f2jLBg&YV0kr$HxupRcz&czAKiw9i#v zt7gR4{hSHuWhAxnN^>=A-)?^JpkqZvg+bXHiKgb}f`Wn#(_Y`-FMoQve&XwEx}XM* zs8+~{l>@&w= z=0fLo#tXRn0E`-;t3*HxvmQK%WnuY}dS=E(9*GYYvAfF@4GL_}+x@m-P3w1udgvTtPEa$acwmJq{)*R3(CuvJGb-Id^p&?=hv(3imGW3_kKSnb|F?w@Mrrn ztb!{&~OV zv+nI}x!d>2+0{rCKR@^Q`TTlt!CX;Y{qc;kzj5xZ5*ypo4_>@D@$2jBofRLI#P#Ez zOmzPxATG{+;OXh0)<9xlHvaT*~18C;!`~B&cwqy!7HJ5KpI_g#S z?vAI6%aX0~jEuDv4;tOp#P04pJzZZ|RP-pwj9jJ}XVdOgWs7XwxRK$^jE}bRbw3#0 z77K2^IfuVI)9l);yrIF(uW<>uz}6=7?&RK2Doe1B&f+1J|kPElcj|Flf^=jY>3 z6!}e7>&?8h#Bgu*_f5}s?%Kul;Mucd8oezdAWb;@xHll?#Z6|^;LRJqjQ|B zWziKW6NZ>wC7q|I>j!U6^KEJ_cluN%u}tA*6%aBQ#KdW!}s6tvAH92bZlba~!;71ntv^5edrCasC+X>_&_92_*V)+GZoZOuGCNu6=nWkG zqm7ei%s8>9@^j_Sr{XW~>=ZW5zV@V3o$bQ%^Nas{IDBUIw7HhWVvLOaMkOy2vaYV` z++Uxq=Krs0UoA7kis1k(YeLATwU;N+zqm%P`|MgatpG4M1Z(o#nxb4cC$jOGu z_ZEVhN3TGmB>#TDUmmq}m3fA%%aRY^7Qcx1{Tkz`KYmoiRlQ_AHRa=zD=R017A54| z*zj@LQ&9Q2cDtKl(UagF8Ox?0KUl)nL>#=b@-ia_=gqUf-}fh9xm&g@rzIEIL6;Ya5y8Ke_16T$Gc3ex7YhTid6r;gdIR-=6;an}Xf{nx;~dls`ZG zE56^|So8bs^_ZPSt*+hEHk7`;rc>%O%VcKxeOc%2d9j=vFQ)sK`z^@;O_RP3S?X{1 zvw!VwW%G%bRW8;)mo$2sYGa;Ob;hIY<;f}YWEiR!Oxa(*d%=wx((i2)m6Tf12Ejgj z`gG>-&GPyDb`|`fO}D*Lxv$rCc!0VDod*+6Y^rpwE5tb@=9dB)5(BmLJ38(Z|N2(T zGx_9^OG~};cPP)?yjgf})mJ4;%bBj-V&YZu-BN1{1waPR&b+%zR6ATR{b*O}mYkmn z3%6$D>`z6SS?!n(8r(Wz>lHlJWr;?_&LYdWxHtyTJbJtQyI764yLRjl5V*k3-+#%) zEiUchAy?3e@$$uL3JMCxlefRlo3UyMw2$)~G|gD>P-)SkMIXQDI6FAV&zUoaOC@7R zv_=8MWuP!UbolVjl9xdX->(vpmp|Xc%H7ekCx=%~uJ%h{_Js`@6Pw!3Y_W&}wKQ_l zUSG@o^5Wv@w72RiDw`@k3W0icFC*C;lzGh;KrO}Hu{!mOx#02f`cvC-Z|7d>Z)tI< z`wDV_XoOZ^#qOuZnI_8Uui*xSE?&JjZ%RDBg7|ncfVWI8Ttg9-PmaVb7 z%QmK;kGGr`y(z^LH1<^V^psKdHJPNOB!ihgpz9HTzx%9cYC07(wpIGt>`Yqmyi?LV zY*E|uT$|ax8cOiIxnDm)H+tKXh&Y!gabSmQPP(-%w|VnsW6&V>%}q~#{4!hY)+@ze zVP*B;{e3BSclVgs*q8VB%d_*zsOajRjkT2;vu>w2IQ{Jq)viZ4k&V9L**Y8gFLUOvL zoSdABib}!PSE1SWb{0QhtIP&kX7&B<_iM=?KYqOVYQtvZNs}gJ)y|zXscZFWZ9YD} zO0D`&Pc)^>at=H^oEV`a_Ug(?=ZJ`ieR0b>JPH?qqFl2GG*s{9b#Bu1DN|aSo0I3g zwkUk0a(4Fb#PoFcP9fEdo1YFwzdHQzaJ#41u45g7hb1jPY3hh^|Nbt&W2u#k%R4nl z_2%j4ckJRtMeliw7b~AU=~=lBG_Pl2W%cP!@p*%i7YTirgZJ*_=bxtQyIK&H2E?I;wMwJuBe@Bp;LRX|AS)WyZeL3b&g+_b||eRcTyg~`YHc%;o* zpgj(qHy-m&uaDT+v@Q3xQObz~p0?NBo2To=PU{cXPzu^3@o7T(i7C@R-kI^zQ2FLk z_28-^hBu8t|NgygN=jZYa_DJChew>N`|*VQc!T@$GtWXhKCD6eJ3I=71qC|~CLG8( z^PkiH9gbB~QI}Sq{4>X=G7P*nLP<$!XAo#onkEb*@KA2i_gyuXr$GIVi8YHri(|Na zW*W(Dd)L(AF;PHJF!QzE9YatbQ&3QFVhm^{6d?w99oEF%4L5U+ynS<4Sp}Qc*gnyVD_(oSB(9<80ckEt$@bkM*vI+iSJddpeVXl@;h*a{;Me;`7o1ofF?D)V&Ry1%!rT!$ z5ocDa#P2+EU|-&WxwVIX9O6EIagi&xjqT}(W_I6LA;u`yrj%-{OpR0RQRd= zCz+OdPgm2_w5-jN%n;TQ+$jm_`6#(Y{>oq$<9>D}WmWq9(y!YrEvG7|s;btyi+L~1 zeW{^zbTX*tI5Fu!6YH7GDP^bj^M5ZZQ@eEO(wEuNN|V$%DP^J2AfP$#&RuUYFKf`)lAz`zqxuEgH*Ze<`-^u?+}?NFUPte(l6`$W|G>F( zddJONT#CTiYsriq{~q3Jm^)Y2IIZURtE<`hJNvG#zHXF!to)^8vm;}3$CH_$URQ?* zGg~ub_tB<<3~q}BC!ajhBYD3VwD_cckN&)Qsd-U$MxTs6KmUL1*H>wSnLZM|Zm+}E zaLKu-=tHuX@J#y$(!FkP-o6F7xUo_C#f^=&`}Un(5xCe&PHrhST|X_s)28DI*RMQN+$dhY(I6o`J@N6evpF|582+8U!1$c1t%bp(2MP0aBLXfc zC~XH%w|f|Ve$W5sKMQl&HQ5>eYV)4Cv+^hvSwkEWY{|-C&c$VNN)RO$3O+yE_b&f(*_-|~wac|nhAZdZwG|XR=?*S(x9eIew#^7$ zcw`Qj>Yw+fIrIM*EE5XYc~p4YyICC_PmFs(^HKXez5O1%VlDW%im~D3#nxMU?=AK^ zea>Rn;j6_31t%-cwd*h~SoOc))z`|#4WgS@T&~s#bU(NG@9Fkp-RY5M&*=yM->A5z zVxwrL=Jho{|1CC~AR~XaZ~gC-*oVdsX%?CR+tH*Tj<#1Sh@cyIN^>)>D ze`f6HP7KMk?0BZ%mwI-#qDqV%t3zkdgS#7~Z>#V3c=Xh`Qg8ahFWclZEfP4M&i?Z3 zO<(2p*E+F+>#J06{yNIS@NeFCw)W}O5vQ-b{%p7|^=^m77Dk5mY_XLerhdO^UN0y9 z{ZC)?(JB*ZOE!0N|8M#dy$8>^yxv}ad=A%bwSA6JMNFk~KO2nfp6(D>q#3DHlnav9;z0a;O97t%0y*y7rRq zc-*N}kIw#QH77V%ZIQ|0m07h#=EznFj{DoC8nmB(i$1^p${zc9TkqcZ@v(Tx5qIW* z<=3laA~(-eUcHW8DqLSCrT%5t6>f<`jjhQa*WG9Rerm(3ug5()Y#0xm`~P+6@9EK8 zANsmFM07z#(UWbVd2w^S_9-$*^ju&5gQfPT-ABRWj2jXTE)MgydA;TE+8SA}urJ3F zSghNHu31eIJ|SfCWNS>?aSnsnzs%p< zVY_+yx5ss-w%&`@7611=YTePEo@%w=XXB1zv=4SNdnAl%$9$bMV;FjB=hHMILGhr@(+%R$17@iF($O#++6>l zkzFb>zP;^T;)@H*i)__y?%cW4u=LgciccpGZhPmo@8UWyC8eUpY+_?K_djL+Dd{`^`=X4K7U$m0IluJ=_Y9f*xaV)DY>qfH=U1_*=qy_|*Zcci z%jpScwvpwo2QQv=9-ZU%=H-5kK(0Gl zS2j;GwA>Z2EA3_UuK1uWSJub(%h%0lT)sSg;g;={pV`cEqvpS^{Pyh|kDSc}Ug>Lw z$;YlZy^L-<)5z%X_!$3_ml26S{{4QLb7Mo_!DjZP{QUW^e^&0j&wB9S!N!x5_b+g4 z{$(jw|6*Yt=qluIZ*PlM-GA2to*r^oa>Tv(?WwPGC$Bp?=kU3UQ`u_&HypQStixXR_}1+;NO-r5PiK z>1xi;pLJ*3{(oF24@ce%T^|QJa`@vB;pR_!{r^9E7iV<67_?qtMPPo=;$vUup8WI2 zrbxGVea5vlnhg1SK1$X8wmTLcUwih}*7`di{_MH)zA3>(O49zHa&~>O-EoIbAyo;} zSH9cw?z$Bf6)pVsu?)P1l&j_FoMZFn^B&$B!+WdVV)8QOeUDz;I;nE}e&tSK>E($< zEq(I;n=U?KaHu&_xTsI{;Um{=n(n67vGS=IMkOq7@BhCwYt@!L{}Ml5+83Jhzeq;3 z=J$oVJ3h1e{xlRHoPERi_srL|Uf!v%Tjm{!wedcEegA^1v+oP-%QqEd?|46-2cDKAorH}wFQoOYrLkO@|p2L@_wy(@2#!xH?>=tr6 zWUp`9TA&-fI^ec{U;m?`8}nOlExR-G(4S>C>Wkg>dfwmHe{&0expa7VIB1dfs*sgu z{{Owdy!BRDo+I;2rq-yfSxg20{!}vT*~67ySXT$GE4Htx`u#ljZmUj`U+n*d7UJ)t zLcZ(WS-|+<)lX(FnJ3epZ1Zfmvu{ydzOL)uW^-^qHSgxO&bv8xStnX2y}n=g%D(>HT3lQGB2lnIIaJgd+*zBas9w`F*j$=yS&J?n4wQ@@4nEI zqg|qm9UUDyEppHJ*MdfzCRPU}{_k^6)6~>FGxM|X-QD(Q?(F>R;q9&c#qN&onKa}5 z|MxY;?UhSCH)pEu@yP6*lkH_q(c)9F(kA$I2gQ#{GXxv|a$@;CD(7~CbafLr* z+vglP7WZ)Td9@F(*YE#$OnUv}_xDe!{nc1k_KxS-{{M4NJUpCe_V)O;+}kg2ef_oQ zx%vH?xsJ{A&XoDp)Yw#if0ul)sa2xaO|E(kKSN{Vwx1Js-3AAO%aLQCD?7UN_sx0B z{y-&tx&Qo=U)dkzq&_-wa*FQjxo@}2wDCyh)!tj|UY>Pr&B>pipWob&IGMMqvbu;% zM#u8cG-tm`(DDJ-Lu=nnn)0(;Ca&)1Ri;;=VTOhqcg_vO`^|!Yl=dHK3($(!#=f@$n z;MGMFE_c1nsRXs2C*J<_sQdim7cV#*va+<&&&^3ZJFE1_@#DeU^JJw=vySYk+`i4@ z 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 AwEzGB literal 70362 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 diff --git a/assets/screenshot2.png b/assets/screenshot2.png index 4e889c1b2d767f816ba463287ad9da1bb4397771..d386a2c88b29bc5348283e6fedc2a5f374413326 100644 GIT binary patch literal 53402 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 M>FVdQ&MBb@0OR2x$p8QV literal 51963 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

  • 3dzn{Cm&2Ui0(wyQe%Up1CD_ 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 Date: Wed, 24 Sep 2025 11:05:02 +0200 Subject: [PATCH 04/11] Fix typo --- source/tests/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/tests/tests.py b/source/tests/tests.py index eae823b..3f58021 100755 --- a/source/tests/tests.py +++ b/source/tests/tests.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# ruff: noqa: S101 - Aseerts used in tests +# ruff: noqa: S101 - Asserts used in tests # ruff: noqa: SLF001 - Private members used in tests import os -- GitLab From 4ec59a45b6f15e4c0ba5f118a25c95f96fbaaf0d Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Thu, 25 Sep 2025 09:14:10 +0200 Subject: [PATCH 05/11] Ensure only one download error popup is shown regardless of n of errors --- source/monophony/ui/windows/main_window.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/monophony/ui/windows/main_window.py b/source/monophony/ui/windows/main_window.py index 90ce160..675b62e 100644 --- a/source/monophony/ui/windows/main_window.py +++ b/source/monophony/ui/windows/main_window.py @@ -78,6 +78,10 @@ class MainWindow(Adw.ApplicationWindow): self._application = self.props.application self._application.hold() + self._download_fail_window = MessageWindow( + _('Download Failed'), _('Some songs could not be downloaded') + ) + self._player = Player() self._player.connect( 'recents-changed', @@ -450,9 +454,7 @@ class MainWindow(Adw.ApplicationWindow): def _on_download_finished(self, task: DownloadTask): self._on_download_status_changed(task) if not task.result: - MessageWindow( - _('Download Failed'), _('Some songs could not be downloaded') - ).present(self) + self._download_fail_window.present(self) def _on_download_status_changed(self, _task: DownloadTask | None=None): # Never canceled -- GitLab From af8d3b0424b2a9cd2b4d300e2833fc02f5fdf9d1 Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Thu, 25 Sep 2025 09:32:32 +0200 Subject: [PATCH 06/11] Only update playlists after add window closed if needed --- source/monophony/ui/windows/add_window.py | 3 +++ source/monophony/ui/windows/main_window.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/source/monophony/ui/windows/add_window.py b/source/monophony/ui/windows/add_window.py index d6a9e65..0edb8ab 100644 --- a/source/monophony/ui/windows/add_window.py +++ b/source/monophony/ui/windows/add_window.py @@ -13,6 +13,7 @@ class AddWindow(MemoryDebugger, Adw.Dialog): self._selected_lists = [] self._group = group + self.did_anything = False self._playlists_group = Adw.PreferencesGroup() self._playlists_group.props.visible = False @@ -92,9 +93,11 @@ class AddWindow(MemoryDebugger, Adw.Dialog): ) entry_row.props.text = '' self.add_button.props.sensitive = True + self.did_anything = True def _on_add(self): for playlist in self._selected_lists: playlists.add_songs(self._group, playlist.title) + self.did_anything = True self.close() diff --git a/source/monophony/ui/windows/main_window.py b/source/monophony/ui/windows/main_window.py index 675b62e..da95d1b 100644 --- a/source/monophony/ui/windows/main_window.py +++ b/source/monophony/ui/windows/main_window.py @@ -400,7 +400,7 @@ class MainWindow(Adw.ApplicationWindow): add_window = AddWindow(group) add_window.connect( 'closed', - lambda _window, ref: MainWindow._update_playlists(ref()), + lambda window, ref: MainWindow._on_add_window_closed(ref(), window), weakref.ref(self) ) add_window.present(self) @@ -420,6 +420,10 @@ class MainWindow(Adw.ApplicationWindow): def _on_add_to_queue(self, group: Group): self._player.add_to_queue(group) + def _on_add_window_closed(self, window: AddWindow): + if window.did_anything: + self._update_playlists() + def _on_buffering_changed(self, progress: float): self._player_bar.update_buffering(progress) -- GitLab From 29c3a3212f08fde2b8215fc203932da751626af0 Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Thu, 25 Sep 2025 09:43:06 +0200 Subject: [PATCH 07/11] Remove yt test case due to bot blocking from yt --- source/tests/tests.py | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/source/tests/tests.py b/source/tests/tests.py index 3f58021..04e9c8d 100755 --- a/source/tests/tests.py +++ b/source/tests/tests.py @@ -7,10 +7,9 @@ import shutil import time import unittest -from monophony import ID, NAME, __version__, playlists, settings, yt +from monophony import ID, NAME, __version__, playlists, settings from monophony.data import Group, Song from monophony.playlists import ImportTask -from monophony.yt import GetArtistTask, SearchTask class BaseTestCase(unittest.TestCase): @@ -156,33 +155,5 @@ class SettingsTestCase(BaseTestCase): assert(settings._read() == values) -class YTTestCase(BaseTestCase): - def test_get_album_or_playlist(self): - assert(yt.get_album_or_playlist('OLAK5uy_k_lweiBStMgoHOTyUzrzYRPHorT9LogLI')) - - def test_get_artist(self): - task = GetArtistTask(args=('UCPHjpfnnGklkRBBTd0k6aHg', '', 1)) - task.start() - while task.is_running(): - time.sleep(1) - assert(task.result) - - def test_get_similar_songs(self): - assert(yt.get_similar_songs(Song(yt_id='lYBUbBu4W08'))) - - def test_get_song(self): - assert(yt.get_song('lYBUbBu4W08')) - - def test_get_song_uri(self): - assert(yt.get_song_uri(Song(yt_id='lYBUbBu4W08'))) - - def test_search(self): - task = SearchTask(args=('avicii', '', 1)) - task.start() - while task.is_running(): - time.sleep(1) - assert(task.result) - - if __name__ == '__main__': unittest.main() -- GitLab From 137c9735c576497db370688aae1f70572630fb20 Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Thu, 25 Sep 2025 10:08:47 +0200 Subject: [PATCH 08/11] Add check for mo files in CI --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f2a3e51..d0a298c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,6 +23,7 @@ test: - ruff check source - appstreamcli validate --explain --strict source/data/metainfo.xml - desktop-file-validate source/data/*.desktop + - find locales/ | grep -vqz \.mo # .mo files are generated automatically - cd source/ - make flatpak -- GitLab From f2d7544196cd46b7685d93a07c40afaf2ccc87e5 Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Thu, 25 Sep 2025 10:26:17 +0200 Subject: [PATCH 09/11] Fix .mo files check --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d0a298c..8ba1977 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,7 +23,7 @@ test: - ruff check source - appstreamcli validate --explain --strict source/data/metainfo.xml - desktop-file-validate source/data/*.desktop - - find locales/ | grep -vqz \.mo # .mo files are generated automatically + - find source/locales/ | tr "\n" " " | grep -vq \.mo - cd source/ - make flatpak -- GitLab From 41ec08f2075f7455b6876baf7ab83b7babe22b21 Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Thu, 25 Sep 2025 10:30:46 +0200 Subject: [PATCH 10/11] Simplify pipeline --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8ba1977..1621504 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,9 +24,7 @@ test: - appstreamcli validate --explain --strict source/data/metainfo.xml - desktop-file-validate source/data/*.desktop - find source/locales/ | tr "\n" " " | grep -vq \.mo - - - cd source/ - - make flatpak + - make --directory=source/ flatpak - flatpak run --filesystem=$CI_PROJECT_DIR/source/tests:ro --command=tests/tests.py --env=LOG_LEVELS=ERRO ${APP_ID} -- GitLab From 4ffd290baa2c4b073e67521dc5cf7cfe2629974d Mon Sep 17 00:00:00 2001 From: zehkira <9485872-zehkira@users.noreply.gitlab.com> Date: Thu, 25 Sep 2025 10:39:04 +0200 Subject: [PATCH 11/11] Fix pipeline --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1621504..82c0a9b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,7 +25,7 @@ test: - desktop-file-validate source/data/*.desktop - find source/locales/ | tr "\n" " " | grep -vq \.mo - make --directory=source/ flatpak - - flatpak run --filesystem=$CI_PROJECT_DIR/source/tests:ro --command=tests/tests.py --env=LOG_LEVELS=ERRO ${APP_ID} + - flatpak run --filesystem=$CI_PROJECT_DIR/source/tests/:ro --command=source/tests/tests.py --env=LOG_LEVELS=ERRO ${APP_ID} .build: -- GitLab