From 35b3edf7eecea1340736ff3de05a6b18b3e76c11 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sat, 28 Sep 2024 21:06:45 +0700 Subject: [PATCH 01/35] [fix] Remove debug log from tray applet --- openfreebuds_qt/tray/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openfreebuds_qt/tray/main.py b/openfreebuds_qt/tray/main.py index b3a1726..04609e1 100644 --- a/openfreebuds_qt/tray/main.py +++ b/openfreebuds_qt/tray/main.py @@ -75,7 +75,6 @@ async def _update_ui(self, event: OfbCoreEvent): """ state = await self.ofb.get_state() - log.info(f"theme={self.config.get_tray_icon_theme()}") # Update icon icon = create_tray_icon(self.config.get_tray_icon_theme(), From 4128805803cdbf12adb838f46524a207a3df4888 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 30 Sep 2024 17:40:43 +0700 Subject: [PATCH 02/35] [fix] Max 35 symbols, really?... --- scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml b/scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml index 156cecb..28cac0e 100644 --- a/scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml +++ b/scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml @@ -3,7 +3,7 @@ pw.mmk.OpenFreebuds OpenFreebuds - Open-source client app for HUAWEI FreeBuds earphones + HUAWEI FreeBuds client application MelianMiko From 769de64acd6d1af781af74e8526b9a54d1151c9f Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 30 Sep 2024 20:12:43 +0700 Subject: [PATCH 03/35] [DevScripts] Add universal make.py build/install script --- .github/workflows/on_push.yml | 8 +- README.md | 15 +- .../build_debian/debian => debian}/.gitignore | 0 .../build_debian/debian => debian}/changelog | 0 .../build_debian/debian => debian}/control | 0 .../build_debian/debian => debian}/copyright | 0 debian/rules | 13 ++ debian/source/options | 2 + openfreebuds_qt/app/main.py | 2 +- .../assets/dual_connect_device.png | Bin 1018 -> 0 bytes openfreebuds_qt/assets/icon.png | Bin 8068 -> 0 bytes openfreebuds_qt/assets/icon_settings.png | Bin 1306 -> 0 bytes openfreebuds_qt/assets/overlay_playing.png | Bin 710 -> 0 bytes openfreebuds_qt/assets/overlay_primary.png | Bin 1200 -> 0 bytes .../assets}/pw.mmk.OpenFreebuds.desktop | 0 .../assets}/pw.mmk.OpenFreebuds.metainfo.xml | 6 +- .../assets}/pw.mmk.OpenFreebuds.png | Bin scripts/build_debian/.gitignore | 2 - scripts/build_debian/Makefile | 26 --- scripts/build_debian/build.sh | 13 -- scripts/build_debian/debian/rules | 5 - scripts/build_debian/debian/source/options | 4 - .../build_debian/pw.mmk.OpenFreebuds.desktop | 1 - .../pw.mmk.OpenFreebuds.metainfo.xml | 1 - scripts/build_debian/pw.mmk.OpenFreebuds.png | 1 - scripts/build_flatpak/pw.mmk.OpenFreebuds.yml | 13 +- scripts/bump_version.py | 2 +- .../build.sh => flatpak_install.sh} | 8 +- scripts/make.py | 185 ++++++++++++++++++ scripts/make_qt_parts.sh | 5 - 30 files changed, 221 insertions(+), 91 deletions(-) rename {scripts/build_debian/debian => debian}/.gitignore (100%) rename {scripts/build_debian/debian => debian}/changelog (100%) rename {scripts/build_debian/debian => debian}/control (100%) rename {scripts/build_debian/debian => debian}/copyright (100%) create mode 100755 debian/rules create mode 100644 debian/source/options delete mode 100644 openfreebuds_qt/assets/dual_connect_device.png delete mode 100644 openfreebuds_qt/assets/icon.png delete mode 100644 openfreebuds_qt/assets/icon_settings.png delete mode 100644 openfreebuds_qt/assets/overlay_playing.png delete mode 100644 openfreebuds_qt/assets/overlay_primary.png rename {scripts/build_flatpak => openfreebuds_qt/assets}/pw.mmk.OpenFreebuds.desktop (100%) rename {scripts/build_flatpak => openfreebuds_qt/assets}/pw.mmk.OpenFreebuds.metainfo.xml (98%) rename {scripts/build_flatpak => openfreebuds_qt/assets}/pw.mmk.OpenFreebuds.png (100%) delete mode 100644 scripts/build_debian/.gitignore delete mode 100644 scripts/build_debian/Makefile delete mode 100755 scripts/build_debian/build.sh delete mode 100755 scripts/build_debian/debian/rules delete mode 100644 scripts/build_debian/debian/source/options delete mode 120000 scripts/build_debian/pw.mmk.OpenFreebuds.desktop delete mode 120000 scripts/build_debian/pw.mmk.OpenFreebuds.metainfo.xml delete mode 120000 scripts/build_debian/pw.mmk.OpenFreebuds.png rename scripts/{build_flatpak/build.sh => flatpak_install.sh} (64%) create mode 100755 scripts/make.py delete mode 100755 scripts/make_qt_parts.sh diff --git a/.github/workflows/on_push.yml b/.github/workflows/on_push.yml index d36c47b..033a286 100644 --- a/.github/workflows/on_push.yml +++ b/.github/workflows/on_push.yml @@ -65,11 +65,13 @@ jobs: run: poetry install - name: Change in-app version run: python3 ./scripts/bump_version.py git - - name: Run build script - run: bash ./scripts/build_debian/build.sh + - name: Build + run: dpkg-buildpackage -b + - name: Move release to dist dir + run: mv ../*.deb ./dist - name: Upload bundle uses: actions/upload-artifact@v4 with: name: Debian package - path: ./scripts/build_debian/openfreebuds* + path: ./dist/* if-no-files-found: error diff --git a/README.md b/README.md index 88337e1..a5aa1c8 100644 --- a/README.md +++ b/README.md @@ -87,13 +87,7 @@ poetry install ### Just launch without installation ```shell -# Compile Qt Designer & Linguist sources -./scripts/make_qt_parts.sh - -# Launch -poetry run python -m openfreebuds_qt -vcs - -# use --help for options description +./scripts/make.py build_launch ``` ### Windows @@ -111,11 +105,10 @@ Output binaries will be located in `scripts\build_win32\dist` Install all packaging dependencies automated way: `apt install build-essentials && ./scripts/install_dpkg_dependencies.sh`. -Go to `scripts/build_debian` and run build script: - ```shell -cd scripts/build_debian -./build.sh +dpkg-buildpackage -b ``` +Output file will be located in parent folder (`../*.deb`). + ![Extra dialogs preview](docs/preview_2.png) diff --git a/scripts/build_debian/debian/.gitignore b/debian/.gitignore similarity index 100% rename from scripts/build_debian/debian/.gitignore rename to debian/.gitignore diff --git a/scripts/build_debian/debian/changelog b/debian/changelog similarity index 100% rename from scripts/build_debian/debian/changelog rename to debian/changelog diff --git a/scripts/build_debian/debian/control b/debian/control similarity index 100% rename from scripts/build_debian/debian/control rename to debian/control diff --git a/scripts/build_debian/debian/copyright b/debian/copyright similarity index 100% rename from scripts/build_debian/debian/copyright rename to debian/copyright diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..a11be5d --- /dev/null +++ b/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_build: + python ./scripts/make.py build + +override_dh_auto_install: + python ./scripts/make.py install debian/openfreebuds/usr debian/openfreebuds/usr/lib/python3/dist-packages + +override_dh_clean: + git clean -Xfd diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000..80ab2ac --- /dev/null +++ b/debian/source/options @@ -0,0 +1,2 @@ +tar-ignore = "*venv" +tar-ignore = ".git" diff --git a/openfreebuds_qt/app/main.py b/openfreebuds_qt/app/main.py index f13ff1b..4bd7b88 100644 --- a/openfreebuds_qt/app/main.py +++ b/openfreebuds_qt/app/main.py @@ -37,7 +37,7 @@ def __init__(self, ctx: IOfbQtApplication): self.setupUi(self) # Win32 staff - self.setWindowIcon(QIcon(str(ASSETS_PATH / "icon.png"))) + self.setWindowIcon(QIcon(str(ASSETS_PATH / "pw.mmk.OpenFreebuds.png"))) if sys.platform == "win32": self.body_content.setStyleSheet(WIN32_BODY_STYLE) diff --git a/openfreebuds_qt/assets/dual_connect_device.png b/openfreebuds_qt/assets/dual_connect_device.png deleted file mode 100644 index 4e612a05175c4e0cf5a65a352deeea5d0bcb2be2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1018 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 T`)hUxg52im>gTe~DWM4f6q+Xn diff --git a/openfreebuds_qt/assets/overlay_primary.png b/openfreebuds_qt/assets/overlay_primary.png deleted file mode 100644 index 96ebd4374857af52df162af0bed2db9a7215cfc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1200 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU~I{Bb`J1#c2+1T%1_J8No8Qr zm{>c}*5j~)%+dJhrLNk1ViO-tSZH z*A(1)v?lEJnp*#VhYB2{BcEBF4P3IJL$yOdVR0YVstp3mLzR!Zw0)Z__ix2x@sp~P zS2MZwv0ho=RP^UT{fFJrn=MRVsOsM3czxBFAr+wvOsnQq_y&CXB0 zL8o-;K1M#i#*ad&>#O%zU%7jGfBLPze;Isvi>3=02{{6zg)Pb3-G$*l2rk&Wd@@jk zv%n*=7#M@sL734=V|E2lkiEpy*OmPRhZMJ@x!8Z5W(EePwVp1HAr-gY-g3--6d=Ix z;HAaHV&gq|E-m{L8;gETs{F=JL&``(Fs|PBtMbt8_Tepru z&{5>*ft?e!`s+=1zFvCqvrUMIYKGl620eZKkKtQxzkSa-;bh8%(!2K=*1cw{<+{O? zu{G*P`j)7*?-@Z7g>xhsu9tp%@ZiB~W|P^zHTtv8KD*8~L51`9+18=Ga|+6!mhmjTCj)N=WO=FC~M=10_8v2(!-qSh{Y|64xl z&&!vYJNgeke*Bo<*?_0H*KR?G*25C3*rBPNZG zUFW#^`fIpw{OxUlla{ppzVy;0YTDPVvWmL8b8-jn-j(%J6Mj=-HRJsA|JgNpx6P{N zwQt|P{SD)cWu3E@v7TF;DK%@^$!7sm{>eKqun>FVdQ I&MBb@05Uol^Z)<= diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.desktop b/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.desktop similarity index 100% rename from scripts/build_flatpak/pw.mmk.OpenFreebuds.desktop rename to openfreebuds_qt/assets/pw.mmk.OpenFreebuds.desktop diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml b/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml similarity index 98% rename from scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml rename to openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml index 28cac0e..598dd94 100644 --- a/scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml +++ b/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml @@ -78,15 +78,15 @@ https://github.com/melianmiko/OpenFreebuds - + Tray indicator and context menu with common features https://raw.githubusercontent.com/melianmiko/OpenFreebuds/refs/heads/main/docs/preview_0.png - + Device gesture settings (may vary depending on device model) https://raw.githubusercontent.com/melianmiko/OpenFreebuds/refs/heads/main/docs/app_0.png - + Application user interface settings https://raw.githubusercontent.com/melianmiko/OpenFreebuds/refs/heads/main/docs/app_1.png diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.png b/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.png similarity index 100% rename from scripts/build_flatpak/pw.mmk.OpenFreebuds.png rename to openfreebuds_qt/assets/pw.mmk.OpenFreebuds.png diff --git a/scripts/build_debian/.gitignore b/scripts/build_debian/.gitignore deleted file mode 100644 index 25343b7..0000000 --- a/scripts/build_debian/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/dist -openfreebuds* diff --git a/scripts/build_debian/Makefile b/scripts/build_debian/Makefile deleted file mode 100644 index ee2dc6b..0000000 --- a/scripts/build_debian/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -DESTDIR := ${PWD}/dist - -DIST_PACKAGES_PATH := /usr/lib/python3/dist-packages -WHEEL_FILE := $(shell find ${PWD} -maxdepth 1 -name '*.whl') - -prepare: - bash ../make_qt_parts.sh - cd ../.. && poetry build - cp ../../dist/openfreebuds* . - -install: - pip install --upgrade --no-dependencies --target="${DESTDIR}${DIST_PACKAGES_PATH}" ${WHEEL_FILE} - mkdir -p ${DESTDIR}/usr/bin - mv ${DESTDIR}${DIST_PACKAGES_PATH}/bin/* ${DESTDIR}/usr/bin/ - ln -s ${DESTDIR}/usr/bin/openfreebuds_qt ${DESTDIR}/usr/bin/openfreebuds - sed -i '1 s/^.*$$/\#\!\/usr\/bin\/env python3/' ${DESTDIR}/usr/bin/* - rm -rf ${DESTDIR}${DIST_PACKAGES_PATH}/bin - mkdir -p ${DESTDIR}/usr/share/icons/hicolor/256x256/apps - install -Dm644 ./pw.mmk.OpenFreebuds.png ${DESTDIR}/usr/share/icons/hicolor/256x256/apps/pw.mmk.OpenFreebuds.png - mkdir -p ${DESTDIR}/usr/share/applications - install -Dm644 ./pw.mmk.OpenFreebuds.desktop ${DESTDIR}/usr//share/applications/pw.mmk.OpenFreebuds.desktop - mkdir -p ${DESTDIR}/usr/share/metainfo - install -Dm644 pw.mmk.OpenFreebuds.metainfo.xml -t ${DESTDIR}/usr/share/metainfo - -clean: - rm -rf dist *.tar.gz *.whl diff --git a/scripts/build_debian/build.sh b/scripts/build_debian/build.sh deleted file mode 100755 index 55a24bf..0000000 --- a/scripts/build_debian/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "$0")" - -rm ../../dist/* -rm openfreebuds* - -make prepare - -dpkg-buildpackage -S -dpkg-buildpackage -b - -mv ../openfreebuds_* . diff --git a/scripts/build_debian/debian/rules b/scripts/build_debian/debian/rules deleted file mode 100755 index abde6ef..0000000 --- a/scripts/build_debian/debian/rules +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/make -f - -%: - dh $@ - diff --git a/scripts/build_debian/debian/source/options b/scripts/build_debian/debian/source/options deleted file mode 100644 index 7d262f7..0000000 --- a/scripts/build_debian/debian/source/options +++ /dev/null @@ -1,4 +0,0 @@ -tar-ignore = "*venv" -tar-ignore = ".git" -tar-ignore = "builddir" -tar-ignore = "**/__pycache__" diff --git a/scripts/build_debian/pw.mmk.OpenFreebuds.desktop b/scripts/build_debian/pw.mmk.OpenFreebuds.desktop deleted file mode 120000 index a2cb092..0000000 --- a/scripts/build_debian/pw.mmk.OpenFreebuds.desktop +++ /dev/null @@ -1 +0,0 @@ -../build_flatpak/pw.mmk.OpenFreebuds.desktop \ No newline at end of file diff --git a/scripts/build_debian/pw.mmk.OpenFreebuds.metainfo.xml b/scripts/build_debian/pw.mmk.OpenFreebuds.metainfo.xml deleted file mode 120000 index 57f5196..0000000 --- a/scripts/build_debian/pw.mmk.OpenFreebuds.metainfo.xml +++ /dev/null @@ -1 +0,0 @@ -../build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml \ No newline at end of file diff --git a/scripts/build_debian/pw.mmk.OpenFreebuds.png b/scripts/build_debian/pw.mmk.OpenFreebuds.png deleted file mode 120000 index 2b13467..0000000 --- a/scripts/build_debian/pw.mmk.OpenFreebuds.png +++ /dev/null @@ -1 +0,0 @@ -../build_flatpak/pw.mmk.OpenFreebuds.png \ No newline at end of file diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml index 11f77cd..8f4c86e 100644 --- a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml +++ b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml @@ -36,20 +36,13 @@ modules: - python3-requirements.json - - name: PyQtApp + - name: OpenFreebuds buildsystem: simple build-commands: - - pip3 install --prefix=/app --no-deps *.whl - touch /app/is_container - - install -Dm644 pw.mmk.OpenFreebuds.png -t /app/share/icons/hicolor/256x256/apps - - install -Dm644 pw.mmk.OpenFreebuds.desktop -t /app/share/applications - - install -Dm644 pw.mmk.OpenFreebuds.metainfo.xml -t /app/share/metainfo + - python ./make.py install /app sources: - type: file - path: pw.mmk.OpenFreebuds.desktop - - type: file - path: pw.mmk.OpenFreebuds.metainfo.xml - - type: file - path: pw.mmk.OpenFreebuds.png + path: ../../scripts/make.py - type: dir path: ../../dist diff --git a/scripts/bump_version.py b/scripts/bump_version.py index 958ce5c..fdaca7a 100644 --- a/scripts/bump_version.py +++ b/scripts/bump_version.py @@ -191,7 +191,7 @@ def main(): bump_pyproject(str(PROJECT_ROOT / "pyproject.toml")) bump_nsis(str(PROJECT_ROOT / "scripts/build_win32/openfreebuds.nsi")) bump_debian(PROJECT_ROOT / "scripts/build_debian/debian/changelog") - bump_metainfo(str(PROJECT_ROOT / "scripts/build_flatpak/pw.mmk.OpenFreebuds.metainfo.xml")) + bump_metainfo(str(PROJECT_ROOT / "openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml")) create_version_info(PROJECT_ROOT / "openfreebuds_qt/version_info.py") create_flatpak_staff() diff --git a/scripts/build_flatpak/build.sh b/scripts/flatpak_install.sh similarity index 64% rename from scripts/build_flatpak/build.sh rename to scripts/flatpak_install.sh index 91eebc3..475eed4 100755 --- a/scripts/build_flatpak/build.sh +++ b/scripts/flatpak_install.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash -cd "$(dirname "$0")"/../.. +cd "$(dirname "$0")"/.. -rm -rf scripts/build_flatpak/release -./scripts/make_qt_parts.sh -poetry build +# Compile wheel +./scripts/install.py build +# Build & install flatpak cd scripts/build_flatpak flatpak-builder \ --force-clean \ diff --git a/scripts/make.py b/scripts/make.py new file mode 100755 index 0000000..8379530 --- /dev/null +++ b/scripts/make.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 + +""" +Unified Linux build/install script for OpenFreebuds +Should work in (mostly) all distributions +""" + +import os +import shutil +import subprocess +import sys +from pathlib import Path +from sys import version_info + +PROJECT_ROOT = Path(__file__).parent +if PROJECT_ROOT.name == "scripts": + PROJECT_ROOT = PROJECT_ROOT.parent + +# Constants +DEFAULT_DESTINATION = "/usr/local" +PYTHON_XX = f"python{version_info.major}.{version_info.minor}" +PYTHON_LIB_PATH_OPTIONS = [ + f"lib/{PYTHON_XX}/dist-packages", + f"lib/python3/dist-packages", + f"lib/python/dist-packages", + f"lib/{PYTHON_XX}/site-packages", + f"lib/python3/site-packages", + f"lib/python/site-packages", +] +QT_L_RELEASE_PATH_OPTIONS = [ + "/usr/lib/qt6/bin/lrelease", + "/usr/bin/lrelease", + "lrelease", +] +ALLOWED_TASKS = [ + "build", + "install", + "build_install", + "launch", + "build_launch" +] + +# Base params +if len(sys.argv) < 2 or sys.argv[1] not in ALLOWED_TASKS: + print(f"Usage: ./make.py <{'|'.join(ALLOWED_TASKS)}> [DEST_DIR] [PYTHON_LIBS_DIR]") + raise SystemExit(1) +TASK = sys.argv[1] +DEST_DIR = Path(DEFAULT_DESTINATION if len(sys.argv) < 3 else sys.argv[2]) +PYTHON_LIBS_DIR = None if len(sys.argv) < 4 else Path(sys.argv[3]) + +# Determinate task +DO_INSTALL = "install" in TASK +DO_LAUNCH = "launch" in TASK +DO_BUILD = "build" in TASK + +# Ensure environment +if sys.platform == "win32" and TASK != "build": + print("-- Can't install under Windows, use pyinstaller") + raise SystemExit(1) +if os.environ.get("VIRTUAL_ENV", None) is not None: + print("-- Launch this script outside of virtualenv") + raise SystemExit(1) + +# Find python dest dir +if PYTHON_LIBS_DIR is None: + for option in PYTHON_LIB_PATH_OPTIONS: + if (DEST_DIR / option).exists(): + PYTHON_LIBS_DIR = DEST_DIR / option + PYTHON_LIBS_DIR.mkdir(exist_ok=True, parents=True) + break + +if PYTHON_LIBS_DIR is None: + print("-- Error: Can't find python packages location, provide them manually") + raise SystemExit(1) + +print(f"Going to {TASK} OpenFreebuds") +if DO_INSTALL: + print(f"root={DEST_DIR}, python_libs={PYTHON_LIBS_DIR}") + +# Source compilation tasks +if DO_BUILD: + # Compile Qt Designer layouts + print("Compile Qt Designer files") + DESIGNER_DIR = PROJECT_ROOT / "openfreebuds_qt" / "designer" + result = subprocess.run(["env", "poetry", "run", "pyuic6", DESIGNER_DIR]) + if result.returncode != 0: + print("Failed, old pyuic? Will try single file mode...") + for ui_file in DESIGNER_DIR.iterdir(): + if not ui_file.name.endswith(".ui"): + continue + print(f"Compile {ui_file}") + result = subprocess.run(["poetry", "run", "pyuic6", + "-o", str(ui_file).replace(".ui", ".py"), + ui_file]) + if result.returncode != 0: + print("-- PyUiC failed") + raise SystemExit(1) + + # Compile Qt translations + L_RELEASE_EXEC = None + for option in QT_L_RELEASE_PATH_OPTIONS: + if Path(option).exists(): + L_RELEASE_EXEC = option + break + + if L_RELEASE_EXEC is not None: + print("Compile Qt translations") + files_to_compile = [] + for translation in (PROJECT_ROOT / "openfreebuds_qt/assets/i18n").iterdir(): + if not translation.name.endswith(".ts"): + continue + files_to_compile.append(str(translation)) + result = subprocess.run([L_RELEASE_EXEC, "-silent", *files_to_compile]) + if result.returncode != 0: + print("-- lrelease failed") + raise SystemExit(1) + else: + print("Warn: Can't find Qt lrelease executable, skip compiling translations") + + # Compile Python bundle + print("Compile Python wheel") + POETRY_DIST = PROJECT_ROOT / "dist" + shutil.rmtree(POETRY_DIST, ignore_errors=True) + result = subprocess.run(["poetry", "build", "-q"]) + if result.returncode != 0: + print("-- Poetry build failed") + raise SystemExit(1) + +# Launch task +if DO_LAUNCH: + print('----------------------------------------------------------------') + print("Launching OpenFreebuds") + subprocess.run(["poetry", "run", "python", "-m", "openfreebuds_qt", "-vcs"]) + raise SystemExit(0) + +# Wheel install tasks +if DO_INSTALL: + DISTRIBUTION_PATH = PROJECT_ROOT if not (PROJECT_ROOT / "dist").exists() else PROJECT_ROOT / "dist" + WHEEL_FILE = list(DISTRIBUTION_PATH.glob("*.whl")) + if len(WHEEL_FILE) == 0: + print("-- Error: Can't find wheel file to install") + raise SystemExit(1) + WHEEL_FILE = WHEEL_FILE[0] + + # Install python package + print("Install openfreebuds python package") + result = subprocess.run(["pip", "install", "-q", + "--upgrade", + "--no-dependencies", + f"--target={PYTHON_LIBS_DIR}", + WHEEL_FILE]) + if result.returncode != 0: + print("-- pip package install failure") + raise SystemExit(1) + + # Install binaries + BIN_DIR = PYTHON_LIBS_DIR / "bin" + TARGET_BIN_DIR = DEST_DIR / "bin" + TARGET_BIN_DIR.mkdir(exist_ok=True, parents=True) + + if BIN_DIR != TARGET_BIN_DIR: + for filename in ["openfreebuds_qt", "openfreebuds_cmd"]: + print(f"Install {TARGET_BIN_DIR / filename}") + shutil.copy(BIN_DIR / filename, TARGET_BIN_DIR) + os.unlink(BIN_DIR / filename) + print(f"Install {TARGET_BIN_DIR}/openfreebuds (symlink)") + (TARGET_BIN_DIR / "openfreebuds").unlink(missing_ok=True) + os.symlink("./openfreebuds_qt", TARGET_BIN_DIR / "openfreebuds") + + # Copy desktop integration files to system environment + ASSETS_DIR = PYTHON_LIBS_DIR / "openfreebuds_qt" / "assets" + + + def install_asset(asset, dest): + full_dest = DEST_DIR / dest + full_dest.mkdir(exist_ok=True, parents=True) + print(f"Install {full_dest / asset}") + shutil.copy(ASSETS_DIR / asset, full_dest / asset) + + + install_asset("pw.mmk.OpenFreebuds.desktop", "share/applications") + install_asset("pw.mmk.OpenFreebuds.metainfo.xml", "share/metainfo") + install_asset("pw.mmk.OpenFreebuds.png", "share/icons/hicolor/256x256/apps") + + print("Done") diff --git a/scripts/make_qt_parts.sh b/scripts/make_qt_parts.sh deleted file mode 100755 index ada8463..0000000 --- a/scripts/make_qt_parts.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -cd "$(dirname "$0")"/.. - -poetry run pyuic6 openfreebuds_qt/designer -/usr/lib/qt6/bin/lrelease openfreebuds_qt/assets/i18n/*.ts From 997d3ae2b946b5f5f3cc5bec56c7e0807ca0db9c Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 30 Sep 2024 20:18:57 +0700 Subject: [PATCH 04/35] [misc] bump dependencies --- poetry.lock | 198 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 100 insertions(+), 100 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1999894..538cfa0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -28,102 +28,102 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.7" +version = "3.10.8" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df23cb35bec54b73fba371c7c904994433651458acf8bfb7c84464fef5834c0a"}, - {file = "aiohttp-3.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33a6d023b207ad8227e607814c0020b42c53e01a66004fc0f2555e1a4941282"}, - {file = "aiohttp-3.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d23df9f01c8945d03cffcdd9ba9bfd88aa21ac567a39d0ac4d0c80499ed0d23"}, - {file = "aiohttp-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ddf2c8c9ec6bb3f5c057e5c95605adb8e3f1e2d999e8801736f448aff29280e"}, - {file = "aiohttp-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d09e40e2ae6723af487ffde019055d0b6ce4eae0749fcfe9de624b61f1af6ec"}, - {file = "aiohttp-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc1f4e0f4b1ae9289b4d0cc3bf5d6d55176c38ef1d41484550f3f9a0a78bedae"}, - {file = "aiohttp-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636e3efb0bb024817cefa1ef86d678d1a73eb210ae162aff4234214060011ff5"}, - {file = "aiohttp-3.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bab2544f09cd1db154c105e03b1c941032fd7237da5da184595771999ca90daa"}, - {file = "aiohttp-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:431852e77cd72f60a0278f8cf557c8e568cd856f755a4b6c5232c7d8c6343d2e"}, - {file = "aiohttp-3.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6bae913cbb183cd34863905088ef26a17c75332bd6bdd451ee8bf158c987cf19"}, - {file = "aiohttp-3.10.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:278cd430ba93a157ad1faf490fdd6051801085ffa31a27762133472555e56888"}, - {file = "aiohttp-3.10.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e083e29b6db8e34a507cd678f89eab3ae5f307486ea6010c6473436d3769628d"}, - {file = "aiohttp-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:150deb28d5302cfec89fc31ea4bce774df06f5c03d95519f7588ca6517a472d7"}, - {file = "aiohttp-3.10.7-cp310-cp310-win32.whl", hash = "sha256:e19337d6552af197ebb8c886daea0b938ae34eff776c1fa914ad433f6db3970f"}, - {file = "aiohttp-3.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:bff7ef30cb6fc186ea6dda9e19d6105b1c213e3a3f759b5a23c271c778027260"}, - {file = "aiohttp-3.10.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1378164474a3866f7684a95efede1bee4016cd104bc10bf885e492c4459b715a"}, - {file = "aiohttp-3.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:87d0e52b2905dbc1aeffcbf0611fa82e27874764332c11b984293a4b91cc8e9f"}, - {file = "aiohttp-3.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2783754bfcee0b13b8e55932b418cf8984c17099fd1b37341d4696447d0c328"}, - {file = "aiohttp-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d26881d98274ef0dbd4f069f383e5e90eb6e42e957289db14c47186386832ce"}, - {file = "aiohttp-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e152296b2c50417445eacdb2353d3c10e702f6593aa774277510fb7761304302"}, - {file = "aiohttp-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf1cd9bfd598899396bdb8a4dc5234144a77e482e7489972b7956cf66e272872"}, - {file = "aiohttp-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:871c2bf68ecc55056e5e3b0ae5929a1149f41c4255bbf99b1f858005f63360d1"}, - {file = "aiohttp-3.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd8a0a0ef895e4c3f1afd31c2a6f89d68a94baacdbe2eb9bf90ac54b997cf99b"}, - {file = "aiohttp-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:99c11c5d632fa2222cc5805105841f6f3c40df116368fde40fbd71f8b14ea692"}, - {file = "aiohttp-3.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8fbf91559400fe1a98d84af36f5a66aa59c359ac3cb113b17d304ced6a4601b4"}, - {file = "aiohttp-3.10.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:73f151a1e21369a84d56b91a209590c23270c847463029fdcbda710516217644"}, - {file = "aiohttp-3.10.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:80531f6f4fff5a1f7e495afbc4aff5c4230b605f26d56c40ecad27a269665608"}, - {file = "aiohttp-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:164068b338c52dfe44f3490c70ef7b33c0e73d441c89f599ae2d93f7dcf3e395"}, - {file = "aiohttp-3.10.7-cp311-cp311-win32.whl", hash = "sha256:a84fe27904dbb43a236532d6d841d6132200b7bb53ba73d0300b0b586ceab6cc"}, - {file = "aiohttp-3.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:beda1abd7b23d489a5b66a46eba5a9e0db58e4ad91d68697409eeabda343fb9d"}, - {file = "aiohttp-3.10.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:68120c12c98bfc0e024ef1279be5f41327a54a5094710adc970ecc9724b91871"}, - {file = "aiohttp-3.10.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e1a9b4026b6fe41adde784e308b0ad0d6a8b5cc9062f9c349125fd57149bc8a9"}, - {file = "aiohttp-3.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85d8a1d716516ef92c769eadb020600d27223899018ef8d07c09c117001cc7d5"}, - {file = "aiohttp-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87652147515031dafc1b37c9c3c42fbe9e2697af6264ec26080a6fe603cc5196"}, - {file = "aiohttp-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c6140d6cbf8eebbcf1528364ce0b26d0a95788111659cfc008fba3a12fc874f"}, - {file = "aiohttp-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:342600665e74eea20b3286045ebeb0aa2f9cececf2eb0acc6f6817205b112b29"}, - {file = "aiohttp-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b7794b3d23451e355b4a87959943125afff8dd31d8059651c2734de12f9e7f2"}, - {file = "aiohttp-3.10.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d8d12d6a192f7b9f8a335cad8634a4f081d8319b75dd42257a1a3e557848d00"}, - {file = "aiohttp-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b5d8c94fd23f41007799ec657e18661f9f8c5b566a1e4fe944e3514e505a6b49"}, - {file = "aiohttp-3.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a1fe407bec2f14a3d79ec92aa767b930857a6782589ea87ac76fd8081dea3dab"}, - {file = "aiohttp-3.10.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7ed4435dcf507ef2de5b4be64276933eb19c78e5c7d00ca376fcd9a67d0139a0"}, - {file = "aiohttp-3.10.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c161f9e353f291d23069a8f67180fd52c76d72d4671f4f53602ea9ac29f47d50"}, - {file = "aiohttp-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:caf083bf26b1e286ab1929dbd8d8cab6230160576a0ed5e3bfb3487bb19474c2"}, - {file = "aiohttp-3.10.7-cp312-cp312-win32.whl", hash = "sha256:4296dd120e7e9728625eef1091039aff1a454c7147913d47839876c94b202226"}, - {file = "aiohttp-3.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:10d19997f2f8d49d53b76163b71e263bb7b23f48041d0d4050a43445a0052c35"}, - {file = "aiohttp-3.10.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:582536d3d7f95a6d4d072d2326dd03eeb1549c1cc86d02c9bcec71899f4c66f2"}, - {file = "aiohttp-3.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:365eff442a47b13e0e12c37240a6f75940ebee0b7943af43c84d5b43643fc80c"}, - {file = "aiohttp-3.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2e0083e6f9f9cb0a0bedd694782e7fb8a54eb4de40e1743d9bb526f1c1eea88"}, - {file = "aiohttp-3.10.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da5a03cbe746f182f7b61e119dde24d388cf77965fea320bc8aba61b75039d06"}, - {file = "aiohttp-3.10.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b210484fccff00cafa9bd8abedea8749b6d975df8c8e21c82d92bb25403db85"}, - {file = "aiohttp-3.10.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b75cfa1e5fc7c87fc5f9de7124bb039b898791bb87207d2107bed5e3509670f"}, - {file = "aiohttp-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b4aa816cd3ab876f96ce8c6986648392137cbd6feddbf4189322515f34e1f6"}, - {file = "aiohttp-3.10.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3915944c87c9bf488db4ca1ae6edca40b5bc77c4c2cf2f49b69886bc47b97db1"}, - {file = "aiohttp-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd658aeaa65fb99fcc3b93882bb33cbd600501d40473488aec163a981d7b05ee"}, - {file = "aiohttp-3.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:aeea07c89a5a53463c70957feb85d4b846982c0f054b521fc44f52862e7871cf"}, - {file = "aiohttp-3.10.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f8aaa0bc8e39352684982b378ba3f7e32e78a746da433aaeceb7e93d7fdf9ce3"}, - {file = "aiohttp-3.10.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f25a79ac4ac0bd94cf283d3e86e6f3ec78fc39e2de6949b902c342148b7b5f6"}, - {file = "aiohttp-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fc3538efae4e4df222a563559f8766234f49e845e8dbb2dd477eb8f3fd97242"}, - {file = "aiohttp-3.10.7-cp313-cp313-win32.whl", hash = "sha256:eea89c47ae8d592f7563f4355132fe844b5e2f8660292deacc292253bef291cd"}, - {file = "aiohttp-3.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:7ce1b54feaaf264e28a4474e13635d302a59aafb720b18c3c2885b8f35ce5040"}, - {file = "aiohttp-3.10.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7a372f9ea521741667cec2ef4a64419448030411af2e844dfa8dbbb8074baea6"}, - {file = "aiohttp-3.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:feff2170b23921a526f31d78c8f76bbb9cde825e78035286d8571ce0c81901ab"}, - {file = "aiohttp-3.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa42c4e78925a438a6f7df0d9b165d29cdc0a44fc5ce838d6c293a0161a2bd9a"}, - {file = "aiohttp-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ced77f4dd0c4f0107ee96f8df162b984470ac9f94ef93dd44dba62838fd85cde"}, - {file = "aiohttp-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13085c0129a906b001d87dd43e247155f6c76820d98147c079b746e8a0665b17"}, - {file = "aiohttp-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92100555f86b314ed840ed61d937fc30ca39ad453c9aa9020414a3cce955d9b"}, - {file = "aiohttp-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77bc82d7b10f377957ba8e99bb1b13d946e9e9038fe89ba0888ad0b12e60c9c0"}, - {file = "aiohttp-3.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6052d92b47b8cf3736b1f01ac8f83cf02f188ef7542848055a5e227db0e16cb"}, - {file = "aiohttp-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:82fa5fb983922b03f2b08d1140550c68b50313305115639e19b13489c284c30c"}, - {file = "aiohttp-3.10.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:0246659d9a54a23a83f11842bdd58f335a1370aa66b376eeae16b7cf29009dde"}, - {file = "aiohttp-3.10.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:befc2f0794bc4bbbb1f8d0e245d32ee13331205b58f54910789e9e78d2a6fbf5"}, - {file = "aiohttp-3.10.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:9cd67e5c84cb75a471b2e35f3fb0da52e6d359d1794d3465a87052fb240e64b5"}, - {file = "aiohttp-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:af10344fb1ee195b2cd5840b61d8c8121b16d3b3baa2da5a86cf4001a7e5bd98"}, - {file = "aiohttp-3.10.7-cp38-cp38-win32.whl", hash = "sha256:81d3fc1b187656b6b465ed4ed4c9858f16ff2d9864da6225d80b8018abd7739b"}, - {file = "aiohttp-3.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:b6fb89edeadfd69df75f8cea97c3533805a9960cc56034ad296abe9b18771842"}, - {file = "aiohttp-3.10.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:318824b98a2bdf84e9a21d413737a3c4f27bbad0a9ce16141488f631dbffb9b2"}, - {file = "aiohttp-3.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:63c9de949e05a5f729aecba6bf4b3d5403846caf546ea5020f8b9bf315bd8f12"}, - {file = "aiohttp-3.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0245e1a71f3503b01d2c304529779a70277ccc0fe9847b48d437363de6e4336e"}, - {file = "aiohttp-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14dbfb208ffe3388e0770fd23bf9114cc933c10bb1dba35b538f3c9d685334d8"}, - {file = "aiohttp-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f6b014f2176d2774b759b8e2951af4a613385ebcc08841cb5c0ca6d5dee74ee"}, - {file = "aiohttp-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcfabf9338fed009fd9e11bf496a927ea67b1ce15d34847cb0a98aa6f042b989"}, - {file = "aiohttp-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:171f1f5364a0ef5873480e6fddc3870ee37f1dfe216fa67507bbd4c91306f110"}, - {file = "aiohttp-3.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87e243b1df27ff685ab08228b7a938c0530beb60ad3dea7554da1554d46c9ad4"}, - {file = "aiohttp-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fee4d2246b091b7e252cd5bcdbd4362fa21c3cc6a445fef54de793731546ab24"}, - {file = "aiohttp-3.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bfa8c8af8c92e3d6c1eff02cf5127f62c1e7564e7b0f1a9767035f81a2e6bb20"}, - {file = "aiohttp-3.10.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f44f09b67a458400215d9efedb9cfb5e3256dbeb2cc2da68e4592b7b36bac0c9"}, - {file = "aiohttp-3.10.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b5f8270946777d6971c27479cb6e7f54578be960928a8922cb59130e856d8484"}, - {file = "aiohttp-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e8ccaa99871303323bd2cda120043039729497642da5c6f53e066b19f73d9df8"}, - {file = "aiohttp-3.10.7-cp39-cp39-win32.whl", hash = "sha256:ce7c12bfbb1579e81cdf2e7db4338f8c768da2493aa0db60a858a542d551563c"}, - {file = "aiohttp-3.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:189979c7e9d8f40236534760daf5b41d2026d5ebabdf913e771d9b6bfbc992af"}, - {file = "aiohttp-3.10.7.tar.gz", hash = "sha256:18c72a69ba20713f26fa40932cac17437b0c1d25edff2e27437a204c12275bd9"}, + {file = "aiohttp-3.10.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a1ba7bc139592339ddeb62c06486d0fa0f4ca61216e14137a40d626c81faf10c"}, + {file = "aiohttp-3.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85e4d7bd05d18e4b348441e7584c681eff646e3bf38f68b2626807f3add21aa2"}, + {file = "aiohttp-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69de056022e7abf69cb9fec795515973cc3eeaff51e3ea8d72a77aa933a91c52"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee3587506898d4a404b33bd19689286ccf226c3d44d7a73670c8498cd688e42c"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe285a697c851734285369614443451462ce78aac2b77db23567507484b1dc6f"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10c7932337285a6bfa3a5fe1fd4da90b66ebfd9d0cbd1544402e1202eb9a8c3e"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd9716ef0224fe0d0336997eb242f40619f9f8c5c57e66b525a1ebf9f1d8cebe"}, + {file = "aiohttp-3.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ceacea31f8a55cdba02bc72c93eb2e1b77160e91f8abd605969c168502fd71eb"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9721554bfa9e15f6e462da304374c2f1baede3cb06008c36c47fa37ea32f1dc4"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:22cdeb684d8552490dd2697a5138c4ecb46f844892df437aaf94f7eea99af879"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e56bb7e31c4bc79956b866163170bc89fd619e0581ce813330d4ea46921a4881"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3a95d2686bc4794d66bd8de654e41b5339fab542b2bca9238aa63ed5f4f2ce82"}, + {file = "aiohttp-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d82404a0e7b10e0d7f022cf44031b78af8a4f99bd01561ac68f7c24772fed021"}, + {file = "aiohttp-3.10.8-cp310-cp310-win32.whl", hash = "sha256:4e10b04542d27e21538e670156e88766543692a0a883f243ba8fad9ddea82e53"}, + {file = "aiohttp-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:680dbcff5adc7f696ccf8bf671d38366a1f620b5616a1d333d0cb33956065395"}, + {file = "aiohttp-3.10.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:33a68011a38020ed4ff41ae0dbf4a96a202562ecf2024bdd8f65385f1d07f6ef"}, + {file = "aiohttp-3.10.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c7efa6616a95e3bd73b8a69691012d2ef1f95f9ea0189e42f338fae080c2fc6"}, + {file = "aiohttp-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb9b9764cfb4459acf01c02d2a59d3e5066b06a846a364fd1749aa168efa2be"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7f270f4ca92760f98a42c45a58674fff488e23b144ec80b1cc6fa2effed377"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6984dda9d79064361ab58d03f6c1e793ea845c6cfa89ffe1a7b9bb400dfd56bd"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f6d47e392c27206701565c8df4cac6ebed28fdf6dcaea5b1eea7a4631d8e6db"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a72f89aea712c619b2ca32c6f4335c77125ede27530ad9705f4f349357833695"}, + {file = "aiohttp-3.10.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36074b26f3263879ba8e4dbd33db2b79874a3392f403a70b772701363148b9f"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e32148b4a745e70a255a1d44b5664de1f2e24fcefb98a75b60c83b9e260ddb5b"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5aa1a073514cf59c81ad49a4ed9b5d72b2433638cd53160fd2f3a9cfa94718db"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d3a79200a9d5e621c4623081ddb25380b713c8cf5233cd11c1aabad990bb9381"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e45fdfcb2d5bcad83373e4808825b7512953146d147488114575780640665027"}, + {file = "aiohttp-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f78e2a78432c537ae876a93013b7bc0027ba5b93ad7b3463624c4b6906489332"}, + {file = "aiohttp-3.10.8-cp311-cp311-win32.whl", hash = "sha256:f8179855a4e4f3b931cb1764ec87673d3fbdcca2af496c8d30567d7b034a13db"}, + {file = "aiohttp-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:ef9b484604af05ca745b6108ca1aaa22ae1919037ae4f93aaf9a37ba42e0b835"}, + {file = "aiohttp-3.10.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ab2d6523575fc98896c80f49ac99e849c0b0e69cc80bf864eed6af2ae728a52b"}, + {file = "aiohttp-3.10.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f5d5d5401744dda50b943d8764508d0e60cc2d3305ac1e6420935861a9d544bc"}, + {file = "aiohttp-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de23085cf90911600ace512e909114385026b16324fa203cc74c81f21fd3276a"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4618f0d2bf523043866a9ff8458900d8eb0a6d4018f251dae98e5f1fb699f3a8"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21c1925541ca84f7b5e0df361c0a813a7d6a56d3b0030ebd4b220b8d232015f9"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:497a7d20caea8855c5429db3cdb829385467217d7feb86952a6107e033e031b9"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c887019dbcb4af58a091a45ccf376fffe800b5531b45c1efccda4bedf87747ea"}, + {file = "aiohttp-3.10.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40d2d719c3c36a7a65ed26400e2b45b2d9ed7edf498f4df38b2ae130f25a0d01"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57359785f27394a8bcab0da6dcd46706d087dfebf59a8d0ad2e64a4bc2f6f94f"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a961ee6f2cdd1a2be4735333ab284691180d40bad48f97bb598841bfcbfb94ec"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fe3d79d6af839ffa46fdc5d2cf34295390894471e9875050eafa584cb781508d"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9a281cba03bdaa341c70b7551b2256a88d45eead149f48b75a96d41128c240b3"}, + {file = "aiohttp-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c6769d71bfb1ed60321363a9bc05e94dcf05e38295ef41d46ac08919e5b00d19"}, + {file = "aiohttp-3.10.8-cp312-cp312-win32.whl", hash = "sha256:a3081246bab4d419697ee45e555cef5cd1def7ac193dff6f50be761d2e44f194"}, + {file = "aiohttp-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:ab1546fc8e00676febc81c548a876c7bde32f881b8334b77f84719ab2c7d28dc"}, + {file = "aiohttp-3.10.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b1a012677b8e0a39e181e218de47d6741c5922202e3b0b65e412e2ce47c39337"}, + {file = "aiohttp-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2df786c96c57cd6b87156ba4c5f166af7b88f3fc05f9d592252fdc83d8615a3c"}, + {file = "aiohttp-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8885ca09d3a9317219c0831276bfe26984b17b2c37b7bf70dd478d17092a4772"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dbf252ac19860e0ab56cd480d2805498f47c5a2d04f5995d8d8a6effd04b48c"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b2036479b6b94afaaca7d07b8a68dc0e67b0caf5f6293bb6a5a1825f5923000"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:365783e1b7c40b59ed4ce2b5a7491bae48f41cd2c30d52647a5b1ee8604c68ad"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:270e653b5a4b557476a1ed40e6b6ce82f331aab669620d7c95c658ef976c9c5e"}, + {file = "aiohttp-3.10.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8960fabc20bfe4fafb941067cda8e23c8c17c98c121aa31c7bf0cdab11b07842"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f21e8f2abed9a44afc3d15bba22e0dfc71e5fa859bea916e42354c16102b036f"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fecd55e7418fabd297fd836e65cbd6371aa4035a264998a091bbf13f94d9c44d"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:badb51d851358cd7535b647bb67af4854b64f3c85f0d089c737f75504d5910ec"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e860985f30f3a015979e63e7ba1a391526cdac1b22b7b332579df7867848e255"}, + {file = "aiohttp-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71462f8eeca477cbc0c9700a9464e3f75f59068aed5e9d4a521a103692da72dc"}, + {file = "aiohttp-3.10.8-cp313-cp313-win32.whl", hash = "sha256:177126e971782769b34933e94fddd1089cef0fe6b82fee8a885e539f5b0f0c6a"}, + {file = "aiohttp-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:98a4eb60e27033dee9593814ca320ee8c199489fbc6b2699d0f710584db7feb7"}, + {file = "aiohttp-3.10.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ffef3d763e4c8fc97e740da5b4d0f080b78630a3914f4e772a122bbfa608c1db"}, + {file = "aiohttp-3.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:597128cb7bc5f068181b49a732961f46cb89f85686206289d6ccb5e27cb5fbe2"}, + {file = "aiohttp-3.10.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f23a6c1d09de5de89a33c9e9b229106cb70dcfdd55e81a3a3580eaadaa32bc92"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da57af0c54a302b7c655fa1ccd5b1817a53739afa39924ef1816e7b7c8a07ccb"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e7a6af57091056a79a35104d6ec29d98ec7f1fb7270ad9c6fff871b678d1ff8"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32710d6b3b6c09c60c794d84ca887a3a2890131c0b02b3cefdcc6709a2260a7c"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b91f4f62ad39a8a42d511d66269b46cb2fb7dea9564c21ab6c56a642d28bff5"}, + {file = "aiohttp-3.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:471a8c47344b9cc309558b3fcc469bd2c12b49322b4b31eb386c4a2b2d44e44a"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc0e7f91705445d79beafba9bb3057dd50830e40fe5417017a76a214af54e122"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:85431c9131a9a0f65260dc7a65c800ca5eae78c4c9931618f18c8e0933a0e0c1"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:b91557ee0893da52794b25660d4f57bb519bcad8b7df301acd3898f7197c5d81"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:4954e6b06dd0be97e1a5751fc606be1f9edbdc553c5d9b57d72406a8fbd17f9d"}, + {file = "aiohttp-3.10.8-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a087c84b4992160ffef7afd98ef24177c8bd4ad61c53607145a8377457385100"}, + {file = "aiohttp-3.10.8-cp38-cp38-win32.whl", hash = "sha256:e1f0f7b27171b2956a27bd8f899751d0866ddabdd05cbddf3520f945130a908c"}, + {file = "aiohttp-3.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:c4916070e12ae140110aa598031876c1bf8676a36a750716ea0aa5bd694aa2e7"}, + {file = "aiohttp-3.10.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5284997e3d88d0dfb874c43e51ae8f4a6f4ca5b90dcf22995035187253d430db"}, + {file = "aiohttp-3.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9443d9ebc5167ce1fbb552faf2d666fb22ef5716a8750be67efd140a7733738c"}, + {file = "aiohttp-3.10.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b667e2a03407d79a76c618dc30cedebd48f082d85880d0c9c4ec2faa3e10f43e"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98fae99d5c2146f254b7806001498e6f9ffb0e330de55a35e72feb7cb2fa399b"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8296edd99d0dd9d0eb8b9e25b3b3506eef55c1854e9cc230f0b3f885f680410b"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ce46dfb49cfbf9e92818be4b761d4042230b1f0e05ffec0aad15b3eb162b905"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c38cfd355fd86c39b2d54651bd6ed7d63d4fe3b5553f364bae3306e2445f847"}, + {file = "aiohttp-3.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:713dff3f87ceec3bde4f3f484861464e722cf7533f9fa6b824ec82bb5a9010a7"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21a72f4a9c69a8567a0aca12042f12bba25d3139fd5dd8eeb9931f4d9e8599cd"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6d1ad868624f6cea77341ef2877ad4e71f7116834a6cd7ec36ec5c32f94ee6ae"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a78ba86d5a08207d1d1ad10b97aed6ea48b374b3f6831d02d0b06545ac0f181e"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:aff048793d05e1ce05b62e49dccf81fe52719a13f4861530706619506224992b"}, + {file = "aiohttp-3.10.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d088ca05381fd409793571d8e34eca06daf41c8c50a05aeed358d2d340c7af81"}, + {file = "aiohttp-3.10.8-cp39-cp39-win32.whl", hash = "sha256:ee97c4e54f457c366e1f76fbbf3e8effee9de57dae671084a161c00f481106ce"}, + {file = "aiohttp-3.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:d95ae4420669c871667aad92ba8cce6251d61d79c1a38504621094143f94a8b4"}, + {file = "aiohttp-3.10.8.tar.gz", hash = "sha256:21f8225f7dc187018e8433c9326be01477fb2810721e048b33ac49091b19fb4a"}, ] [package.dependencies] @@ -853,16 +853,16 @@ PyQt6-sip = ">=13.8,<14" [[package]] name = "pyqt6-qt6" -version = "6.7.2" +version = "6.7.3" description = "The subset of a Qt installation needed by PyQt6." optional = false python-versions = "*" files = [ - {file = "PyQt6_Qt6-6.7.2-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:065415589219a2f364aba29d6a98920bb32810286301acbfa157e522d30369e3"}, - {file = "PyQt6_Qt6-6.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f817efa86a0e8eda9152c85b73405463fbf3266299090f32bbb2266da540ead"}, - {file = "PyQt6_Qt6-6.7.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:05f2c7d195d316d9e678a92ecac0252a24ed175bd2444cc6077441807d756580"}, - {file = "PyQt6_Qt6-6.7.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:fc93945eaef4536d68bd53566535efcbe78a7c05c2a533790a8fd022bac8bfaa"}, - {file = "PyQt6_Qt6-6.7.2-py3-none-win_amd64.whl", hash = "sha256:b2d7e5ddb1b9764cd60f1d730fa7bf7a1f0f61b2630967c81761d3d0a5a8a2e0"}, + {file = "PyQt6_Qt6-6.7.3-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:f517a93b6b1a814d4aa6587adc312e812ebaf4d70415bb15cfb44268c5ad3f5f"}, + {file = "PyQt6_Qt6-6.7.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8551732984fb36a5f4f3db51eafc4e8e6caf18617365830285306f2db17a94c2"}, + {file = "PyQt6_Qt6-6.7.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:50c7482bcdcf2bb78af257fb10ed8b582f8daf91d829782393bc50ac5a0a900c"}, + {file = "PyQt6_Qt6-6.7.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:cb525fdd393332de60887953029276a44de480fce1d785251ae639580f5e7246"}, + {file = "PyQt6_Qt6-6.7.3-py3-none-win_amd64.whl", hash = "sha256:36ea0892b8caeb983af3f285f45fb8dfbb93cfd972439f4e01b7efb2868f6230"}, ] [[package]] @@ -1167,4 +1167,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "c6a9fdc0713c90fcc208829e76d5f06d56a0c3d4277fa73697bc3646fdbbaa68" +content-hash = "36a8e787f00f13027aa5b57ada474f9b73a4222097ec3f3be11e3fe479c0b92f" diff --git a/pyproject.toml b/pyproject.toml index 6c54aac..0fbf1ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ openfreebuds_cmd = 'openfreebuds_cmd:main' python = ">=3.10,<3.13" pillow = "^10.4.0" psutil = "^6.0.0" -aiohttp = "^3.10.5" +aiohttp = "^3.10.7" qasync = "^0.27.1" pynput = "^1.7.7" winsdk = { version = "1.0.0b10", platform = "win32" } From ada49556c63d1332efe4c34838323f8b3c42e486 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 30 Sep 2024 20:24:15 +0700 Subject: [PATCH 05/35] [fix] bump_version.py crash --- debian/rules | 3 ++- scripts/bump_version.py | 2 +- scripts/install_dpkg_dependencies.sh | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/rules b/debian/rules index a11be5d..dc827b9 100755 --- a/debian/rules +++ b/debian/rules @@ -10,4 +10,5 @@ override_dh_auto_install: python ./scripts/make.py install debian/openfreebuds/usr debian/openfreebuds/usr/lib/python3/dist-packages override_dh_clean: - git clean -Xfd + git clean -Xfd + diff --git a/scripts/bump_version.py b/scripts/bump_version.py index fdaca7a..f62b462 100644 --- a/scripts/bump_version.py +++ b/scripts/bump_version.py @@ -190,7 +190,7 @@ def main(): # Launch everything bump_pyproject(str(PROJECT_ROOT / "pyproject.toml")) bump_nsis(str(PROJECT_ROOT / "scripts/build_win32/openfreebuds.nsi")) - bump_debian(PROJECT_ROOT / "scripts/build_debian/debian/changelog") + bump_debian(PROJECT_ROOT / "debian/changelog") bump_metainfo(str(PROJECT_ROOT / "openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml")) create_version_info(PROJECT_ROOT / "openfreebuds_qt/version_info.py") create_flatpak_staff() diff --git a/scripts/install_dpkg_dependencies.sh b/scripts/install_dpkg_dependencies.sh index 2faf86d..8ac6ccd 100755 --- a/scripts/install_dpkg_dependencies.sh +++ b/scripts/install_dpkg_dependencies.sh @@ -15,4 +15,4 @@ apt install -y $(awk ' gsub(/[^a-z0-9_.+-].*$/, "", dep) if (dep && !seen[dep]++) print dep } - }' scripts/build_debian/debian/control) + }' debian/control) From 87424343c83a21dbf01650f684f43ce176395f51 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Wed, 2 Oct 2024 18:20:00 +0700 Subject: [PATCH 06/35] [fix] Metainfo description --- openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml b/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml index 598dd94..8e4ce44 100644 --- a/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml +++ b/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml @@ -3,7 +3,7 @@ pw.mmk.OpenFreebuds OpenFreebuds - HUAWEI FreeBuds client application + Companion app for HUAWEI earphones MelianMiko From b000d43c5066f61ff69974c640bdc478298050bc Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Wed, 2 Oct 2024 18:24:56 +0700 Subject: [PATCH 07/35] [fix][critical] Crash when using old devices, like 4i/SE (#39) --- .../assets/debug_profiles/huawei_4i.json | 26 +++++++++---------- openfreebuds_qt/main.py | 2 ++ openfreebuds_qt/tray/menu_equalizer.py | 2 ++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/openfreebuds/assets/debug_profiles/huawei_4i.json b/openfreebuds/assets/debug_profiles/huawei_4i.json index 0ad8465..edf0003 100644 --- a/openfreebuds/assets/debug_profiles/huawei_4i.json +++ b/openfreebuds/assets/debug_profiles/huawei_4i.json @@ -8,30 +8,28 @@ "state": { "in_ear": "false" }, - "battery": { - "global": 90, - "left": 90, - "right": 90, - "case": 85, - "is_charging": "false" - }, "anc": { "mode": "normal", "mode_options": "normal,cancellation,awareness" }, + "battery": { + "global": 100, + "left": 100, + "right": 100, + "case": 70, + "is_charging": "false" + }, "action": { - "double_tap_left": "tap_action_off", - "double_tap_right": "tap_action_off", - "double_tap_options": "tap_action_pause,tap_action_prev,tap_action_next,tap_action_assistant,tap_action_off", - "long_tap_left": "tap_action_off", - "long_tap_right": "tap_action_off", + "double_tap_left": "tap_action_pause", + "double_tap_right": "tap_action_pause", + "double_tap_options": "tap_action_pause,tap_action_next,tap_action_prev,tap_action_assistant,tap_action_off", + "long_tap_left": "tap_action_switch_anc", "long_tap_options": "tap_action_off,tap_action_switch_anc", "noise_control_left": "noise_control_off_on_aw", - "noise_control_right": "noise_control_off_on_aw", "noise_control_options": "noise_control_off_on,noise_control_off_on_aw,noise_control_on_aw,noise_control_off_aw" }, "config": { - "auto_pause": "false" + "auto_pause": "true" }, "service": { "language": "", diff --git a/openfreebuds_qt/main.py b/openfreebuds_qt/main.py index edf600b..a5762b7 100644 --- a/openfreebuds_qt/main.py +++ b/openfreebuds_qt/main.py @@ -181,6 +181,8 @@ async def _stage_shortcut(self): self._exit(0) async def restore_device(self): + if self.args.virtual_device: + return name = self.config.get("device", "name", None) address = self.config.get('device', "address", None) if address is not None: diff --git a/openfreebuds_qt/tray/menu_equalizer.py b/openfreebuds_qt/tray/menu_equalizer.py index 415c7bb..4b88ac9 100644 --- a/openfreebuds_qt/tray/menu_equalizer.py +++ b/openfreebuds_qt/tray/menu_equalizer.py @@ -20,6 +20,8 @@ def __init__(self, parent: QWidget, ctx: IOfbQtApplication): async def update_ui(self): current = await self.ofb.get_property("sound", "equalizer_preset") options = await self.ofb.get_property("sound", "equalizer_preset_options") + if options is None: + return self.clear() for code in options.split(","): From 5416abe818be2f1c1bf69a07e4c3a908f128daf8 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Wed, 2 Oct 2024 19:04:18 +0700 Subject: [PATCH 08/35] [fix] Add .gitignore for Flatpak --- scripts/build_flatpak/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 scripts/build_flatpak/.gitignore diff --git a/scripts/build_flatpak/.gitignore b/scripts/build_flatpak/.gitignore new file mode 100644 index 0000000..14230ab --- /dev/null +++ b/scripts/build_flatpak/.gitignore @@ -0,0 +1,4 @@ +* +!.gitignore +!pw.mmk.OpenFreebuds.yml +!python3-requirements.json From 7eb3bd3832f12655df8183696f1b86e821c9f18b Mon Sep 17 00:00:00 2001 From: Lobo <88998991+Lobooooooo14@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:15:50 -0300 Subject: [PATCH 09/35] [i18n] complete pt-BR translations (#41) --- openfreebuds_qt/assets/i18n/pt-BR.ts | 254 +++++++++++++-------------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/openfreebuds_qt/assets/i18n/pt-BR.ts b/openfreebuds_qt/assets/i18n/pt-BR.ts index 8bf6e0a..32558fb 100644 --- a/openfreebuds_qt/assets/i18n/pt-BR.ts +++ b/openfreebuds_qt/assets/i18n/pt-BR.ts @@ -7,17 +7,17 @@ Unsupported device - + Dispositivo não compatível This device isn't supported, for yet. But if you want, you can force connect them. To do that, select an exiting profile. Then application will try to apply it to your device. You can change profile in any time. - + Este dispositivo ainda não é compatível. Mas se quiser, você pode forçar a conexão deles. Para fazer isso, selecione um perfil existente. Em seguida, o aplicativo tentará aplicá-lo ao seu dispositivo. Você pode alterar o perfil a qualquer momento. Notice that this may be dangerous for some kind of devices. Continue only if you know what you're doing. - + Observe que isso pode ser perigoso para alguns tipos de dispositivos. Continue apenas se você souber o que está fazendo. @@ -40,7 +40,7 @@ Voices - + Vozes @@ -89,7 +89,7 @@ Equalizer preset… - + Predefinição do equalizador… @@ -97,12 +97,12 @@ New version available, click here to view - + Nova versão disponível, clique aqui para visualizar Hide - + Esconder @@ -112,7 +112,7 @@ Exit - + Saída @@ -120,7 +120,7 @@ Client application for HUAWEI FreeBuds wireless earphone series. Free and open source. - + Cliente para a série de fones de ouvido sem fio HUAWEI FreeBuds. Gratuito e de código aberto. @@ -140,7 +140,7 @@ Libraries - + Bibliotecas @@ -148,12 +148,12 @@ You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. - + Você está executando uma versão mais antiga do Qt do que o esperado. É altamente recomendável mudar para a versão Flatpak, porque a versão mais antiga do Qt pode prejudicar sua experiência de uso do OpenFreebuds. This warning will be shown only once. Please, test Flatpak version before reporting bugs. - + Este aviso será mostrado apenas uma vez. Por favor, teste a versão Flatpak antes de relatar bugs. @@ -161,7 +161,7 @@ To use this feature, additional dependencies is required. Install it now? - + Para usar este recurso, são necessárias dependências adicionais. Instalar agora? @@ -179,12 +179,12 @@ All available device information fields: - + Todos os campos de informações do dispositivo disponíveis: Property - + Propriedade @@ -194,12 +194,12 @@ Battery last charged: - + Última carga da bateria: Unknown - + Desconhecido @@ -207,37 +207,37 @@ Interaction - + Interação Wear detection - + Detecção de desgaste Pause audio or switch device when earphones are removed - + Pause o áudio ou troque de dispositivo quando os fones de ouvido forem removidos Networking - + Rede Enable low-latency mode - + Ative o modo de baixa latência Will try to reduce audio latency when using headphones in enviroments with high network load. This option is disabled when your earphones are disconnected. Applies only for current device. - + Tentaremos reduzir a latência de áudio ao usar fones de ouvido em ambientes com alta carga de rede. Esta opção fica desativada quando seus fones de ouvido estão desconectados. Aplica-se apenas ao dispositivo atual. Service - + Serviço @@ -247,7 +247,7 @@ Due to technical restrictions, current language can't be readden from device and this field will be empty. - + Devido a restrições técnicas, o idioma atual não pode ser lido no dispositivo e este campo estará vazio. @@ -255,17 +255,17 @@ Select device automatically - + Selecione o dispositivo automaticamente OpenFreebuds will automatically detect currently connected headset and switch to them. - + OpenFreebuds detectará automaticamente os fones de ouvido conectados no momento e mudará para eles. List bellow shows all Bluetooth devices paired with your device. Select appropriate device that you want to use with OpenFreebuds: - + A lista abaixo mostra todos os dispositivos Bluetooth emparelhados com o seu dispositivo. Selecione o dispositivo apropriado que deseja usar com OpenFreebuds: @@ -288,12 +288,12 @@ Allow connecting to multiple devices at once - + Permitir conexão com vários dispositivos ao mesmo tempo This page allows you to manage devices that are paired with your headphones. - + Esta página permite gerenciar dispositivos emparelhados com seus fones de ouvido. @@ -303,7 +303,7 @@ Device name: - + Nome do dispositivo: @@ -334,12 +334,12 @@ Unpair device - + Desemparelhar dispositivo Do you really want to unpair %1 from your headphones? - + Você realmente deseja desemparelhar %1 dos seus fones de ouvido? @@ -347,12 +347,12 @@ OpenFreebuds ran into error. Please, save bugreport and send them to developer. Bugreport will be generated after clicking on "Close" button. - + O OpenFreebuds apresentou um erro. Por favor, salve o relatório de bug e envie-o ao desenvolvedor. Um relatório de erros será gerado após clicar no botão "Fechar". Application will be closed, restart it manually. - + O aplicativo será fechado, reinicie-o manualmente. @@ -360,42 +360,42 @@ Welcome - + Bem-vindo This application allows you to manage your HUAWEI Bluetooth earphones. To access them, look for headphones icon in system tray panel, near other icons. - + Este aplicativo permite que você gerencie seus fones de ouvido Bluetooth HUAWEI. Para acessá-los, procure o ícone de fones de ouvido no painel da bandeja do sistema (system tray), próximo a outros ícones. Left-click on this icon will cycle through noise cancellation modes (can be configured), right-click will provide access to full battery status and main options. Settings window provides access to all features. - + Clicar com o botão esquerdo neste ícone percorrerá os modos de cancelamento de ruído (pode ser configurado), clicar com o botão direito fornecerá acesso ao status total da bateria e às opções principais. A janela Configurações fornece acesso a todos os recursos. If you're running under GNOME shell and can't find tray icon, please, check FAQ. - + Se você estiver executando o shell do GNOME e não conseguir encontrar o ícone da bandeja, verifique o FAQ. Launch OpenFreebuds at system boot - + Inicie o OpenFreebuds na inicialização do sistema You could change this options anytime later in settings. - + Você pode alterar essas opções a qualquer momento nas configurações. FAQ - + Perguntas frequentes Get started - + Começar @@ -408,7 +408,7 @@ Triple-tap - + Toque três vezes @@ -418,17 +418,17 @@ Left - + Esquerdo Power button double-tap - Ação do botão liga/desliga + Botão liga/desliga: Tocar duas vezes Customize device touch panel(s) and button(s) behaviour. This settings are stored inside your device and will work also if OpenFreebuds is closed. - + Personalize o comportamento dos painéis de toque e dos botões do dispositivo. Essas configurações são armazenadas dentro do seu dispositivo e funcionarão também se o OpenFreebuds estiver fechado. @@ -443,7 +443,7 @@ Right - + Direito @@ -522,32 +522,32 @@ Enable global keyboard shortcuts - + Habilite atalhos de teclado globais Here you can configure system-wide keyboard shortcuts for OpenFreebuds - + Aqui você pode configurar atalhos de teclado para todo o sistema para OpenFreebuds Action - + Ação Shortcut - + Atalho Hint: to remove already assigned shortcut, press Esc while recording a new one. - + Dica: para remover o atalho já atribuído, pressione Esc enquanto grava um novo. Press new shortcut… - + Pressione o novo atalho… @@ -555,27 +555,27 @@ Compatibility - + Compatibilidade Enable MPRIS helper service - + Habilitar serviço auxiliar MPRIS Try this option if auto-pause doesn't work with your desktop environment. Note that only MPRIS-compatible media players are supported (mostly all standalone player and browsers support this API). - + Experimente esta opção se a pausa automática não funcionar no seu ambiente de trabalho. Observe que apenas reprodutores de mídia compatíveis com MPRIS são suportados (quase todos os reprodutores e navegadores independentes suportam esta API). Force use X11 backend (may look better in GNOME-based desktop environments) - + Forçar o uso do backend X11 (pode parecer melhor em ambientes de desktop baseados em GNOME) Restart application to apply. - + Reinicie o aplicativo para aplicar. @@ -585,37 +585,37 @@ Fresh versions of OpenFreebuds are written in Qt6, and uses system-wide Qt UI theme. So, if application color scheme didn't match with system, or it looks ugly, you should configure global Qt style settings. - + Novas versões do OpenFreebuds são escritas em Qt6 e usam o tema Qt UI para todo o sistema. Portanto, se o esquema de cores do aplicativo não corresponder ao sistema ou parecer feio, você deverá definir as configurações globais de estilo Qt. In KDE, LxQT or other Qt-based desktop environments, use system appearance settings. Otherwise, configure qt manually or use any configuration tool like qt6ct. - + No KDE, LxQT ou outros ambientes de desktop baseados em Qt, use as configurações de aparência do sistema. Caso contrário, configure o qt manualmente ou use qualquer ferramenta de configuração como qt6ct. Keyboard shortcuts - + Atalhos de teclado Looks like you're using Wayland desktop environment. Due to that OpenFreebuds built-in global hotkeys won't work. If you want to use keyboard shortcuts, setup them from your desktop environment settings. - + Parece que você está usando o ambiente de desktop Wayland. Devido a isso, as teclas de atalho globais integradas do OpenFreebuds não funcionarão. Se você quiser usar atalhos de teclado, configure-os nas configurações do ambiente de área de trabalho. Documentation - + Documentação Ensure bus access - + Garanta acesso ao barramento Looks like you're running under Flatpak. To use this feature, OpenFreebuds need to have access to entire session bus, otherwise it won't find any working media players. Ensure that you're granted this permission, refer to FAQ for more details. - + Parece que você está executando o Flatpak. Para usar este recurso, o OpenFreebuds precisa ter acesso a todo o barramento de sessão, caso contrário, não encontrará nenhum reprodutor de mídia funcionando. Certifique-se de ter essa permissão concedida. Consulte as Perguntas frequentes para obter mais detalhes. @@ -623,7 +623,7 @@ Select device - + Selecione o dispositivo @@ -643,12 +643,12 @@ Sound quality - + Qualidade de som Other settings - + Outras configurações @@ -658,17 +658,17 @@ User interface - + Interface do usuário Keyboard shortcuts - + Atalhos de teclado Linux-related - + Relacionado ao Linux @@ -678,42 +678,42 @@ Help: FAQ - + Ajuda: Perguntas frequentes (FAQ) Help: Remote control - + Ajuda: Controle remoto Bugreport… - Reportar erro + Reportar erro… Check for updates… - + Verifique se há atualizações… Remote access… - + Acesso remoto… Temporary replace device - + Dispositivo de substituição temporária Close this window - + Fechar esta janela Exit OpenFreebuds - + Sair do OpenFreebuds @@ -721,12 +721,12 @@ Manual connect… - Conexão manual + Conexão manual… Enter bluetooth address of your device and select their model from list. You can find bluetooth address in system settings. - + Digite o endereço bluetooth do seu dispositivo e selecione o modelo na lista. Você pode encontrar o endereço do Bluetooth nas configurações do sistema. @@ -744,12 +744,12 @@ Remote access… - + Acesso remoto… Change this settings only if you know what you're doing. Require restart of all OpenFreebuds instances and all clients. - + Altere essas configurações somente se você souber o que está fazendo. Exige reinicialização de todas as instâncias do OpenFreebuds e de todos os clientes. @@ -759,12 +759,12 @@ Require following secret to be present in X-Secret header - + Exige que a seguinte chave secreta está presente no cabeçalho X-Secret (enter new secret key) - + (insira a nova chave secreta) @@ -772,117 +772,117 @@ Configure sound-related device settings. - + Configurações relacionadas ao som. Sound quality preference: - + Preferência de qualidade de som: Prioritize sound quality - + Priorize a qualidade do som Device will prefer loseless audio codecs, like LDAC. - + O dispositivo preferirá codificadores de áudio sem perdas, como LDAC. Prioritize connection quality - + Priorize a qualidade da conexão Device will prefer audio codes with better compression, like AAC - + O dispositivo preferirá codificadores de áudio com melhor compactação, como AAC Equalizer - + Equalizador Preset: - + Predefinição: Customize preset - + Personalizar predefinição Save changes? - + Salvar alterações? Save - + Salvar New preset… - + Nova predefinição… Delete… - + Excluir… Export to file… - + Exportar para arquivo… Load file… - + Carregar arquivo… Create new equalizer preset - + Crie uma nova predefinição de equalizador Enter new preset name: - + Insira o novo nome predefinido: Failed - + Falhou Can't create: too many custom preset created in device. - + Não é possível criar: muitas predefinições personalizadas criadas no dispositivo. Delete equalizer mode? - + Excluir modo do equalizador? Will delete following mode: - + Será excluído o seguinte modo: Save equalizer preset to file… - + Salvar predefinição do equalizador em arquivo… Load equalizer preset from file… - + Carregar predefinição do equalizador do arquivo… @@ -895,7 +895,7 @@ Right headphone: - + Fone de ouvido direito: @@ -945,7 +945,7 @@ Leave application - + Sair do aplicativo @@ -953,17 +953,17 @@ Looking for UI theme settings? Now it follows system-wide configuration. - + Procurando configurações de tema? Agora ele segue as configurações do sistema. Main - + Principal Launch at system startup - + Iniciar na inicialização do sistema @@ -973,12 +973,12 @@ System - + Sistema Restart OpenFeebuds to apply changes - + Reinicie o OpenFeebuds para aplicar as alterações @@ -988,12 +988,12 @@ Notify about new versions - + Notificar sobre novas versões Check for new versions, but don't notify - + Verifique se há novas versões, mas não notificar @@ -1003,7 +1003,7 @@ Tray applet - + Aplicativo da bandeja (tray applet) @@ -1028,17 +1028,17 @@ Left-click action - + Ação do clique com o botão esquerdo Show dual-connect device switcher in menu (if available) - + Mostrar alternador de dispositivos de conexão dupla no menu (se disponível) Show equalizer preset switcher in menu (if available) - + Mostrar alternador de predefinições do equalizador no menu (se disponível) @@ -1046,7 +1046,7 @@ OpenFreebuds: Connecting to device… - + OpenFreebuds: Conectando ao dispositivo… @@ -1069,7 +1069,7 @@ Next noise control mode - + Proximo modo de cancelamento de ruído @@ -1079,17 +1079,17 @@ Enable noise cancellation - + Ativar cancelamento de ruído Enable awareness mode - + Ativar modo de sensibilidade ao ambiente Enable low-latency mode - + Ativar o modo de baixa latência From 46b54e1e6ce6fb76401a551f00648e48f8332e44 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 7 Oct 2024 19:22:40 +0700 Subject: [PATCH 10/35] [fix][i18n] Typo 'codes' -> 'codec', close #42 --- openfreebuds_qt/assets/i18n/en.ts | 2 +- openfreebuds_qt/assets/i18n/es.ts | 2 +- openfreebuds_qt/assets/i18n/pt-BR.ts | 2 +- openfreebuds_qt/assets/i18n/ru.ts | 2 +- openfreebuds_qt/designer/sound_quality.ui | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openfreebuds_qt/assets/i18n/en.ts b/openfreebuds_qt/assets/i18n/en.ts index d4ecddb..9202b0e 100644 --- a/openfreebuds_qt/assets/i18n/en.ts +++ b/openfreebuds_qt/assets/i18n/en.ts @@ -795,7 +795,7 @@ - Device will prefer audio codes with better compression, like AAC + Device will prefer audio codec with better compression, like AAC diff --git a/openfreebuds_qt/assets/i18n/es.ts b/openfreebuds_qt/assets/i18n/es.ts index 334871a..7316308 100644 --- a/openfreebuds_qt/assets/i18n/es.ts +++ b/openfreebuds_qt/assets/i18n/es.ts @@ -795,7 +795,7 @@ - Device will prefer audio codes with better compression, like AAC + Device will prefer audio codec with better compression, like AAC El dispositivo preferirá los códigos de audio con mejor compresión, como AAC diff --git a/openfreebuds_qt/assets/i18n/pt-BR.ts b/openfreebuds_qt/assets/i18n/pt-BR.ts index 32558fb..6fdd8aa 100644 --- a/openfreebuds_qt/assets/i18n/pt-BR.ts +++ b/openfreebuds_qt/assets/i18n/pt-BR.ts @@ -796,7 +796,7 @@ - Device will prefer audio codes with better compression, like AAC + Device will prefer audio codec with better compression, like AAC O dispositivo preferirá codificadores de áudio com melhor compactação, como AAC diff --git a/openfreebuds_qt/assets/i18n/ru.ts b/openfreebuds_qt/assets/i18n/ru.ts index f0e04b5..7a33145 100644 --- a/openfreebuds_qt/assets/i18n/ru.ts +++ b/openfreebuds_qt/assets/i18n/ru.ts @@ -796,7 +796,7 @@ - Device will prefer audio codes with better compression, like AAC + Device will prefer audio codec with better compression, like AAC Устройство будет предпочитать аудио-кодеки с лучшим сжатием, вроде AAC diff --git a/openfreebuds_qt/designer/sound_quality.ui b/openfreebuds_qt/designer/sound_quality.ui index 918e7b2..8b5bc14 100644 --- a/openfreebuds_qt/designer/sound_quality.ui +++ b/openfreebuds_qt/designer/sound_quality.ui @@ -57,7 +57,7 @@ - Device will prefer audio codes with better compression, like AAC + Device will prefer audio codec with better compression, like AAC true From 360fe339f94d8f9b3b73adf08f0195a7199288d5 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 7 Oct 2024 19:31:23 +0700 Subject: [PATCH 11/35] [i18n] sync translations --- openfreebuds_qt/assets/i18n/en.ts | 6 +++--- openfreebuds_qt/assets/i18n/es.ts | 6 +++--- openfreebuds_qt/assets/i18n/pt-BR.ts | 6 +++--- openfreebuds_qt/assets/i18n/ru.ts | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/openfreebuds_qt/assets/i18n/en.ts b/openfreebuds_qt/assets/i18n/en.ts index 9202b0e..5457b96 100644 --- a/openfreebuds_qt/assets/i18n/en.ts +++ b/openfreebuds_qt/assets/i18n/en.ts @@ -145,12 +145,12 @@ OfbQtApplication - + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. @@ -1043,7 +1043,7 @@ OfbTrayIcon - + OpenFreebuds: Connecting to device… diff --git a/openfreebuds_qt/assets/i18n/es.ts b/openfreebuds_qt/assets/i18n/es.ts index 7316308..a270c87 100644 --- a/openfreebuds_qt/assets/i18n/es.ts +++ b/openfreebuds_qt/assets/i18n/es.ts @@ -145,12 +145,12 @@ OfbQtApplication - + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. Está ejecutando una versión de Qt antigua. Te recomendamos cambiar a la versión de Flatpak, ya que una versión anterior de Qt puede afectar negativamente a la experiencia de uso de OpenFreebuds. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. Este aviso sólo se mostrará una vez. Por favor, prueba la versión de Flatpak antes de informar de errores. @@ -1043,7 +1043,7 @@ OfbTrayIcon - + OpenFreebuds: Connecting to device… OpenFreebuds: Conectando al dispositivo… diff --git a/openfreebuds_qt/assets/i18n/pt-BR.ts b/openfreebuds_qt/assets/i18n/pt-BR.ts index 6fdd8aa..5eab898 100644 --- a/openfreebuds_qt/assets/i18n/pt-BR.ts +++ b/openfreebuds_qt/assets/i18n/pt-BR.ts @@ -146,12 +146,12 @@ OfbQtApplication - + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. Você está executando uma versão mais antiga do Qt do que o esperado. É altamente recomendável mudar para a versão Flatpak, porque a versão mais antiga do Qt pode prejudicar sua experiência de uso do OpenFreebuds. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. Este aviso será mostrado apenas uma vez. Por favor, teste a versão Flatpak antes de relatar bugs. @@ -1044,7 +1044,7 @@ OfbTrayIcon - + OpenFreebuds: Connecting to device… OpenFreebuds: Conectando ao dispositivo… diff --git a/openfreebuds_qt/assets/i18n/ru.ts b/openfreebuds_qt/assets/i18n/ru.ts index 7a33145..e43e8e4 100644 --- a/openfreebuds_qt/assets/i18n/ru.ts +++ b/openfreebuds_qt/assets/i18n/ru.ts @@ -146,12 +146,12 @@ OfbQtApplication - + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. Вы используете более старую версию Qt, чем ожидалось. Рекомендуем перейти на Flatpak-версию программы, поскольку более старые версии Qt, предоставляемые вашей системой, могут пагубно повлиять на работу программы. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. Это предупреждение будет показано лишь один раз. Перед тем, как сообщать о проблемах, протестируйте Flatpak версию. @@ -1044,7 +1044,7 @@ OfbTrayIcon - + OpenFreebuds: Connecting to device… OpenFreebuds: Подключение… From fe073f6b2b4618aa5f2a39315efaea7aaff41ee6 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 7 Oct 2024 19:32:45 +0700 Subject: [PATCH 12/35] [fix] debian scripts --- debian/rules | 2 +- debian/source/options | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index dc827b9..058aae8 100755 --- a/debian/rules +++ b/debian/rules @@ -10,5 +10,5 @@ override_dh_auto_install: python ./scripts/make.py install debian/openfreebuds/usr debian/openfreebuds/usr/lib/python3/dist-packages override_dh_clean: - git clean -Xfd + git clean -xfd -e accent.json diff --git a/debian/source/options b/debian/source/options index 80ab2ac..b7d44b1 100644 --- a/debian/source/options +++ b/debian/source/options @@ -1,2 +1,4 @@ tar-ignore = "*venv" tar-ignore = ".git" +tar-ignore = "accent.json" + From fe38a99138f215eb74ae3324ee5d461029f8aa20 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 8 Oct 2024 21:01:04 +0700 Subject: [PATCH 13/35] [Feature] Add ANC & batter to main window --- .../app/helper/device_control_view_helper.py | 114 +++ openfreebuds_qt/app/main.py | 4 + .../assets/icon/action/settings.png | Bin 1306 -> 9781 bytes .../assets/icon/main_window/anc_awr.png | Bin 0 -> 3305 bytes .../assets/icon/main_window/anc_off.png | Bin 0 -> 3897 bytes .../assets/icon/main_window/anc_on.png | Bin 0 -> 3767 bytes .../assets/icon/main_window/batt_c.png | Bin 0 -> 634 bytes .../assets/icon/main_window/batt_l.png | Bin 0 -> 1359 bytes .../assets/icon/main_window/batt_r.png | Bin 0 -> 1884 bytes openfreebuds_qt/designer/main_window.ui | 943 ++++++++++++++---- openfreebuds_qt/qt_i18n.py | 10 + openfreebuds_qt/tray/menu_anc_level.py | 9 +- openfreebuds_qt/utils/icon/qt_icon.py | 5 +- scripts/flatpak_install.sh | 2 +- 14 files changed, 902 insertions(+), 185 deletions(-) create mode 100644 openfreebuds_qt/app/helper/device_control_view_helper.py create mode 100644 openfreebuds_qt/assets/icon/main_window/anc_awr.png create mode 100644 openfreebuds_qt/assets/icon/main_window/anc_off.png create mode 100644 openfreebuds_qt/assets/icon/main_window/anc_on.png create mode 100644 openfreebuds_qt/assets/icon/main_window/batt_c.png create mode 100644 openfreebuds_qt/assets/icon/main_window/batt_l.png create mode 100644 openfreebuds_qt/assets/icon/main_window/batt_r.png diff --git a/openfreebuds_qt/app/helper/device_control_view_helper.py b/openfreebuds_qt/app/helper/device_control_view_helper.py new file mode 100644 index 0000000..5aee6b9 --- /dev/null +++ b/openfreebuds_qt/app/helper/device_control_view_helper.py @@ -0,0 +1,114 @@ +import asyncio +from typing import Optional + +from PyQt6.QtCore import pyqtSlot +from PyQt6.QtGui import QIcon +from PyQt6.QtWidgets import QApplication +from qasync import asyncSlot + +from openfreebuds import OfbEventKind, IOpenFreebuds +from openfreebuds.utils import reverse_dict +from openfreebuds_qt.designer.main_window import Ui_OfbMainWindowDesign +from openfreebuds_qt.generic import IOfbQtApplication +from openfreebuds_qt.qt_i18n import get_anc_level_names +from openfreebuds_qt.utils import OfbCoreEvent, blocked_signals, get_img_colored + + +class OfbQtDeviceControlViewHelper: + def __init__(self, ctx: IOfbQtApplication, ui: Ui_OfbMainWindowDesign): + self.ui = ui + self.ctx = ctx + self.ofb = ctx.ofb + self._task: Optional[asyncio.Task] = None + + self.anc_level_option_names = get_anc_level_names() + + text_color = ctx.palette().text().color().getRgb() + icon_batt_l = get_img_colored("batt_l", text_color, "icon/main_window", (16, 16)) + icon_batt_r = get_img_colored("batt_r", text_color, "icon/main_window", (16, 16)) + icon_batt_c = get_img_colored("batt_c", text_color, "icon/main_window", (16, 16)) + + self.ui.anc_awr.setIcon(QIcon(get_img_colored("anc_awr", text_color, "icon/main_window"))) + self.ui.anc_off.setIcon(QIcon(get_img_colored("anc_off", text_color, "icon/main_window"))) + self.ui.anc_on.setIcon(QIcon(get_img_colored("anc_on", text_color, "icon/main_window"))) + + self.ui.batt_l_icon.setPixmap(icon_batt_l) + self.ui.batt_r_icon.setPixmap(icon_batt_r) + self.ui.batt_c_icon.setPixmap(icon_batt_c) + self.ui.anc_on.clicked.connect(self.set_anc_on) + self.ui.anc_off.clicked.connect(self.set_anc_off) + self.ui.anc_awr.clicked.connect(self.set_anc_awr) + self.ui.anc_level.currentTextChanged.connect(self.set_anc_level) + self.ui.control_root.setVisible(False) + + def set_anc_level(self, value): + code = reverse_dict(self.anc_level_option_names).get(value) + self._task = asyncio.create_task( + self.ofb.set_property("anc", "level", code) + ) + + def set_anc_off(self): + self._task = asyncio.create_task( + self.ofb.set_property("anc", "mode", "normal") + ) + + def set_anc_on(self): + self._task = asyncio.create_task( + self.ofb.set_property("anc", "mode", "cancellation") + ) + + def set_anc_awr(self): + self._task = asyncio.create_task( + self.ofb.set_property("anc", "mode", "awareness") + ) + + async def update_ui(self, event: OfbCoreEvent): + force_render = False + + if event.kind_match(OfbEventKind.STATE_CHANGED): + state = await self.ofb.get_state() + visible = state == IOpenFreebuds.STATE_CONNECTED + self.ui.control_root.setVisible(visible) + force_render = True + + title = "OpenFreebuds" + if visible: + title, _ = await self.ofb.get_device_tags() + elif state == IOpenFreebuds.STATE_WAIT: + title = QApplication.translate("OfbQtDeviceControlViewHelper", "Connecting…") + self.ui.device_name_view.setText(title) + + if event.is_changed("battery", "") or force_render: + battery = await self.ofb.get_property("battery") + if battery is not None: + await self._update_battery(battery) + + if event.is_changed("anc") or force_render: + anc = await self.ofb.get_property("anc") + self.ui.anc_root.setVisible(anc is not None) + if anc is not None: + await self._update_anc(anc) + + async def _update_anc(self, anc: dict): + mode = anc["mode"] + self.ui.anc_on.setChecked(mode == "cancellation") + self.ui.anc_awr.setChecked(mode == "awareness") + self.ui.anc_off.setChecked(mode == "normal") + + level = anc.get("level", None) + self.ui.anc_level.setVisible(level is not None) + if level is not None: + options = anc["level_options"].split(",") + with blocked_signals(self.ui.anc_level): + self.ui.anc_level.clear() + for opt_code in options: + self.ui.anc_level.addItem(self.anc_level_option_names.get(opt_code, opt_code)) + self.ui.anc_level.setCurrentText(self.anc_level_option_names.get(level, level)) + + async def _update_battery(self, battery: dict): + is_tws = "case" in battery + self.ui.batt_l_root.setVisible(is_tws) + self.ui.batt_r_root.setVisible(is_tws) + self.ui.batt_l_text.setText(f'{battery.get("left", "--")}%') + self.ui.batt_r_text.setText(f'{battery.get("right", "--")}%') + self.ui.batt_c_text.setText(f'{battery.get("case" if is_tws else "global", "--")}%') diff --git a/openfreebuds_qt/app/main.py b/openfreebuds_qt/app/main.py index 4bd7b88..9b61d03 100644 --- a/openfreebuds_qt/app/main.py +++ b/openfreebuds_qt/app/main.py @@ -13,6 +13,7 @@ from openfreebuds_qt.app.dialog.manual_connect import OfbQtManualConnectDialog from openfreebuds_qt.app.dialog.rpc_config import OfbQtRpcConfig from openfreebuds_qt.app.helper import OfbQtSettingsTabHelper +from openfreebuds_qt.app.helper.device_control_view_helper import OfbQtDeviceControlViewHelper from openfreebuds_qt.app.helper.update_widget_helper import OfbQtUpdateWidgetHelper from openfreebuds_qt.app.module import OfbQtAboutModule, OfbQtSoundQualityModule, OfbQtLinuxExtrasModule, \ OfbQtHotkeysModule, OfbQtGesturesModule, OfbQtDualConnectModule, OfbQtDeviceOtherSettingsModule, \ @@ -53,6 +54,7 @@ def __init__(self, ctx: IOfbQtApplication): # Helpers self.tabs = OfbQtSettingsTabHelper(self.tabs_list_content, self.body_content) self.update_view = OfbQtUpdateWidgetHelper(self.updater_root, self.updater_header, self.ctx) + self.control_view = OfbQtDeviceControlViewHelper(self.ctx, self) # Asyncio & update loop staff self._ui_update_task: Optional[asyncio.Task] = None @@ -171,6 +173,8 @@ async def _update_ui(self, event: OfbCoreEvent): visible = await self.ofb.get_state() == IOpenFreebuds.STATE_CONNECTED self._device_section_set_visible(visible) + await self.control_view.update_ui(event) + for mod in self._ui_modules: try: await mod.update_ui(event) diff --git a/openfreebuds_qt/assets/icon/action/settings.png b/openfreebuds_qt/assets/icon/action/settings.png index 321c4ec5e561f8923cb0ce169476154bfae3bd69..c6e70e59f2484264b71ef3a7ce30326b120fafa9 100644 GIT binary patch literal 9781 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 delta 1278 zcmVMvP2)o;X4*6gyb%U{*3z;%VZjqH2`y&$_H|-r}rQYOHln z{=#rxTUq8h%@HKAh$V;+AftvdDzFfvRU^ejn)VYO{$a;2l1nC68H^kYsDDC*7u)_A1A@Civu@kp$F|)(0Rqp!mDcuG8^G)*>Gif2Jp%f+fs5<5 zChq~4JHXJBE*X*|1!(#U1>pURz9|O`+yXsoZf~u9oIU^<>MC^u92^4UMao|Hcz39? zw|~#H`uhQaVsflLgJr(}01yBMVoOIv0RI600RN!9r<0R~As>GT9t;y2BjOpZ0009Q zNklm=8?#+4cxkmv&7-0RufQtbz z00zJS7{DOwZ&AN1CEc6aeI3kdOs-aUn-TN>+5T()oRqNN~J;< z7Z;RBB*N@ZxP#x`-ll4`N}uWF<%KpkH{}D6$z-TruhUmltJP?IeO(>^x~|jH(-VD1 zrBaDhRh0ukHk+kxl*wd%wO>lmZZMflVrglq=P&Kl)D(Y4M@PT?+}+(Bgb-c5^W@|N zJ3BkQg+X1{F+V>aS*VQ1%Ozmj_Wxsq_V)I=`Us}e zX?eong8u8uv9#Bg?W{a{i`i$^)QWE;k02%jG(@vn;F8&hxy!01%iJ zUR_lXb1psc6Nrhx3|c??mto=FflRF_+Km*KP_V` z6bgU1zrV-$_&5MyZf-8p@b)A10Yq{i8wAdP=Ck=&ECx-}P$(2SE`eAq*0_#(3H(@n zK(q0&EURO?d_LcDv@LBIv@DA>O=}5*`Fx(<-{0j7gQ}{MZQCto$M=15U6)+f{m=+W zXN6~GX4)WpB*$^&v%<|YVRv_zN~KcUz&$?C>!)eodwsz2@-hs=fTAe4xw*m7(NSN9 oL9P$rlo$hG01SWuFo40;Khp)#e{FW`0RR9107*qoM6N<$g4VlDy#N3J diff --git a/openfreebuds_qt/assets/icon/main_window/anc_awr.png b/openfreebuds_qt/assets/icon/main_window/anc_awr.png new file mode 100644 index 0000000000000000000000000000000000000000..6d110e43d10d417c11abef4515440d21909761e7 GIT binary patch literal 3305 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 n%@ky8|S-@fSgA}O7 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 literal 0 HcmV?d00001 diff --git a/openfreebuds_qt/assets/icon/main_window/anc_on.png b/openfreebuds_qt/assets/icon/main_window/anc_on.png new file mode 100644 index 0000000000000000000000000000000000000000..32768df0584a9b59ab0356a493237c287750e9db GIT binary patch literal 3767 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 literal 0 HcmV?d00001 diff --git a/openfreebuds_qt/assets/icon/main_window/batt_c.png b/openfreebuds_qt/assets/icon/main_window/batt_c.png new file mode 100644 index 0000000000000000000000000000000000000000..ef22e96ce73ade02c6915f50130aadf862af9f56 GIT binary patch literal 634 zcmV-=0)_pFP)EX>4Tx04R}tkv&MmKpe$iQ>CR;9PA+C5U@JgMMcD^R-p(LLaorMgUO{|(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwX9p)m7b)?7X`w}o2gm(*ckglc4iFj@rdd64K+|nA zolJHJF^dtIS;m|srQkWf?&0I>U5saWpZjz4sd9ukimrqa%~VP)VN-?EX>4Tx04R}tkv&MmKpe$iQ>CR;9PA+C5U@JgMMcD^R-p(LLaorMgUO{|(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwX9p)m7b)?7X`w}o2gm(*ckglc4iFj@rdd64K+|nA zolJHJF^dtIS;m|srQkWf?&0I>U5saWpZjz4sdeSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00SpUL_t(|+U=V$i{d~O$Nx82L{dl!C2b1#6D$auLO(^i z5HR1xBHtl`*acIhuolHE2rF2Jg|tzNC>TuIw|nm1$*Fs8CO=4#nI`X-OlF38 z?;8NZ6){Z{s;WX!6v(m+Ns=InA_PG|sZ;_0WLbtZO_3xC;y6YWMF_(XzVE}fZCo3= z^krEVO{Y^@tyXj{tJR98( zXhi${o<58Heov#(h;&{5zXCkZqs?YR|BKCLL!Re-zJN}rLqQPGtq=qObvm8T5#YKm z?RGo5HFmolxvqOX0j*YxCX)%>I+MwSTCLV)1sH}w^ZA^<3iJ7#48ypL0K+h7u~^Vo zW3gDAD&fzc{a1SP?|lg;42O?9zrvV5<)83R0$kUnyzrOV;r)637N@#%rpQ066OJ3t zUF0~9^Vgq`@B5#=SbFa{*za3I&+|~P*O52s^*TJyJ8FL;K-YCNn@!}EX0r)h*FP4} zZnsgXRB|hzQmLTbZoe-;(=;?14dk6hqXA9Rjs+NofpWQ=djaKg8HQmT3%I|(2QR#i zl>vZdS*X=&+z6=EYOpNpmw<l{*2|Y89qwf~l$s zUQtyQOi>hW1t^LFCd)Fn0%TbRlO%~-0g@zviK57@08tdd1VP|dfFKB9FGtioQYw|e zz&R|-GH!#7 literal 0 HcmV?d00001 diff --git a/openfreebuds_qt/assets/icon/main_window/batt_r.png b/openfreebuds_qt/assets/icon/main_window/batt_r.png new file mode 100644 index 0000000000000000000000000000000000000000..d78bf08924aa23e3a4ceee8377797ee267d2b209 GIT binary patch literal 1884 zcmV-i2c!6jP)EX>4Tx04R}tkv&MmKpe$iQ>CR;9PA+C5U@JgMMcD^R-p(LLaorMgUO{|(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwX9p)m7b)?7X`w}o2gm(*ckglc4iFj@rdd64K+|nA zolJHJF^dtIS;m|srQkWf?&0I>U5saWpZjz4sdeSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00lHjL_t(|+U;AxOJZFV|M^BJab_W*84bZJW^mJ{UAfUk z#6T@(oB?p3nW|nUtqX~M-fSRd+%J~)_ z^);nE>s{^H=MU~eJnx=&fA`#T?m6#X4FEz7;dDA-wOYaRJj`Y@3L1$Y!%hCXKx(9X^dc|0Df z#k(>iz~OMv*47rq<8i7L;_*0bZEcam;ZP3&V`F0!3WX?_%Tc|M%jGB(3eniu*y9oq z3l}b@C7_2`5b8~YfiXy26qA1eb++2MK2m}Ijetu4B!}<9+1po72$sNihQ2+f1g+dSRyp{x9 zw(ae0#UzA6q4ESc91cY(Z9NDunM`zgda5{;zFtq@E**g&2+-+tP%{#V1lHHr74@Oh z=^zNgZ6~O-l2eTZ++4?{Qi*c89337W(#pz8RaWrwMI#Q(9WHQ0y@%;M^05C8xpb8W3@9)RN#DuI|DwR^K z>vGH-fQlQ0dPAepz;3t8%AK8^Db{s4X3pt!!e}(A#?iI4wNx>~=fW*Vi#UJuUmpdS%+Jp= zIevFl6BI>J6;{A;9NxWqCoA{q(TLpiJ|A{>cb~j3*xlWQ&*!^M001m6FC(2!HtZQUa$9dY3^oI(>Y+4=ZVXWwd^Mrzu%8WNR#iA_WJ$) z%EehUkI6L;N;Z$mqIp=ld0f7EVFJx7GtEm=&1-XLUYu-Ro&8^3o-TV~f+f^`ef}RX W(i!T>OpenFreebuds - #side_panel, #tabs_list_content { + #side_panel, #side_panel_content, #tabs_list_content { background: palette(base); border: none; } @@ -49,202 +49,793 @@ 0 - + + + + 0 + 0 + + - 220 + 210 0 - 220 + 210 16777215 - - - 0 - - - 0 + + true + + + + + 0 + 0 + 208 + 598 + - - 0 + + + 0 + 0 + - - 0 + + + 0 + 0 + - - 0 + + + 16777215 + 16777215 + - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - false - - - true - - - - - 0 - 0 - 218 - 444 - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + - - false + + + 0 + 24 + + + + 16 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 24 + + + + + 16777215 + 24 + + + + + true + + + + OpenFreebuds + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 24 + 24 + + + + + 24 + 24 + + + + QPushButton::menu-indicator{width:0px;} + + + + + + + 24 + 24 + + + + true + + + + - - - - - - - - - - - - New version available, click here to view - - - true - - - - - - - - true - - - - OpenFreebuds v99 - - - - - - - - 0 - 0 - - - - - 60 - 16777215 - - - - Hide - - - true - - - - - - - Update now - - - - - - - - - - - 0 - 0 - - - - - 0 + + + + + + 0 + 0 + - - 8 + + + 0 + 0 + - - 8 + + + 0 + + + 9 + + + 0 + + + 9 + + + 9 + + + + + + 4 + + + 0 + + + 8 + + + 0 + + + 8 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + 16 + 16 + + + + + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 32 + 0 + + + + + 16777215 + 16777215 + + + + + 8 + + + + Left headphone + + + 10% + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + 16 + 16777215 + + + + + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 32 + 0 + + + + + 16777215 + 16777215 + + + + + 8 + + + + Right headphone + + + 10% + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + 16 + 16 + + + + + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 32 + 0 + + + + + 16777215 + 16777215 + + + + + 8 + + + + Battery level + + + 10% + + + Qt::AlignCenter + + + + + + + + + + + + + + 0 + 48 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Cancellation + + + + + + + 48 + 48 + + + + true + + + true + + + + + + + + 0 + 0 + + + + Disable noise control + + + + + + + 48 + 48 + + + + true + + + true + + + + + + + + 0 + 0 + + + + Awareness + + + + + + + 48 + 48 + + + + true + + + true + + + + + + + + + + + 16777215 + 16777215 + + + + true + + + + + + + + + + + + + Qt::Vertical - - 8 + + + 20 + 40 + - - 8 + + + + + + - - - - - 0 - 32 - - - - - 16777215 - 32 - - - - QPushButton::menu-indicator{width:0px;} - - - - - - - 24 - 24 - - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + + + + New version available, click here to view + + + true + + + + + + + + true + + + + OpenFreebuds v99 + + + + + + + + 0 + 0 + + + + + 60 + 16777215 + + + + Hide + + + true + + + + + + + Update now + + + + + + + + @@ -263,7 +854,7 @@ 0 0 - 728 + 738 598 diff --git a/openfreebuds_qt/qt_i18n.py b/openfreebuds_qt/qt_i18n.py index 2acaa9b..25da1cf 100644 --- a/openfreebuds_qt/qt_i18n.py +++ b/openfreebuds_qt/qt_i18n.py @@ -1,6 +1,16 @@ from PyQt6.QtWidgets import QApplication +def get_anc_level_names(): + return { + "comfort": QApplication.translate("OfbDeviceAncLevelTrayMenu", "Comfortable"), + "normal": QApplication.translate("OfbDeviceAncLevelTrayMenu", "Normal"), + "ultra": QApplication.translate("OfbDeviceAncLevelTrayMenu", "Ultra"), + "dynamic": QApplication.translate("OfbDeviceAncLevelTrayMenu", "Dynamic"), + "voice_boost": QApplication.translate("OfbDeviceAncLevelTrayMenu", "Voice boost"), + } + + def get_eq_preset_names(): return { "equalizer_preset_default": QApplication.translate("EqPresetName", "Default"), diff --git a/openfreebuds_qt/tray/menu_anc_level.py b/openfreebuds_qt/tray/menu_anc_level.py index e1de5bd..550c6d0 100644 --- a/openfreebuds_qt/tray/menu_anc_level.py +++ b/openfreebuds_qt/tray/menu_anc_level.py @@ -3,6 +3,7 @@ from qasync import asyncSlot from openfreebuds import IOpenFreebuds +from openfreebuds_qt.qt_i18n import get_anc_level_names from openfreebuds_qt.tray.menu_generic import OfbQtTrayMenuCommon @@ -10,13 +11,7 @@ class OfbDeviceAncLevelTrayMenu(OfbQtTrayMenuCommon): def __init__(self, parent: QWidget, ofb: IOpenFreebuds): super().__init__(parent, ofb) - self.anc_level_option_names = { - "comfort": self.tr("Comfortable"), - "normal": self.tr("Normal"), - "ultra": self.tr("Ultra"), - "dynamic": self.tr("Dynamic"), - "voice_boost": self.tr("Voice boost"), - } + self.anc_level_option_names = get_anc_level_names() self.setTitle(self.tr("Intensity…")) self.anc_level_actions: dict[str, QAction] = {} diff --git a/openfreebuds_qt/utils/icon/qt_icon.py b/openfreebuds_qt/utils/icon/qt_icon.py index 1a9e91f..7b042d5 100644 --- a/openfreebuds_qt/utils/icon/qt_icon.py +++ b/openfreebuds_qt/utils/icon/qt_icon.py @@ -8,10 +8,13 @@ @functools.cache -def get_img_colored(name: str, color, base_dir: str = "icon/action") -> QPixmap: +def get_img_colored(name: str, color, base_dir: str = "icon/action", scale_to = None) -> QPixmap: image = Image.open(ASSETS_PATH / f"{base_dir}/{name}.png") image = image_combine_mask(image, foreground=Image.new("RGBA", image.size, color=color), background=Image.new("RGBA", image.size, color="#00000000")) + if scale_to: + image.thumbnail(scale_to) + return ImageQt.toqpixmap(image) diff --git a/scripts/flatpak_install.sh b/scripts/flatpak_install.sh index 475eed4..d2ba8e7 100755 --- a/scripts/flatpak_install.sh +++ b/scripts/flatpak_install.sh @@ -3,7 +3,7 @@ cd "$(dirname "$0")"/.. # Compile wheel -./scripts/install.py build +./scripts/make.py build # Build & install flatpak cd scripts/build_flatpak From 5b6713e38da77358d13bd80e8fc03cf9c75f136f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Guti=C3=A9rrez?= Date: Tue, 8 Oct 2024 16:07:25 +0200 Subject: [PATCH 14/35] [i18n] Fix ES translation issues (#44) --- openfreebuds_qt/assets/i18n/es.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openfreebuds_qt/assets/i18n/es.ts b/openfreebuds_qt/assets/i18n/es.ts index a270c87..937c92b 100644 --- a/openfreebuds_qt/assets/i18n/es.ts +++ b/openfreebuds_qt/assets/i18n/es.ts @@ -796,7 +796,7 @@ Device will prefer audio codec with better compression, like AAC - El dispositivo preferirá los códigos de audio con mejor compresión, como AAC + El dispositivo preferirá los codecs de audio con mejor compresión, como AAC @@ -952,7 +952,7 @@ Looking for UI theme settings? Now it follows system-wide configuration. - ¿Buscas la configuración del tema de IU? Ahora sigue la configuración del sistema. + Configuración de la UI y el tema. @@ -1002,7 +1002,7 @@ Tray applet - Widget de bandeja + Widget de la bandeja From b1d5727814bc238fde0e5758f606956fa364dba1 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Wed, 9 Oct 2024 19:26:56 +0700 Subject: [PATCH 15/35] [DevTools] Fix make.py build in win32 --- scripts/make.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/make.py b/scripts/make.py index 8379530..64d2520 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -54,7 +54,7 @@ DO_BUILD = "build" in TASK # Ensure environment -if sys.platform == "win32" and TASK != "build": +if sys.platform == "win32" and DO_INSTALL: print("-- Can't install under Windows, use pyinstaller") raise SystemExit(1) if os.environ.get("VIRTUAL_ENV", None) is not None: @@ -69,7 +69,7 @@ PYTHON_LIBS_DIR.mkdir(exist_ok=True, parents=True) break -if PYTHON_LIBS_DIR is None: +if PYTHON_LIBS_DIR is None and sys.platform != "win32": print("-- Error: Can't find python packages location, provide them manually") raise SystemExit(1) @@ -82,7 +82,7 @@ # Compile Qt Designer layouts print("Compile Qt Designer files") DESIGNER_DIR = PROJECT_ROOT / "openfreebuds_qt" / "designer" - result = subprocess.run(["env", "poetry", "run", "pyuic6", DESIGNER_DIR]) + result = subprocess.run(["poetry", "run", "pyuic6", DESIGNER_DIR]) if result.returncode != 0: print("Failed, old pyuic? Will try single file mode...") for ui_file in DESIGNER_DIR.iterdir(): @@ -99,7 +99,7 @@ # Compile Qt translations L_RELEASE_EXEC = None for option in QT_L_RELEASE_PATH_OPTIONS: - if Path(option).exists(): + if shutil.which(option) is not None: L_RELEASE_EXEC = option break From fbb3ba91a92f84f91f705ca9c81586f3d0803ce2 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 13 Oct 2024 13:40:58 +0700 Subject: [PATCH 16/35] [Feature] Add 6i equalizer presets (#43) --- docs/research.ods | Bin 46197 -> 51385 bytes .../driver/huawei/handler/config_equalizer.py | 149 ++++++++++++------ openfreebuds/driver/huawei/package.py | 12 +- openfreebuds_qt/app/module/sound_quality.py | 24 ++- openfreebuds_qt/qt_i18n.py | 2 + scripts/debug/huawei_pkg_parse.py | 17 ++ 6 files changed, 150 insertions(+), 54 deletions(-) create mode 100644 scripts/debug/huawei_pkg_parse.py diff --git a/docs/research.ods b/docs/research.ods index b9bfcf8947ab5b38ebd94be452f926afdde0b53c..d976dc867ed9e212363fea4ad12f49b2c2b24cd4 100644 GIT binary patch literal 51385 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%0DeXu==dv*+y9r6wc! zwf{DgnIAt$AaJPht&V74`PQIP_qP|6 zuhHN%?pzU$K=0K4DF1J%bHxz>g^wh+h*IqUg;&OAhf_jpAnhVPML3U+1JyN;$;;nE zSXW zxuy9>=Z;1?01EClzFr~@75%35b5N|%_0A2c3l}-v@Sxsx1oBaLNo3U@l3?R}GanJB zQs1q$HuLEPa;<^Ku}dPPKZ?0aKxE>|FBkp7KzWi*-+f%6Wzn+AbHQupdpm7LHR>j@&PoMLRg4+ zXRvM};PI-YD~j9@)qz5^YS8K3hc2l8(HIQv*S=KH%>BubriPr>fYVm4b3{|4V<9cK zj`E&9+tFV}mj%(UUHfupJcE3L>|pR`^xEqqz1uE8a^2D=d4T`%sg7g*l!x3yc}KU-Vp*3_+<>6x0I?qhwPr`5%_ z(Obqt$PvY|PlsYlaQPEuzX3y@KM)kg1ago`AmCUNw^XQiD@crzzj(ACNlB}bKOD@Z zot4P!GZxE^9@u_WbPkE_^XyR87^Ba@77uL4i!ua*SjE;vS9nwI?iffi$R6q*E{5JL z6b(ZHNX##S_Ti$Q!5n%&Mt%tq8+Wq&(0(q0oq*+*q3nPmAwbg!Fk*L8Q(Kx@UB`R( z$Hb3r-g+0-KC36`>%U%Y3lIrq-+1}I_jDr1mXEkNA4&?q3qMQTCflpFZLc<1ysu`s zY~b4}V4x5~;QUBg{&XL&*W9jC4n-SHfh)BA_*5}qc!Xg2^>K9LC+DKvR4##GZ*q=b z_I!?qs?9p{-q8smaAbSZR(NM{2|=9CQ;-ARDI3xki!;@K<}0GC6588=de@$2W(>f; z6Kfq5lKyk+N_4SZ!E#|F22JtQ`+EHI_Z37;*{~sad~%BNz@i!El*JzHMqX%m$^Ut6 zhHGj|usO-Y{!urpzIak|HW#9aF{1pY|I z*z(@?e{A;GT-k!%Pb+*zOOThCt=q8O8BOsV&MQd4Tjvr+ar2|-Ev2uOymo8oJIM;6 zr9NykU99=!W3xeQMTbOi?%$C-VA=asQUQLUdRc9o*eUeUl!Q-1Np~`n$KJqpkq*GRj)ArXmB=v$<$ZJY+2J$Tc%%$_aP*${v*ZO ziUTW9wdSHv$Y7XJiRNl^_`7)VGf%_P)F0xOrJ^#kg&n5j0)j72LWLQP;cC%OeWyvT zpWPMt$$_`QT%HjSTrOTlXJlaR{>yQGuytWZD>M-s_?zm%twkL;fWxi1+}xl^Eidtr zWzv8#S!a^?u)Xg~nVxQS($<1ALNV!x{=SU2SRJ~m4+%okg%NFkvh9To=V%RC27RPs50By}V*cYx5 zU0;LCv2(3=BObq)cgJfB`Kr=@#4tp6z5QdWmS949+78CU#bAGNXTDwx)&0drKPHFY z9Ku;jWB8B!y(4hBzvea;lUb(HD1^`#@2cSxn3U^XMBu95-~RqqS%5?M)|yO!()C86 zQ|oQcb>%0uWV-}HlV|D@{Bd_RlBPAksH=RLlU8zDV`ocQ9Sh_diw&c+QSvcIRMLBh zl9lb{{G6r&x6jikm)SGa#(e_G8<mT7zZ`C)`fOZM-_-RbH8t+3}hQPoXGzhDMbN+^h=*D$rSFWV#-nLtDk_^jV zl{XvR_u@4lVBhxWn4k8cbJkPHc)Fi2ok%nrgNxSa$u=v&bq%QD4mmQZLDpkHXv$tx zSodQ6^0_7ZlidDdM8-9Ej>$`o;TT|ID{9>Yvu4WmOhED+b7H7)?2~0T7(FyO^QN7c zA}A&oZ|Lve=|z~iii)xR?ti?Xptvry+qjAR?(UbQ$%69rM+3^9^3cJnN!!w|vQJ@r z9QdP(48ZZcX_@1&=%eYnV*0=GVyFB#)_Tj=7AEHUzpJS>9L0Mc#+&<(% z?eEzUxX#?7ns%=CtB4etp2nN>vrIT^_OwijqyP*^aBAW~Iqn+_d4*nLkoZ_}QR*)v zfpwe%v1iQN4O`Ur@1ghb<3Ju)^xrBG&X$A`SlySywZ9?$VDTgjoAcUOEG{wH+cUvJ zKT9_pLD8}d#)q_)SkQSpqId@OAEyy|@y@VLwQb|q8*Qf*0 zp~$XAPS-lz+vkn(0XZ$KE7tc5Q0MZ5J0aw8FUL{!m-=Ty0@xUeBu-IN|UuJO2 z;z{m^Z#_D5GL*A5Xs?O8^>BnHL_Wi%O6jM0PC6fi8%87CmI{J%=7>XHHk)+W9qbsr z$UAp?X8Lfuw6tW|^OQvcE-dUE$Z?gu?cR}&RIIgLC>2&i zH4C!4F2jY9WiHlLb^*3XoFohjqDJej$&?2dW1M;T`fQyh6Q zAM2}_h8>AgdH2cXK)1AD8zrb?uT|edmU-ShTlDXH~_%6hkE1U=( z#x!T^{EKZ*Vyg0P8aralT9f%s?QyI3B(~W&mvvEWH7Bg}ejF&dk707MPh#y^TErn6 zYRY>wa^e~s`#*bYUMnza=;zdO>GAX_&2rPWS!Ujqf7_H#Rvr@*f1Vt>~Zns9ovYI`T(4)aj_%}jsx8? zI25|#e9i7fSrI9I=5KyOdI}7ax$wIXPDA)Aca8w;r2LXns-y0f^Y)^tYfcif5T!l9 zG>W%PoBl6&4{=j1>znq#i)E)Z^ip7X&n_({`@)84?-ef4|HpV`j2YL_<^ClE*<2Hs zH+jn714vi-rQ{SDrFiL*3$1AiWhzTD<-?X0zF zCqB^kL8m@dqvNu%OPO{LqAIUo{ySZu+@EcK^dj(q+;7IhjrBpq3f?=2L8(fqTn_Qf zE6I#<92WmVr&2T>>YZNwX(R`n>A~iii6sY(r5DRyW_$dh?F0^XB5w#LK}LMIc$Q5A z#ikY-)3wT9|FjQ}v?m_tp@k0=cH6t%1rvd`!Dr&JU3JSO{WQaa;5P@`I13OJdpQeK z5H5t<SSG`8dcH$&a{twFGx5+vyg zfu^BIvtD;D@lVB6CJ1yk9(&eww+BAD+X4%jwlj#Wak7lXo_)RkU9HKsgi{rtvBWoe zW1*GxF9?*g76LNP-T+Sn7NHdVvyMot)9@T}vVk%eE6e7PR7i3E+bW zA(0=ySm!v`-`J854*dnt`?rZc7TZ*R+Is=1{v3O%I-*-6t|*5Qo2*4R-YPapUQ_ut z?%g+z`(stix53k9&)$DyV`U#M4*zrEYa>BotbBum>1kx)zk&OW_Y^_}_-=gDsc)CZ zg6}Vq+=B-mE^cYXi~?{1+kag_O2()2!}Fvk?ofVO5hJOccw4b*8|~HBm}Klje9qoj zQJh@@{ki_dB0Dbi$7-vqEApLXfMV-aqR$~EHNK}XiKEr~{<&;+O=Xo#t{=Hq&$giV zC=VCf$fBa70Ftj8ItSS~gvO$I+7RmfEc$SvLGaey10A(DkZOuc>!6_qP@-GOtdmE7 z^KUqkG~31gSm&lCK{2BiPda0VcTb^!@_+5#Kf#w&1)#g3o6ZU)@@UXv$wy%k^2 zYAH?ry1sRYt&F;a48OG+ez;H#9j(KBdDB8#QNTR3IS?uey}x|EfRvM7LikIM$cG*; zR{SPxuk^6PsZ{tOHrEoq=64NA|GlW0&}t4ypHQ?!U*b=dJX2sIGeV4=PIrW3F$F80FSt{H?-7RRoq0_sYA zsb|TX3&+WZxK#*hZaa3JGVL4StI3VjD@gTrbvcw+(oS8$IWfnn z0*kwQ89szqkpAhB7clBu(8>$6w16`a(<*+kYi>VYaF;T?Edyl5SSC>qBqW4zL8PWD zEYmgfMkun4!M-PIJyOCMdel3=l+6fnz#gkpQRZ85cAB+UT~+YxZ5_rdZg;B6i=qk& zV`th!sI^Es#YuZ1pfeh2TD+t8%LCg57a@C)eo$rovIwCH7&|*5#~ZK;X5j>!xs7ryLe#MvkVKn^o9%yNlYL{+DB|CNy&xHP2a~5`24x2?9Q$h|sZv0x==EcW9H8n{EhsI^e^)MTN`+3JX^w6 zwW1sd`>Tc0A9EdNQ|mkQ`kWX(xNa@bHTV5qR8gA6G8+wE()Mp(95kB^-^6evEp>6z z_wbwk!yriHm*JRZHBcs1&uTW#M_*u{Tp%X?x4}}fR*I$m^MD3G>(C(gmNY{_9!1@y zvYsR%jGVl^L1B}o+(^&+(D+aE_{@X@L6ZRG9QHZl*A zYdMBF&teg0LRTt+j;r1I17 zhKaJTcI1GNBWD?)(W=Pn=d#jQyVU$QD!>x3*~MX|CifDjdGh zm00gw$=T$`tp-^KZ-QIENK8#IYRckPRndl;{^To(6b zV4ewi9GGknS^ZgnEAm(H+dq+573so88@XdA^lHu6AP^Azz^13L+oSEyCb$~yf| zvxTPBFlHF8-EF^Me*Y5%Q)Q>ES!QQAjkLc}u*48kG3Li#e^qikjs-TR{X972*H6{? z|NidSAO`h5@eRP;)QzS}vq=Sq>QxpH>-%h5_yd)0R&U61=c?v~Wm!%q7aK^N*CMHY zIXT;Bf7v|qREyq%y61O`%&KF$=Qz(hw>5+`vr$N7d4%KsyUza}zFSXvRzh#@UjHZt zzN&QPs6CXx4{`4!pv?yrJs~6uO79cNae7 zyzExL9CmH!cXqbV8$U;P!@NKr&;)B6JrD8QH))KG=M>++w{ZNv63=DsmN6LSWu^T8 zpiuu8ROJ5)vigSq_#c$f|7!66;!6I108#$u?rxye_Q%Gg#RPW?@qZ5TmCEVwhevmI zigxIr+|+C*@zLf62C}QGCwvTx!F|) zKK3^G#HUAFxx3y>eNV)MY6FLt;~PYhpvzgCY&{@2$J8+Z@eAQl*Y#$aZ+-2G&75(I zzg=z1j16$vap%t(-LG%QCJJnZln`Ol zSM}>~ynbCg4IAP=N_KqmeMjb5cObiLkD;vN4M_6dD7ydXSc4+{-{slDFuL?CpZoqs zy>R^df%0z;b}a4=IU!u_%`mKXJ~86o8e1?@g-3w}W8+T$hhFqyVmO#$;K7-e?YcY#Kg-y^YjxQ!C~UO=gemtNpS6dfz~YNBjFd`Yc>;X0MLM zYsY~JKJuOi)rYyfo~ru#oBrgK@vg_O0UzcQEms~)15mj=-f?a=I&IFBpMA0-Wia-s zeL2>*);=`%bxX|pE#l?mcyPR2LPRzW%`^lKX3PSfS03u^Zv?fH$=_9!Sk-$X>A60W zn=c;^41M=#yxQ*hSj7gUt%~9=f4_N|Kb{3X_H=0Z9b#o7*Zl4pW}9oq>lhrH=fL~( z%8g!{GAeGBZI#o3DcDjgoX21t6VN?o941md^Fn%ZmIgU<5rQfNIL#JzZ{weJv%Db$ zB+XZisyl|(#IRB;X4&eG80MYb0XN3aFea}SuAAg-2Gz!9XR}5oURt5qx#oh{r$Kqbuh{Va~P3q^+~>k?6m95&W2u&9AOG{~`QLO7$T&PM}p z69SvK+~mC8dIhVo;8Am*Q&A;4?DPgoo84JHMs9#q=Zfim%#^8V9i6kwG1=i)sj3uh z8=Z^o0<*!vZJ>yTruQkYUs~s%W&r4^X$(}{qr3ykpfE^H7)Key8_pwVCML(qOAEFQ z+wLYu(%x>Sy1vPNIQn5GK|-bRY^9~+85Emi%5O(>X|!-ix-8i8Z=wF?@eb>=N;j#Y z8GB`rC(q&d>c*b965D(F=-rc(WEs=M2~XB}w%s(r7NoICSa7GhJ}G#60g%2`rdS%k zW%^S*d^1pTyaJcK-P7Z5H#7mRmh^IK(M4X?^velwd-NL@d2)c@YxEB}dbYv_Z1!GW zZ2U1cA$t2AMkfm$LzfOHZZ*CQZIS9I2NLEnD2^5(4vy51i``*ZS@~gd3bOm!gmiK& zRe2E+=8&4W!9xkag?zDqh*{MKW_SSZi(lGvtLLxh$NSpH-IM=?Bf_x zYr;lvWXZsP#02^TjrJNUqL9X;)$e&HJ>`>E>un=WRXmhj<;F`J|7zHyZpj1u3j!bc zh|jUL?>+pVw}I+B_;aSVfc%SdJ!K;)pFLc2}MCH*mo1%|)>vj%1kEuDzS{f4ufB=;SG}qvqJ`Eyvt6I*j!9_b|nc z4Aq?67CKVx8=xvh&8|(0-Q`O;yz$NU7u7Pb&!s1TpN%BO=GF%H64oZ4LkW0Uzu2)! z5VHp_0L(EqQ3jAPDtbk4bU2aWjqf6qPBt~c_v^JN`#(U?C;^j&?d;4P`q!)ak^1iHt4)S+=5>eN76P)6t&yDU83s$-qA}x0j~5|K656aB6W3#@ zNFTP#a}n-I#yb7J-?4>SImWv5pKF$n9=N_90VSubN5uBeR2FgneB+ny)8>)Cd5!mo z(^QP7Rj?)|dTobLbJOKyWs=ehx$Dsh8GC*x{yC(yRmXi5Z5j6L{!M-h`Fayian<;z z*fo=R4*2~+_C=#409CTZ+2*uOczxU?9ClA$J^J(QbvR%v=?PmkYF?3sOm`VAhCF$_ z8gNsR2w@D>@kzG8&P63@lL-vg7pkb>(4u2}JZi)K>5-qnwD$X7zc#++X2U!Ush9_W z**<5XF7=P%M=v1}50q_#gPQov5=49j57i`!_{sa|zo%JQe`B)LC=PXmorT&Nt8w{5 zyM$!R^qRDA65~?E(;w3lgr&Za(N;$$qyyp&13ah-h3(AgwuPz>y@Z*cGDm30qFTy| z%qVWi1q!TOG>7rrDiqY~9m& z3S2&FD%8#8Ivr%7fY1Iz+!v%-+0}|7j@ow+#CwKe>;>6>w7Xr|VG2__Y2oAz1g=GD zdVjub%7QhkF()2dFCJAW8Zg@m?2JPHEFWA5(0&7=|?4zNNDK|1=&U zMCO_jmgU&k4big>Xn;L6Ivkt(MgSdsR(-zTt-r#P4X9%zkD~&V{uP&to3G6zC+uL{ zScGZ*tRZdXb?xB`LbNO7P+7D{+vQl^7FbxZp%PtaZ_<%js;KDEN;y8;<0|6$)1aZP zNO(|_ov%a9Q06Fr^s7>mMO*#dVDh1sX8*Ay-*JLjG0T^Kh1+iZQxt3YOlZLHdn#nb z!w+R7s+y1k$}}jA*%V!`)kdzob9|^RDPj!_Hi*=Y$nIy$&oEQT&Ae3H3tgq$0rfAi zl|jB&2=w}Ib@=Ud|Nix%id0z3IVX~^U2RyL!(SL=p_X!p8wECul-hbREg9O;P*yT& zZwrsAz#h2jA9w$Xrq_Tq>`4SPXQ160zfEwtsuES-Z;qBTnZQ|s6S$B`ql(NM&#c6; z*K;z1taw~bNw&GoZGI$iX{n%Nc4BkBPb6^Y@97y(Ln(@&S|Xe_v`TYP<(YU_x~O1; zq>{GJk;J<>`i}6oCSn;Z7TBC8k6~f$`<$I6&*xuNYekTdlF)ZeLRnaVWt@9@Kh77E z9(VJeLJ6lw|XV}y<@hqe!0S!&~@04c22gtd*0h-g8Ur5ci{!-y+L%zdLR}?B$qbTJj zZT!rwbX8KKU!fI|fb>7hV<3vl4kxfk(;y1ka1&gnnksvmPVGgEb6z0QPatr|49Rsd zOLHR`*`|oeQ)M!ThIoc#An!*n!Y|Xc)})G_eYR}RI;AmqCm>)d12B<+Ub&Q~IF%-@ zWj=hcgoDD;E8v6G61Jt~P_I?p^kID#)bu1puyaaUcj0y(o&ju>D zJ)v!DFkFJ&p&16iPt-U3LL@@l$mcl8i7&s62;Xx~;=3s|&trcQ@y>F4YRS=Xql=)@ zY{-0i`X;uCnJ>u&0!}_DtWeSCL*h2SIlFjxmgRHTeHRUk%;B0(WSMW=V0=dH5}kbJ zIVtx=!z5R*l~e>xCcZGzf7JLoBC-D+lk?yczFqXwz3~+7*A@>_qTyVebz~P`n1b=r zFJs+^MJ$oiX!3Yyc@#33r8L^%X@e7;6zuz2lV1zUB}&R7W&y2lbDC;yK!vzmF_~&R zTLB&7(l=Yw`gEo-EXA386c*Lw{OOnziT&`uH=%b;+-rw1p6$$h7j6svbbJ zs9u@1v9JuJFa`#!jHy3XF9rQn98_XmZq1i5Q)!YVw^3Fg-aP%J>2aC-ryGGqdH_i@ za3sfMGgl_&9B#*xb4?mKM7b7Z_JYSkuVZb~HBmLOaLG=d@UI6ZgV*xnz1eo}++e9D zDT8bK_;RzJh5hN5D#Vuqd8z+VD2iKOK9ygwUx9Xs)MrZ z??w2iEk*81v4c4kqSH>RgTV=d%$O)ws-2cULLz$+R0F1q@lt(qSLFyZQ5{$gGvQ(9XN$~&T7}fpn3{3s8mBasG z8r8R<00N8?$%f+B+r*@O@I9j{V3Ak5p#@$ooaAHVdm}rS0^3zLI`H^GqI|72tI@Ev zD#An7LR-g)kTLd%IDb*0o^j@^+dsdQTdO|>A_l?%2-+U-#3seC8Xnr(+ORG80_3@q zhNUA=ORe4NI?;z?$8}kL+9+XC|5k`_>DhZc;E&BRdTY*Ud!UD!(AvZCWY>MD=SQPR zVTb9t8kOMYxqR`FK(dpHl9j6c3nM(EPkCVn6j|Tl}{@b=E zz>yrX6fG+wmKwO_$bM=a*dr(?xTvz0Wk~9y=yY=Y`ZS(n*x*|jYj1?pFJKeDtkN(K z*m=5!{dl$+x;YJeyuIGyJ7MqOZp)W(S_mDhiqXsC|0&%cZzRHw@?tZoa%I0KwhVT4 z9wG;w0b^@QFOSg0>qm_asT@AFftEc$O~E=A0+Opv9aDA}dw#yn2p2tY+@AlKWa|fX zpdiAK8ki(Inh=o)7YAJ@wPpj6or`X zGY~cCKlL#{BW0h);H|BUpzb3p^wR{M)-V%!y}4OSP9K5olb}#a0R3s`jTwY(#WZj& z-}@MS_+yLr%k;&G_gtVr=at10t~Tebpx3R^h*XyzSotJG+;T!>t+0b?oTotbX&0(TziSe zsCZ?^iGhu=CJC@o;mspBJ1Wi|ASzxjyp$9B>tlwAoi!nZPD9l|rH6cXCr)p(4S9cI zm&;Ihr2Yu|>c)g(lm~E=$|fQ43&ZBLl1ujdT4iF-XyQ=qdgSQV?7Bp8Z+9)3gq!x^ z+?rCoUCLfNa%aqgI-kTxt@T1r;%4tIxp2S1f*rPwr1z^vGUHs)rO zrp;2;W&_&8O9KGPtt_{B%*dcAQLG4z7r|&-rr~kURg2kVlT&(&Un1@f)Z4jKRs977v9YjM%v=8Qe#NZ1*x1OW z5_JC@V3th2_^mUZ0+cfteWIGvDR>%A!{1C3{Z3mffqbP_M$xfZ)-6P6lHWbL0AAb4 zMcEJ(Gn0`(ytn@qd$jd<#`X%^;ZXT7!DP$kE6&(vUg6)5OTwW~xjtX1g}j#9L!Y`u zgl!aJlrwSw6IK6hx%{kxQ8k{0m6ouCg6aJahhT8s&-_VE4hCj2tfQ|4PU=%k2GN~c zeU*w^S>==3a!gs}qiQuZ^?}qRaLuGCcy*G~yL9A@zvBAwv@=pnDCd$XGWi%bMFkRO zT3mF$P!|>m(U@0{U^53qb}i5dQ>004%Uk^M?d$ys%)Ak7J}y*`0<9s>KL;`VQ3tol z?!UR0I&Uj-Fs={YVEUm7?2tD4ZDsG1J>VVx<8|BH7r5(&xG?gb(v|*FQ6mi_)$~W* zw#{)p_{NT!cJ=vTGF(R308e?3d;HNg&?J8L(o#1q)7@xZU~kxs?}2_*o)DZ)Z}|I3 zDW$j-c>D9}Kxy+eZ>krsDaOW4hlKlMJ<(-Vd z$xB?$yw|BgD1FyW$9vO;H*D~d(L98lfWha}fQa?o#`Rk8J$+2dYKwZIUsN@|lBMHiFhm&gLKF2o8QOupE?^PTIN_I4$EN?Xq z46Nc{`+B)4H}UVRQ_Ja$9q))@wK)_AOl=-AC8N-U4RgKBDkHw?(eS~C%e#)zyhyMlKTkgyzrIGKW?9F!=bv8vogDu;(%8LF>M@O_0Gly zSnHzOb4Ne1KE$vdPP^dBCSXsQ9{a%pY2hxXtS`Q-r{BLWoD{+DsbFgv;Kw4@+ZRiO zw>vM`D+l0=ckp%S4hDHy7sIPzH$Cuu^TQ@Va>S!o(IXx(Y*9H&c^JZHZ5im=javJ9sG32*??c_ew`yLAnO!JRr zv#+tMRSx+@s>homkPy*^WRv{;XF6W>kNp04HQUQ;zrKmB9!bp4>)|}bAYm}<8d)Us zaJe0g4ul;hUl9I8OuV(1MQL(mn=I~(+V0lj|zcUzj(Dx(I#bEC>@iBi`p|uPlSl_voYTe)M zh?(TJsEOsgXui!nODgcW4ZOxy?yD_UDR`{% ztU+j4{q_M?w8gsU%TDyJ9r#g2M|7^M3Len{;Qfqd`y!ySV|B`webf(klxuUoqdlWaTAg#< z5DrEYjq^y6kPJrR%8Lou%)-2kToKZML24(gktK) zGjrM7(iZ|f-qs-t95RwPeZn;pT&8)N zc<;RJTR31o{wMaIN@#D{Excet8__~dYLk%zNi7?{>e%gAIS93ls*^Jm*J~C0TkF&1 z|4G!sgm7Zwzi+}k0pPZtVXZKxPi<|yzd>62)W+{}IDkvT+@I=pn@{pX0w<(LVj<{z zjXHZc4SXk_?g@*ubf1y`)l=#~5cFdm`>X z2o=VDA7lDc9dGCzHRZbtWNPy6jEIYYk<(vIKfVM4UoKke3aNHHr9;Zi1b$+{R`R1& zMsF56s$x#qU<9M!x?Rw3Z)GKresOX{#8I^drd1i@dlM98jD${$+?%!|o_0}Tit)P@ z@?GG9V1pb{g2WdJDLoYO9VI@3RBg&tQSfAZ&P#MF(wlKJej51e?D+}K+i5Ij{3JI& zaJYH=J0$Oy#-G~RMzPl2x5bu(4)g3Toii{w&(t61#)rB39ULv5RT(R2x}%Ta$8w+r z*r*L)x*)%aHN|PtOkvHjCZSUk)*$XioZi&j)&@+934saRVNc3#gnFt#)phG$E22qO|}s_Vg}xi0!-7v zeWJ%VWeXqv{_+W__i*Y>2(-R=l!xT1aSrYjjIm;^a2)=FZkWKo>jM@E-mA(#kD0(H zwlE@m!=GXL{4+3}&O2Bjc2q^>O|pYNf)BRlA4B#oGAX@2wtn_uD~nJ0RgN-msxrv_1LLmb|>z z31ff<@x>SW?mso2fKZxntuU?c@nOvDOv4_-sehfyvVr`rYgXswi{%KIdT9NNEu_{l#J# zCf;r|*e~Yy4S~RpZ{IMyXN#&5agCIxU&4FGz{I1VqRKE4l+K7CV`~5v9?)HXpcK-2^w_ZJ>?b9h54%NCICq%}@@y;Yvh{M-!XS=R5+*ETpeIvH0q)iz~ zxsN-m4@SJO`9#Bx8CgK(M@B*Z${Y?}V?eF~j+sca?i0_{fw{qbKA8cA=pTKFz-h|; z_Z%Kj^j>t*_guF$!HQ!Tnk~&=8?R_#jdQ#ef43XhKJf1oeiO#MdyfWIB%&#w$MX@F z8rZ3j@{~03%lV&RIg_=)Cq{hy z%TTE1PfLD|VA7ffDkdZ0$U+1o?m?c{1HA8EVnHuk&tkP*0C2fU;rsghB<6pQeVpQT zT;wbUmA!qs+kXbFzoG@)AB1&nq|_L{u)oef8F#%ZJJF-VyR01M@wJ%piyKX-eG@?b z&U^U!yt@9}5&Vt(H}T->6chFXGU?UmeAtlH$Y+nk7Be4uQUXeTG{gaBZG1$yjel`B zO&BWc0r!hf>p=S}VaF@M(+&gl``voSYbP9W$AGI+59j3&=0)Y42K+`JMh(n+UNsR| zxk$JJb`qsA>hZfS$o>Nvq5q}vOZUr5*c~7R)kJDouOsSwJ?E|*@RK&1DV7XuBgV#2 zW{lLbuYj#FL$X;Jx36ygbD#S9j#o8 zsIqbdrdo1_;pz6R^Uf35PCKoCWfcyV;GBz;3_bHT+&MQW93fK5AVmApeu4)UDr=YXe)jbO zusYA4x&gZ@#HquL2$(NFsJhR@puGAcN|{hmYsb|9&Jgc7t#QJt>C7G<={Cch+?R>i zj-^lvGE8K6(gD&>RNS=fU49ev$%yVoqWoKa!CZX9s8#4JL6J`tK9tlJ_={)myIq!` zsSGxsU1`o9aWZKd_PoFZZz&y^c(d<7%ne*npNz~S43ch9?pfj}cRR*4#nUT~>Yzk0 z8j}bmW{0;hXF!ere=>`ad}f7|U5NN9s7pti0`*Oh-?k6!nzM(}IDeuSCg3^9HT!q8U#A0#Id`4ez z3y$*kz&|lF^2cnjrSYSM={s;S*WZ)gn{RtefW5kGFbh&|1yAdm86WRYQwAgBz{>|W z^4&^i`DD<8*xl>xY!2(zhg7kqUBsWXsV|tQCH%-z$Wl#MlNsqa&wz4x9pg~e%dgF^ z65LO$@Q3TP9M7>prp*t}#015NNTwmm~j`M>`3aGbn`x5N`Xd(X>D zFChO@M@;cSC(?(Qz}2X@hYy|kSeMjgfrO&M&xw!3rGOP+nQT{_l&$415s#(nTn{NsD66w`Of091Jp{=B(9N_xNc$-1_ zk8IHXd#wXw-6F6jTdOQ8aXx#BEO|VR`@OC*CzjkN-^}zj+Cnb%hp`K$m)>Y>M zIS@&lyx^Xp#9qb~!^uA%(y6D7=t&IvCt$eW9O{=1B)O>Q?@s?tah>KG)2+9XnFm2> zec-0meh~dbaIw~8{$_yF>isIi_J^Pg;3kSA96X5}cQ{o~ncMC^<5TR&Aj3N z&08oR2)ef+>iYXrwwszI=7a|p`DAYW5b{mc80{M)q-)oTN}H zM2Eci4A>Gkqlw|Ga-!YO%G_R?UuL)cZ!Rxq1@(rKolb?9WYI%+HMK#=qS1CbBP$**`%HXsoivk;-347DzQ4&}P zm@xLE(y|ZDOXBsn#A*{X9c|$>=Y9El*iYcUwi8B6I`RHXTf9zKUd{~nULHi9oV~NR zH*3U4zPe*nh*34kVHeoVJ(LJ3V3Pu)4r%$ch{6Q~>p&@7KRPil8hM^0cL#L8G-Hz{1_aYN2`okfA~6I_cif{-{Gl_2 zuxsn2XT}Xu)LBPHX3VG!Dfe^iM$A>=^UdCv=d z)r{rt261L%xbiXvZg5J~nxb|3WOy7&R?sxxq%GM8HT{5l0#uRXf*H5UdY?4uW*RHw z7&LhJ*eYuhqYfoUkBfJ{6wYgz<%?OoRcp8wrm(eRl-9=oow?JJx&2~f6y>}F^L>n( z-MB@G_3koLa@aZ2)408}84v($L19||Ad$0{6pRh}+ZW6!zbh$U%Vc{Otr&%R0}yR} zwZEGMbyf4IRO|m(YCJ1Fzm5fSE=n$dHeEyAqJQhXo}x7A3+j6OI?Yl<_ZV?KELTqI z@TH+19bi{Hmvcyq&BRu zm2@l-$)6n*Ok@h7474)y?k_zHsy>p^rL?NqcXtyy5<4G}>P%bfOz9Og*4=db>D8;3 zA)kZkZJe^6B~`*%<>MA<)Eu8yHKkyyeW|Ey^}{=Kw%Mo%>t*vp1pad<2>H^#9Y4@pjvo;gz0I&k^(Z>p>ZPf%Nlj0LCnMLsWvBwimXHbw-IuXHK3 zqbpxLD7N)qVxIa6h=ifH6=J{`msf64OY#-f&0P3Xa|3XzdXj06Vv2^PkA zP}D@gE57Mb=&JN)bAU*HH%X|-c9j7-zb7T)GzyRI6FrC(ccPxiEEksq##lJ8r2yBU z;sKv}vWN-dI>@ZBkh))c2VK%+Fj~_b+?pIGGR+7rJ@NQR8%}9X`0ack5(@nb=mrN; zSkPhb0po+y87iZRT?F%1B}V?Xw}N7j9&|H;$f&IXABlXeeatzT;yU+MCg$$pw5m{t9H{u?OFyB;w=Ua746L)7AG`!5eXPMX_3ykm!u>$&9*gaf<*rOl4sO}1SXJwnhTWL+wLq*+wGt4ps?O=6Qt`(^@n#8<_nHIP; z$ZS4q%>QvBckZN<>kNWmKYU~~`6R&}?MLl_S4WC@t19xWT>{TD5Yb;2G;mJ`IAaU) zqE5nrTt^$o{>uKbX7K3Euh`%w{R+fy6vtf?$YEC1JIG;5*x`H$i6af2$rXP(97l2NA? zW#gGH;m|i$z3uh^HAZ+Lo(!sa zD*S5}`sf{1g-Ytet26zpHwL(L8zPG*myXXu+tzl)@36&Gt#fR}C;VhihuWRxEH`yf zcv^cPB89tI{O;XttbkQ)i=pe)_619spDLR6d+m&X@RLi$uT0Zdvwa4}pXH|%zg4N@ zPc(y#xK(C%2=Ywhs0GY^qmAO7*zJ{*Dk(co=bIf4^|Bi_VgVF zr=#7Mew4{B;}E=4pD#Tkj%g|-Y6G9M9J42_)-u>6%V;JuC;v$+vV}GL=R&O02fpr> zHRQ5Hm1Qlij8&?Z3ISlRfRR&jbwc{Lh<5V?<$gjT!XoAKx0)+v`^NPA`kU*U1MR&O zn_b%KF~zhprLH!IvTn2peONKf_wPlQ`kgsQCpe5&zc^)l?iO#mX;S{b`uGZ{Ho9(Y z+}+(NAy{#zIKdrS+}+*U;I6?Pf;$w~mf{rm0xj;+;@bA6z2E)r``z`gf3lL9b7uC; z$s}v`v!DI!BYf5D9Dw^y*Tp7c?Xytl8;xqby}gGjv>FXfX^=D{R#s-|UZ;QR{6sIW zEDSb)|KNs)V??T4(WKjo_|@x=!~cOCf}kzg?ImVzPP0BOEcP1b$kaf$MwSE%aQyz% zol}5Y2g|8V*jJ~CL;6kYT*-yNPnF>C}#C|paFDfPFt4Y zm_PZM{S%n~d*H13@ z)dis3KPg>ZcUjvlHlg=qKG1yng_Z;@1~eQzM>G{*M#gF=7KwfTUfdKu?u}(N;AK6a zl2)n9(l2q-CH{@cE_8qC%d6|O6$nzcIKg7uhOr#ck7`l^NLxC1B{#yWC9L4=!}_ac z9PFamK>-Cs7-plT07Roq1qhwK*ViE0@v}28As;Pp)w=C*(R_S{@emGw2oaDmxyQzd z6OPA5@>3i49S6?F&=G(2V7VCpI8<&X&w$-ugIi}#2BA}nHqv-I z!zNAaAJrW54E9^mDZt9#{xvE@zC?W>9fD=dTr*UpoJ_d`|EUf#h36YC%%_2|y1$#0 zLa>-osJAc_P8vGQEk(QWlj9FXbeG%>!B|@HCsJXY3BOAZYLS(kHi-_Aw}K10xZg{Z zcWc_$j+V`=jvt}t2AEqa5F{CRTkRP~u+#hu!f#UqN3WVx$UqLcZSc{)=Z)gxJ!di> zE=%d%y8cQQ?M0szLx1h=>~lPnX;XM51MH#GYZ?Ve{xHOg zQ*f(VtqL@87OIy8nhbuQSe_@YdUQWW^HRkgCg$ijnn@OavB@}SBu-1hI=D`nV%=zb zqgTHT7I`bcqCD^;5D_lS>&Hyetc&D;#I64K@OG2$HyqtW(fet41lXX`46r6-ZY=_E zox4b+|2DRu(`%ha4orRT9xZ4{NAaZyBkxnz*Gb1WEcm<@>g*wF<-_^RZ$flUw%Pg$ zA7I{iUE>>YH)js;3l=ypkq_-ZVsp}o#3!VKHB+cIiskY~*hH{a4^QZCd;Rj#P&H(? zvk0S?1@N*Qsomh}5cichB@2-rOYyZ3ADL!YLL{n^1AVJ4PErgWcNYuc!`>sXR$FOg z06(cRY4G3!x0V5n8nK|6qJ(Do%j#g!PWyGmnpHcbr<(bd2ly4APbCuEKsqrIbi*dF zTv%|6#rNU3k-*A=9+C?k)R$yK3}S%+-me!|1(ZdNrOqaBCHI=7KC6!k*CP!r^?tr3 zsx77*zcaow#^6VI8B21)$1$E=$Z~Y)3L4ek8-Qo(rvHLHc79xjGUfvEk$G=uo>*sZ z(h%$k^3kW4OCLMelIzGGJ1-AyF3@EHcQ-_5-D8b=ohZMNET`o8ntUYSIy^yC5~_0N z$s-+>EQ^)4d=DIT%-v>E85B3WtA#~5;Ik$*Ms`G%1q%f1%mbTa`{4fTtUkP5i<<$=W9?mIO&EV zU`JzOnkpyc$aVlw{!`KJlH6ncx)$r2ad*dYySZ>9Ia#k&J1~adO08$5e+wg#M9%`M z=!t+iIbTTt7LF}-?k|x{q1gdcGj}5@&y;zzyN*Z88lKimGBrqlI*$`D1+YjxN;-=c#ve&R~m<%j#_q+g_k!AFK0Oe zW00KZI7__sz6hbTk|eZdJgzNqL+PtOh#U3x@M!D5$47RcFQc0vr$*uKMrSZBiCOu} z5%8zJ@G6OKI~^Q`PS_T{siY@Zfi_4mhjCaqQ7OpHt0NzJbU&DBIf7L=P4avBwB4DC zp3y-hkUv&+A&yOO#x(LQ6_b0ULLGcus6=KxGEdn10(0CxpDdF*@@LFN< zOjbMfOGCbmQ(%ba!4e`acj_p{xgTuiP&dD28orw@AviyxN^_e}B{;XA3}5=(QDr$6 zFH#0x=WEZ)2zg9#J2EG}XIJ$Q49Bl&I@UtIbFKe$y@(76I@b!a@#N}RP7|SkrYHVo zTHJ9}?N%Ip5lS0INyXRMeIY6#jkf=Zm!=dlcyzI|s}R@#F35TBi1BF8wgt(s?Z_Iw zUG`4C*rbAfeKc%0nF3=u=kABurssYkW>No|`z4n94_X4n3ggBSHAcvvr?rmzOL0v% zm}ChGPp&V(>84&Se*Ua~0Qi~kA!!agnQ*D=c`*Bnk8+yJzvc2cEMH;zgP*tm#ZH8J zn!uIMX|`BfbnG<~_UGOr8T}LFu;H%~f56kDkju@?sHNsx@cfhc5d>9)a3(u=%1R>N zKA$>sJ}T&Hj1NYRSRrNaGd~1?pnf^y7vs$**t>I;DNqc~$)3-+ihM;Oc(`BKWuh!Q z+OnH9TV?mwyPz3j7+DnhE$TBb{Rn3?lLesdQ$I5O`~nT$9q~SI)0if0<*rm3g-q+) z++zg&(xz$yD+n<~bM%#Xqh@B=-6}u#PEb{%LHqmJe1F(P&*&w++lqKwgKaom=qQR0 z|J?ZkY|<+0d~=}BblsSw^|^Iwzf8dB@FW@Yn=x!gl>iv%I4@im;JvBi4J6n99?kFN ztK;Q`YTGdKMW0LC@$1{9$mXuN{NV;O`0p9ZmefH(V0c>BXe{TOBSkHVx3eNOdK#|2 z{SlN$pHwr7br0%a(TrH(nuEhf@W<2pYnR*y3f2gQihhdW#&uLBxlc&CvV+vlu-=wi3wL(JQJFqGK@9PfDHa_>Po6b30R$`yKf{e{o9_qSd9uO<7XCXi)G zwXWaI4w5$*3&XNMDEhyo%8as>0AA_2Oh_!|i$p{IfU7G#wZ}Q@k zSxz%17Vs`~!`6ttR~!mp<~3G|<5U~U>iSDZ1x0g%Y3SBthi=U&2r=N-WbODq3gA;> zG^%czDG){?qKalnk+@F+p56P@9myh@qEpxoe6K#9$X~wGiPn-E`tAf&&}pL*ya*oo z%WRZ{BGODlkcB;v%j`7v7C}omIwm1e>7qeeW|M?EY8>PbrG($haAlSUD@q`E6p4OB z^5D~LDRlMpSt*8%U7m&F;?mL(Ra=Z$#9SPK!g`sDnUmX=kA6Ny*wUQLKc>Rn6*XO% zamRgtg|B6(k#nzfkW(@SpC3DgB5Y_vTIDNeQ(xhk8Ke^!lr zQB{u`k>X?6qNy(XfWk*tlkH{$S!J>yeG6W-DmBz5>$Y+vdX@GLs(LEy(V|3%qsb*A z3X55@R1vn4bHY#Yk9WAQuI=z3(UZ9m`>m6CnitvzNofm1QJrl|K-w`vf>vo)4Xw5g z0x6wbRE=Wlw6_#n01JFdPGrzTN}NDar6ea;kk~hHNR*?`&>AO!Gdesnga6dKB5-uQ z{a4#=*~D-r=%yD6u{;4JcQO;Ciq-C51GnsHU?SCFlr7})7F}6qY<4j5$D4DMWFyli zp-EH-Te6XUA_}Np(gPV%Q@;f_)=FfvTa15m?+cto+62U`is-%~sJ?f}NLUu8j zmX$#DAGHuOa-PMgt%!f^f6loqVO{uPJA{}}6JauIJ_E}mHGOZggV2t}G7yXu%#bH) z5E&oP6ag^gGy$>FnnbbCavihvkqY4UyH>f`npet3TnjQcN}ASF_U$0M zMHJ9D0^c;lrz>Kh{IU%z!+0!USr#VnCzuT%r>7C=3yq@;`L7xHW;H^Jo{#Rsp z@(08=Y{6}F-?ZzLE|@r=jAeo}h}vU-rbP%j+|OkFLDb>mH4RYg|FsRB8e;&We+ zr_C;9<^;zJShdiXIDd=JW|fx<^i`3x95rzbyfx4vyh4mK&UP@9@+2}JV)FPPON6`iCawkGp`N3tgk(hF8<-Ekw{=^W+ zzM2KPCMQ+R$WDknu zc`OL*LpnImMz3|!2W27!f018J?W%(FF6a73f`SavAYX5&j-tvu^CUKNd6m%7Ykckm zWv%Ov4rXQ9pZwtTR?9CkgVWn$QX6wP!T7(WZtpkp=Ku}_^w2hfZ^Mz%nNoE_nAfS= zi*h3Uxr(i+XmZ!~2`LiFQh@iB{LEW$8oD9Xz*p*$y8MyVISNFS13GUtFnCL?s;hOB z8*;KfS7*;XB|?LTdp)4Jq17%_EV}i`&?yeVA>u8E-J!Z~rF973Yl6e{?00ppz+Y!= zJx)62hA%F@a&fM)`&^1x63rGp!CH4hZ9euB%Y|p=V6%!K+4#AYel`QZy5vMj@7{}h zTgD-1U2Lf|paS-Iq+eB_fAE}(eXtFZ8PpDUKVRao$ZPhqmJD{m@$xLnrDWtU%0>LT z&Q6hV*0Y4kXvdr9LNoTTCe~=$l`ZRu%%G#l$7PyGsl@$CUnZXQRXNgYo)O2X=@Jix*FQlzm=&Ok96`ETVxo(t^?G(_5?T>2-wDz(4fzJr1g1ttM6MJ zCf!D_tPZepHQLgaqF?(P-b4Fosw;jUdleNJd*8@vf`7jk-(6sm#Y9fyUzUcq9@8HG zV!!=5jW|eNfxqzWK@q>2z~E{crOph}fb;HDULAh_u^!^2Kl+Ry{$;NqlAM!{9Z>+c zI;4WWmz18Qy)88n5#QSN!^KKX%YeeZanu+-XJBRerRh&@hxyH&?!u8n)sOqlBL^)b zF5J+qTyWC&U+S!%IMB$FtyfFgQ-`Ne4f{Syi1FHnoJ|YkQ=B>`Eypj2ycTd;d*2V@ z4-d}ks#k^YBpm`c^BgpFG^o0)*y*)bjn%x-cpn83PdhEP!i{6)CDmTjc<-8}{oeGt zW7Ia0tok;9xeDs=8zFE@B|bXa*qmOEiH(>iWgT2uLPz=%O`Ia&!jAzC&?FOMOz)A? zfjBc!8XHRRlp4b_XdXQ>$|5Cro(^CBXrpw!6(xX z?6qO8U)NVx5U8Y7V-Q{aRXF6K{LH@XxF@>veK{#O=D!*VnXsXiTbBWuYyF~Uii&s|al2~2C%OroJY22yKZwayyW3M=mqUNdjIznopA4>t zRiQT*y%932E*<)us1V!6Lgd?vA@3n*@2q59N`SAY+@(o3x#ni*G22WGS!hetwaug( z#vpwI9LmbWdiCRC#c)1!$t{J%A!)V6Zex+@#akYj1h0k+eB%}GyyCT&<9TTlIJE&} z+pr27l|S#&qE})j|M}!Gkz%%TS-@EjwG`4l-5KXmfsgr=!71}zIk%(iGbqMjC?m^z z_>0UuN;NY)U&|{3l+*2ynA+Fv9#8svH`}L4GGQ-{D}y$l*)V_hx64ZL>5g-m{=bK-m5J9sbdG!1xoqd+_6*qy z_-2@^rr3Jd>~&I1TlySi&2qiMTau z^I~!~$II~TNVlb(hKD~%dOOL1*H$&hJ_aFSJ}|mPO_;F7ITp9?57$n)EYjKhwwms5 z^vGa*^?;DD9^;>W6>3so+WXSgBWE%1Ew`YsX>U)EcjDZ}{S)Bl@;bnj-bUv!Rbgk-kyg=kpM{|1lu*(`6V+5_x%n>zZAel~iI zA%}mFRl4!IZC}a| zVB6u5H*-`GS;VH~+lORT4mXe5Q~BnJ%-fzs8F68KfxI%?F8-c%nq#0>*^J5 zk>1@0U~NDp`r8EH{9^}h(V~!=hJ1GwAu}p3pO}A{*x=|O&2Da{i~8}(+SMq)GTVKN zksLeIH7>uAei^mC`a`fkUa=7btSD6N8Q5NFbt?^?e%3TM+x!Z?Vc)2MC7#dULAqh3 zI2ZqlA8&3w_-M1>ruu@L8%)ZVx}IpTZM{iF6HNONzAXl!;oke*u-y|_+tsM6LoScv zmfp0626SU`u7gNj-ec8?oNQkRy$D2mViZf%sK>HtO{5ec%>s%O!@n5qq2H zgj;#9Lvq0w>pFje$VC-}uHtna;wa{-BWGXn1Fm@NbU~ePC4@bH@CV`v9Y#@Zsnr^g z{b$)2gGHXRs^zl_V-!NZ_Niai)kcqg6UlV*+B-a2(AC$?KxnXjZ*p>4UY{W5ksYe| z8n_c|LrpO%i}*M?iHCg{gA$)sDZ!_Khn;uh241mlV5&=wx^>p)<_$CnBCmnQJ8nmE zwQGt26fwGD5M>Hgua=&YV1K)O8|pYdfog+@Gf~c#1q@aBPK!RSn07 zS$RQnGm*Yk8ZHm?K! zVaDb4!5JMUfS99nXsp#rHV(ea{+3z=hF%yD$9KCI+${?VMcHFWsI`>hBmky%B9?Dw zP@V2T;K*=X2~c*ox8wsX=65T_|B|Ty@fp_bqgW;URj5;w_?~)=52lrU1zTnQ1N`Vm znWpfBVRvS&$@8yX_1iQXxoP0LrMwlF*J&#f>K>~b$#{^}f5UX=VK=%eV|mUxe+MJ2 zCo0IQE=bc_t6K-(#YU^Us))ECw)B=5pRiDh{H!`KQbyho%_M%}q~l97DyuWdo-$?b zotnJ-NAA*whzQPk%-Qni8@>)+k@ssW2;mrg2-G|mvp5W%`+9Lsq#cc3V|wk934}?v0(_6l`NXR6%e57h}U4#%_Bm?+xK?%v<$oS zX&WnBB<2Tzb@B1pSH;8b_?K4tmL3%5A8VW>(;n7W4_fti{5g6dik=>wepaLeY{fcjJ|6G!&&b|fq*t{!o~Dv|a# zmjR|T@s+Mrg;oY6)Jg-X#jbZIGot%)su5zuhBbA%%lTH_itSF}D~cGcK=uAo_9cT1 ztt_=B3VG3Fji!@$V-`v)c*RbW=Ov`{M?Z{ZVM^6E3~(t$SUxm${K-hM^0^gDqT%YuVBD}(q%?;ODIuAE)gF22M&X z@e-jwV|-`C@i)_M%M7pPuQCJWy^z~X!pBCl-DCQe)(jm!%qiW?D~i9Y?BxSoJuxHD zRuM@MG>`LGZwaW1IlF$TEQM8c#-VldY^wJgiJ*UnOL7At5TvK*@G9!9`UF-dFA{QX z`>OF2SYs-RQpyC8n~GEJkYk&6oz6+UZ}P@2C~JTtwwwe7$HMDi6{@r68xW+y)>Pc=%I3o&UgIO4d+DX=ZQ=B%78dNe%f;nbX zIaXxU5{^F8DB*@RX**+yq09Q_ja~Oc(I(=y6}J~H+K6!xO9)7X3O!1;35IH<#>tX`V>VxWWF&LLiOkYyrdNMP?vQ!M3tM# zw8H^q;q$it4_FVkPl)1P42mwoW_Lu#DU-;!Dfa*rYCcAy&bp#qZo>pPG)=+5HC3c7 zV}`fBu|TDo78F`yHhODD#f`@)@Nd2S_8BTOm$)27y7iD`SzNd+z1ow|V3k|qK@~ka zK?bI`6kRVQsK@e^vEE1!Mq1)U6fymVVvh^@7+(w1n5No?#+X9zFjJPb8ubUajZ&@n zYKO23a1 zE`NVf{TH&m-T6qa&l#&ut!ZiXlWmV8WM*6~%WADIN1RaqGAl3VrJk!nRTk^&suNDB z-U%e{`Ski?Cvj>^N&(|eQjAEb&AjekNDuyhVtUr|P6Tt{aW=#{3xObl!#L6^ zQ@~vr#(=lRa&VtmHAm%L7h1QrNBYQp3N{vdnF4!ox%{r~G0FSsZZ{)KosOFaw%)3r zr+k6ii-ztm=A>|V9K>R&!riyr%1bV}WTfr8RNSp9;&(U#9=mtE(}Vb0w{Is-w)zG^ z`sh99!w6jdfO@(8)P{pJH9LYK<@E~BHM%>LmZ<0)?@ph6ZD54cNr>-wELk(Hk;R56ZsVKCnh-euQ#_PvNX(Xt zJr}{8)^rK%#4l%ia&$7^FvT^S$ID^;ZyD}Xz%&2<a{H!zNvW;w0 zz3Ru!WJTSQ=ukV~Ee0Y5_`d@}GWET7Q^J3dz2rLFdsxJL8{ESV$(+BAJvgh$%xsvN z-Sxt|l~DT4FF3A0nXm9<$5ZzV0V}XINUZo@Kga23lD;yzm$j#;5ldkNnCq^vVN`bF zK!G1)MBAdF0$=#|kg2;2Qpu;j-`W#BSuTt`yH%%*knpK~k19Jk&buc<7QOS*{?)g~ zcM3n8KVui{<i$wtghAqq>u{07L#Hv_t;J|3d!3EWfH7f3B{Qf)z{v4sCiYQqmwO zJT`-+fIs{ss+|N0s0C7wq1HC8JbP z4NyL;k+9@C&()>uw$Jw%Z!;!-A%gV{$&0Ay#61sJr1oO+an&x&b!jKJCc}=uuhXw! zlND!;k!tF>dDg}hY;Q^VV|Xvn(wI61>uz{jeb!c<(n9lhO~aVzy{Y)Gb_bX`27I7C zeSzQ=;O*mY-*TkCdW#w4_8_q`_|ADV4D-P(`+FT!2T@JGbDm!&ye;4hCfDg&&v^Pn zJuv5enDV0Lb9x%ly~fT`)8k_;tlgYg_ea#T@;BxHSM53KSf;!J zk+m&EEJ&_@Y*7s=NqAq$=!4WLEZP2kgcz2uGR$wfaBpwKh@SPNqSMPh2>Xnm-zt%H zMbr6u_B?Kxl=OgWa$lU}cEsXF5>=^(w=MwA&yt$l z?v17{xJw(l>HAhJaqO@11}40j)Tq0Q_?FHb3r|5%n_C?CmL(H?Iye#umf8B~&AyUS z#Cu8{OmN@^u7gJ1_LGIYwQLsfACnGHJx)AD(WAJkNv;OKlwkaXp6p_dN!LGsVE z$_@(+$}11biy>$|UP!4hX&<5dr1Emb?g>mv4Clr0a$<^&*r;r+0uy6fmy3zXS-w#a zGxdMsla@M?n?+q$8&uP}RehN5*f~sVt7iJI3keTKpLK^OG)pGR3@FiMl8zSM0{*D4ZCYkU`j^^vc^0wClJ^Hz*}; z1rZA3u1n2v@#4IC0nVZfFl>{y3t#YiZ+r~pme*f36bY}#6B3- zFKj9*_t;Z+KNW@mF6u-G9_oupOqsy=_k;@3RD(_nazE*F=`S-TwY|%$iwn?n4Nj_C zYRbJekrb3-^#_b5toIoITR*F$L=kcxtysGX=1;*@XeX7{dmnged6{ zdfM^ximiI)G_Z^Q9`|YqdVNOWFS;2u-DoHuWGqN-I%qQZr~WbMX`A0U$rbDfn@k9( z9-5^HjQ%pM@S9dc85K=!<4BnhXDWDuki1_kN@hM9HdM|83z8#hB8&{U)zDhGJpH}hm~+( z=YC^fLaY4^3l6SEJ(YX}2>#R6a%VS(3=anvh6o1-+w+gb8W#@E+1$n6#@f@1)5FT< ze+G<(hW2N`Z<;`5iM23GZI~tOJN#oQ#e-Q|dzo|kIXnGdtJo<^MNw2ZIG{3|^8dMD zBmo?pr?r=ty^F2qKTZF2hu#G6Zz`iI!@>P|ll|jzaBxrLso0}0=>Ck#xg}VW2R15p zBse(Y|2Rm=|Kp%K8#p*iR~Ij97q9;w0y4^<{rIq#HSCO!4S!ny E1;ki}9{>OV diff --git a/openfreebuds/driver/huawei/handler/config_equalizer.py b/openfreebuds/driver/huawei/handler/config_equalizer.py index 0414619..bc1b390 100644 --- a/openfreebuds/driver/huawei/handler/config_equalizer.py +++ b/openfreebuds/driver/huawei/handler/config_equalizer.py @@ -4,7 +4,6 @@ from openfreebuds.driver.huawei.driver.generic import OfbDriverHandlerHuawei from openfreebuds.driver.huawei.package import HuaweiSppPackage from openfreebuds.exceptions import OfbNotSupportedError, OfbTooManyItemsError -from openfreebuds.utils import reverse_dict from openfreebuds.utils.logger import create_logger KNOWN_BUILT_IN_PRESETS = { @@ -13,6 +12,14 @@ 3: "equalizer_preset_treble", 9: "equalizer_preset_voices", } +FAKE_BUILT_IN_PRESETS = [ + (-56, "equalizer_preset_symphony", "0f0f0afb0f190ffb322d"), + (-55, "equalizer_preset_hi_fi_live", "fb141e0a0000e7f60a00"), +] +FAKE_BUILT_IN_PRESETS_COPY_NAME = { + "equalizer_preset_symphony": "Symphony (copy)", + "equalizer_preset_hi_fi_live": "Hi-Fi Live (Copy)", +} log = create_logger("OfbHuaweiEqualizerPresetHandler") @@ -34,20 +41,34 @@ def __init__( self, w_presets: Optional[dict[int, str]] = None, w_custom: bool = False, + w_fake_built_in: bool = False, w_custom_rows: int = 10, w_custom_max_count: int = 3, ): self.w_custom: bool = w_custom self.w_custom_rows = w_custom_rows self.w_custom_max_count = w_custom_max_count + self.w_fake_built_in = w_fake_built_in self.w_options_predefined: bool = w_presets is not None self.changes_saved: bool = True - self.custom_preset_values: dict[int, bytes] = {} self.current_rollback_data: bytes = b"" - self.options: Optional[dict[int, str]] = None + self.default_preset_data: list[tuple[Optional[int], str, Optional[str]]] = [] + self.data_overrides: dict[int, str] = {} + + # Predefined set of built-in modes if w_presets: - self.options = {i: f"equalizer_preset_{name}" for i, name in w_presets.items()} + for i, name in w_presets: + self.default_preset_data.append((i, name, None)) + + # Load predefined presets + if w_fake_built_in: + self.default_preset_data.extend((a, b, None) for a, b, c in FAKE_BUILT_IN_PRESETS) + self.data_overrides = {i: d for i, _, d in FAKE_BUILT_IN_PRESETS} + elif w_custom: + self.default_preset_data.extend((None, b, c) for _, b, c in FAKE_BUILT_IN_PRESETS) + + self.preset_data = self.default_preset_data async def on_init(self): resp = await self.driver.send_package( @@ -68,14 +89,12 @@ async def set_property(self, group: str, prop: str, value): raise OfbNotSupportedError("Impossible error") async def _toggle_save(self, save: bool): - mode_str = await self.driver.get_property("sound", "equalizer_preset", None) - mode_id = reverse_dict(self.options).get(mode_str) + mode_id, mode_str, data = await self.find_current_mode() if mode_id is None: + log.info(f"Skip unknown mode override {mode_str}") return if save: - data = await self.driver.get_property("sound", "equalizer_rows", "[]") - data = b"".join([x.to_bytes(1, byteorder="big", signed=True) for x in json.loads(data)]) log.info(f"Will save persistent preset data={data}, mode_id={mode_id}") else: data = self.current_rollback_data @@ -92,15 +111,14 @@ async def _toggle_save(self, save: bool): self.changes_saved = True async def _change_current_mode(self, value: str): - mode_str = await self.driver.get_property("sound", "equalizer_preset", None) - mode_id = reverse_dict(self.options).get(mode_str) + mode_id, mode_str, mode_data = await self.find_current_mode() if mode_id is None: - log.info(f"Skip unknown mode {mode_str}/{mode_id}") + log.info(f"Skip unknown mode override {mode_str}") return data = b"".join([x.to_bytes(1, byteorder="big", signed=True) for x in json.loads(value)]) if self.changes_saved: - self.current_rollback_data = self.custom_preset_values[mode_id] + self.current_rollback_data = mode_data log.info(f"Will replace id={mode_id}, label={mode_str} with data={data}") pkg = HuaweiSppPackage.change_rq( @@ -113,53 +131,73 @@ async def _change_current_mode(self, value: str): self.changes_saved = False async def _delete_current_mode(self): - mode_str = await self.driver.get_property("sound", "equalizer_preset", None) - mode_id = reverse_dict(self.options).get(mode_str) + mode_id, mode_str, mode_data = await self.find_current_mode() if mode_id is None: + log.info(f"Skip unknown mode deletion {mode_str}") return - data = self.custom_preset_values[mode_id] - log.info(f"Delete id={mode_id}, label={mode_str}, data={data}") + log.info(f"Delete id={mode_id}, label={mode_str}, data={mode_data}") pkg = HuaweiSppPackage.change_rq( b"\x2b\x49", - _build_payload(mode_id, mode_str, data, 2) + _build_payload(mode_id, mode_str, mode_data, 2) ) await self.driver.send_package(pkg) await self.on_init() + async def find_current_mode(self): + mode_str = await self.driver.get_property("sound", "equalizer_preset", None) + candidates = [(p_id, label, data) for p_id, label, data in self.preset_data if label == mode_str] + if len(candidates) < 1: + return None, None, None + return candidates[0] + async def _set_current_mode(self, mode_str): - mode_id = reverse_dict(self.options).get(mode_str) + candidates = [(p_id, label, data) for p_id, label, data in self.preset_data if label == mode_str] + custom_modes = [p_id for p_id, _, data in self.preset_data if data is not None and p_id is not None] + mode_id = 0 + + # What is going to do? + if len(candidates) < 1: + # Create new mode from scratch + mode_data = b"\x00" * self.w_custom_rows + current_mode = await self.driver.get_property("sound", "equalizer_preset", None) + candidate_data = [data for _, label, data in self.preset_data if label == current_mode] + if len(candidate_data) > 0 and candidate_data[0] is not None: + mode_data = candidate_data[0] + + do_create = True + elif candidates[0][0] is None: + # Load predefined mode as custom + mode_id, _, mode_data = candidates[0] + mode_str = FAKE_BUILT_IN_PRESETS_COPY_NAME.get(mode_str, mode_str) + do_create = True + else: + mode_id, mode_str_orig, mode_data = candidates[0] + if mode_id in self.data_overrides: + mode_data = self.data_overrides[mode_id] + do_create = False - # New mode creation - if mode_id is None: + # Preparation for new mode + if do_create: if not self.w_custom: raise OfbNotSupportedError("Device didn't support custom equalizer presets") - if len(self.custom_preset_values.keys()) >= self.w_custom_max_count: + if len(custom_modes) >= self.w_custom_max_count: raise OfbTooManyItemsError() - mode_id = 100 - while mode_id in self.custom_preset_values: + while mode_id in custom_modes: mode_id += 1 - data = b"\x00" * self.w_custom_rows + self.preset_data.append((mode_id, mode_str, mode_data)) + log.info(f"Will create new preset id={mode_id}, data={mode_data}") - current_mode = await self.driver.get_property("sound", "equalizer_preset", None) - current_mode_id = reverse_dict(self.options).get(current_mode) - if current_mode_id in self.custom_preset_values: - data = self.custom_preset_values[current_mode_id] - - self.custom_preset_values[mode_id] = data - log.info(f"Will create new preset id={mode_id}, data={data}") - - if mode_id in self.custom_preset_values: + if mode_data is not None: # Is custom mode, use advanced payload - data = self.custom_preset_values[mode_id] pkg = HuaweiSppPackage.change_rq( b"\x2b\x49", - _build_payload(mode_id, mode_str, data, 1) + _build_payload(mode_id, mode_str, mode_data, 1) ) else: - # Is built'in mode + # Is built-in mode pkg = HuaweiSppPackage.change_rq( b"\x2b\x49", [(1, mode_id)] @@ -179,48 +217,59 @@ async def on_package(self, package: HuaweiSppPackage): } available_modes = package.find_param(3) + self.preset_data = [] if not self.w_options_predefined and len(available_modes) > 0: - self.options = {i: KNOWN_BUILT_IN_PRESETS.get(i, f"preset_{i}") for i in available_modes} - # log.info(f"Read built-in options {self.options}") + for p_id in available_modes: + self.preset_data.append((p_id, KNOWN_BUILT_IN_PRESETS.get(p_id, f"preset_{p_id}"), None)) + self.preset_data.extend(self.default_preset_data) custom_modes = package.find_param(8) if self.w_custom and len(custom_modes) > 0: offset = 0 - self.custom_preset_values = {} while offset < len(custom_modes): mode_id, mode_label, mode_data = _parse_custom_mode(custom_modes[offset:offset + 36]) - self.custom_preset_values[mode_id] = mode_data - self.options[mode_id] = mode_label + self.preset_data.append((mode_id, mode_label, mode_data)) # log.info(f"Read custom mode id={mode_id}, label={mode_label}, data={mode_data}") offset += 36 - new_props["equalizer_preset_options"] = ",".join(self.options.values()) + log.info(self.preset_data) + + new_props["equalizer_preset_options"] = ",".join([l for _, l, _ in self.preset_data]) + if self.w_custom and not self.w_fake_built_in: + new_props["equalizer_preset_create_options"] = ",".join(l for _, l, _ in FAKE_BUILT_IN_PRESETS) current_mode = package.find_param(2) if len(current_mode) == 1: current_mode = int.from_bytes(current_mode, byteorder="big", signed=True) - new_props["equalizer_preset"] = self.options.get(current_mode, f"unknown_{current_mode}") - - if current_mode in self.custom_preset_values: - data = _eq_bytes_to_array(self.custom_preset_values[current_mode]) - new_props["equalizer_rows"] = json.dumps(data) + value = f"unknown_{current_mode}" + for p_id, label, data in self.preset_data: + if p_id == current_mode: + value = label + if data is not None: + new_props["equalizer_rows"] = json.dumps(_eq_bytes_to_array(data)) + break + new_props["equalizer_preset"] = value await self.driver.put_property("sound", None, new_props, extend_group=True) -def _eq_bytes_to_array(data: bytes): +def _eq_bytes_to_array(data: bytes | str): + if isinstance(data, str): + data = bytes.fromhex(data) return [int.from_bytes((x,), byteorder="big", signed=True) for x in data] def _parse_custom_mode(data: bytes): count_lines = data[1] - data_lines = data[2:2 + count_lines] + data_lines = data[2:2 + count_lines].hex() label = data[2 + count_lines:].split(b"\x00", 1)[0].decode("utf8") return data[0], label, data_lines -def _build_payload(mode_id: int, mode_str: str, data: bytes, action: int): +def _build_payload(mode_id: int, mode_str: str, data: bytes | str, action: int): + if isinstance(data, str): + data = bytes.fromhex(data) return [ (1, mode_id), (2, len(data)), diff --git a/openfreebuds/driver/huawei/package.py b/openfreebuds/driver/huawei/package.py index 91d8e32..159fc4c 100644 --- a/openfreebuds/driver/huawei/package.py +++ b/openfreebuds/driver/huawei/package.py @@ -43,16 +43,22 @@ def to_table_string(self): """ Pretty-print this pacakge contents """ + hex_len = 40 + for p_type in self.parameters: + cand_l = self.parameters[p_type].hex() + if len(cand_l) > hex_len: + hex_len = len(cand_l) + out = build_table_row(12, "COMMAND_ID") out += build_table_row(10, "2 bytes") - out += build_table_row(40, self.command_id.hex(), []) + "\n" + out += build_table_row(hex_len, self.command_id.hex(), []) + "\n" - out += 70 * "=" + "\n" + out += (30 + hex_len) * "=" + "\n" for p_type in self.parameters: p_value = self.parameters[p_type] out += build_table_row(12, f"PARAM {p_type}") out += build_table_row(10, f"{len(p_value)} bytes") - out += build_table_row(40, p_value.hex()) + out += build_table_row(hex_len, p_value.hex()) if all(c < 128 for c in p_value): # ASCII string diff --git a/openfreebuds_qt/app/module/sound_quality.py b/openfreebuds_qt/app/module/sound_quality.py index 03fd504..09289c7 100644 --- a/openfreebuds_qt/app/module/sound_quality.py +++ b/openfreebuds_qt/app/module/sound_quality.py @@ -36,6 +36,7 @@ def __init__(self, *args, **kwargs): self._eq_last_options: list[str] = [] self._eq_rows: list[QSlider] = [] self._last_preset_data: list[int] = [] + self._last_copy_options: list[str] = [] self.setupUi(self) @@ -101,7 +102,10 @@ async def update_ui(self, event: OfbCoreEvent): if event.is_changed("sound", "equalizer_preset"): value = sound.get("equalizer_preset") options = sound.get("equalizer_preset_options") + copy_options = sound.get("equalizer_preset_create_options") self.eq_root.setVisible(value is not None and options is not None) + if copy_options is not None: + self._last_copy_options = list(copy_options.split(",")) if options is not None: self._eq_last_options = list(options.split(",")) fill_combo_box(self.eq_preset_box, self._eq_last_options, self.eq_preset_option_names, value) @@ -140,7 +144,25 @@ async def on_sq_set_quality(self): async def on_eq_preset_change(self, index: int): async with qt_error_handler("OfbQtSoundQualityModule_ChangePreset", self.ctx): value = self._eq_last_options[index] - await self.ofb.set_property("sound", "equalizer_preset", value) + + if value in self._last_copy_options: + dialog = QMessageBox(QMessageBox.Icon.Information, + self.tr("Notice"), + self.tr("This preset isn't available in your device, it will be created as " + "custom preset."), + QMessageBox.StandardButton.Ok) + dialog.setWindowModality(Qt.WindowModality.WindowModal) + await run_dialog_async(dialog) + + try: + await self.ofb.set_property("sound", "equalizer_preset", value) + except OfbTooManyItemsError: + dialog = QMessageBox(QMessageBox.Icon.Critical, + self.tr("Failed"), + self.tr("Can't create: too many custom preset created in device."), + QMessageBox.StandardButton.Ok) + dialog.setWindowModality(Qt.WindowModality.WindowModal) + await run_dialog_async(dialog) @asyncSlot() async def new_preset(self): diff --git a/openfreebuds_qt/qt_i18n.py b/openfreebuds_qt/qt_i18n.py index 25da1cf..0ac80f8 100644 --- a/openfreebuds_qt/qt_i18n.py +++ b/openfreebuds_qt/qt_i18n.py @@ -17,6 +17,8 @@ def get_eq_preset_names(): "equalizer_preset_hardbass": QApplication.translate("EqPresetName", "Bass-boost"), "equalizer_preset_treble": QApplication.translate("EqPresetName", "Treble-boost"), "equalizer_preset_voices": QApplication.translate("EqPresetName", "Voices"), + "equalizer_preset_symphony": QApplication.translate("EqPresetName", "Symphony"), + "equalizer_preset_hi_fi_live": QApplication.translate("EqPresetName", "Hi-Fi Live"), } diff --git a/scripts/debug/huawei_pkg_parse.py b/scripts/debug/huawei_pkg_parse.py new file mode 100644 index 0000000..a1f046c --- /dev/null +++ b/scripts/debug/huawei_pkg_parse.py @@ -0,0 +1,17 @@ +import sys +import traceback + +from openfreebuds.driver.huawei.package import HuaweiSppPackage + +while True: + try: + data = bytes.fromhex(input("HEX-stream: ")) + if data[0] != b"\x5a": + data = b"\x5a\x00" + data.split(b"\x5a\x00", 1)[1] + + print("") + print(f"HEX: {data.hex()}") + print("") + print(HuaweiSppPackage.from_bytes(data).to_table_string()) + except Exception: + traceback.print_exc() From 208692db265833d95508741e0f1ec03e1341e5ce Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 13 Oct 2024 13:45:16 +0700 Subject: [PATCH 17/35] [Feature] Add initial 6i driver impl (#43) --- .github/workflows/on_push.yml | 1 + openfreebuds/driver/constants.py | 1 + .../huawei/driver/per_model/__init__.py | 1 + .../driver/huawei/driver/per_model/buds_6i.py | 32 +++++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 openfreebuds/driver/huawei/driver/per_model/buds_6i.py diff --git a/.github/workflows/on_push.yml b/.github/workflows/on_push.yml index 033a286..8bfa1b6 100644 --- a/.github/workflows/on_push.yml +++ b/.github/workflows/on_push.yml @@ -59,6 +59,7 @@ jobs: - uses: actions/checkout@v4 - name: Install dev-dependencies run: | + sudo apt update sudo apt install -y build-essential sudo ./scripts/install_dpkg_dependencies.sh - name: Set up poetry environment diff --git a/openfreebuds/driver/constants.py b/openfreebuds/driver/constants.py index 0c5b1e3..85ccf0f 100644 --- a/openfreebuds/driver/constants.py +++ b/openfreebuds/driver/constants.py @@ -3,6 +3,7 @@ DEVICE_TO_DRIVER_MAP = { "HUAWEI FreeBuds 5i": OfbDriverHuawei5I, + "HUAWEI FreeBuds 6i": OfbDriverHuawei6I, "HUAWEI FreeBuds SE": OfbDriverHuaweiSe, "HUAWEI FreeLace Pro": OfbDriverHuaweiLacePro, "HUAWEI FreeLace Pro 2": OfbDriverHuaweiLacePro2, diff --git a/openfreebuds/driver/huawei/driver/per_model/__init__.py b/openfreebuds/driver/huawei/driver/per_model/__init__.py index c4eceea..7bba6e1 100644 --- a/openfreebuds/driver/huawei/driver/per_model/__init__.py +++ b/openfreebuds/driver/huawei/driver/per_model/__init__.py @@ -1,5 +1,6 @@ from .buds_4i import OfbDriverHuawei4I from .buds_5i import OfbDriverHuawei5I +from .buds_6i import OfbDriverHuawei6I from .buds_pro import OfbDriverHuaweiPro from .buds_pro_2 import OfbDriverHuaweiPro2 from .buds_pro_3 import OfbDriverHuaweiPro3 diff --git a/openfreebuds/driver/huawei/driver/per_model/buds_6i.py b/openfreebuds/driver/huawei/driver/per_model/buds_6i.py new file mode 100644 index 0000000..a6cf415 --- /dev/null +++ b/openfreebuds/driver/huawei/driver/per_model/buds_6i.py @@ -0,0 +1,32 @@ +from openfreebuds.driver.huawei.driver.generic import OfbDriverHuaweiGeneric +from openfreebuds.driver.huawei.handler import * + + +class OfbDriverHuawei6I(OfbDriverHuaweiGeneric): + """ + HUAWEI FreeBuds 6i + """ + def __init__(self, address): + super().__init__(address) + self._spp_service_port = 1 + self.handlers = [ + OfbHuaweiInfoHandler(), + OfbHuaweiStateInEarHandler(), + OfbHuaweiBatteryHandler(), + OfbHuaweiAncHandler(w_cancel_lvl=True, w_cancel_dynamic=True), + OfbHuaweiActionDoubleTapHandler(w_in_call=True), + OfbHuaweiActionTripleTapHandler(), + OfbHuaweiActionLongTapSplitHandler(w_right=True), + OfbHuaweiActionSwipeGestureHandler(), + OfbHuaweiConfigAutoPauseHandler(), + OfnHuaweiSoundQualityPreferenceHandler(), + OfbHuaweiLowLatencyPreferenceHandler(), + OfbHuaweiEqualizerPresetHandler(w_presets={ + 1: "default", + 2: "hardbass", + 3: "treble", + 9: "voices", + }, w_fake_built_in=True, w_custom=True), + OfbHuaweiVoiceLanguageHandler(), + OfbHuaweiDualConnectHandler(w_auto_connect=False), + ] From 785a185784b9deaabea59466b0502ad193570c0b Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 13 Oct 2024 13:53:45 +0700 Subject: [PATCH 18/35] [Docs] Add 6i page --- README.md | 1 + docs/devices/HUAWEI_FreeBuds_6i.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 docs/devices/HUAWEI_FreeBuds_6i.md diff --git a/README.md b/README.md index a5aa1c8..059f05a 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ If your device isn't listed here, you could try to use it with profile for other - [HUAWEI FreeBuds 4i](./docs/devices/HUAWEI_FreeBuds_4i.md) - **HONOR Earbuds 2** is same - [HUAWEI FreeBuds 5i](./docs/devices/HUAWEI_FreeBuds_5i.md) +- [HUAWEI FreeBuds 6i](./docs/devices/HUAWEI_FreeBuds_6i.md) - [HUAWEI FreeBuds Pro](./docs/devices/HUAWEI_FreeBuds_Pro.md) - [HUAWEI FreeBuds Pro 2](./docs/devices/HUAWEI_FreeBuds_Pro_2.md) - [HUAWEI FreeBuds Pro 3](./docs/devices/HUAWEI_FreeBuds_Pro_3.md) diff --git a/docs/devices/HUAWEI_FreeBuds_6i.md b/docs/devices/HUAWEI_FreeBuds_6i.md new file mode 100644 index 0000000..f58e06b --- /dev/null +++ b/docs/devices/HUAWEI_FreeBuds_6i.md @@ -0,0 +1,30 @@ +# HUAWEI FreeBuds 6i + +Protocol: Huawei SPP, port 1 + +## Features + +- Fetch device information and battery level: ❓ (untested) +- Fetch in-ear status: ❓ (untested) +- Sound quality preference: ❓ (untested) +- Low-latency mode: ❓ (untested) +- Control noise cancellation: ❓ (untested) + - With cancellation level + - With dynamic cancellation +- Set double-tap action: ❓ (untested) +- Set triple-tap action: ❓ (untested) +- Set swipe action: ❓ (untested) +- Wear detection (aka auto-pause) configuration: ❓ (untested) +- Set long-tap action: ❓ (untested) + - Split configuration store +- Change voice language: ❓ (untested) + - English, Chinese +- Dual connect: ❓ (untested) +- Equalizer: ❓ (untested) + - 4 built-in presets + - 2 fake built-in presets (stored in application) + - Up to ??? custom presets in-device memory + +## Not planned features + +- Firmware update From 019341d7e403600ba1eede4cfc21f5d4b08e415e Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 13 Oct 2024 14:04:18 +0700 Subject: [PATCH 19/35] [Feature] Add "Show main window" shortcut --- docs/research.ods | Bin 51385 -> 51772 bytes openfreebuds/shortcuts.py | 4 ++++ openfreebuds_qt/qt_i18n.py | 1 + 3 files changed, 5 insertions(+) diff --git a/docs/research.ods b/docs/research.ods index d976dc867ed9e212363fea4ad12f49b2c2b24cd4..95925c9fa8fd5021b9af172d5d1e8d25cfb461eb 100644 GIT binary patch delta 30301 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 JZ?g&j{6F=&Inn?C delta 29903 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 diff --git a/openfreebuds/shortcuts.py b/openfreebuds/shortcuts.py index df6ea17..60dea38 100644 --- a/openfreebuds/shortcuts.py +++ b/openfreebuds/shortcuts.py @@ -1,4 +1,5 @@ import openfreebuds_backend +from openfreebuds.constants import OfbEventKind from openfreebuds.exceptions import OfbAlreadyRunningError, OfbSystemError, OfbNotSupportedError from openfreebuds.manager.generic import IOpenFreebuds from openfreebuds.utils.logger import create_logger @@ -80,6 +81,9 @@ async def do_toggle_connect(self): else: await self.do_connect() + async def do_show_main_window(self): + await self.ofb.send_message(OfbEventKind.QT_BRING_SETTINGS_UP) + async def do_disconnect(self): state = await self.ofb.get_state() if state == IOpenFreebuds.STATE_DISCONNECTED: diff --git a/openfreebuds_qt/qt_i18n.py b/openfreebuds_qt/qt_i18n.py index 0ac80f8..a54c342 100644 --- a/openfreebuds_qt/qt_i18n.py +++ b/openfreebuds_qt/qt_i18n.py @@ -24,6 +24,7 @@ def get_eq_preset_names(): def get_shortcut_names(): return { + "show_main_window": QApplication.translate("ShortcutName", "Show OpenFreebuds window"), "connect": QApplication.translate("ShortcutName", "Connect device"), "disconnect": QApplication.translate("ShortcutName", "Disconnect device"), "toggle_connect": QApplication.translate("ShortcutName", "Connect/disconnect device"), From c9157b69c5a598853d7fe8172e8723e2bf2a3971 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 13 Oct 2024 14:17:36 +0700 Subject: [PATCH 20/35] [Fix] Last charged field --- openfreebuds_qt/tray/menu.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openfreebuds_qt/tray/menu.py b/openfreebuds_qt/tray/menu.py index d187f8a..44b775a 100644 --- a/openfreebuds_qt/tray/menu.py +++ b/openfreebuds_qt/tray/menu.py @@ -146,8 +146,9 @@ async def _update_battery(self, battery: dict): ) # Update last charged config field - if battery.get("global", 0) - 20 > self.config.get("last_battery", self.device_mac_address, 0): - self.config.set("last_battery", self.device_mac_address, battery.get("global", 0)) + global_battery = battery.get("global", 0) + last_charged = self.config.get("last_charged", self.device_mac_address, 0) + if global_battery > 95 and datetime.now().timestamp() > last_charged + 3600: self.config.set("last_charged", self.device_mac_address, datetime.now().timestamp()) self.config.save() From a539e28fd6b4eb61788c7b29b8421e1e27cd20ea Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 13 Oct 2024 18:49:46 +0700 Subject: [PATCH 21/35] [Fix] Crash if w_presets declared in driver (#43) --- openfreebuds/driver/huawei/handler/config_equalizer.py | 4 ++-- scripts/make.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openfreebuds/driver/huawei/handler/config_equalizer.py b/openfreebuds/driver/huawei/handler/config_equalizer.py index bc1b390..6033988 100644 --- a/openfreebuds/driver/huawei/handler/config_equalizer.py +++ b/openfreebuds/driver/huawei/handler/config_equalizer.py @@ -57,8 +57,8 @@ def __init__( self.data_overrides: dict[int, str] = {} # Predefined set of built-in modes - if w_presets: - for i, name in w_presets: + if w_presets is not None: + for i, name in w_presets.items(): self.default_preset_data.append((i, name, None)) # Load predefined presets diff --git a/scripts/make.py b/scripts/make.py index 64d2520..010713b 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -69,7 +69,7 @@ PYTHON_LIBS_DIR.mkdir(exist_ok=True, parents=True) break -if PYTHON_LIBS_DIR is None and sys.platform != "win32": +if PYTHON_LIBS_DIR is None and DO_INSTALL: print("-- Error: Can't find python packages location, provide them manually") raise SystemExit(1) From 3e4f2f847698b11df2e7f0dc16034fe5b8c9a2dd Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 14 Oct 2024 17:59:31 +0700 Subject: [PATCH 22/35] [Feature] Trayless mode --- openfreebuds/driver/generic/spp.py | 4 +- openfreebuds_qt/app/dialog/first_run.py | 16 +++-- openfreebuds_qt/app/main.py | 14 ++-- openfreebuds_qt/app/module/ui_settings.py | 16 ++++- openfreebuds_qt/designer/first_run_dialog.ui | 30 ++++----- openfreebuds_qt/designer/main_window.ui | 29 ++++---- openfreebuds_qt/designer/ui_settings.ui | 70 +++++++++++++------- openfreebuds_qt/main.py | 36 ++++++++-- 8 files changed, 144 insertions(+), 71 deletions(-) diff --git a/openfreebuds/driver/generic/spp.py b/openfreebuds/driver/generic/spp.py index 5b766f1..870561e 100644 --- a/openfreebuds/driver/generic/spp.py +++ b/openfreebuds/driver/generic/spp.py @@ -51,8 +51,8 @@ async def stop(self): await self.__task_recv self.__task_recv = None - self._writer.close() - # await self._writer.wait_closed() + if self._writer is not None: + self._writer.close() self._writer = None self.started = False diff --git a/openfreebuds_qt/app/dialog/first_run.py b/openfreebuds_qt/app/dialog/first_run.py index c241492..064b6a7 100644 --- a/openfreebuds_qt/app/dialog/first_run.py +++ b/openfreebuds_qt/app/dialog/first_run.py @@ -2,10 +2,11 @@ import webbrowser from PyQt6.QtCore import pyqtSlot -from PyQt6.QtWidgets import QDialog +from PyQt6.QtWidgets import QDialog, QSystemTrayIcon from qasync import asyncSlot import openfreebuds_backend +from openfreebuds import OfbEventKind from openfreebuds_qt.config import OfbQtConfigParser from openfreebuds_qt.constants import LINK_WEBSITE_HELP from openfreebuds_qt.designer.first_run_dialog import Ui_OfbQtFirstRunDialog @@ -26,7 +27,8 @@ def __init__(self, ctx): self.autostart_checkbox.setChecked(not self.config.is_containerized_app) self.autostart_checkbox.setEnabled(not self.config.is_containerized_app) - self.linux_notice.setVisible(sys.platform == 'linux') + self.background_checkbox.setChecked(QSystemTrayIcon.isSystemTrayAvailable()) + self.background_checkbox.setEnabled(QSystemTrayIcon.isSystemTrayAvailable()) preview_fn = "ofb_linux_preview" if sys.platform == 'linux' else "ofb_win32_preview" preview_image = get_img_colored(preview_fn, @@ -39,12 +41,16 @@ async def on_confirm(self): async with qt_error_handler("OfbQtFirstRunDialog_Confirm", self.ctx): self.hide() - if self.autostart_checkbox.isChecked(): - openfreebuds_backend.set_run_at_boot(True) - + openfreebuds_backend.set_run_at_boot(self.autostart_checkbox.isChecked()) + self.config.set("ui", "background", self.background_checkbox.isChecked()) self.config.set("ui", "first_run_finished", True) self.config.save() + if not self.background_checkbox.isChecked(): + self.ctx.main_window.show() + + await self.ctx.ofb.send_message(OfbEventKind.QT_SETTINGS_CHANGED) + @pyqtSlot() def on_faq_click(self): webbrowser.open(LINK_WEBSITE_HELP) diff --git a/openfreebuds_qt/app/main.py b/openfreebuds_qt/app/main.py index 9b61d03..5b618c1 100644 --- a/openfreebuds_qt/app/main.py +++ b/openfreebuds_qt/app/main.py @@ -5,8 +5,8 @@ from PyQt6.QtCore import pyqtSlot from PyQt6.QtGui import QIcon, QKeySequence -from PyQt6.QtWidgets import QMenu -from qasync import asyncSlot +from PyQt6.QtWidgets import QMenu, QSystemTrayIcon +from qasync import asyncSlot, asyncClose from openfreebuds import IOpenFreebuds, OfbEventKind from openfreebuds.utils.logger import create_logger @@ -34,6 +34,7 @@ def __init__(self, ctx: IOfbQtApplication): self.ctx = ctx self.ofb = ctx.ofb self.config = OfbQtConfigParser.get_instance() + self.tray_available = QSystemTrayIcon.isSystemTrayAvailable() self.setupUi(self) @@ -123,7 +124,7 @@ def _fill_extras_menu(self): hide_action = self.extra_menu.addAction(self.tr("Close this window")) hide_action.setShortcut(QKeySequence('Ctrl+W')) # noinspection PyUnresolvedReferences - hide_action.triggered.connect(self.hide) + hide_action.triggered.connect(self.hide_or_exit) exit_action = self.extra_menu.addAction(self.tr("Exit OpenFreebuds")) exit_action.setShortcut(QKeySequence('Ctrl+Q')) @@ -212,9 +213,14 @@ def _device_section_set_visible(self, visible): def closeEvent(self, e): if self.isVisible(): e.ignore() - self.hide() + self.hide_or_exit() return + def hide_or_exit(self): + self.hide() + if not self.config.get("ui", "background", True) or not self.tray_available: + self.on_exit() + def showEvent(self, e): e.accept() self._ui_update_task = asyncio.create_task(self._update_loop()) diff --git a/openfreebuds_qt/app/module/ui_settings.py b/openfreebuds_qt/app/module/ui_settings.py index 0325a76..5e3e002 100644 --- a/openfreebuds_qt/app/module/ui_settings.py +++ b/openfreebuds_qt/app/module/ui_settings.py @@ -1,6 +1,7 @@ import sys from PyQt6.QtCore import QLocale +from PyQt6.QtWidgets import QSystemTrayIcon from qasync import asyncSlot from openfreebuds import OfbEventKind @@ -11,7 +12,7 @@ from openfreebuds_qt.config import OfbQtConfigParser from openfreebuds_qt.designer.ui_settings import Ui_OfbQtUiSettingsModule from openfreebuds_qt.qt_i18n import get_shortcut_names -from openfreebuds_qt.utils import blocked_signals, list_available_locales +from openfreebuds_qt.utils import blocked_signals, list_available_locales, OfbCoreEvent log = create_logger("OfbQtUiSettingsModule") @@ -67,12 +68,25 @@ def __init__(self, *args, **kwargs): with blocked_signals(self.tray_dc_toggle): self.tray_dc_toggle.setChecked(self.config.get("ui", "tray_show_dual_connect", False)) + async def update_ui(self, event: OfbCoreEvent): + if not event.kind_match(OfbEventKind.QT_SETTINGS_CHANGED): + return + with blocked_signals(self.autostart_toggle): self.autostart_toggle.setChecked(is_run_at_boot()) if self.config.is_containerized_app: self.autostart_toggle.setVisible(False) + with blocked_signals(self.background_toggle): + self.background_toggle.setEnabled(QSystemTrayIcon.isSystemTrayAvailable()) + self.background_toggle.setChecked(self.config.get("ui", "background", True)) + + @asyncSlot(bool) + async def on_background_toggle(self, value: bool): + self.config.set("ui", "background", value) + self.config.save() + @asyncSlot(bool) async def on_autostart_toggle(self, value: bool): set_run_at_boot(value) diff --git a/openfreebuds_qt/designer/first_run_dialog.ui b/openfreebuds_qt/designer/first_run_dialog.ui index c73da20..4ffafca 100644 --- a/openfreebuds_qt/designer/first_run_dialog.ui +++ b/openfreebuds_qt/designer/first_run_dialog.ui @@ -131,26 +131,10 @@ - - - - - 0 - 0 - - - - If you're running under GNOME shell and can't find tray icon, please, check FAQ. - - - true - - - - Qt::Vertical + Qt::Orientation::Vertical @@ -170,6 +154,16 @@ + + + + Mininize to system tray instead of closing + + + true + + + @@ -204,7 +198,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal diff --git a/openfreebuds_qt/designer/main_window.ui b/openfreebuds_qt/designer/main_window.ui index aa29640..3fcbbdf 100644 --- a/openfreebuds_qt/designer/main_window.ui +++ b/openfreebuds_qt/designer/main_window.ui @@ -68,6 +68,9 @@ 16777215 + + Qt::ScrollBarPolicy::ScrollBarAlwaysOff + true @@ -76,8 +79,8 @@ 0 0 - 208 - 598 + 210 + 600 @@ -164,7 +167,7 @@ OpenFreebuds - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -334,7 +337,7 @@ - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -373,7 +376,7 @@ 10% - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -458,7 +461,7 @@ - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -497,7 +500,7 @@ 10% - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -582,7 +585,7 @@ - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -621,7 +624,7 @@ 10% - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -764,7 +767,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -854,8 +857,8 @@ 0 0 - 738 - 598 + 740 + 600 @@ -865,7 +868,7 @@ - true + false diff --git a/openfreebuds_qt/designer/ui_settings.ui b/openfreebuds_qt/designer/ui_settings.ui index 0f7119b..82ac63f 100644 --- a/openfreebuds_qt/designer/ui_settings.ui +++ b/openfreebuds_qt/designer/ui_settings.ui @@ -27,33 +27,17 @@ Main - - - - Launch at system startup - - - - - + + - Language: + Restart OpenFeebuds to apply changes - - - - - System - - - - - - + + - Restart OpenFeebuds to apply changes + Launch at system startup @@ -83,6 +67,29 @@ + + + + Language: + + + + + + + + System + + + + + + + + Mininize to system tray instead of closing + + + @@ -148,7 +155,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -274,6 +281,22 @@ + + background_toggle + toggled(bool) + OfbQtUiSettingsModule + on_background_toggle() + + + 247 + 202 + + + 247 + 261 + + + on_tray_eq_toggle(bool) @@ -283,5 +306,6 @@ on_tray_shortcut_choose(int) on_autostart_toggle(bool) on_updater_policy_choose(int) + on_background_toggle() diff --git a/openfreebuds_qt/main.py b/openfreebuds_qt/main.py index a5762b7..25874eb 100644 --- a/openfreebuds_qt/main.py +++ b/openfreebuds_qt/main.py @@ -6,7 +6,7 @@ from typing import Optional from PyQt6.QtCore import QLibraryInfo, QLocale, QTranslator, QT_VERSION_STR -from PyQt6.QtWidgets import QMessageBox +from PyQt6.QtWidgets import QMessageBox, QSystemTrayIcon from qasync import QEventLoop from openfreebuds import IOpenFreebuds, create as create_ofb, OfbEventKind @@ -30,6 +30,7 @@ def __init__(self, args): super().__init__(sys.argv) self.args = args + self.tray_available = QSystemTrayIcon.isSystemTrayAvailable() # Config folder if not STORAGE_PATH.is_dir(): @@ -117,9 +118,14 @@ async def boot(self): if float(".".join(QT_VERSION_STR.split(".")[:2])) < 6.7: self.show_old_qt_warning() + # System tray icon not available + if not self.tray_available: + self.show_no_tray_warning() + self.main_window.show() + # Show UI self.tray.show() - if self.args.settings: + if not self.config.get("ui", "background", True) or self.args.settings: self.main_window.show() if not self.config.get("ui", "first_run_finished", False): OfbQtFirstRunDialog(self).show() @@ -132,7 +138,7 @@ async def boot(self): async def exit(self, ret_code: int = 0): await self.tray.close() - self.main_window.close() + self.main_window.hide() if self.ofb.role == "standalone": await self.ofb.destroy() @@ -194,8 +200,28 @@ def exec_async(self): self.event_loop.run_until_complete(self.close_event.wait()) self.event_loop.close() + def show_no_tray_warning(self): + if self.config.get("warn", "no_tray", False): + return + + paragraph_1 = self.tr("System tray not available, application won't work in background. " + "This will make some features, like global hotkeys, unavailable.") + paragraph_2 = self.tr("If you're running under GNOME shell, please, check FAQ. " + "This warning will be shown only once.") + + QMessageBox( + QMessageBox.Icon.Warning, + "OpenFreebuds", + paragraph_1 + "\n\n" + paragraph_2, + QMessageBox.StandardButton.Ok, + self.main_window + ).show() + + self.config.set("warn", "no_tray", True) + self.config.save() + def show_old_qt_warning(self): - if self.config.get("ui", "old_qt", False): + if self.config.get("warn", "old_qt", False): return paragraph_1 = self.tr( @@ -214,5 +240,5 @@ def show_old_qt_warning(self): self.main_window ).show() - self.config.set("ui", "old_qt", True) + self.config.set("warn", "old_qt", True) self.config.save() From 8883c33124ddf5305ad5f94b89f68374b250ffb5 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 14 Oct 2024 18:44:18 +0700 Subject: [PATCH 23/35] [Fix] First-run dialog height --- debian/rules | 3 +-- openfreebuds_qt/designer/first_run_dialog.ui | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/debian/rules b/debian/rules index 058aae8..033bf30 100755 --- a/debian/rules +++ b/debian/rules @@ -7,8 +7,7 @@ override_dh_auto_build: python ./scripts/make.py build override_dh_auto_install: - python ./scripts/make.py install debian/openfreebuds/usr debian/openfreebuds/usr/lib/python3/dist-packages + python3 ./scripts/make.py install debian/openfreebuds/usr debian/openfreebuds/usr/lib/python3/dist-packages override_dh_clean: git clean -xfd -e accent.json - diff --git a/openfreebuds_qt/designer/first_run_dialog.ui b/openfreebuds_qt/designer/first_run_dialog.ui index 4ffafca..5b285b1 100644 --- a/openfreebuds_qt/designer/first_run_dialog.ui +++ b/openfreebuds_qt/designer/first_run_dialog.ui @@ -102,7 +102,7 @@ - + 0 0 @@ -118,7 +118,7 @@ - + 0 0 From 7f86ff271395c3faf4b1b14ebd80f1fd4e8d768d Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 14 Oct 2024 18:45:27 +0700 Subject: [PATCH 24/35] [Docs] Mark 6i as tested, close #43 --- docs/devices/HUAWEI_FreeBuds_6i.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/devices/HUAWEI_FreeBuds_6i.md b/docs/devices/HUAWEI_FreeBuds_6i.md index f58e06b..a3dc413 100644 --- a/docs/devices/HUAWEI_FreeBuds_6i.md +++ b/docs/devices/HUAWEI_FreeBuds_6i.md @@ -4,23 +4,23 @@ Protocol: Huawei SPP, port 1 ## Features -- Fetch device information and battery level: ❓ (untested) -- Fetch in-ear status: ❓ (untested) -- Sound quality preference: ❓ (untested) -- Low-latency mode: ❓ (untested) -- Control noise cancellation: ❓ (untested) +- Fetch device information and battery level: ✅ +- Fetch in-ear status: ✅ +- Sound quality preference: ✅ +- Low-latency mode: ✅ +- Control noise cancellation: ✅ - With cancellation level - With dynamic cancellation -- Set double-tap action: ❓ (untested) -- Set triple-tap action: ❓ (untested) -- Set swipe action: ❓ (untested) -- Wear detection (aka auto-pause) configuration: ❓ (untested) -- Set long-tap action: ❓ (untested) +- Set double-tap action: ✅ +- Set triple-tap action: ✅ +- Set swipe action: ✅ +- Wear detection (aka auto-pause) configuration: ✅ +- Set long-tap action: ✅ - Split configuration store -- Change voice language: ❓ (untested) +- Change voice language: ✅ - English, Chinese -- Dual connect: ❓ (untested) -- Equalizer: ❓ (untested) +- Dual connect: ✅ +- Equalizer: ✅ - 4 built-in presets - 2 fake built-in presets (stored in application) - Up to ??? custom presets in-device memory From e9c2470accb04fad0e6ff45b7c2d7f599004aa6b Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Mon, 14 Oct 2024 18:48:32 +0700 Subject: [PATCH 25/35] [fix] Skip first run wizard if tray not available --- openfreebuds_qt/main.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openfreebuds_qt/main.py b/openfreebuds_qt/main.py index 25874eb..fbeb123 100644 --- a/openfreebuds_qt/main.py +++ b/openfreebuds_qt/main.py @@ -118,16 +118,16 @@ async def boot(self): if float(".".join(QT_VERSION_STR.split(".")[:2])) < 6.7: self.show_old_qt_warning() - # System tray icon not available - if not self.tray_available: - self.show_no_tray_warning() - self.main_window.show() - # Show UI self.tray.show() if not self.config.get("ui", "background", True) or self.args.settings: self.main_window.show() - if not self.config.get("ui", "first_run_finished", False): + + # System tray availability + if not self.tray_available: + self.show_no_tray_warning() + self.main_window.show() + elif not self.config.get("ui", "first_run_finished", False): OfbQtFirstRunDialog(self).show() except SystemExit as e: self.qt_app.exit(e.args[0]) From a69c8585b229e52756f50f6514ef0c1c27f30b12 Mon Sep 17 00:00:00 2001 From: Lobo <88998991+Lobooooooo14@users.noreply.github.com> Date: Tue, 15 Oct 2024 06:22:47 -0300 Subject: [PATCH 26/35] [i18n] Fixes in pt-BR translations (#45) * [i18n] complete pt-BR translations * [i18n] fix pt-br translations issues * [i18n] fix "Wear detection" translation --- openfreebuds_qt/assets/i18n/pt-BR.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openfreebuds_qt/assets/i18n/pt-BR.ts b/openfreebuds_qt/assets/i18n/pt-BR.ts index 5eab898..8f91312 100644 --- a/openfreebuds_qt/assets/i18n/pt-BR.ts +++ b/openfreebuds_qt/assets/i18n/pt-BR.ts @@ -102,12 +102,12 @@ Hide - Esconder + Ocultar Update now - Atualizar + Atualizar agora @@ -169,7 +169,7 @@ Firmware version: - Versão do firmware + Versão do firmware: @@ -212,7 +212,7 @@ Wear detection - Detecção de desgaste + Detecção de uso @@ -227,7 +227,7 @@ Enable low-latency mode - Ative o modo de baixa latência + Ativar o modo de baixa latência @@ -1008,7 +1008,7 @@ Icon color - Tema de ícones + Cor do ícone @@ -1064,7 +1064,7 @@ Connect/disconnect device - Conectar ou desconectar + Conectar/desconectar dispositivo From be9cf3559950c543a004b525553e77005d092d7a Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 15 Oct 2024 16:35:09 +0700 Subject: [PATCH 27/35] [i18n] Sync translations --- openfreebuds_qt/assets/i18n/en.ts | 196 +++++++++++++++++------- openfreebuds_qt/assets/i18n/es.ts | 218 ++++++++++++++++++--------- openfreebuds_qt/assets/i18n/pt-BR.ts | 218 ++++++++++++++++++--------- openfreebuds_qt/assets/i18n/ru.ts | 218 ++++++++++++++++++--------- 4 files changed, 585 insertions(+), 265 deletions(-) diff --git a/openfreebuds_qt/assets/i18n/en.ts b/openfreebuds_qt/assets/i18n/en.ts index 5457b96..88ccaf6 100644 --- a/openfreebuds_qt/assets/i18n/en.ts +++ b/openfreebuds_qt/assets/i18n/en.ts @@ -22,55 +22,65 @@ EqPresetName - + Default - + Bass-boost - + Treble-boost - + Voices + + + Symphony + + + + + Hi-Fi Live + + OfbDeviceAncLevelTrayMenu - + Comfortable - + Normal - + Ultra - + Dynamic - + Voice boost - + Intensity… @@ -93,6 +103,36 @@ OfbMainWindowDesign + + + Left headphone + + + + + Right headphone + + + + + Battery level + + + + + Cancellation + + + + + Disable noise control + + + + + Awareness + + New version available, click here to view @@ -145,12 +185,22 @@ OfbQtApplication - + + System tray not available, application won't work in background. This will make some features, like global hotkeys, unavailable. + + + + + If you're running under GNOME shell, please, check FAQ. This warning will be shown only once. + + + + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. @@ -163,6 +213,14 @@ + + OfbQtDeviceControlViewHelper + + + Connecting… + + + OfbQtDeviceInfoModule @@ -373,12 +431,12 @@ - If you're running under GNOME shell and can't find tray icon, please, check FAQ. + Launch OpenFreebuds at system boot - Launch OpenFreebuds at system boot + Mininize to system tray instead of closing @@ -620,97 +678,97 @@ OfbQtMainWindow - + Select device - + Device info - + Dual-connect - + Gestures - + Sound quality - + Other settings - + Application - + User interface - + Keyboard shortcuts - + Linux-related - + About… - + Help: FAQ - + Help: Remote control - + Bugreport… - + Check for updates… - + Remote access… - + Temporary replace device - + Close this window - + Exit OpenFreebuds @@ -844,42 +902,54 @@ - - Create new equalizer preset + + Notice - - Enter new preset name: + + This preset isn't available in your device, it will be created as custom preset. - + + Failed - + + Can't create: too many custom preset created in device. - + + Create new equalizer preset + + + + + Enter new preset name: + + + + Delete equalizer mode? - + Will delete following mode: - + Save equalizer preset to file… - + Load equalizer preset from file… @@ -959,6 +1029,11 @@ Main + + + Restart OpenFeebuds to apply changes + + Launch at system startup @@ -966,37 +1041,37 @@ - Language: + Update checker: - System + Notify about new versions - Restart OpenFeebuds to apply changes + Check for new versions, but don't notify - Update checker: + Disabled - Notify about new versions + Language: - Check for new versions, but don't notify + System - Disabled + Mininize to system tray instead of closing @@ -1051,42 +1126,47 @@ ShortcutName - + + Show OpenFreebuds window + + + + Connect device - + Disconnect device - + Connect/disconnect device - + Next noise control mode - + Disable noise control - + Enable noise cancellation - + Enable awareness mode - + Enable low-latency mode diff --git a/openfreebuds_qt/assets/i18n/es.ts b/openfreebuds_qt/assets/i18n/es.ts index 937c92b..ca013bf 100644 --- a/openfreebuds_qt/assets/i18n/es.ts +++ b/openfreebuds_qt/assets/i18n/es.ts @@ -22,55 +22,65 @@ EqPresetName - + Default Por defecto - + Bass-boost Refuerzo de graves - + Treble-boost Refuerzo de agudos - + Voices Voces + + + Symphony + + + + + Hi-Fi Live + + OfbDeviceAncLevelTrayMenu - + Comfortable Cómodo - + Normal Normal - + Ultra Ultra - + Dynamic Dinámico - + Voice boost Potenciar voces - + Intensity… Intensidad… @@ -93,6 +103,36 @@ OfbMainWindowDesign + + + Left headphone + + + + + Right headphone + + + + + Battery level + + + + + Cancellation + + + + + Disable noise control + Deshabilitar cancelación de ruido + + + + Awareness + Exterior + New version available, click here to view @@ -145,12 +185,22 @@ OfbQtApplication - + + System tray not available, application won't work in background. This will make some features, like global hotkeys, unavailable. + + + + + If you're running under GNOME shell, please, check FAQ. This warning will be shown only once. + + + + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. Está ejecutando una versión de Qt antigua. Te recomendamos cambiar a la versión de Flatpak, ya que una versión anterior de Qt puede afectar negativamente a la experiencia de uso de OpenFreebuds. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. Este aviso sólo se mostrará una vez. Por favor, prueba la versión de Flatpak antes de informar de errores. @@ -163,6 +213,14 @@ Para utilizar esta función, se requieren dependencias adicionales. ¿Quieres instalarlas ahora? + + OfbQtDeviceControlViewHelper + + + Connecting… + + + OfbQtDeviceInfoModule @@ -373,13 +431,13 @@ - If you're running under GNOME shell and can't find tray icon, please, check FAQ. - Si está ejecutando GNOME shell y no puedes encontrar el icono de la bandeja, por favor, consulta las FAQ. + Launch OpenFreebuds at system boot + Iniciar OpenFreebuds al arrancar el sistema - Launch OpenFreebuds at system boot - Iniciar OpenFreebuds al arrancar el sistema + Mininize to system tray instead of closing + @@ -620,97 +678,97 @@ OfbQtMainWindow - + Select device Seleccionar dispositivo - + Device info Información del dispositivo - + Dual-connect Conexión dual - + Gestures Gestos - + Sound quality Calidad del sonido - + Other settings Otros ajustes - + Application Aplicación - + User interface Interfaz de usuario - + Keyboard shortcuts Atajo del teclado - + Linux-related Ajustes de Linux - + About… Acerca de… - + Help: FAQ Ayuda: FAQ - + Help: Remote control Ayuda: Control remoto - + Bugreport… Reportar errores… - + Check for updates… Comprobar actualizaciones… - + Remote access… Acceso remoto… - + Temporary replace device Reemplazar dispositivo temporalmente - + Close this window Cerrar la ventana - + Exit OpenFreebuds Cerrar OpenFreebuds @@ -844,42 +902,54 @@ Cargar archivo… - - Create new equalizer preset - Nuevo perfil del equalizador + + Notice + - - Enter new preset name: - Nombre del perfil: + + This preset isn't available in your device, it will be created as custom preset. + - + + Failed Falló - + + Can't create: too many custom preset created in device. No se puede crear: demasiados perfiles personalizados creados en el dispositivo. - + + Create new equalizer preset + Nuevo perfil del equalizador + + + + Enter new preset name: + Nombre del perfil: + + + Delete equalizer mode? ¿Borrar el perfil de equalizador? - + Will delete following mode: Se borrará el siguiente perfil: - + Save equalizer preset to file… Guardar perfil del equalizador… - + Load equalizer preset from file… Cargar perfil del equalizador… @@ -961,23 +1031,13 @@ - Launch at system startup - Abrir al arrancar el sistema - - - - Language: - Idioma: - - - - System - Sistema + Restart OpenFeebuds to apply changes + Reinicia OpenFeebuds para aplicar los cambios - Restart OpenFeebuds to apply changes - Reinicia OpenFeebuds para aplicar los cambios + Launch at system startup + Abrir al arrancar el sistema @@ -999,6 +1059,21 @@ Disabled Deshabilitado + + + Language: + Idioma: + + + + System + Sistema + + + + Mininize to system tray instead of closing + + Tray applet @@ -1051,42 +1126,47 @@ ShortcutName - + + Show OpenFreebuds window + + + + Connect device Conectar dispositivo - + Disconnect device Desconectar dispositivo - + Connect/disconnect device Conectar/desconectar dispositivo - + Next noise control mode Siguiente modo de cancelación de ruido - + Disable noise control Deshabilitar cancelación de ruido - + Enable noise cancellation Habilitar cancelación de ruido - + Enable awareness mode Deshabilitar modo exterior - + Enable low-latency mode Activar el modo de baja latencia diff --git a/openfreebuds_qt/assets/i18n/pt-BR.ts b/openfreebuds_qt/assets/i18n/pt-BR.ts index 8f91312..29ccd7c 100644 --- a/openfreebuds_qt/assets/i18n/pt-BR.ts +++ b/openfreebuds_qt/assets/i18n/pt-BR.ts @@ -23,55 +23,65 @@ EqPresetName - + Default Padrão - + Bass-boost Reforço de graves - + Treble-boost Aumento de agudos - + Voices Vozes + + + Symphony + + + + + Hi-Fi Live + + OfbDeviceAncLevelTrayMenu - + Comfortable Confortável - + Normal Normal - + Ultra Ultra - + Dynamic Dinâmico - + Voice boost Aumento de voz - + Intensity… Intensidade… @@ -94,6 +104,36 @@ OfbMainWindowDesign + + + Left headphone + + + + + Right headphone + + + + + Battery level + + + + + Cancellation + + + + + Disable noise control + Desabilitar cancelamento de ruído + + + + Awareness + Sensibilidade ao ambiente + New version available, click here to view @@ -146,12 +186,22 @@ OfbQtApplication - + + System tray not available, application won't work in background. This will make some features, like global hotkeys, unavailable. + + + + + If you're running under GNOME shell, please, check FAQ. This warning will be shown only once. + + + + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. Você está executando uma versão mais antiga do Qt do que o esperado. É altamente recomendável mudar para a versão Flatpak, porque a versão mais antiga do Qt pode prejudicar sua experiência de uso do OpenFreebuds. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. Este aviso será mostrado apenas uma vez. Por favor, teste a versão Flatpak antes de relatar bugs. @@ -164,6 +214,14 @@ Para usar este recurso, são necessárias dependências adicionais. Instalar agora? + + OfbQtDeviceControlViewHelper + + + Connecting… + + + OfbQtDeviceInfoModule @@ -374,13 +432,13 @@ - If you're running under GNOME shell and can't find tray icon, please, check FAQ. - Se você estiver executando o shell do GNOME e não conseguir encontrar o ícone da bandeja, verifique o FAQ. + Launch OpenFreebuds at system boot + Inicie o OpenFreebuds na inicialização do sistema - Launch OpenFreebuds at system boot - Inicie o OpenFreebuds na inicialização do sistema + Mininize to system tray instead of closing + @@ -621,97 +679,97 @@ OfbQtMainWindow - + Select device Selecione o dispositivo - + Device info Dispositivo - + Dual-connect Conexão dupla - + Gestures Gestos - + Sound quality Qualidade de som - + Other settings Outras configurações - + Application Aplicativo - + User interface Interface do usuário - + Keyboard shortcuts Atalhos de teclado - + Linux-related Relacionado ao Linux - + About… Sobre… - + Help: FAQ Ajuda: Perguntas frequentes (FAQ) - + Help: Remote control Ajuda: Controle remoto - + Bugreport… Reportar erro… - + Check for updates… Verifique se há atualizações… - + Remote access… Acesso remoto… - + Temporary replace device Dispositivo de substituição temporária - + Close this window Fechar esta janela - + Exit OpenFreebuds Sair do OpenFreebuds @@ -845,42 +903,54 @@ Carregar arquivo… - - Create new equalizer preset - Crie uma nova predefinição de equalizador + + Notice + - - Enter new preset name: - Insira o novo nome predefinido: + + This preset isn't available in your device, it will be created as custom preset. + - + + Failed Falhou - + + Can't create: too many custom preset created in device. Não é possível criar: muitas predefinições personalizadas criadas no dispositivo. - + + Create new equalizer preset + Crie uma nova predefinição de equalizador + + + + Enter new preset name: + Insira o novo nome predefinido: + + + Delete equalizer mode? Excluir modo do equalizador? - + Will delete following mode: Será excluído o seguinte modo: - + Save equalizer preset to file… Salvar predefinição do equalizador em arquivo… - + Load equalizer preset from file… Carregar predefinição do equalizador do arquivo… @@ -962,23 +1032,13 @@ - Launch at system startup - Iniciar na inicialização do sistema - - - - Language: - Idioma: - - - - System - Sistema + Restart OpenFeebuds to apply changes + Reinicie o OpenFeebuds para aplicar as alterações - Restart OpenFeebuds to apply changes - Reinicie o OpenFeebuds para aplicar as alterações + Launch at system startup + Iniciar na inicialização do sistema @@ -1000,6 +1060,21 @@ Disabled Desabilitado + + + Language: + Idioma: + + + + System + Sistema + + + + Mininize to system tray instead of closing + + Tray applet @@ -1052,42 +1127,47 @@ ShortcutName - + + Show OpenFreebuds window + + + + Connect device Conectar dispositivo - + Disconnect device Desconectar dispositivo - + Connect/disconnect device Conectar/desconectar dispositivo - + Next noise control mode Proximo modo de cancelamento de ruído - + Disable noise control Desabilitar cancelamento de ruído - + Enable noise cancellation Ativar cancelamento de ruído - + Enable awareness mode Ativar modo de sensibilidade ao ambiente - + Enable low-latency mode Ativar o modo de baixa latência diff --git a/openfreebuds_qt/assets/i18n/ru.ts b/openfreebuds_qt/assets/i18n/ru.ts index e43e8e4..c7ad299 100644 --- a/openfreebuds_qt/assets/i18n/ru.ts +++ b/openfreebuds_qt/assets/i18n/ru.ts @@ -23,55 +23,65 @@ EqPresetName - + Default По умолчанию - + Bass-boost Больше баса - + Treble-boost Больше высоких - + Voices Вокал + + + Symphony + + + + + Hi-Fi Live + + OfbDeviceAncLevelTrayMenu - + Comfortable Комфортный - + Normal Обычный - + Ultra Максимальное - + Dynamic Динамический - + Voice boost Усиление голоса - + Intensity… Интенсивность… @@ -94,6 +104,36 @@ OfbMainWindowDesign + + + Left headphone + + + + + Right headphone + + + + + Battery level + + + + + Cancellation + + + + + Disable noise control + Отключить шумоподавление + + + + Awareness + Прозрачность + New version available, click here to view @@ -146,12 +186,22 @@ OfbQtApplication - + + System tray not available, application won't work in background. This will make some features, like global hotkeys, unavailable. + + + + + If you're running under GNOME shell, please, check FAQ. This warning will be shown only once. + + + + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. Вы используете более старую версию Qt, чем ожидалось. Рекомендуем перейти на Flatpak-версию программы, поскольку более старые версии Qt, предоставляемые вашей системой, могут пагубно повлиять на работу программы. - + This warning will be shown only once. Please, test Flatpak version before reporting bugs. Это предупреждение будет показано лишь один раз. Перед тем, как сообщать о проблемах, протестируйте Flatpak версию. @@ -164,6 +214,14 @@ Для этой функции требуется дополнительный компонент. Перейти к установке? + + OfbQtDeviceControlViewHelper + + + Connecting… + + + OfbQtDeviceInfoModule @@ -374,13 +432,13 @@ - If you're running under GNOME shell and can't find tray icon, please, check FAQ. - Если вы используете рабочий стол GNOME и не можете найти значок в области уведомлений, ознакомьтесь с ЧаВо. + Launch OpenFreebuds at system boot + Запускать OpenFreebuds при включении ПК - Launch OpenFreebuds at system boot - Запускать OpenFreebuds при включении ПК + Mininize to system tray instead of closing + @@ -621,97 +679,97 @@ OfbQtMainWindow - + Select device Выбрать устройство - + Device info Об устройстве - + Dual-connect Двойное подключение - + Gestures Жесты - + Sound quality Качество звука - + Other settings Прочие настройки - + Application Приложение - + User interface Внешний вид - + Keyboard shortcuts Сочетания клавиш - + Linux-related Linux-специфичное - + About… О программе… - + Help: FAQ Помощь: Частые вопросы - + Help: Remote control Помощь: Удалённое управление - + Bugreport… Отчёт об ошибке… - + Check for updates… Проверить наличие обновлений… - + Remote access… Удалённый доступ… - + Temporary replace device Временно сменить устройство - + Close this window Закрыть окно - + Exit OpenFreebuds Закрыть OpenFreebuds @@ -845,42 +903,54 @@ Загрузить файл… - - Create new equalizer preset - Создать новую предустановку эквалайзера + + Notice + - - Enter new preset name: - Введите название новой предустановки: + + This preset isn't available in your device, it will be created as custom preset. + - + + Failed Ошибка - + + Can't create: too many custom preset created in device. Не удалось: достигнут лимит кол-ва предустановок в памяти устройства. - + + Create new equalizer preset + Создать новую предустановку эквалайзера + + + + Enter new preset name: + Введите название новой предустановки: + + + Delete equalizer mode? Удалить предустановку эквалайзера? - + Will delete following mode: Будет удалена предустановка: - + Save equalizer preset to file… Сохранить предустановку в файл… - + Load equalizer preset from file… Загрузить предустановку из файла… @@ -962,23 +1032,13 @@ - Launch at system startup - Запускать вместе с системой - - - - Language: - Язык интерфейса: - - - - System - Системный + Restart OpenFeebuds to apply changes + Перезапустите OpenFreebuds для применения изменения - Restart OpenFeebuds to apply changes - Перезапустите OpenFreebuds для применения изменения + Launch at system startup + Запускать вместе с системой @@ -1000,6 +1060,21 @@ Disabled Отключить + + + Language: + Язык интерфейса: + + + + System + Системный + + + + Mininize to system tray instead of closing + + Tray applet @@ -1052,42 +1127,47 @@ ShortcutName - + + Show OpenFreebuds window + + + + Connect device Подключить устройство - + Disconnect device Отсоединить устройство - + Connect/disconnect device Подключить/отключить устройство - + Next noise control mode Следующий режим шумоподавления - + Disable noise control Отключить шумоподавление - + Enable noise cancellation Включить шумоподавление - + Enable awareness mode Включить режим прозрачность - + Enable low-latency mode Включить режим низкой задержки From 4b26e5efed69e5fafc4a6ac2fdc778b4c5088680 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 15 Oct 2024 16:51:57 +0700 Subject: [PATCH 28/35] [i18n] Update ru translation --- openfreebuds_qt/assets/i18n/ru.ts | 1422 ++++++++++++++--------------- 1 file changed, 711 insertions(+), 711 deletions(-) diff --git a/openfreebuds_qt/assets/i18n/ru.ts b/openfreebuds_qt/assets/i18n/ru.ts index c7ad299..513a0f2 100644 --- a/openfreebuds_qt/assets/i18n/ru.ts +++ b/openfreebuds_qt/assets/i18n/ru.ts @@ -1,1175 +1,1175 @@ - - # - + + # + Dialog - - Unsupported device - Неподдерживаемое устройство + + Unsupported device + Неподдерживаемое устройство - - This device isn't supported, for yet. But if you want, you can force connect them. To do that, select an exiting profile. Then application will try to apply it to your device. You can change profile in any time. - Это устройство не поддерживается, по крайней мере сейчас. Но, вы можете подключить его принудительно. Для этого выберите профиль в списке ниже. Тогда приложение попытается применить его к вашему устройству. Эту настройку можно изменить в любое время. + + This device isn't supported, for yet. But if you want, you can force connect them. To do that, select an exiting profile. Then application will try to apply it to your device. You can change profile in any time. + Это устройство не поддерживается, по крайней мере сейчас. Но, вы можете подключить его принудительно. Для этого выберите профиль в списке ниже. Тогда приложение попытается применить его к вашему устройству. Эту настройку можно изменить в любое время. - - Notice that this may be dangerous for some kind of devices. Continue only if you know what you're doing. - Учтите, что это может быть опасным для некоторых устройств. Продолжайте только если знаете, что делаете. + + Notice that this may be dangerous for some kind of devices. Continue only if you know what you're doing. + Учтите, что это может быть опасным для некоторых устройств. Продолжайте только если знаете, что делаете. - - + + EqPresetName - - Default - По умолчанию + + Default + По умолчанию - - Bass-boost - Больше баса + + Bass-boost + Больше баса - - Treble-boost - Больше высоких + + Treble-boost + Больше высоких - - Voices - Вокал + + Voices + Вокал - - Symphony - + + Symphony + Симфония - - Hi-Fi Live - + + Hi-Fi Live + Живая музыка - - + + OfbDeviceAncLevelTrayMenu - - Comfortable - Комфортный + + Comfortable + Комфортный - - Normal - Обычный + + Normal + Обычный - - Ultra - Максимальное + + Ultra + Максимальное - - Dynamic - Динамический + + Dynamic + Динамический - - Voice boost - Усиление голоса + + Voice boost + Усиление голоса - - Intensity… - Интенсивность… + + Intensity… + Интенсивность… - - + + OfbDeviceDualConnectTrayMenu - - Dual-connect… - Двойное подключение… + + Dual-connect… + Двойное подключение… - - + + OfbDeviceEqualizerTrayMenu - - Equalizer preset… - Эквалайзер… + + Equalizer preset… + Эквалайзер… - - + + OfbMainWindowDesign - - Left headphone - + + Left headphone + Левый наушник - - Right headphone - + + Right headphone + Правый наушник - - Battery level - + + Battery level + Заряд батареи - - Cancellation - + + Cancellation + Шумоподавление - - Disable noise control - Отключить шумоподавление + + Disable noise control + Отключить шумоподавление - - Awareness - Прозрачность + + Awareness + Прозрачность - - New version available, click here to view - Доступна новая версия, нажмите здесь для просмотра + + New version available, click here to view + Доступна новая версия, нажмите здесь для просмотра - - Hide - Скрыть + + Hide + Скрыть - - Update now - Обновить сейчас + + Update now + Обновить сейчас - - Exit - Выход + + Exit + Выход - - + + OfbQtAboutModule - - Client application for HUAWEI FreeBuds wireless earphone series. Free and open source. - Приложения для управления наушниками серии HUAWEI FreeBuds Бесплатно и с открытым исходным кодом. + + Client application for HUAWEI FreeBuds wireless earphone series. Free and open source. + Приложения для управления наушниками серии HUAWEI FreeBuds Бесплатно и с открытым исходным кодом. - - Website - Веб-сайт + + Website + Веб-сайт - - Source code - Исходный код + + Source code + Исходный код - - Supported devices - Совместимые устройства + + Supported devices + Совместимые устройства - - Libraries - Библиотеки + + Libraries + Библиотеки - - + + OfbQtApplication - - System tray not available, application won't work in background. This will make some features, like global hotkeys, unavailable. - + + System tray not available, application won't work in background. This will make some features, like global hotkeys, unavailable. + Область уведомлений недоступна, приложение не сможет работать в фоне. Из-за этого часть функций, вроде сочетаний клавиш, могут быть недоступны. - - If you're running under GNOME shell, please, check FAQ. This warning will be shown only once. - + + If you're running under GNOME shell, please, check FAQ. This warning will be shown only once. + Если вы используете GNOME Shell, ознакомьтесь с ЧаВо.Это предупреждение больше не будет показано. - - You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. - Вы используете более старую версию Qt, чем ожидалось. Рекомендуем перейти на Flatpak-версию программы, поскольку более старые версии Qt, предоставляемые вашей системой, могут пагубно повлиять на работу программы. + + You're running under older version of Qt than expected. It's strongly recommended to switch to Flatpak release, because older Qt version may fail your experience of using OpenFreebuds. + Вы используете более старую версию Qt, чем ожидалось. Рекомендуем перейти на Flatpak-версию программы, поскольку более старые версии Qt, предоставляемые вашей системой, могут пагубно повлиять на работу программы. - - This warning will be shown only once. Please, test Flatpak version before reporting bugs. - Это предупреждение будет показано лишь один раз. Перед тем, как сообщать о проблемах, протестируйте Flatpak версию. + + This warning will be shown only once. Please, test Flatpak version before reporting bugs. + Это предупреждение будет показано лишь один раз. Перед тем, как сообщать о проблемах, протестируйте Flatpak версию. - - + + OfbQtDependencyMissingDialog - - To use this feature, additional dependencies is required. Install it now? - Для этой функции требуется дополнительный компонент. Перейти к установке? + + To use this feature, additional dependencies is required. Install it now? + Для этой функции требуется дополнительный компонент. Перейти к установке? - - + + OfbQtDeviceControlViewHelper - - Connecting… - + + Connecting… + Подключение… - - + + OfbQtDeviceInfoModule - - Firmware version: - Версия прошивки: + + Firmware version: + Версия прошивки: - - Bluetooth address: - Bluetooth адрес: + + Bluetooth address: + Bluetooth адрес: - - All available device information fields: - Вся известная информация об устройстве: + + All available device information fields: + Вся известная информация об устройстве: - - Property - Свойство + + Property + Свойство - - Value - Значение + + Value + Значение - - Battery last charged: - Последняя зарядка аккумулятора: + + Battery last charged: + Последняя зарядка аккумулятора: - - Unknown - Неизвестно + + Unknown + Неизвестно - - + + OfbQtDeviceOtherSettingsModule - - Interaction - Взаимодействие + + Interaction + Взаимодействие - - Wear detection - Обнаружение ношения + + Wear detection + Обнаружение ношения - - Pause audio or switch device when earphones are removed - Приостанавливать звук или переключать устройства при извлечении наушника + + Pause audio or switch device when earphones are removed + Приостанавливать звук или переключать устройства при извлечении наушника - - Networking - Связь + + Networking + Связь - - Enable low-latency mode - Включить режим низкой задержки + + Enable low-latency mode + Включить режим низкой задержки - - Will try to reduce audio latency when using headphones in enviroments with high network load. This option is disabled when your earphones are disconnected. Applies only for current device. - Устройство попытается снизить задержку звука для использования в окружении с высокой сетевой засорённостью. Эта опция отключится при отсоединении наушников. Применяется только для текущего клиента. + + Will try to reduce audio latency when using headphones in enviroments with high network load. This option is disabled when your earphones are disconnected. Applies only for current device. + Устройство попытается снизить задержку звука для использования в окружении с высокой сетевой засорённостью. Эта опция отключится при отсоединении наушников. Применяется только для текущего клиента. - - Service - Служебное + + Service + Служебное - - Device language: - Язык устройства: + + Device language: + Язык устройства: - - Due to technical restrictions, current language can't be readden from device and this field will be empty. - Из-за технических ограничений прочитать текущий язык устройства невозможно, и поле выше будет пустым. + + Due to technical restrictions, current language can't be readden from device and this field will be empty. + Из-за технических ограничений прочитать текущий язык устройства невозможно, и поле выше будет пустым. - - + + OfbQtDeviceSelectModule - - Select device automatically - Выбирать устройство автоматически + + Select device automatically + Выбирать устройство автоматически - - OpenFreebuds will automatically detect currently connected headset and switch to them. - OpenFreebuds будет автоматически определять подключенные наушники и переключится на них. + + OpenFreebuds will automatically detect currently connected headset and switch to them. + OpenFreebuds будет автоматически определять подключенные наушники и переключится на них. - - List bellow shows all Bluetooth devices paired with your device. Select appropriate device that you want to use with OpenFreebuds: - Список ниже содержит все Bluetooth-устройства, сопряжённые с вашим ПК. Выберите нужное для работы с OpenFreebuds. + + List bellow shows all Bluetooth devices paired with your device. Select appropriate device that you want to use with OpenFreebuds: + Список ниже содержит все Bluetooth-устройства, сопряжённые с вашим ПК. Выберите нужное для работы с OpenFreebuds: - - Device isn't listed here - Устройства нет в списке + + Device isn't listed here + Устройства нет в списке - - Refresh list - Обновить список + + Refresh list + Обновить список - - + + OfbQtDualConnectModule - - Refresh - Обновить + + Refresh + Обновить - - Allow connecting to multiple devices at once - Разрешить мульти-подключение + + Allow connecting to multiple devices at once + Разрешить мульти-подключение - - This page allows you to manage devices that are paired with your headphones. - Эта страница позволяет управлять устройствами, сопряжёнными с вашими наушниками. + + This page allows you to manage devices that are paired with your headphones. + Эта страница позволяет управлять устройствами, сопряжёнными с вашими наушниками. - - Bluetooth address: - Bluetooth адрес: + + Bluetooth address: + Bluetooth адрес: - - Device name: - Название: + + Device name: + Название: - - Preferred device - Предпочитаемое устройство + + Preferred device + Предпочитаемое устройство - - Auto connect this device - Авто-подключение + + Auto connect this device + Авто-подключение - - - Connect - Подключить + + + Connect + Подключить - - Unpair - Удалить + + Unpair + Удалить - - Disconnect - Отсоединить + + Disconnect + Отсоединить - - Unpair device - Отменить сопряжение устройства + + Unpair device + Отменить сопряжение устройства - - Do you really want to unpair %1 from your headphones? - Вы действительно хотите разорвать сопряжение между %1 и вашими наушниками? + + Do you really want to unpair %1 from your headphones? + Вы действительно хотите разорвать сопряжение между %1 и вашими наушниками? - - + + OfbQtErrorDialog - - OpenFreebuds ran into error. Please, save bugreport and send them to developer. Bugreport will be generated after clicking on "Close" button. - В работе OpenFreebuds произошла критическая ошибка. Пожалуйста, сохраните отчёт об ошибке и передайте разработчику, он будет сформирован после нажатия на кнопку "Закрыть". + + OpenFreebuds ran into error. Please, save bugreport and send them to developer. Bugreport will be generated after clicking on "Close" button. + В работе OpenFreebuds произошла критическая ошибка. Пожалуйста, сохраните отчёт об ошибке и передайте разработчику, он будет сформирован после нажатия на кнопку "Закрыть". - - Application will be closed, restart it manually. - Программа будет закрыта, перезапустите её вручную. + + Application will be closed, restart it manually. + Программа будет закрыта, перезапустите её вручную. - - + + OfbQtFirstRunDialog - - Welcome - Добро пожаловать + + Welcome + Добро пожаловать - - This application allows you to manage your HUAWEI Bluetooth earphones. To access them, look for headphones icon in system tray panel, near other icons. - Это приложение позволяет управлять вашим устройством серии HUAWEI FreeBuds. Для доступа к ним, найдите значок в виде наушников на панели задачи, в области уведомлений. + + This application allows you to manage your HUAWEI Bluetooth earphones. To access them, look for headphones icon in system tray panel, near other icons. + Это приложение позволяет управлять вашим устройством серии HUAWEI FreeBuds. Для доступа к ним, найдите значок в виде наушников на панели задачи, в области уведомлений. - - Left-click on this icon will cycle through noise cancellation modes (can be configured), right-click will provide access to full battery status and main options. Settings window provides access to all features. - Левый клик по этой иконке будет переключать режимы шумоподавления (поведение можно настроить), правый клик предоставит доступ к полной информации о состоянии батареи утройства и основным опциям. Окно "Настройки" предоставит полный доступ до всех опций, доступных в устройстве. + + Left-click on this icon will cycle through noise cancellation modes (can be configured), right-click will provide access to full battery status and main options. Settings window provides access to all features. + Левый клик по этой иконке будет переключать режимы шумоподавления (поведение можно настроить), правый клик предоставит доступ к полной информации о состоянии батареи утройства и основным опциям. Окно "Настройки" предоставит полный доступ до всех опций, доступных в устройстве. - - Launch OpenFreebuds at system boot - Запускать OpenFreebuds при включении ПК + + Launch OpenFreebuds at system boot + Запускать OpenFreebuds при включении ПК - - Mininize to system tray instead of closing - + + Mininize to system tray instead of closing + Скрывать в облесть уведомлений вместо закрытия - - You could change this options anytime later in settings. - Вы можете изменить свой выбор в любой момент через настройки программы. + + You could change this options anytime later in settings. + Вы можете изменить свой выбор в любой момент через настройки программы. - - FAQ - ЧаВо + + FAQ + ЧаВо - - Get started - Начать + + Get started + Начать - - + + OfbQtGesturesModule - - Long-tap - Долгое касание + + Long-tap + Долгое касание - - Triple-tap - Тройное касание + + Triple-tap + Тройное касание - - Double-tap in call - …во время звонка + + Double-tap in call + …во время звонка - - Left - Левый + + Left + Левый - - Power button double-tap - Двойной клик кнопки питания + + Power button double-tap + Двойной клик кнопки питания - - Customize device touch panel(s) and button(s) behaviour. This settings are stored inside your device and will work also if OpenFreebuds is closed. - Настройка поведения сенсорных панелей и кнопок на устройстве. Эти настройки хранятся прямо в устройстве и работают даже если OpenFreebuds закрыт. + + Customize device touch panel(s) and button(s) behaviour. This settings are stored inside your device and will work also if OpenFreebuds is closed. + Настройка поведения сенсорных панелей и кнопок на устройстве. Эти настройки хранятся прямо в устройстве и работают даже если OpenFreebuds закрыт. - - Swipe gesture - Свайп по наушнику + + Swipe gesture + Свайп по наушнику - - Double-tap - Двойное касание + + Double-tap + Двойное касание - - Right - Правый + + Right + Правый - - Preferred modes - Предпочитаемые режимы + + Preferred modes + Предпочитаемые режимы - - Answer to call - Ответить на звонок + + Answer to call + Ответить на звонок - - Adjust volume - Изменить громкость + + Adjust volume + Изменить громкость - - Voice assistant - Голосовой помощник + + Voice assistant + Голосовой помощник - - Next track - Следующий трек + + Next track + Следующий трек - - - Disabled - Отключено + + + Disabled + Отключено - - Play/pause - Воспроизвести/Пауза + + Play/pause + Воспроизвести/Пауза - - Prev track - Предыдущий трек + + Prev track + Предыдущий трек - - Switch device - Сменить устройство + + Switch device + Сменить устройство - - Switch noise control mode - Сменить режим шумоподавления + + Switch noise control mode + Сменить режим шумоподавления - - Off and cancellation - Отключен и шумоподавление + + Off and cancellation + Отключен и шумоподавление - - Cycle all modes - Все режимы + + Cycle all modes + Все режимы - - Cancellation and awareness - Прозрачность и шумоподавление + + Cancellation and awareness + Прозрачность и шумоподавление - - Off and awareness - Отключен и прозрачность + + Off and awareness + Отключен и прозрачность - - + + OfbQtHotkeysModule - - Enable global keyboard shortcuts - Включить глобальные сочетания клавиш + + Enable global keyboard shortcuts + Включить глобальные сочетания клавиш - - Here you can configure system-wide keyboard shortcuts for OpenFreebuds - Здесь вы можете настроить глобальные сочетания клавиш для OpenFreebuds. + + Here you can configure system-wide keyboard shortcuts for OpenFreebuds + Здесь вы можете настроить глобальные сочетания клавиш для OpenFreebuds - - Action - Действие + + Action + Действие - - Shortcut - Действие + + Shortcut + Действие - - Hint: to remove already assigned shortcut, press Esc while recording a new one. - Подсказка: для удавления уже назначенного сочетания нажмите Esc во время записи нового + + Hint: to remove already assigned shortcut, press Esc while recording a new one. + Подсказка: для удавления уже назначенного сочетания нажмите Esc во время записи нового. - - Press new shortcut… - Нажмите новое сочетание клавиш… + + Press new shortcut… + Нажмите новое сочетание клавиш… - - + + OfbQtLinuxExtrasModule - - Compatibility - Совместимость + + Compatibility + Совместимость - - Enable MPRIS helper service - Включить службу урпаления MPRIS + + Enable MPRIS helper service + Включить службу урпаления MPRIS - - Try this option if auto-pause doesn't work with your desktop environment. Note that only MPRIS-compatible media players are supported (mostly all standalone player and browsers support this API). - Попробуйте включить эту опцию, если авто-пауза не работает с вашим рабочим столом. Учтите, что работать это будет только с MPRIS-совместимыми проигрывателями (большинство отдельных программ и браузеров поддерживают этот API). + + Try this option if auto-pause doesn't work with your desktop environment. Note that only MPRIS-compatible media players are supported (mostly all standalone player and browsers support this API). + Попробуйте включить эту опцию, если авто-пауза не работает с вашим рабочим столом. Учтите, что работать это будет только с MPRIS-совместимыми проигрывателями (большинство отдельных программ и браузеров поддерживают этот API). - - Force use X11 backend (may look better in GNOME-based desktop environments) - Принудительно использовать X11 (может выглядеть лучше в GNOME) + + Force use X11 backend (may look better in GNOME-based desktop environments) + Принудительно использовать X11 (может выглядеть лучше в GNOME) - - Restart application to apply. - Перезапустите приложение для применения изменений. + + Restart application to apply. + Перезапустите приложение для применения изменений. - - Theme - Тема + + Theme + Тема - - Fresh versions of OpenFreebuds are written in Qt6, and uses system-wide Qt UI theme. So, if application color scheme didn't match with system, or it looks ugly, you should configure global Qt style settings. - Новые версии OpenFreebuds написаны с использованием Qt6, и используют общесистемные настройки внешнего вида. Потому, если цветовая схема программы не совпадает с системной, или выглядит плохо, настройте тему для Qt приложений в системе. + + Fresh versions of OpenFreebuds are written in Qt6, and uses system-wide Qt UI theme. So, if application color scheme didn't match with system, or it looks ugly, you should configure global Qt style settings. + Новые версии OpenFreebuds написаны с использованием Qt6, и используют общесистемные настройки внешнего вида. Потому, если цветовая схема программы не совпадает с системной, или выглядит плохо, настройте тему для Qt приложений в системе. - - In KDE, LxQT or other Qt-based desktop environments, use system appearance settings. Otherwise, configure qt manually or use any configuration tool like qt6ct. - В KDE, LxQT и иных основанных на Qt рабочих столах, используйте системные настройки внешнего вида. Иначе измените настройки вручную или воспользуйтесь утилитой наподобии qt6ct. + + In KDE, LxQT or other Qt-based desktop environments, use system appearance settings. Otherwise, configure qt manually or use any configuration tool like qt6ct. + В KDE, LxQT и иных основанных на Qt рабочих столах, используйте системные настройки внешнего вида. Иначе измените настройки вручную или воспользуйтесь утилитой наподобии qt6ct. - - Keyboard shortcuts - Сочетания клавиш + + Keyboard shortcuts + Сочетания клавиш - - Looks like you're using Wayland desktop environment. Due to that OpenFreebuds built-in global hotkeys won't work. If you want to use keyboard shortcuts, setup them from your desktop environment settings. - Похоже что вы используете рабочий стол на базе Wayland. В связи с этим, встроенный функционал по управлению сочетаниями клавиш недоступен. Если вам они нужны, сконфигурируйте их вручную через настройки рабочего стола. + + Looks like you're using Wayland desktop environment. Due to that OpenFreebuds built-in global hotkeys won't work. If you want to use keyboard shortcuts, setup them from your desktop environment settings. + Похоже что вы используете рабочий стол на базе Wayland. В связи с этим, встроенный функционал по управлению сочетаниями клавиш недоступен. Если вам они нужны, сконфигурируйте их вручную через настройки рабочего стола. - - Documentation - Справка + + Documentation + Справка - - Ensure bus access - Убедитесь в доступности DBus + + Ensure bus access + Убедитесь в доступности DBus - - Looks like you're running under Flatpak. To use this feature, OpenFreebuds need to have access to entire session bus, otherwise it won't find any working media players. Ensure that you're granted this permission, refer to FAQ for more details. - Похоже что вы запустили OpenFreebuds через Flatpak. Однако для работы этой функции требуются доступ к DBus-шине пользователя, иначе программа не увидит запущенные медиаплееры. Убедитесь, что доступ предоставлен, см. ЧаВо чтобы узнать подробнее. + + Looks like you're running under Flatpak. To use this feature, OpenFreebuds need to have access to entire session bus, otherwise it won't find any working media players. Ensure that you're granted this permission, refer to FAQ for more details. + Похоже что вы запустили OpenFreebuds через Flatpak. Однако для работы этой функции требуются доступ к DBus-шине пользователя, иначе программа не увидит запущенные медиаплееры. Убедитесь, что доступ предоставлен, см. ЧаВо чтобы узнать подробнее. - - + + OfbQtMainWindow - - Select device - Выбрать устройство + + Select device + Выбрать устройство - - Device info - Об устройстве + + Device info + Об устройстве - - Dual-connect - Двойное подключение + + Dual-connect + Двойное подключение - - Gestures - Жесты + + Gestures + Жесты - - Sound quality - Качество звука + + Sound quality + Качество звука - - Other settings - Прочие настройки + + Other settings + Прочие настройки - - Application - Приложение + + Application + Приложение - - User interface - Внешний вид + + User interface + Внешний вид - - Keyboard shortcuts - Сочетания клавиш + + Keyboard shortcuts + Сочетания клавиш - - Linux-related - Linux-специфичное + + Linux-related + Linux-специфичное - - About… - О программе… + + About… + О программе… - - Help: FAQ - Помощь: Частые вопросы + + Help: FAQ + Помощь: Частые вопросы - - Help: Remote control - Помощь: Удалённое управление + + Help: Remote control + Помощь: Удалённое управление - - Bugreport… - Отчёт об ошибке… + + Bugreport… + Отчёт об ошибке… - - Check for updates… - Проверить наличие обновлений… + + Check for updates… + Проверить наличие обновлений… - - Remote access… - Удалённый доступ… + + Remote access… + Удалённый доступ… - - Temporary replace device - Временно сменить устройство + + Temporary replace device + Временно сменить устройство - - Close this window - Закрыть окно + + Close this window + Закрыть окно - - Exit OpenFreebuds - Закрыть OpenFreebuds + + Exit OpenFreebuds + Закрыть OpenFreebuds - - + + OfbQtManualConnectDialog - - Manual connect… - Ручное подключение… + + Manual connect… + Ручное подключение… - - Enter bluetooth address of your device and select their model from list. You can find bluetooth address in system settings. - Введите Bluetooth-адрес устройства и выберите его модель из списка. Найти Bluetooth-адрес можно в настройках системы. + + Enter bluetooth address of your device and select their model from list. You can find bluetooth address in system settings. + Введите Bluetooth-адрес устройства и выберите его модель из списка. Найти Bluetooth-адрес можно в настройках системы. - - Bluetooth address: - Bluetooth адрес: + + Bluetooth address: + Bluetooth адрес: - - Device profile: - Профиль устройства: + + Device profile: + Профиль устройства: - - + + OfbQtRpcConfig - - Remote access… - Удалённый доступ… + + Remote access… + Удалённый доступ… - - Change this settings only if you know what you're doing. Require restart of all OpenFreebuds instances and all clients. - Изменяйте эти настройки только если знаете, что делаете. Потребуется перезапуск всех запущенных OpenFreebuds и иных клиентов. + + Change this settings only if you know what you're doing. Require restart of all OpenFreebuds instances and all clients. + Изменяйте эти настройки только если знаете, что делаете. Потребуется перезапуск всех запущенных OpenFreebuds и иных клиентов. - - Allow controlling OpenFreebuds through network - Разрешить управлять OpenFreebuds через сеть + + Allow controlling OpenFreebuds through network + Разрешить управлять OpenFreebuds через сеть - - Require following secret to be present in X-Secret header - Требовать наличие ключа ниже в HTTP-заголовке X-Secret + + Require following secret to be present in X-Secret header + Требовать наличие ключа ниже в HTTP-заголовке X-Secret - - (enter new secret key) - (введите новый секретный ключ) + + (enter new secret key) + (введите новый секретный ключ) - - + + OfbQtSoundQualityModule - - Configure sound-related device settings. - Настройки устройства, связанные с качеством звука. + + Configure sound-related device settings. + Настройки устройства, связанные с качеством звука. - - Sound quality preference: - Предпочтение по качеству звука: + + Sound quality preference: + Предпочтение по качеству звука: - - Prioritize sound quality - Предпочитать качество звука + + Prioritize sound quality + Предпочитать качество звука - - Device will prefer loseless audio codecs, like LDAC. - Устройство будет предпочитать аудио-кодеки с лучшим качеством звука, вроде LDAC + + Device will prefer loseless audio codecs, like LDAC. + Устройство будет предпочитать аудио-кодеки с лучшим качеством звука, вроде LDAC. - - Prioritize connection quality - Предпочитать качество связи + + Prioritize connection quality + Предпочитать качество связи - - Device will prefer audio codec with better compression, like AAC - Устройство будет предпочитать аудио-кодеки с лучшим сжатием, вроде AAC + + Device will prefer audio codec with better compression, like AAC + Устройство будет предпочитать аудио-кодеки с лучшим сжатием, вроде AAC - - Equalizer - Эквалайзер + + Equalizer + Эквалайзер - - Preset: - Предустановка: + + Preset: + Предустановка: - - Customize preset - Настроить + + Customize preset + Настроить - - Save changes? - Сохранить изменения? + + Save changes? + Сохранить изменения? - - Save - Сохранить + + Save + Сохранить - - New preset… - Новый… + + New preset… + Новый… - - Delete… - Удалить… + + Delete… + Удалить… - - Export to file… - Экспорт в файл… + + Export to file… + Экспорт в файл… - - Load file… - Загрузить файл… + + Load file… + Загрузить файл… - - Notice - + + Notice + Примечание - - This preset isn't available in your device, it will be created as custom preset. - + + This preset isn't available in your device, it will be created as custom preset. + Эта предустановка не поддерживается вашим устройством, она будет импортирована как кастомная. - - - Failed - Ошибка + + + Failed + Ошибка - - - Can't create: too many custom preset created in device. - Не удалось: достигнут лимит кол-ва предустановок в памяти устройства. + + + Can't create: too many custom preset created in device. + Не удалось: достигнут лимит кол-ва предустановок в памяти устройства. - - Create new equalizer preset - Создать новую предустановку эквалайзера + + Create new equalizer preset + Создать новую предустановку эквалайзера - - Enter new preset name: - Введите название новой предустановки: + + Enter new preset name: + Введите название новой предустановки: - - Delete equalizer mode? - Удалить предустановку эквалайзера? + + Delete equalizer mode? + Удалить предустановку эквалайзера? - - Will delete following mode: - Будет удалена предустановка: + + Will delete following mode: + Будет удалена предустановка: - - Save equalizer preset to file… - Сохранить предустановку в файл… + + Save equalizer preset to file… + Сохранить предустановку в файл… - - Load equalizer preset from file… - Загрузить предустановку из файла… + + Load equalizer preset from file… + Загрузить предустановку из файла… - - + + OfbQtTrayMenu - - Left headphone: - Левый наушник: + + Left headphone: + Левый наушник: - - Right headphone: - Правый наушник + + Right headphone: + Правый наушник: - - Battery case: - Зарядный чехол: + + Battery case: + Зарядный чехол: - - Battery: - Батарея: + + Battery: + Батарея: - - Disable noise control - Отключить + + Disable noise control + Отключить - - Noise cancelling - Шумоподавление + + Noise cancelling + Шумоподавление - - Awareness - Прозрачность + + Awareness + Прозрачность - - Disconnect - Отсоединить + + Disconnect + Отсоединить - - Connect - Подключить + + Connect + Подключить - - Settings… - Настройки… + + Settings… + Настройки… - - Bugreport… - Отчёт об ошибке… + + Bugreport… + Отчёт об ошибке… - - Leave application - Закрыть программу + + Leave application + Закрыть программу - - + + OfbQtUiSettingsModule - - Looking for UI theme settings? Now it follows system-wide configuration. - Ищите настройку темы оформления? Теперь она всегда соответствует системной настройке. + + Looking for UI theme settings? Now it follows system-wide configuration. + Ищите настройку темы оформления? Теперь она всегда соответствует системной настройке. - - Main - Главное + + Main + Главное - - Restart OpenFeebuds to apply changes - Перезапустите OpenFreebuds для применения изменения + + Restart OpenFeebuds to apply changes + Перезапустите OpenFreebuds для применения изменения - - Launch at system startup - Запускать вместе с системой + + Launch at system startup + Запускать вместе с системой - - Update checker: - Проверка обновлений: + + Update checker: + Проверка обновлений: - - Notify about new versions - Уведомлять о новых версиях + + Notify about new versions + Уведомлять о новых версиях - - Check for new versions, but don't notify - Проверять, но не оповещать + + Check for new versions, but don't notify + Проверять, но не оповещать - - Disabled - Отключить + + Disabled + Отключить - - Language: - Язык интерфейса: + + Language: + Язык интерфейса: - - System - Системный + + System + Системный - - Mininize to system tray instead of closing - + + Mininize to system tray instead of closing + Скрывать в облесть уведомлений вместо закрытия - - Tray applet - Иконка в области уведомлений + + Tray applet + Иконка в области уведомлений - - Icon color - Цвет значка в трее + + Icon color + Цвет значка в трее - - Auto-select - Авто + + Auto-select + Авто - - Light - Светлый + + Light + Светлый - - Dark - Тёмный + + Dark + Тёмный - - Left-click action - Действие по левому клику + + Left-click action + Действие по левому клику - - Show dual-connect device switcher in menu (if available) - Показывать опции двойного подключения в меню (если доступно) + + Show dual-connect device switcher in menu (if available) + Показывать опции двойного подключения в меню (если доступно) - - Show equalizer preset switcher in menu (if available) - Показывать опции эквалайзера в меню (если доступно) + + Show equalizer preset switcher in menu (if available) + Показывать опции эквалайзера в меню (если доступно) - - + + OfbTrayIcon - - OpenFreebuds: Connecting to device… - OpenFreebuds: Подключение… + + OpenFreebuds: Connecting to device… + OpenFreebuds: Подключение… - - + + ShortcutName - - Show OpenFreebuds window - + + Show OpenFreebuds window + Показать окно OpenFreebuds - - Connect device - Подключить устройство + + Connect device + Подключить устройство - - Disconnect device - Отсоединить устройство + + Disconnect device + Отсоединить устройство - - Connect/disconnect device - Подключить/отключить устройство + + Connect/disconnect device + Подключить/отключить устройство - - Next noise control mode - Следующий режим шумоподавления + + Next noise control mode + Следующий режим шумоподавления - - Disable noise control - Отключить шумоподавление + + Disable noise control + Отключить шумоподавление - - Enable noise cancellation - Включить шумоподавление + + Enable noise cancellation + Включить шумоподавление - - Enable awareness mode - Включить режим прозрачность + + Enable awareness mode + Включить режим прозрачность - - Enable low-latency mode - Включить режим низкой задержки + + Enable low-latency mode + Включить режим низкой задержки - + From 16d3505ffc6571519efb341629d10562e71789bd Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 5 Nov 2024 19:25:32 +0700 Subject: [PATCH 29/35] [Docs] Add Flatpak to README, fix typo --- README.md | 17 ++++++++++++----- docs/download_buttons/flathub.png | Bin 0 -> 3256 bytes docs/download_buttons/windows.png | Bin 0 -> 6005 bytes 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 docs/download_buttons/flathub.png create mode 100644 docs/download_buttons/windows.png diff --git a/README.md b/README.md index 059f05a..3cc7ab7 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Features ![Settings preview](docs/preview_1.png) -Device compatability +Device compatibility ------------------------ See device page to get information about supported features. @@ -54,15 +54,22 @@ May also work with newer/older devices in same series. If you want to get better Download & install ----------------- -- **Windows**: [Download here](https://mmk.pw/en/openfreebuds/). -- **Debian/Ubuntu**: +[![Download for Windows](./docs/download_buttons/windows.png)](https://mmk.pw/en/openfreebuds/) +[![Available in FlatHub](./docs/download_buttons/flathub.png)](https://flathub.org/apps/pw.mmk.OpenFreebuds) + + +#### Debian/Ubuntu ```shell curl -s https://deb.mmk.pw/setup | sudo bash - sudo apt install openfreebuds ``` -- **Arch Linux**: `openfreebuds` [available in AUR](https://aur.archlinux.org/packages/openfreebuds). -- **Flatpak**: _Coming soon_ + +#### Arch Linux + +`openfreebuds` [available in AUR](https://aur.archlinux.org/packages/openfreebuds). + +#### Test builds Most recent `dev`-binaries can be found in [GitHub Actions](https://github.com/melianmiko/OpenFreebuds/actions/workflows/on_push.yml) build artifacts. diff --git a/docs/download_buttons/flathub.png b/docs/download_buttons/flathub.png new file mode 100644 index 0000000000000000000000000000000000000000..d15301cff2ad7bb6ef180322783696b084dc6675 GIT binary patch literal 3256 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 UP38F$AhAF+M|X#Fb^&q!17>0ldjJ3c literal 0 HcmV?d00001 From aa671abf5d428cc079c746ebf50ab43572ca806b Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 5 Nov 2024 19:25:52 +0700 Subject: [PATCH 30/35] [DevScripts] Allow running make.py inside venv --- scripts/make.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/make.py b/scripts/make.py index 010713b..78eb669 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -15,6 +15,7 @@ PROJECT_ROOT = Path(__file__).parent if PROJECT_ROOT.name == "scripts": PROJECT_ROOT = PROJECT_ROOT.parent +SHELL_PREFIX = [] if sys.platform != "linux" else ["/usr/bin/env"] # Constants DEFAULT_DESTINATION = "/usr/local" @@ -57,9 +58,9 @@ if sys.platform == "win32" and DO_INSTALL: print("-- Can't install under Windows, use pyinstaller") raise SystemExit(1) -if os.environ.get("VIRTUAL_ENV", None) is not None: - print("-- Launch this script outside of virtualenv") - raise SystemExit(1) +# if os.environ.get("VIRTUAL_ENV", None) is not None: +# print("-- Launch this script outside of virtualenv") +# raise SystemExit(1) # Find python dest dir if PYTHON_LIBS_DIR is None: @@ -82,14 +83,14 @@ # Compile Qt Designer layouts print("Compile Qt Designer files") DESIGNER_DIR = PROJECT_ROOT / "openfreebuds_qt" / "designer" - result = subprocess.run(["poetry", "run", "pyuic6", DESIGNER_DIR]) + result = subprocess.run([*SHELL_PREFIX, "poetry", "run", "pyuic6", DESIGNER_DIR]) if result.returncode != 0: print("Failed, old pyuic? Will try single file mode...") for ui_file in DESIGNER_DIR.iterdir(): if not ui_file.name.endswith(".ui"): continue print(f"Compile {ui_file}") - result = subprocess.run(["poetry", "run", "pyuic6", + result = subprocess.run([*SHELL_PREFIX, "poetry", "run", "pyuic6", "-o", str(ui_file).replace(".ui", ".py"), ui_file]) if result.returncode != 0: @@ -121,7 +122,7 @@ print("Compile Python wheel") POETRY_DIST = PROJECT_ROOT / "dist" shutil.rmtree(POETRY_DIST, ignore_errors=True) - result = subprocess.run(["poetry", "build", "-q"]) + result = subprocess.run([*SHELL_PREFIX, "poetry", "build", "-q"]) if result.returncode != 0: print("-- Poetry build failed") raise SystemExit(1) @@ -130,7 +131,7 @@ if DO_LAUNCH: print('----------------------------------------------------------------') print("Launching OpenFreebuds") - subprocess.run(["poetry", "run", "python", "-m", "openfreebuds_qt", "-vcs"]) + subprocess.run([*SHELL_PREFIX, "poetry", "run", "python", "-m", "openfreebuds_qt", "-vcs"]) raise SystemExit(0) # Wheel install tasks From 2f0faa43afaa4fc974a0d22fa4ea94d4988554bf Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 5 Nov 2024 20:15:13 +0700 Subject: [PATCH 31/35] [Feature] Autostart manage through Flatpak --- openfreebuds_backend/dummy.py | 2 +- openfreebuds_backend/linux/dbus/background.py | 91 +++++++++++++++++++ openfreebuds_backend/linux/linux_misc.py | 16 +++- openfreebuds_backend/windows/misc_win32.py | 2 +- openfreebuds_qt/app/dialog/first_run.py | 4 +- openfreebuds_qt/app/module/ui_settings.py | 5 +- scripts/build_flatpak/pw.mmk.OpenFreebuds.yml | 2 + scripts/flatpak_install.sh | 2 +- 8 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 openfreebuds_backend/linux/dbus/background.py diff --git a/openfreebuds_backend/dummy.py b/openfreebuds_backend/dummy.py index 5de75e9..b5a5b2d 100644 --- a/openfreebuds_backend/dummy.py +++ b/openfreebuds_backend/dummy.py @@ -16,7 +16,7 @@ def is_run_at_boot(): return False -def set_run_at_boot(val): +async def set_run_at_boot(val): log.info("run_at_boot " + str(val)) diff --git a/openfreebuds_backend/linux/dbus/background.py b/openfreebuds_backend/linux/dbus/background.py new file mode 100644 index 0000000..dcec65c --- /dev/null +++ b/openfreebuds_backend/linux/dbus/background.py @@ -0,0 +1,91 @@ +import asyncio + +from dbus_next import BusType +from dbus_next.aio import ProxyObject, MessageBus + +ALLOWED_PORTALS = [ + "org.freedesktop.impl.portal.desktop.gnome", +] + +introspection = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + + +class FreedesktopBackgroundPortalProxy: + def __init__(self, dbus_object: ProxyObject): + self.dbus_interface = dbus_object.get_interface("org.freedesktop.impl.portal.Background") + + async def enable_autostart(self, app_id: str, command: list[str], value): + return await self.dbus_interface.call_enable_autostart(app_id, value, command, 0) + + async def is_running(self, app_id): + return app_id in await self.get_app_state() + + async def get_app_state(self): + return {k: v.value for k, v in dict(await self.dbus_interface.call_get_app_state()).items()} + + @staticmethod + async def get(): + bus = await MessageBus(bus_type=BusType.SESSION).connect() + + dbus_introspect = await bus.introspect("org.freedesktop.DBus", "/org/freedesktop/DBus") + dbus_obj = bus.get_proxy_object("org.freedesktop.DBus", "/org/freedesktop/DBus", + dbus_introspect) + dbus = dbus_obj.get_interface("org.freedesktop.DBus") + + # noinspection PyUnresolvedReferences + for name in await dbus.call_list_names(): + if name in ALLOWED_PORTALS: + obj = bus.get_proxy_object(name, "/org/freedesktop/portal/desktop", introspection) + return FreedesktopBackgroundPortalProxy(obj) + + return None + + +if __name__ == "__main__": + async def main(): + o = await FreedesktopBackgroundPortalProxy.get() + r = await o.get_app_state() + print(r) + + asyncio.run(main()) diff --git a/openfreebuds_backend/linux/linux_misc.py b/openfreebuds_backend/linux/linux_misc.py index 5024e89..cb50fd7 100644 --- a/openfreebuds_backend/linux/linux_misc.py +++ b/openfreebuds_backend/linux/linux_misc.py @@ -3,6 +3,8 @@ import pathlib import subprocess +from openfreebuds_backend.linux.dbus.background import FreedesktopBackgroundPortalProxy + log = logging.getLogger("OfbLinuxBackend") @@ -20,7 +22,7 @@ def is_run_at_boot(): return os.path.isfile(_get_autostart_file_path()) -def set_run_at_boot(val): +async def set_run_at_boot(val): path = _get_autostart_file_path() data = (f"[Desktop Entry]\n" f"Name=OpenFreebuds\n" @@ -43,6 +45,18 @@ def set_run_at_boot(val): os.unlink(path) log.debug("Removed autostart file: " + path) + # Sync with DBus (Flatpak) + try: + o = await FreedesktopBackgroundPortalProxy.get() + r = await o.enable_autostart( + "pw.mmk.OpenFreebuds", + ["/usr/bin/flatpak", "run", "pw.mmk.OpenFreebuds"], + val + ) + log.debug(f"Enable autostart through portal result {r}") + except AttributeError: + log.debug("Sync with portal failed, no valid portal found") + def _get_autostart_file_path(): autostart_dir = pathlib.Path.home() / ".config/autostart" diff --git a/openfreebuds_backend/windows/misc_win32.py b/openfreebuds_backend/windows/misc_win32.py index 4c2b78f..c8daf8c 100644 --- a/openfreebuds_backend/windows/misc_win32.py +++ b/openfreebuds_backend/windows/misc_win32.py @@ -34,7 +34,7 @@ def is_run_at_boot(): return False -def set_run_at_boot(val): +async def set_run_at_boot(val): with winreg.OpenKey( key=winreg.HKEY_CURRENT_USER, sub_key=r'Software\Microsoft\Windows\CurrentVersion\Run', diff --git a/openfreebuds_qt/app/dialog/first_run.py b/openfreebuds_qt/app/dialog/first_run.py index 064b6a7..8ec539b 100644 --- a/openfreebuds_qt/app/dialog/first_run.py +++ b/openfreebuds_qt/app/dialog/first_run.py @@ -25,8 +25,6 @@ def __init__(self, ctx): if sys.platform == "win32": self.setStyleSheet(WIN32_BODY_STYLE) - self.autostart_checkbox.setChecked(not self.config.is_containerized_app) - self.autostart_checkbox.setEnabled(not self.config.is_containerized_app) self.background_checkbox.setChecked(QSystemTrayIcon.isSystemTrayAvailable()) self.background_checkbox.setEnabled(QSystemTrayIcon.isSystemTrayAvailable()) @@ -41,7 +39,7 @@ async def on_confirm(self): async with qt_error_handler("OfbQtFirstRunDialog_Confirm", self.ctx): self.hide() - openfreebuds_backend.set_run_at_boot(self.autostart_checkbox.isChecked()) + await openfreebuds_backend.set_run_at_boot(self.autostart_checkbox.isChecked()) self.config.set("ui", "background", self.background_checkbox.isChecked()) self.config.set("ui", "first_run_finished", True) self.config.save() diff --git a/openfreebuds_qt/app/module/ui_settings.py b/openfreebuds_qt/app/module/ui_settings.py index 5e3e002..9fddfb3 100644 --- a/openfreebuds_qt/app/module/ui_settings.py +++ b/openfreebuds_qt/app/module/ui_settings.py @@ -75,9 +75,6 @@ async def update_ui(self, event: OfbCoreEvent): with blocked_signals(self.autostart_toggle): self.autostart_toggle.setChecked(is_run_at_boot()) - if self.config.is_containerized_app: - self.autostart_toggle.setVisible(False) - with blocked_signals(self.background_toggle): self.background_toggle.setEnabled(QSystemTrayIcon.isSystemTrayAvailable()) self.background_toggle.setChecked(self.config.get("ui", "background", True)) @@ -89,7 +86,7 @@ async def on_background_toggle(self, value: bool): @asyncSlot(bool) async def on_autostart_toggle(self, value: bool): - set_run_at_boot(value) + await set_run_at_boot(value) @asyncSlot(bool) async def on_tray_eq_toggle(self, value: bool): diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml index 8f4c86e..da5ed42 100644 --- a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml +++ b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml @@ -20,6 +20,8 @@ finish-args: - --system-talk-name=org.bluez # Display tray icon - --talk-name=org.kde.StatusNotifierWatcher + # Check running state, enable/disable autostart + - --talk-name=org.freedesktop.impl.portal.desktop.gnome cleanup-commands: - /app/cleanup-BaseApp.sh diff --git a/scripts/flatpak_install.sh b/scripts/flatpak_install.sh index d2ba8e7..b36df27 100755 --- a/scripts/flatpak_install.sh +++ b/scripts/flatpak_install.sh @@ -7,7 +7,7 @@ cd "$(dirname "$0")"/.. # Build & install flatpak cd scripts/build_flatpak -flatpak-builder \ +flatpak run org.flatpak.Builder \ --force-clean \ --user \ --install-deps-from=flathub \ From d88f7066a56fbe61c05ee2caf611d9839ac2e41b Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 5 Nov 2024 20:51:01 +0700 Subject: [PATCH 32/35] [Fix] Flatpak ConfigLock detection --- openfreebuds_qt/config/config_lock.py | 8 ++++- openfreebuds_qt/config/dbus_config_lock.py | 32 +++++++++++++++++++ openfreebuds_qt/main.py | 2 +- scripts/build_flatpak/pw.mmk.OpenFreebuds.yml | 2 +- 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 openfreebuds_qt/config/dbus_config_lock.py diff --git a/openfreebuds_qt/config/config_lock.py b/openfreebuds_qt/config/config_lock.py index d38bacf..8de1932 100644 --- a/openfreebuds_qt/config/config_lock.py +++ b/openfreebuds_qt/config/config_lock.py @@ -6,6 +6,7 @@ from openfreebuds.constants import STORAGE_PATH from openfreebuds.utils.logger import create_logger +from openfreebuds_qt.config.dbus_config_lock import DBusConfigLock log = create_logger("ConfigLock") @@ -15,7 +16,12 @@ class ConfigLock: owned: bool = False @staticmethod - def acquire(): + async def acquire(): + if sys.platform == "linux" and os.path.isfile("/app/is_container"): + ConfigLock.owned = await DBusConfigLock.acquire() + log.info(f"DBus ConfigLock result {ConfigLock.owned}") + return + if ConfigLock._path.is_file(): try: with open(ConfigLock._path, "r") as f: diff --git a/openfreebuds_qt/config/dbus_config_lock.py b/openfreebuds_qt/config/dbus_config_lock.py new file mode 100644 index 0000000..830c041 --- /dev/null +++ b/openfreebuds_qt/config/dbus_config_lock.py @@ -0,0 +1,32 @@ +import asyncio + +from dbus_next.aio import MessageBus +from dbus_next.service import ServiceInterface + + +class DBusConfigLock: + task: asyncio.Task + bus: MessageBus + + @staticmethod + async def acquire(): + bus = await MessageBus().connect() + DBusConfigLock.bus = bus + + dbus_introspect = await bus.introspect("org.freedesktop.DBus", "/org/freedesktop/DBus") + dbus_obj = bus.get_proxy_object("org.freedesktop.DBus", "/org/freedesktop/DBus", + dbus_introspect) + dbus = dbus_obj.get_interface("org.freedesktop.DBus") + + # noinspection PyUnresolvedReferences + for name in await dbus.call_list_names(): + if name == "pw.mmk.OpenFreebuds": + bus.disconnect() + return False + + # Provide void DBus service + interface = ServiceInterface('pw.mmk.OpenFreebuds') + bus.export('/com/example/sample0', interface) + await bus.request_name('pw.mmk.OpenFreebuds') + + return True diff --git a/openfreebuds_qt/main.py b/openfreebuds_qt/main.py index fbeb123..33055e6 100644 --- a/openfreebuds_qt/main.py +++ b/openfreebuds_qt/main.py @@ -55,7 +55,6 @@ def __init__(self, args): logging.getLogger(tag).disabled = True # App configuration - ConfigLock.acquire() self.config.update_fallback_values(self) # Qt base configs @@ -86,6 +85,7 @@ def start(args): async def boot(self): try: + await ConfigLock.acquire() await self._stage_setup_ofb() if self.args.shortcut != "": diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml index da5ed42..653ca99 100644 --- a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml +++ b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml @@ -20,7 +20,7 @@ finish-args: - --system-talk-name=org.bluez # Display tray icon - --talk-name=org.kde.StatusNotifierWatcher - # Check running state, enable/disable autostart + # Enable/disable autostart from in-app settings - --talk-name=org.freedesktop.impl.portal.desktop.gnome cleanup-commands: From 3e9d99b1f4e57d52aadc90cc1426669a6ce2f4e7 Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Tue, 5 Nov 2024 21:31:45 +0700 Subject: [PATCH 33/35] [Fix] Add KDE portal to allowed .Background portals --- openfreebuds_backend/linux/dbus/background.py | 1 + scripts/build_flatpak/pw.mmk.OpenFreebuds.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/openfreebuds_backend/linux/dbus/background.py b/openfreebuds_backend/linux/dbus/background.py index dcec65c..7dff6ca 100644 --- a/openfreebuds_backend/linux/dbus/background.py +++ b/openfreebuds_backend/linux/dbus/background.py @@ -5,6 +5,7 @@ ALLOWED_PORTALS = [ "org.freedesktop.impl.portal.desktop.gnome", + "org.freedesktop.impl.portal.desktop.kde", ] introspection = """ diff --git a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml index 653ca99..995087e 100644 --- a/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml +++ b/scripts/build_flatpak/pw.mmk.OpenFreebuds.yml @@ -22,6 +22,7 @@ finish-args: - --talk-name=org.kde.StatusNotifierWatcher # Enable/disable autostart from in-app settings - --talk-name=org.freedesktop.impl.portal.desktop.gnome + - --talk-name=org.freedesktop.impl.portal.desktop.kde cleanup-commands: - /app/cleanup-BaseApp.sh From 175c85e102e8256d63536f861846452b42bb8dcc Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 17 Nov 2024 12:39:34 +0700 Subject: [PATCH 34/35] [Feature] Initial FreeBuds Studio driver impl (#47) --- README.md | 1 + docs/devices/HUAWEI_FreeBuds_Studio.md | 19 +++++++++++++ docs/research.ods | Bin 51772 -> 54332 bytes openfreebuds/driver/constants.py | 12 +++++---- .../huawei/driver/per_model/buds_studio.py | 19 +++++++++++++ .../driver/huawei/handler/config_equalizer.py | 25 +++++++++++++++++- 6 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 docs/devices/HUAWEI_FreeBuds_Studio.md create mode 100644 openfreebuds/driver/huawei/driver/per_model/buds_studio.py diff --git a/README.md b/README.md index 3cc7ab7..b2d33de 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ If your device isn't listed here, you could try to use it with profile for other - [HUAWEI FreeBuds Pro 2](./docs/devices/HUAWEI_FreeBuds_Pro_2.md) - [HUAWEI FreeBuds Pro 3](./docs/devices/HUAWEI_FreeBuds_Pro_3.md) - [HUAWEI FreeBuds SE](./docs/devices/HUAWEI_FreeBuds_SE.md) +- [HUAWEI FreeBuds Studio](./docs/devices/HUAWEI_FreeBuds_Studio.md) - [HUAWEI FreeLace Pro](./docs/devices/HUAWEI_FreeLace_Pro.md) - [HUAWEI FreeLace Pro 2](./docs/devices/HUAWEI_FreeLace_Pro_2.md) diff --git a/docs/devices/HUAWEI_FreeBuds_Studio.md b/docs/devices/HUAWEI_FreeBuds_Studio.md new file mode 100644 index 0000000..acc98d4 --- /dev/null +++ b/docs/devices/HUAWEI_FreeBuds_Studio.md @@ -0,0 +1,19 @@ +# HUAWEI FreeBuds Studio + +Protocol: Huawei SPP, port 1 + +## Features + +- Fetch device information and battery level: ❓ (untested) +- Control noise cancellation: ❓ (untested) + - With cancellation level + - With dynamic cancellation +- Wear detection (aka auto-pause) configuration: ❓ (untested) +- Equalizer: ❓ (untested) + - 3 built-in presets, hardcoded + - AI Life didn't see current equalizer mode, looks like application or firmware bug +- Change voice language: ❓ (no information, currently disabled) + +## Not planned features + +- Firmware update diff --git a/docs/research.ods b/docs/research.ods index 95925c9fa8fd5021b9af172d5d1e8d25cfb461eb..2985fc9f96913c56ae4a3130560933e6f231310f 100644 GIT binary patch delta 52212 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 a9tDtv$iWD@7z6=MXHS+qCC7RmBmw|Ly#luY delta 49580 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 diff --git a/openfreebuds/driver/constants.py b/openfreebuds/driver/constants.py index 85ccf0f..05dd15f 100644 --- a/openfreebuds/driver/constants.py +++ b/openfreebuds/driver/constants.py @@ -1,16 +1,18 @@ from openfreebuds.driver.generic_debug import * from openfreebuds.driver.huawei import * +from openfreebuds.driver.huawei.driver.per_model.buds_studio import OfbDriverHuaweiStudio DEVICE_TO_DRIVER_MAP = { + "HONOR Earbuds 2": OfbDriverHuawei4I, + "HUAWEI FreeBuds 4i": OfbDriverHuawei4I, "HUAWEI FreeBuds 5i": OfbDriverHuawei5I, "HUAWEI FreeBuds 6i": OfbDriverHuawei6I, - "HUAWEI FreeBuds SE": OfbDriverHuaweiSe, - "HUAWEI FreeLace Pro": OfbDriverHuaweiLacePro, - "HUAWEI FreeLace Pro 2": OfbDriverHuaweiLacePro2, - "HUAWEI FreeBuds 4i": OfbDriverHuawei4I, - "HONOR Earbuds 2": OfbDriverHuawei4I, "HUAWEI FreeBuds Pro": OfbDriverHuaweiPro, "HUAWEI FreeBuds Pro 2": OfbDriverHuaweiPro2, "HUAWEI FreeBuds Pro 3": OfbDriverHuaweiPro3, + "HUAWEI FreeBuds SE": OfbDriverHuaweiSe, + "HUAWEI FreeBuds Studio": OfbDriverHuaweiStudio, + "HUAWEI FreeLace Pro": OfbDriverHuaweiLacePro, + "HUAWEI FreeLace Pro 2": OfbDriverHuaweiLacePro2, "Debug: Virtual device": OfbFileDeviceDriver, } diff --git a/openfreebuds/driver/huawei/driver/per_model/buds_studio.py b/openfreebuds/driver/huawei/driver/per_model/buds_studio.py new file mode 100644 index 0000000..296592e --- /dev/null +++ b/openfreebuds/driver/huawei/driver/per_model/buds_studio.py @@ -0,0 +1,19 @@ +from openfreebuds.driver.huawei.driver.generic import OfbDriverHuaweiGeneric +from openfreebuds.driver.huawei.handler import * + + +class OfbDriverHuaweiStudio(OfbDriverHuaweiGeneric): + def __init__(self, address): + super().__init__(address) + self._spp_service_port = 1 + self.handlers = [ + OfbHuaweiInfoHandler(), + OfbHuaweiBatteryHandler(w_tws=False), + OfbHuaweiAncHandler(w_cancel_lvl=True, w_cancel_dynamic=True, w_voice_boost=True), + OfbHuaweiConfigAutoPauseHandler(), + OfbHuaweiEqualizerPresetHandler(wo_read=True, w_presets={ + 1: "default", + 2: "hardbass", + 3: "treble", + }), + ] diff --git a/openfreebuds/driver/huawei/handler/config_equalizer.py b/openfreebuds/driver/huawei/handler/config_equalizer.py index 6033988..e60cfe7 100644 --- a/openfreebuds/driver/huawei/handler/config_equalizer.py +++ b/openfreebuds/driver/huawei/handler/config_equalizer.py @@ -42,10 +42,22 @@ def __init__( w_presets: Optional[dict[int, str]] = None, w_custom: bool = False, w_fake_built_in: bool = False, + wo_read: bool = False, w_custom_rows: int = 10, w_custom_max_count: int = 3, ): + """ + Equalizer configuration handler + + @param w_presets: Available built-in presets + @param w_custom: Allow custom modes flag + @param w_fake_built_in: Allow fake built-in modes flag + @param wo_read: Disallow read request flag (for legacy devices) + @param w_custom_rows: Count of equalizer rows, for custom modes + @param w_custom_max_count: Max count of custom modes available in device + """ self.w_custom: bool = w_custom + self.wo_read: bool = wo_read self.w_custom_rows = w_custom_rows self.w_custom_max_count = w_custom_max_count self.w_fake_built_in = w_fake_built_in @@ -71,6 +83,13 @@ def __init__( self.preset_data = self.default_preset_data async def on_init(self): + if self.wo_read: + await self.driver.put_property("sound", None, { + "equalizer_preset": "", + "equalizer_preset_options": ",".join([l for _, l, _ in self.preset_data]) + }, extend_group=True) + return + resp = await self.driver.send_package( HuaweiSppPackage.read_rq(b"\x2b\x4a", [1, 2, 3, 4, 5, 6, 7, 8]) ) @@ -206,7 +225,11 @@ async def _set_current_mode(self, mode_str): self.changes_saved = True await self.driver.send_package(pkg) - await self.on_init() + + if self.wo_read: + await self.driver.put_property("sound", "equalizer_preset", mode_str) + else: + await self.on_init() async def on_package(self, package: HuaweiSppPackage): new_props = { From f0e603db703bcd30ae143308c8c1c12ca2762e2e Mon Sep 17 00:00:00 2001 From: MelianMiko Date: Sun, 17 Nov 2024 12:40:52 +0700 Subject: [PATCH 35/35] [Docs] Drop Accent translate references --- README.md | 3 +-- docs/accent.json | 11 ----------- 2 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 docs/accent.json diff --git a/README.md b/README.md index b2d33de..a939549 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,12 @@

Last release Last AUR release -Translated Test build status

-💿 Download binaries | ❓ FAQ | 🌍 Suggest translation +💿 Download binaries | ❓ FAQ

Tray menu preview diff --git a/docs/accent.json b/docs/accent.json deleted file mode 100644 index c585346..0000000 --- a/docs/accent.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "apiUrl": "https://translate.mmk.pw", - "apiKey": "", - "files": [ - { - "format": "gettext", - "source": "openfreebuds_qt/assets/i18n/en.po", - "target": "openfreebuds_qt/assets/i18n/%slug%.po" - } - ] -}