Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
b6062f1ee0
|
|||
|
651040de70
|
|||
|
|
3f0b3d65ad | ||
|
|
ec449f12d1 | ||
|
2d5932f144
|
|||
|
0a9381b1d2
|
|||
|
20fb7174c1
|
|||
| 8f9e6ea958 | |||
|
|
19aecd75c4 | ||
|
|
905ebe1d61 | ||
|
ae0b3a0f1a
|
@@ -2,8 +2,14 @@ name: Nightly Build - AppImage, Arch, Fedora
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
branch:
|
||||||
|
description: 'Branch to build'
|
||||||
|
required: false
|
||||||
|
default: 'main'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
BUILD_BRANCH: ${{ inputs.branch || 'main' }}
|
||||||
PKGDEST: "/tmp/portprotonqt"
|
PKGDEST: "/tmp/portprotonqt"
|
||||||
PACKAGE: "portprotonqt"
|
PACKAGE: "portprotonqt"
|
||||||
|
|
||||||
@@ -12,7 +18,7 @@ jobs:
|
|||||||
name: Build AppImage
|
name: Build AppImage
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
container:
|
container:
|
||||||
image: archlinux:base-devel@sha256:ebcaeca69c4d416f848aedcd27fe224384fd506f86046526a5d49ec6d9e29db1
|
image: archlinux:base-devel@sha256:d2bd09bd30dc1199ba6f35bc575292957a4a24377fb137d0f999817bc29adc17
|
||||||
options: --privileged --device /dev/fuse
|
options: --privileged --device /dev/fuse
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare container
|
- name: Prepare container
|
||||||
@@ -22,12 +28,14 @@ jobs:
|
|||||||
pacman -Syu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm xorg-server-xvfb zsync
|
pacman -Syu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm xorg-server-xvfb zsync
|
||||||
|
|
||||||
- uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
- uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
with:
|
||||||
|
ref: ${{ env.BUILD_BRANCH }}
|
||||||
|
|
||||||
- name: Install appimage dependencies
|
- name: Install appimage dependencies
|
||||||
run: |
|
run: |
|
||||||
cd build-aux/AppImage
|
cd build-aux/AppImage
|
||||||
chmod +x get-dependencies.sh portprotonqt-appimage.sh
|
chmod +x get-dependencies.sh portprotonqt-appimage.sh
|
||||||
./get-dependencies.sh --git
|
./get-dependencies.sh --local --branch "${{ env.BUILD_BRANCH }}"
|
||||||
|
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: |
|
run: |
|
||||||
@@ -55,9 +63,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
dnf install -y git rpmdevtools python3-devel python3-wheel python3-pip \
|
dnf install -y git rpmdevtools meson ninja-build python3-devel \
|
||||||
python3-build pyproject-rpm-macros systemd-rpm-macros python3-setuptools \
|
systemd-rpm-macros redhat-rpm-config nodejs npm
|
||||||
redhat-rpm-config nodejs npm
|
|
||||||
|
|
||||||
- name: Setup rpmbuild environment
|
- name: Setup rpmbuild environment
|
||||||
run: |
|
run: |
|
||||||
@@ -68,10 +75,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
with:
|
||||||
|
ref: ${{ env.BUILD_BRANCH }}
|
||||||
|
|
||||||
- name: Copy fedora.spec
|
- name: Copy fedora.spec
|
||||||
run: |
|
run: |
|
||||||
cp build-aux/fedora-git.spec /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
sed "s|git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git|git clone -b ${{ env.BUILD_BRANCH }} https://git.linux-gaming.ru/Boria138/PortProtonQt.git|" \
|
||||||
|
build-aux/fedora-git.spec > /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
||||||
chown -R rpmbuild:users /home/rpmbuild
|
chown -R rpmbuild:users /home/rpmbuild
|
||||||
|
|
||||||
- name: Build RPM
|
- name: Build RPM
|
||||||
@@ -88,7 +98,7 @@ jobs:
|
|||||||
name: Build Arch Package
|
name: Build Arch Package
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
container:
|
container:
|
||||||
image: archlinux:base-devel@sha256:ebcaeca69c4d416f848aedcd27fe224384fd506f86046526a5d49ec6d9e29db1
|
image: archlinux:base-devel@sha256:d2bd09bd30dc1199ba6f35bc575292957a4a24377fb137d0f999817bc29adc17
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare container
|
- name: Prepare container
|
||||||
@@ -115,17 +125,20 @@ jobs:
|
|||||||
chown user -R /tmp
|
chown user -R /tmp
|
||||||
chown user -R ..
|
chown user -R ..
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
with:
|
||||||
|
ref: ${{ env.BUILD_BRANCH }}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd /__w/portproton-repo
|
cd /__w/portproton-repo
|
||||||
git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git
|
cp -r "$GITHUB_WORKSPACE" PortProtonQt
|
||||||
cd /__w/portproton-repo/PortProtonQt/build-aux
|
cd /__w/portproton-repo/PortProtonQt/build-aux
|
||||||
|
sed -i "s|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git\")|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git#branch=${{ env.BUILD_BRANCH }}\")|" PKGBUILD-git
|
||||||
chown user -R ..
|
chown user -R ..
|
||||||
su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git"
|
su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git"
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
||||||
|
|
||||||
- name: Upload Arch package
|
- name: Upload Arch package
|
||||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -77,17 +77,17 @@ jobs:
|
|||||||
chown user -R /tmp
|
chown user -R /tmp
|
||||||
chown user -R ..
|
chown user -R ..
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: https://gitea.com/actions/checkout@v4
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd /__w/portproton-repo
|
cd /__w/portproton-repo
|
||||||
git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git
|
cp -r "$GITHUB_WORKSPACE" PortProtonQt
|
||||||
cd /__w/portproton-repo/PortProtonQt/build-aux
|
cd /__w/portproton-repo/PortProtonQt/build-aux
|
||||||
chown user -R ..
|
chown user -R ..
|
||||||
su user -c "yes '' | makepkg --noconfirm -s"
|
su user -c "yes '' | makepkg --noconfirm -s"
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: https://gitea.com/actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Upload Arch package
|
- name: Upload Arch package
|
||||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -109,9 +109,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
dnf install -y git rpmdevtools python3-devel python3-wheel python3-pip \
|
dnf install -y git rpmdevtools meson ninja-build python3-devel \
|
||||||
python3-build pyproject-rpm-macros systemd-rpm-macros python3-setuptools \
|
systemd-rpm-macros redhat-rpm-config nodejs npm
|
||||||
redhat-rpm-config nodejs npm
|
|
||||||
|
|
||||||
- name: Setup rpmbuild environment
|
- name: Setup rpmbuild environment
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Check Translations (disabled until yaspeller is fixed)
|
name: Check Translations
|
||||||
run-name: Check spelling in translation files
|
run-name: Check spelling in translation files
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: https://gitea.com/actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
|
uses: https://gitea.com/actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||||
with:
|
with:
|
||||||
python-version-file: "pyproject.toml"
|
python-version-file: "pyproject.toml"
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ on:
|
|||||||
env:
|
env:
|
||||||
PKGDEST: "/tmp/portprotonqt"
|
PKGDEST: "/tmp/portprotonqt"
|
||||||
PACKAGE: "portprotonqt"
|
PACKAGE: "portprotonqt"
|
||||||
|
PR_REPO_URL: ${{ github.event.pull_request.head.repo.clone_url }}
|
||||||
|
PR_BRANCH: ${{ github.head_ref }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
changes:
|
changes:
|
||||||
@@ -63,7 +65,7 @@ jobs:
|
|||||||
needs: changes
|
needs: changes
|
||||||
if: needs.changes.outputs.appimage == 'true' || github.event_name == 'workflow_dispatch'
|
if: needs.changes.outputs.appimage == 'true' || github.event_name == 'workflow_dispatch'
|
||||||
container:
|
container:
|
||||||
image: archlinux:base-devel@sha256:ebcaeca69c4d416f848aedcd27fe224384fd506f86046526a5d49ec6d9e29db1
|
image: archlinux:base-devel@sha256:d2bd09bd30dc1199ba6f35bc575292957a4a24377fb137d0f999817bc29adc17
|
||||||
options: --privileged --device /dev/fuse
|
options: --privileged --device /dev/fuse
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare container
|
- name: Prepare container
|
||||||
@@ -78,7 +80,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd build-aux/AppImage
|
cd build-aux/AppImage
|
||||||
chmod +x get-dependencies.sh portprotonqt-appimage.sh
|
chmod +x get-dependencies.sh portprotonqt-appimage.sh
|
||||||
./get-dependencies.sh
|
./get-dependencies.sh --local --branch "${{ env.PR_BRANCH }}" --repo "${{ env.PR_REPO_URL }}"
|
||||||
|
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: |
|
run: |
|
||||||
@@ -108,9 +110,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
dnf install -y git rpmdevtools python3-devel python3-wheel python3-pip \
|
dnf install -y git rpmdevtools meson ninja-build python3-devel \
|
||||||
python3-build pyproject-rpm-macros python3-setuptools \
|
systemd-rpm-macros redhat-rpm-config nodejs npm
|
||||||
redhat-rpm-config nodejs npm
|
|
||||||
|
|
||||||
- name: Setup rpmbuild environment
|
- name: Setup rpmbuild environment
|
||||||
run: |
|
run: |
|
||||||
@@ -124,7 +125,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Copy fedora-git.spec
|
- name: Copy fedora-git.spec
|
||||||
run: |
|
run: |
|
||||||
cp build-aux/fedora-git.spec /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
sed "s|git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git|git clone -b ${{ env.PR_BRANCH }} ${{ env.PR_REPO_URL }}|" \
|
||||||
|
build-aux/fedora-git.spec > /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec
|
||||||
chown -R rpmbuild:users /home/rpmbuild
|
chown -R rpmbuild:users /home/rpmbuild
|
||||||
|
|
||||||
- name: Build RPM
|
- name: Build RPM
|
||||||
@@ -143,7 +145,7 @@ jobs:
|
|||||||
needs: changes
|
needs: changes
|
||||||
if: needs.changes.outputs.arch == 'true' || github.event_name == 'workflow_dispatch'
|
if: needs.changes.outputs.arch == 'true' || github.event_name == 'workflow_dispatch'
|
||||||
container:
|
container:
|
||||||
image: archlinux:base-devel@sha256:ebcaeca69c4d416f848aedcd27fe224384fd506f86046526a5d49ec6d9e29db1
|
image: archlinux:base-devel@sha256:d2bd09bd30dc1199ba6f35bc575292957a4a24377fb137d0f999817bc29adc17
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare container
|
- name: Prepare container
|
||||||
@@ -170,17 +172,18 @@ jobs:
|
|||||||
chown user -R /tmp
|
chown user -R /tmp
|
||||||
chown user -R ..
|
chown user -R ..
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd /__w/portproton-repo
|
cd /__w/portproton-repo
|
||||||
git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git
|
git clone -b ${{ env.PR_BRANCH }} ${{ env.PR_REPO_URL }} PortProtonQt
|
||||||
cd /__w/portproton-repo/PortProtonQt/build-aux
|
cd /__w/portproton-repo/PortProtonQt/build-aux
|
||||||
|
sed -i "s|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git\")|source=(\"git+${{ env.PR_REPO_URL }}#branch=${{ env.PR_BRANCH }}\")|" PKGBUILD-git
|
||||||
chown user -R ..
|
chown user -R ..
|
||||||
su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git"
|
su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git"
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
||||||
|
|
||||||
- name: Upload Arch package
|
- name: Upload Arch package
|
||||||
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
uses: https://gitea.com/actions/gitea-upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
- uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
- uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: https://gitea.com/actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
uses: https://gitea.com/actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: https://gitea.com/actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
|
uses: https://gitea.com/actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||||
with:
|
with:
|
||||||
python-version-file: "pyproject.toml"
|
python-version-file: "pyproject.toml"
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ jobs:
|
|||||||
- uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
- uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: https://gitea.com/actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
|
uses: https://gitea.com/actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,52 @@
|
|||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
# Determine if git mode is enabled based on the first argument
|
# Initialize variables
|
||||||
if [ "${1:-}" = "--git" ] || [ "${1:-}" = "-g" ]; then
|
LOCAL_MODE=false
|
||||||
GIT_MODE=true
|
BRANCH="main"
|
||||||
else
|
REPO_URL="https://git.linux-gaming.ru/Boria138/PortProtonQt.git"
|
||||||
GIT_MODE=false
|
|
||||||
fi
|
# Parse arguments
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--local|-l)
|
||||||
|
LOCAL_MODE=true
|
||||||
|
;;
|
||||||
|
--branch)
|
||||||
|
if [ -n "${2:-}" ] && [ "${2#-}" = "$2" ]; then
|
||||||
|
BRANCH="$2"
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
echo "Error: --branch requires an argument"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--repo)
|
||||||
|
if [ -n "${2:-}" ] && [ "${2#-}" = "$2" ]; then
|
||||||
|
REPO_URL="$2"
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
echo "Error: --repo requires an argument"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
ARCH="$(uname -m)"
|
ARCH="$(uname -m)"
|
||||||
PACKAGE_BUILDER="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/make-aur-package.sh"
|
PACKAGE_BUILDER="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/make-aur-package.sh"
|
||||||
EXTRA_PACKAGES="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/get-debloated-pkgs.sh"
|
EXTRA_PACKAGES="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/get-debloated-pkgs.sh"
|
||||||
|
|
||||||
if [ "$GIT_MODE" = true ]; then
|
if [ "$LOCAL_MODE" = true ]; then
|
||||||
echo "Using git version of PortProtonQt..."
|
echo "Using local PKGBUILD-git from repository..."
|
||||||
PPQT_PKGBUILD="https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/build-aux/PKGBUILD-git"
|
PPQT_PKGBUILD=""
|
||||||
else
|
else
|
||||||
echo "Using stable version of PortProtonQt..."
|
echo "Using stable version of PortProtonQt from main branch..."
|
||||||
PPQT_PKGBUILD="https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/build-aux/PKGBUILD"
|
PPQT_PKGBUILD="https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/build-aux/PKGBUILD"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -37,7 +67,12 @@ chmod +x ./make-aur-package.sh
|
|||||||
|
|
||||||
echo "Building PortProtonQt from PKGBUILD..."
|
echo "Building PortProtonQt from PKGBUILD..."
|
||||||
echo "---------------------------------------------------------------"
|
echo "---------------------------------------------------------------"
|
||||||
wget --retry-connrefused --tries=30 "$PPQT_PKGBUILD" -O ./PKGBUILD
|
if [ "$LOCAL_MODE" = true ]; then
|
||||||
|
cp ../PKGBUILD-git ./PKGBUILD
|
||||||
|
else
|
||||||
|
wget --retry-connrefused --tries=30 "$PPQT_PKGBUILD" -O ./PKGBUILD
|
||||||
|
fi
|
||||||
|
sed -i "s|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git\")|source=(\"git+${REPO_URL}#branch=$BRANCH\")|" PKGBUILD
|
||||||
makepkg -si --noconfirm
|
makepkg -si --noconfirm
|
||||||
|
|
||||||
echo "Installing debloated packages..."
|
echo "Installing debloated packages..."
|
||||||
@@ -46,7 +81,7 @@ wget --retry-connrefused --tries=30 "$EXTRA_PACKAGES" -O ./get-debloated-pkgs.sh
|
|||||||
chmod +x ./get-debloated-pkgs.sh
|
chmod +x ./get-debloated-pkgs.sh
|
||||||
./get-debloated-pkgs.sh --add-common --prefer-nano
|
./get-debloated-pkgs.sh --add-common --prefer-nano
|
||||||
|
|
||||||
if [ "$GIT_MODE" = true ]; then
|
if [ "$LOCAL_MODE" = true ]; then
|
||||||
# For git version, we use portprotonqt-git
|
# For git version, we use portprotonqt-git
|
||||||
pacman -Q portprotonqt-git | awk '{print $2}' | cut -d- -f1 > ~/version
|
pacman -Q portprotonqt-git | awk '{print $2}' | cut -d- -f1 > ~/version
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -6,19 +6,16 @@ arch=('any')
|
|||||||
url="https://git.linux-gaming.ru/Boria138/PortProtonQt"
|
url="https://git.linux-gaming.ru/Boria138/PortProtonQt"
|
||||||
license=('GPL-3.0')
|
license=('GPL-3.0')
|
||||||
depends=('python-requests' 'python-babel' 'python-evdev' 'python-pyudev' 'python-orjson'
|
depends=('python-requests' 'python-babel' 'python-evdev' 'python-pyudev' 'python-orjson'
|
||||||
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'python-rapidfuzz' 'icoextract' 'python-pillow' 'perl-image-exiftool' 'xdg-utils' 'python-beautifulsoup4' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'unrar')
|
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'python-rapidfuzz' 'icoextract' 'python-pillow' 'perl-image-exiftool' 'xdg-utils' 'python-beautifulsoup4' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'unrar' 'qt6-svg')
|
||||||
makedepends=('python-'{'build','installer','setuptools','wheel'})
|
makedepends=('meson' 'ninja')
|
||||||
source=("git+https://git.linux-gaming.ru/Boria138/PortProtonQt#tag=v$pkgver")
|
source=("git+https://git.linux-gaming.ru/Boria138/PortProtonQt#tag=v$pkgver")
|
||||||
sha256sums=('SKIP')
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/PortProtonQt"
|
arch-meson PortProtonQt build
|
||||||
python -m build --wheel --no-isolation
|
meson compile -C build
|
||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/PortProtonQt"
|
meson install -C build --destdir "$pkgdir"
|
||||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
|
||||||
cp -r build-aux/share "$pkgdir/usr/"
|
|
||||||
cp -r build-aux/lib "$pkgdir/usr/"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ arch=('any')
|
|||||||
url="https://git.linux-gaming.ru/Boria138/PortProtonQt"
|
url="https://git.linux-gaming.ru/Boria138/PortProtonQt"
|
||||||
license=('GPL-3.0')
|
license=('GPL-3.0')
|
||||||
depends=('python-requests' 'python-babel' 'python-evdev' 'python-pyudev' 'python-orjson'
|
depends=('python-requests' 'python-babel' 'python-evdev' 'python-pyudev' 'python-orjson'
|
||||||
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'icoextract' 'python-pillow' 'python-rapidfuzz' 'perl-image-exiftool' 'xdg-utils' 'python-beautifulsoup4' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'unrar')
|
'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'icoextract' 'python-pillow' 'python-rapidfuzz' 'perl-image-exiftool' 'xdg-utils' 'python-beautifulsoup4' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'unrar' 'qt6-svg')
|
||||||
makedepends=('python-'{'build','installer','setuptools','wheel'})
|
makedepends=('meson' 'ninja')
|
||||||
source=("git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git")
|
source=("git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git")
|
||||||
sha256sums=('SKIP')
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
@@ -17,13 +17,10 @@ pkgver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/PortProtonQt"
|
arch-meson PortProtonQt build
|
||||||
python -m build --wheel --no-isolation
|
meson compile -C build
|
||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/PortProtonQt"
|
meson install -C build --destdir "$pkgdir"
|
||||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
|
||||||
cp -r build-aux/share "$pkgdir/usr/"
|
|
||||||
cp -r build-aux/lib "$pkgdir/usr/"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
%global pypi_name portprotonqt
|
%global pypi_name portprotonqt
|
||||||
%global pypi_version 0.1.1
|
%global pypi_version 0.1.10
|
||||||
%global oname PortProtonQt
|
%global oname PortProtonQt
|
||||||
%global build_timestamp %(date +"%Y%m%d")
|
%global build_timestamp %(date +"%Y%m%d")
|
||||||
%global _python_no_extras_requires 1
|
%global _python_no_extras_requires 1
|
||||||
|
|
||||||
%global rel_build 1.git.%{build_timestamp}%{?dist}
|
%global rel_build 1.git.%{build_timestamp}%{?dist}
|
||||||
|
|
||||||
Name: python-%{pypi_name}-git
|
Name: %{pypi_name}-git
|
||||||
Version: %{pypi_version}
|
Version: %{pypi_version}
|
||||||
Release: %{rel_build}
|
Release: %{rel_build}
|
||||||
Summary: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store (development build)
|
Summary: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store (development build)
|
||||||
@@ -15,21 +15,15 @@ License: GPL-3.0
|
|||||||
URL: https://git.linux-gaming.ru/Boria138/PortProtonQt
|
URL: https://git.linux-gaming.ru/Boria138/PortProtonQt
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
|
|
||||||
|
BuildRequires: meson >= 0.61.2
|
||||||
|
BuildRequires: ninja-build
|
||||||
BuildRequires: python3-devel
|
BuildRequires: python3-devel
|
||||||
BuildRequires: python3-wheel
|
|
||||||
BuildRequires: python3-pip
|
|
||||||
BuildRequires: python3-build
|
|
||||||
BuildRequires: pyproject-rpm-macros
|
|
||||||
BuildRequires: python3dist(setuptools)
|
|
||||||
BuildRequires: git
|
BuildRequires: git
|
||||||
BuildRequires: systemd-rpm-macros
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
%description
|
Obsoletes: python3-%{pypi_name}-git < %{version}-%{release}
|
||||||
%{summary}
|
Provides: python3-%{pypi_name}-git = %{version}-%{release}
|
||||||
|
|
||||||
%package -n python3-%{pypi_name}-git
|
|
||||||
Summary: %{summary}
|
|
||||||
%{?python_provide:%python_provide python3-%{pypi_name}}
|
|
||||||
Requires: python3-babel
|
Requires: python3-babel
|
||||||
Requires: python3-evdev
|
Requires: python3-evdev
|
||||||
Requires: python3-icoextract
|
Requires: python3-icoextract
|
||||||
@@ -48,13 +42,14 @@ Requires: python3-rapidfuzz
|
|||||||
Requires: python3-libarchive-c
|
Requires: python3-libarchive-c
|
||||||
Requires: perl-Image-ExifTool
|
Requires: perl-Image-ExifTool
|
||||||
Requires: xdg-utils
|
Requires: xdg-utils
|
||||||
|
Requires: qt6-qtsvg
|
||||||
Requires: cabextract
|
Requires: cabextract
|
||||||
Requires: gzip
|
Requires: gzip
|
||||||
Requires: unzip
|
Requires: unzip
|
||||||
Requires: curl
|
Requires: curl
|
||||||
Requires: unrar
|
Requires: unrar
|
||||||
|
|
||||||
%description -n python3-%{pypi_name}-git
|
%description
|
||||||
This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store. It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization. Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers. Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup.
|
This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store. It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization. Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers. Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup.
|
||||||
|
|
||||||
%{?python_disable_dependency_generator}
|
%{?python_disable_dependency_generator}
|
||||||
@@ -64,17 +59,17 @@ git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git
|
|||||||
|
|
||||||
%build
|
%build
|
||||||
cd %{oname}
|
cd %{oname}
|
||||||
%pyproject_wheel
|
%meson -Dpython_libdir=%{python3_sitelib} -Dudevdir=%{_udevrulesdir}
|
||||||
|
%meson_build
|
||||||
|
|
||||||
%install
|
%install
|
||||||
cd %{oname}
|
cd %{oname}
|
||||||
%pyproject_install
|
%meson_install
|
||||||
%pyproject_save_files %{pypi_name}
|
%find_lang %{pypi_name}
|
||||||
cp -r build-aux/share %{buildroot}/usr/
|
|
||||||
cp -r build-aux/lib %{buildroot}/usr/
|
|
||||||
|
|
||||||
%files -n python3-%{pypi_name}-git -f %{pyproject_files}
|
%files -f %{oname}/%{pypi_name}.lang
|
||||||
%{_bindir}/%{pypi_name}
|
%{_bindir}/%{pypi_name}
|
||||||
|
%{python3_sitelib}/%{pypi_name}/
|
||||||
%{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg
|
%{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg
|
||||||
%{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml
|
%{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml
|
||||||
%{_udevrulesdir}/60-portprotonqt.rules
|
%{_udevrulesdir}/60-portprotonqt.rules
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
%global oname PortProtonQt
|
%global oname PortProtonQt
|
||||||
%global _python_no_extras_requires 1
|
%global _python_no_extras_requires 1
|
||||||
|
|
||||||
Name: python-%{pypi_name}
|
Name: %{pypi_name}
|
||||||
Version: %{pypi_version}
|
Version: %{pypi_version}
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store
|
Summary: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store
|
||||||
@@ -12,21 +12,15 @@ License: GPL-3.0
|
|||||||
URL: https://git.linux-gaming.ru/Boria138/PortProtonQt
|
URL: https://git.linux-gaming.ru/Boria138/PortProtonQt
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
|
|
||||||
|
BuildRequires: meson >= 0.61.2
|
||||||
|
BuildRequires: ninja-build
|
||||||
BuildRequires: python3-devel
|
BuildRequires: python3-devel
|
||||||
BuildRequires: python3-wheel
|
|
||||||
BuildRequires: python3-pip
|
|
||||||
BuildRequires: python3-build
|
|
||||||
BuildRequires: pyproject-rpm-macros
|
|
||||||
BuildRequires: python3dist(setuptools)
|
|
||||||
BuildRequires: git
|
BuildRequires: git
|
||||||
BuildRequires: systemd-rpm-macros
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
%description
|
Obsoletes: python3-%{pypi_name} < %{version}-%{release}
|
||||||
%{summary}
|
Provides: python3-%{pypi_name} = %{version}-%{release}
|
||||||
|
|
||||||
%package -n python3-%{pypi_name}
|
|
||||||
Summary: %{summary}
|
|
||||||
%{?python_provide:%python_provide python3-%{pypi_name}}
|
|
||||||
Requires: python3-babel
|
Requires: python3-babel
|
||||||
Requires: python3-evdev
|
Requires: python3-evdev
|
||||||
Requires: python3-icoextract
|
Requires: python3-icoextract
|
||||||
@@ -45,13 +39,14 @@ Requires: python3-rapidfuzz
|
|||||||
Requires: python3-libarchive-c
|
Requires: python3-libarchive-c
|
||||||
Requires: perl-Image-ExifTool
|
Requires: perl-Image-ExifTool
|
||||||
Requires: xdg-utils
|
Requires: xdg-utils
|
||||||
|
Requires: qt6-qtsvg
|
||||||
Requires: cabextract
|
Requires: cabextract
|
||||||
Requires: gzip
|
Requires: gzip
|
||||||
Requires: unzip
|
Requires: unzip
|
||||||
Requires: curl
|
Requires: curl
|
||||||
Requires: unrar
|
Requires: unrar
|
||||||
|
|
||||||
%description -n python3-%{pypi_name}
|
%description
|
||||||
This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store. It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization. Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers. Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup.
|
This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store. It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization. Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers. Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup.
|
||||||
|
|
||||||
%{?python_disable_dependency_generator}
|
%{?python_disable_dependency_generator}
|
||||||
@@ -63,17 +58,17 @@ git checkout v%{pypi_version}
|
|||||||
|
|
||||||
%build
|
%build
|
||||||
cd %{oname}
|
cd %{oname}
|
||||||
%pyproject_wheel
|
%meson -Dpython_libdir=%{python3_sitelib} -Dudevdir=%{_udevrulesdir}
|
||||||
|
%meson_build
|
||||||
|
|
||||||
%install
|
%install
|
||||||
cd %{oname}
|
cd %{oname}
|
||||||
%pyproject_install
|
%meson_install
|
||||||
%pyproject_save_files %{pypi_name}
|
%find_lang %{pypi_name}
|
||||||
cp -r build-aux/share %{buildroot}/usr/
|
|
||||||
cp -r build-aux/lib %{buildroot}/usr/
|
|
||||||
|
|
||||||
%files -n python3-%{pypi_name} -f %{pyproject_files}
|
%files -f %{oname}/%{pypi_name}.lang
|
||||||
%{_bindir}/%{pypi_name}
|
%{_bindir}/%{pypi_name}
|
||||||
|
%{python3_sitelib}/%{pypi_name}/
|
||||||
%{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg
|
%{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg
|
||||||
%{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml
|
%{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml
|
||||||
%{_udevrulesdir}/60-portprotonqt.rules
|
%{_udevrulesdir}/60-portprotonqt.rules
|
||||||
|
|||||||
9
build-aux/portprotonqt
Normal file
9
build-aux/portprotonqt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from portprotonqt.app import main
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -12,7 +12,9 @@ BASE_DIR = Path(__file__).parent.parent
|
|||||||
APPIMAGE_RECIPE = BASE_DIR / "build-aux" / "AppImageBuilder.yml"
|
APPIMAGE_RECIPE = BASE_DIR / "build-aux" / "AppImageBuilder.yml"
|
||||||
ARCH_PKGBUILD = BASE_DIR / "build-aux" / "PKGBUILD"
|
ARCH_PKGBUILD = BASE_DIR / "build-aux" / "PKGBUILD"
|
||||||
FEDORA_SPEC = BASE_DIR / "build-aux" / "fedora.spec"
|
FEDORA_SPEC = BASE_DIR / "build-aux" / "fedora.spec"
|
||||||
|
FEDORA_GIT_SPEC = BASE_DIR / "build-aux" / "fedora-git.spec"
|
||||||
PYPROJECT = BASE_DIR / "pyproject.toml"
|
PYPROJECT = BASE_DIR / "pyproject.toml"
|
||||||
|
MESON_BUILD = BASE_DIR / "meson.build"
|
||||||
APP_PY = BASE_DIR / "portprotonqt" / "app.py"
|
APP_PY = BASE_DIR / "portprotonqt" / "app.py"
|
||||||
GITEA_WORKFLOW = BASE_DIR / ".gitea" / "workflows" / "build.yml"
|
GITEA_WORKFLOW = BASE_DIR / ".gitea" / "workflows" / "build.yml"
|
||||||
CHANGELOG = BASE_DIR / "CHANGELOG.md"
|
CHANGELOG = BASE_DIR / "CHANGELOG.md"
|
||||||
@@ -56,6 +58,7 @@ def bump_fedora(path: Path, old: str, new: str) -> bool:
|
|||||||
path.write_text(new_text, encoding='utf-8')
|
path.write_text(new_text, encoding='utf-8')
|
||||||
return bool(count)
|
return bool(count)
|
||||||
|
|
||||||
|
|
||||||
def bump_pyproject(path: Path, old: str, new: str) -> bool:
|
def bump_pyproject(path: Path, old: str, new: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Update version in pyproject.toml under [project]
|
Update version in pyproject.toml under [project]
|
||||||
@@ -69,6 +72,19 @@ def bump_pyproject(path: Path, old: str, new: str) -> bool:
|
|||||||
path.write_text(new_text, encoding='utf-8')
|
path.write_text(new_text, encoding='utf-8')
|
||||||
return bool(count)
|
return bool(count)
|
||||||
|
|
||||||
|
def bump_meson(path: Path, old: str, new: str) -> bool:
|
||||||
|
"""
|
||||||
|
Update version in meson.build
|
||||||
|
"""
|
||||||
|
if not path.exists():
|
||||||
|
return False
|
||||||
|
text = path.read_text(encoding='utf-8')
|
||||||
|
pattern = re.compile(r"(version:\s*)'" + re.escape(old) + r"'")
|
||||||
|
new_text, count = pattern.subn(lambda m: m.group(1) + f"'{new}'", text)
|
||||||
|
if count:
|
||||||
|
path.write_text(new_text, encoding='utf-8')
|
||||||
|
return bool(count)
|
||||||
|
|
||||||
def bump_app_py(path: Path, old: str, new: str) -> bool:
|
def bump_app_py(path: Path, old: str, new: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Update __app_version__ in app.py
|
Update __app_version__ in app.py
|
||||||
@@ -120,7 +136,9 @@ def main():
|
|||||||
(APPIMAGE_RECIPE, bump_appimage),
|
(APPIMAGE_RECIPE, bump_appimage),
|
||||||
(ARCH_PKGBUILD, bump_arch),
|
(ARCH_PKGBUILD, bump_arch),
|
||||||
(FEDORA_SPEC, bump_fedora),
|
(FEDORA_SPEC, bump_fedora),
|
||||||
|
(FEDORA_GIT_SPEC, bump_fedora),
|
||||||
(PYPROJECT, bump_pyproject),
|
(PYPROJECT, bump_pyproject),
|
||||||
|
(MESON_BUILD, bump_meson),
|
||||||
(APP_PY, bump_app_py),
|
(APP_PY, bump_app_py),
|
||||||
(GITEA_WORKFLOW, bump_workflow),
|
(GITEA_WORKFLOW, bump_workflow),
|
||||||
(CHANGELOG, bump_changelog)
|
(CHANGELOG, bump_changelog)
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ README_EN = GUIDE_DIR / "README.md"
|
|||||||
README_RU = GUIDE_DIR / "README.ru.md"
|
README_RU = GUIDE_DIR / "README.ru.md"
|
||||||
LOCALES_PATH = Path(__file__).parent.parent / "portprotonqt" / "locales"
|
LOCALES_PATH = Path(__file__).parent.parent / "portprotonqt" / "locales"
|
||||||
THEMES_PATH = Path(__file__).parent.parent / "portprotonqt" / "themes"
|
THEMES_PATH = Path(__file__).parent.parent / "portprotonqt" / "themes"
|
||||||
|
MESON_BUILD = Path(__file__).parent.parent / "portprotonqt" / "meson.build"
|
||||||
README_FILES = [README_EN, README_RU]
|
README_FILES = [README_EN, README_RU]
|
||||||
POT_FILE = LOCALES_PATH / "messages.pot"
|
POT_FILE = LOCALES_PATH / "portprotonqt.pot"
|
||||||
|
|
||||||
# ---------- Версия проекта ----------
|
# ---------- Версия проекта ----------
|
||||||
def _get_version() -> str:
|
def _get_version() -> str:
|
||||||
@@ -27,16 +28,16 @@ def _get_version() -> str:
|
|||||||
# ---------- Обновление README ----------
|
# ---------- Обновление README ----------
|
||||||
def _update_coverage(lines: list[str]) -> None:
|
def _update_coverage(lines: list[str]) -> None:
|
||||||
# Парсим статистику из вывода pybabel --statistics
|
# Парсим статистику из вывода pybabel --statistics
|
||||||
locales_stats = [line for line in lines if line.endswith(".po")]
|
locales_stats = [line for line in lines if "portprotonqt.po" in line]
|
||||||
# Извлекаем (count, pct, locale) и сортируем
|
# Извлекаем (count, pct, locale) и сортируем
|
||||||
rows = sorted(
|
rows = sorted(
|
||||||
(m := re.search(
|
row for stat in locales_stats
|
||||||
|
if (m := re.search(
|
||||||
r"""(\d+\ of\ \d+).* # message counts
|
r"""(\d+\ of\ \d+).* # message counts
|
||||||
\((\d+\%)\).* # message percentage
|
\((\d+\%)\).* # message percentage
|
||||||
locales\/(.*)\/LC_MESSAGES # locale name""",
|
locales\/([^/]+)\/LC_MESSAGES # locale name""",
|
||||||
stat, re.VERBOSE
|
stat, re.VERBOSE
|
||||||
)) and m.groups()
|
)) and (row := m.groups())
|
||||||
for stat in locales_stats
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for md_file in README_FILES:
|
for md_file in README_FILES:
|
||||||
@@ -59,14 +60,14 @@ def _update_coverage(lines: list[str]) -> None:
|
|||||||
"| Локаль | Прогресс | Переведено |\n"
|
"| Локаль | Прогресс | Переведено |\n"
|
||||||
"| :----- | -------: | ---------: |\n"
|
"| :----- | -------: | ---------: |\n"
|
||||||
)
|
)
|
||||||
fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/messages.po) | {pct} | {count.replace(' of ', ' из ')} |"
|
fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/portprotonqt.po) | {pct} | {count.replace(' of ', ' из ')} |"
|
||||||
else:
|
else:
|
||||||
table_header = (
|
table_header = (
|
||||||
"<!-- Auto-generated coverage table -->\n\n"
|
"<!-- Auto-generated coverage table -->\n\n"
|
||||||
"| Locale | Progress | Translated |\n"
|
"| Locale | Progress | Translated |\n"
|
||||||
"| :----- | -------: | ---------: |\n"
|
"| :----- | -------: | ---------: |\n"
|
||||||
)
|
)
|
||||||
fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/messages.po) | {pct} | {count} |"
|
fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/portprotonqt.po) | {pct} | {count} |"
|
||||||
|
|
||||||
# Собираем строки и добавляем '---' в конце
|
# Собираем строки и добавляем '---' в конце
|
||||||
coverage_table = (
|
coverage_table = (
|
||||||
@@ -100,7 +101,7 @@ def _update_coverage(lines: list[str]) -> None:
|
|||||||
def compile_locales() -> None:
|
def compile_locales() -> None:
|
||||||
CommandLineInterface().run([
|
CommandLineInterface().run([
|
||||||
"pybabel", "compile", "--use-fuzzy", "--directory",
|
"pybabel", "compile", "--use-fuzzy", "--directory",
|
||||||
f"{LOCALES_PATH.resolve()}", "--statistics"
|
f"{LOCALES_PATH.resolve()}", "--domain=portprotonqt", "--statistics"
|
||||||
])
|
])
|
||||||
|
|
||||||
def extract_strings() -> None:
|
def extract_strings() -> None:
|
||||||
@@ -121,10 +122,39 @@ def update_locales() -> None:
|
|||||||
"pybabel", "update",
|
"pybabel", "update",
|
||||||
f"--input-file={POT_FILE.resolve()}",
|
f"--input-file={POT_FILE.resolve()}",
|
||||||
f"--output-dir={LOCALES_PATH.resolve()}",
|
f"--output-dir={LOCALES_PATH.resolve()}",
|
||||||
|
"--domain=portprotonqt",
|
||||||
"--ignore-obsolete",
|
"--ignore-obsolete",
|
||||||
"--update-header-comment",
|
"--update-header-comment",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def _update_meson_locales(new_locales: list[str]) -> None:
|
||||||
|
"""Обновляет список языков в meson.build."""
|
||||||
|
if not MESON_BUILD.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
text = MESON_BUILD.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
# Ищем foreach lang : ['de', 'es', 'pt', 'ru']
|
||||||
|
pattern = r"(foreach\s+lang\s*:\s*\[)([^\]]+)(\])"
|
||||||
|
match = re.search(pattern, text)
|
||||||
|
if not match:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Парсим текущий список языков
|
||||||
|
current_langs_str = match.group(2)
|
||||||
|
current_langs = re.findall(r"'([^']+)'", current_langs_str)
|
||||||
|
|
||||||
|
# Добавляем новые языки и сортируем
|
||||||
|
all_langs = sorted(set(current_langs) | set(new_locales))
|
||||||
|
|
||||||
|
# Формируем новый список
|
||||||
|
new_langs_str = ", ".join(f"'{lang}'" for lang in all_langs)
|
||||||
|
new_text = text[:match.start()] + match.group(1) + new_langs_str + match.group(3) + text[match.end():]
|
||||||
|
|
||||||
|
if new_text != text:
|
||||||
|
MESON_BUILD.write_text(new_text, encoding="utf-8")
|
||||||
|
print(f"Updated meson.build with locales: {all_langs}")
|
||||||
|
|
||||||
def create_new(locales: list[str]) -> None:
|
def create_new(locales: list[str]) -> None:
|
||||||
if not POT_FILE.exists():
|
if not POT_FILE.exists():
|
||||||
extract_strings()
|
extract_strings()
|
||||||
@@ -133,8 +163,11 @@ def create_new(locales: list[str]) -> None:
|
|||||||
"pybabel", "init",
|
"pybabel", "init",
|
||||||
f"--input-file={POT_FILE.resolve()}",
|
f"--input-file={POT_FILE.resolve()}",
|
||||||
f"--output-dir={LOCALES_PATH.resolve()}",
|
f"--output-dir={LOCALES_PATH.resolve()}",
|
||||||
|
"--domain=portprotonqt",
|
||||||
f"--locale={locale}"
|
f"--locale={locale}"
|
||||||
])
|
])
|
||||||
|
# Обновляем meson.build с новыми локалями
|
||||||
|
_update_meson_locales(locales)
|
||||||
|
|
||||||
# ---------- Игнорируемые префиксы для spellcheck ----------
|
# ---------- Игнорируемые префиксы для spellcheck ----------
|
||||||
IGNORED_PREFIXES = ()
|
IGNORED_PREFIXES = ()
|
||||||
@@ -148,6 +181,41 @@ def load_ignored_prefixes(ignore_file=".spellignore"):
|
|||||||
|
|
||||||
IGNORED_PREFIXES = load_ignored_prefixes() + ("PortProton", "flatpak")
|
IGNORED_PREFIXES = load_ignored_prefixes() + ("PortProton", "flatpak")
|
||||||
|
|
||||||
|
# ---------- Проверка fuzzy строк ----------
|
||||||
|
def find_fuzzy_entries(filepath: Path) -> list[tuple[int, str, str]]:
|
||||||
|
"""Находит fuzzy записи в .po файле. Возвращает список (номер_строки, msgid, флаги)."""
|
||||||
|
fuzzy_entries = []
|
||||||
|
lines = filepath.read_text(encoding='utf-8').splitlines()
|
||||||
|
i = 0
|
||||||
|
while i < len(lines):
|
||||||
|
line = lines[i].strip()
|
||||||
|
# Ищем комментарий с флагами, содержащий fuzzy
|
||||||
|
if line.startswith('#,') and 'fuzzy' in line:
|
||||||
|
flags = line[2:].strip()
|
||||||
|
line_num = i + 1
|
||||||
|
# Ищем следующий msgid
|
||||||
|
msgid = ""
|
||||||
|
i += 1
|
||||||
|
while i < len(lines):
|
||||||
|
next_line = lines[i].strip()
|
||||||
|
if next_line.startswith('msgid '):
|
||||||
|
match = re.match(r'^msgid\s+"(.*)"', next_line)
|
||||||
|
if match:
|
||||||
|
msgid = match.group(1)
|
||||||
|
i += 1
|
||||||
|
# Собираем многострочный msgid
|
||||||
|
while i < len(lines) and lines[i].strip().startswith('"'):
|
||||||
|
msgid += lines[i].strip()[1:-1]
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
# Пропускаем пустой msgid (заголовок PO файла)
|
||||||
|
if msgid:
|
||||||
|
fuzzy_entries.append((line_num, msgid, flags))
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
return fuzzy_entries
|
||||||
|
|
||||||
# ---------- Проверка орфографии с параллелизмом ----------
|
# ---------- Проверка орфографии с параллелизмом ----------
|
||||||
speller = YandexSpeller()
|
speller = YandexSpeller()
|
||||||
MSGID_RE = re.compile(r'^msgid\s+"(.*)"')
|
MSGID_RE = re.compile(r'^msgid\s+"(.*)"')
|
||||||
@@ -208,17 +276,36 @@ def main(args) -> int:
|
|||||||
if args.create_new:
|
if args.create_new:
|
||||||
create_new(args.create_new)
|
create_new(args.create_new)
|
||||||
if args.spellcheck:
|
if args.spellcheck:
|
||||||
files = list(LOCALES_PATH.glob("**/*.po")) + [POT_FILE]
|
files = list(LOCALES_PATH.glob("**/portprotonqt.po")) + [POT_FILE]
|
||||||
seen = set(); has_err = False
|
seen = set(); has_err = False
|
||||||
issues_summary = defaultdict(list)
|
issues_summary = defaultdict(list)
|
||||||
|
fuzzy_summary = defaultdict(list)
|
||||||
for f in files:
|
for f in files:
|
||||||
if not f.exists() or f in seen: continue
|
if not f.exists() or f in seen: continue
|
||||||
seen.add(f)
|
seen.add(f)
|
||||||
|
# Проверка fuzzy строк (только для .po файлов)
|
||||||
|
if f.suffix == '.po':
|
||||||
|
fuzzy_entries = find_fuzzy_entries(f)
|
||||||
|
if fuzzy_entries:
|
||||||
|
fuzzy_summary[f] = fuzzy_entries
|
||||||
|
has_err = True
|
||||||
if check_file(f, issues_summary):
|
if check_file(f, issues_summary):
|
||||||
has_err = True
|
has_err = True
|
||||||
else:
|
else:
|
||||||
print(f"✅ {f} — no errors found.")
|
if f not in fuzzy_summary:
|
||||||
if has_err:
|
print(f"✅ {f} — no errors found.")
|
||||||
|
# Вывод fuzzy строк
|
||||||
|
if fuzzy_summary:
|
||||||
|
print("\n⚠️ Fuzzy Entries (require review before release):")
|
||||||
|
for file, entries in fuzzy_summary.items():
|
||||||
|
print(f"\n⚠ {file}")
|
||||||
|
print("-----")
|
||||||
|
for idx, (line_num, msgid, flags) in enumerate(entries, 1):
|
||||||
|
print(f"{idx}. Line {line_num}: [{flags}]")
|
||||||
|
print(f" msgid: \"{msgid[:80]}{'...' if len(msgid) > 80 else ''}\"")
|
||||||
|
print("-----")
|
||||||
|
# Вывод орфографических ошибок
|
||||||
|
if issues_summary:
|
||||||
print("\n📋 Summary of Spelling Errors:")
|
print("\n📋 Summary of Spelling Errors:")
|
||||||
for file, errs in issues_summary.items():
|
for file, errs in issues_summary.items():
|
||||||
print(f"\n✗ {file}")
|
print(f"\n✗ {file}")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
## 📖 Overview
|
## 📖 Overview
|
||||||
|
|
||||||
Localization in `PortProtonQT` is powered by `Babel` using `.po/.mo` files stored under `LC_MESSAGES/messages.po` for each language.
|
Localization in `PortProtonQT` is powered by `Babel` using `.po/.mo` files stored under `LC_MESSAGES/portprotonqt.po` for each language.
|
||||||
|
|
||||||
Current translation status:
|
Current translation status:
|
||||||
|
|
||||||
@@ -21,9 +21,10 @@ Current translation status:
|
|||||||
|
|
||||||
| Locale | Progress | Translated |
|
| Locale | Progress | Translated |
|
||||||
| :----- | -------: | ---------: |
|
| :----- | -------: | ---------: |
|
||||||
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 376 |
|
| [de](./de/LC_MESSAGES/portprotonqt.po) | 0% | 0 of 376 |
|
||||||
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 376 |
|
| [es](./es/LC_MESSAGES/portprotonqt.po) | 0% | 0 of 376 |
|
||||||
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 376 of 376 |
|
| [pt](./pt/LC_MESSAGES/portprotonqt.po) | 0% | 0 of 376 |
|
||||||
|
| [ru](./ru/LC_MESSAGES/portprotonqt.po) | 100% | 376 of 376 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ source .venv/bin/activate
|
|||||||
python dev-scripts/l10n.py --create-new <locale_code>
|
python dev-scripts/l10n.py --create-new <locale_code>
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Edit the file `portprotonqt/locales/<locale>/LC_MESSAGES/messages.po` in Poedit or any text editor.
|
2. Edit the file `portprotonqt/locales/<locale>/LC_MESSAGES/portprotonqt.po` in Poedit or any text editor.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
## 📖 Обзор
|
## 📖 Обзор
|
||||||
|
|
||||||
Локализация в `PortProtonQT` осуществляется через систему `.po/.mo` файлов и управляется утилитой `Babel`. Все переводы находятся в подкаталогах вида `LC_MESSAGES/messages.po` для каждой поддерживаемой локали.
|
Локализация в `PortProtonQT` осуществляется через систему `.po/.mo` файлов и управляется утилитой `Babel`. Все переводы находятся в подкаталогах вида `LC_MESSAGES/portprotonqt.po` для каждой поддерживаемой локали.
|
||||||
|
|
||||||
Текущий статус перевода:
|
Текущий статус перевода:
|
||||||
|
|
||||||
@@ -21,9 +21,10 @@
|
|||||||
|
|
||||||
| Локаль | Прогресс | Переведено |
|
| Локаль | Прогресс | Переведено |
|
||||||
| :----- | -------: | ---------: |
|
| :----- | -------: | ---------: |
|
||||||
| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 376 |
|
| [de](./de/LC_MESSAGES/portprotonqt.po) | 0% | 0 из 376 |
|
||||||
| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 376 |
|
| [es](./es/LC_MESSAGES/portprotonqt.po) | 0% | 0 из 376 |
|
||||||
| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 376 из 376 |
|
| [pt](./pt/LC_MESSAGES/portprotonqt.po) | 0% | 0 из 376 |
|
||||||
|
| [ru](./ru/LC_MESSAGES/portprotonqt.po) | 100% | 376 из 376 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ source .venv/bin/activate
|
|||||||
python dev-scripts/l10n.py --create-new <код_локали>
|
python dev-scripts/l10n.py --create-new <код_локали>
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Отредактируйте файл `portprotonqt/locales/<локаль>/LC_MESSAGES/messages.po` в Poedit или любом текстовом редакторе.
|
2. Отредактируйте файл `portprotonqt/locales/<локаль>/LC_MESSAGES/portprotonqt.po` в Poedit или любом текстовом редакторе.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
83
meson.build
Normal file
83
meson.build
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
project('portprotonqt',
|
||||||
|
version: '0.1.10',
|
||||||
|
meson_version: '>= 0.61.2',
|
||||||
|
license: 'GPL-3.0',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Project directories
|
||||||
|
prefix = get_option('prefix')
|
||||||
|
datadir = prefix / get_option('datadir')
|
||||||
|
bindir = prefix / get_option('bindir')
|
||||||
|
|
||||||
|
# Python module for site-packages path
|
||||||
|
python = import('python')
|
||||||
|
python3 = python.find_installation('python3')
|
||||||
|
pythondir = join_paths(prefix, python3.get_path('purelib'))
|
||||||
|
|
||||||
|
python_libdir = get_option('python_libdir')
|
||||||
|
if python_libdir == ''
|
||||||
|
python_installdir = pythondir
|
||||||
|
else
|
||||||
|
python_installdir = python_libdir
|
||||||
|
endif
|
||||||
|
|
||||||
|
pkgdatadir = python_installdir / meson.project_name()
|
||||||
|
|
||||||
|
conf = configuration_data()
|
||||||
|
conf.set('PYTHON', python.find_installation('python3').full_path())
|
||||||
|
|
||||||
|
# Install Python package
|
||||||
|
subdir('portprotonqt')
|
||||||
|
|
||||||
|
# Install entry point script with proper name to avoid conflict in build directory
|
||||||
|
configured_portprotonqt = configure_file(
|
||||||
|
input: 'build-aux/portprotonqt',
|
||||||
|
output: 'portprotonqt-script',
|
||||||
|
configuration: conf,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install the configured script with the correct name
|
||||||
|
install_data(configured_portprotonqt,
|
||||||
|
install_dir: bindir,
|
||||||
|
install_mode: 'rwxr-xr-x',
|
||||||
|
rename: 'portprotonqt')
|
||||||
|
|
||||||
|
# Install desktop file
|
||||||
|
install_data(
|
||||||
|
'build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop',
|
||||||
|
install_dir: datadir / 'applications',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install icon
|
||||||
|
install_data(
|
||||||
|
'build-aux/share/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg',
|
||||||
|
install_dir: datadir / 'icons/hicolor/scalable/apps',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install metainfo
|
||||||
|
install_data(
|
||||||
|
'build-aux/share/metainfo/ru.linux_gaming.PortProtonQt.metainfo.xml',
|
||||||
|
install_dir: datadir / 'metainfo',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install bash completion
|
||||||
|
install_data(
|
||||||
|
'build-aux/share/bash-completion/completions/portprotonqt',
|
||||||
|
install_dir: datadir / 'bash-completion/completions',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install udev rules
|
||||||
|
udevdir = get_option('udevdir')
|
||||||
|
if udevdir == ''
|
||||||
|
udev = dependency('udev', required: false)
|
||||||
|
if udev.found()
|
||||||
|
udevdir = udev.get_variable(pkgconfig: 'udevdir') / 'rules.d'
|
||||||
|
else
|
||||||
|
udevdir = prefix / get_option('libdir') / 'udev/rules.d'
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
install_data(
|
||||||
|
'build-aux/lib/udev/rules.d/60-portprotonqt.rules',
|
||||||
|
install_dir: udevdir,
|
||||||
|
)
|
||||||
11
meson_options.txt
Normal file
11
meson_options.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
option('udevdir',
|
||||||
|
type: 'string',
|
||||||
|
value: '',
|
||||||
|
description: 'Directory for udev rules (auto-detected if empty)',
|
||||||
|
)
|
||||||
|
|
||||||
|
option('python_libdir',
|
||||||
|
type: 'string',
|
||||||
|
value: '',
|
||||||
|
description: 'Python site-packages directory (auto-detected if empty)',
|
||||||
|
)
|
||||||
@@ -28,7 +28,7 @@ class DetailPageManager:
|
|||||||
self._animations = {}
|
self._animations = {}
|
||||||
self.portproton_api = PortProtonAPI(Downloader(max_workers=4))
|
self.portproton_api = PortProtonAPI(Downloader(max_workers=4))
|
||||||
|
|
||||||
def openGameDetailPage(self, name, description, cover_path=None, appid="", exec_line="", controller_support="",
|
def openGameDetailPage(self, name, description, cover_path=None, appid="", controller_support="", exec_line="",
|
||||||
last_launch="", formatted_playtime="", protondb_tier="", game_source="", anticheat_status=""):
|
last_launch="", formatted_playtime="", protondb_tier="", game_source="", anticheat_status=""):
|
||||||
"""Open detailed game information page showing all game stats, playtime and settings."""
|
"""Open detailed game information page showing all game stats, playtime and settings."""
|
||||||
detailPage = QWidget()
|
detailPage = QWidget()
|
||||||
@@ -210,7 +210,7 @@ class DetailPageManager:
|
|||||||
portprotonLabel.setStyleSheet(self.main_window.theme.STEAM_BADGE_STYLE)
|
portprotonLabel.setStyleSheet(self.main_window.theme.STEAM_BADGE_STYLE)
|
||||||
portprotonLabel.setFixedWidth(badge_width)
|
portprotonLabel.setFixedWidth(badge_width)
|
||||||
portprotonLabel.setVisible(portproton_visible)
|
portprotonLabel.setVisible(portproton_visible)
|
||||||
portprotonLabel.clicked.connect(lambda: self.open_portproton_forum_topic(name))
|
portprotonLabel.clicked.connect(lambda: self.portproton_api.open_ppdb_page(name, exec_line))
|
||||||
|
|
||||||
# WeAntiCheatYet badge
|
# WeAntiCheatYet badge
|
||||||
anticheat_text = GameCard.getAntiCheatText(anticheat_status)
|
anticheat_text = GameCard.getAntiCheatText(anticheat_status)
|
||||||
@@ -869,12 +869,3 @@ class DetailPageManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error starting exit animation: {e}", exc_info=True)
|
logger.error(f"Error starting exit animation: {e}", exc_info=True)
|
||||||
self._exit_animation_in_progress = False
|
self._exit_animation_in_progress = False
|
||||||
|
|
||||||
def open_portproton_forum_topic(self, name):
|
|
||||||
result = self.portproton_api.get_forum_topic_slug(name)
|
|
||||||
base_url = "https://linux-gaming.ru/"
|
|
||||||
if result.startswith("search?q="):
|
|
||||||
url = QUrl(f"{base_url}{result}")
|
|
||||||
else:
|
|
||||||
url = QUrl(f"{base_url}t/{result}")
|
|
||||||
QDesktopServices.openUrl(url)
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class GameCard(QFrame):
|
|||||||
self.portprotonLabel.setStyleSheet(self.theme.STEAM_BADGE_STYLE)
|
self.portprotonLabel.setStyleSheet(self.theme.STEAM_BADGE_STYLE)
|
||||||
self.portprotonLabel.setCardWidth(card_width)
|
self.portprotonLabel.setCardWidth(card_width)
|
||||||
self.portprotonLabel.setVisible(self.portproton_visible)
|
self.portprotonLabel.setVisible(self.portproton_visible)
|
||||||
self.portprotonLabel.clicked.connect(self.open_portproton_forum_topic)
|
self.portprotonLabel.clicked.connect(self.open_ppdb_page)
|
||||||
|
|
||||||
anticheat_text = self.getAntiCheatText(anticheat_status)
|
anticheat_text = self.getAntiCheatText(anticheat_status)
|
||||||
if anticheat_text:
|
if anticheat_text:
|
||||||
@@ -426,14 +426,8 @@ class GameCard(QFrame):
|
|||||||
return "broken"
|
return "broken"
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def open_portproton_forum_topic(self):
|
def open_ppdb_page(self):
|
||||||
result = self.portproton_api.get_forum_topic_slug(self.name)
|
self.portproton_api.open_ppdb_page(self.name, self.exec_line)
|
||||||
base_url = "https://linux-gaming.ru/"
|
|
||||||
if result.startswith("search?q="):
|
|
||||||
url = QUrl(f"{base_url}{result}")
|
|
||||||
else:
|
|
||||||
url = QUrl(f"{base_url}t/{result}")
|
|
||||||
QDesktopServices.openUrl(url)
|
|
||||||
|
|
||||||
def open_protondb_report(self):
|
def open_protondb_report(self):
|
||||||
url = QUrl(f"https://www.protondb.com/app/{self.appid}")
|
url = QUrl(f"https://www.protondb.com/app/{self.appid}")
|
||||||
|
|||||||
@@ -1099,7 +1099,8 @@ class InputManager(QObject):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Handle common UI elements like QMessageBox, QMenu, etc.
|
# Handle common UI elements like QMessageBox, QMenu, etc.
|
||||||
if self._handle_common_ui_elements(button_code):
|
# Only handle press events (value != 0), ignore release events
|
||||||
|
if value != 0 and self._handle_common_ui_elements(button_code):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Handle other QDialogs
|
# Handle other QDialogs
|
||||||
@@ -1550,7 +1551,8 @@ class InputManager(QObject):
|
|||||||
def handle_button_slot(self, button_code: int, value: int) -> None:
|
def handle_button_slot(self, button_code: int, value: int) -> None:
|
||||||
# Handle common UI elements like QMessageBox, QMenu, etc. FIRST
|
# Handle common UI elements like QMessageBox, QMenu, etc. FIRST
|
||||||
# This ensures that any active dialogs are handled before main window logic
|
# This ensures that any active dialogs are handled before main window logic
|
||||||
if self._handle_common_ui_elements(button_code):
|
# Only handle press events (value=1), ignore release events (value=0)
|
||||||
|
if value == 1 and self._handle_common_ui_elements(button_code):
|
||||||
return
|
return
|
||||||
|
|
||||||
active_window = QApplication.activeWindow()
|
active_window = QApplication.activeWindow()
|
||||||
@@ -1591,16 +1593,10 @@ class InputManager(QObject):
|
|||||||
current_tab_index = self._parent.stackedWidget.currentIndex()
|
current_tab_index = self._parent.stackedWidget.currentIndex()
|
||||||
|
|
||||||
if button_code in BUTTONS['confirm'] and isinstance(focused, QLineEdit):
|
if button_code in BUTTONS['confirm'] and isinstance(focused, QLineEdit):
|
||||||
search_edit = None
|
keyboard = getattr(self._parent, 'keyboard', None)
|
||||||
if current_tab_index == 0:
|
if keyboard:
|
||||||
search_edit = getattr(self._parent, 'searchEdit', None)
|
keyboard.show_for_widget(focused)
|
||||||
elif current_tab_index == 1:
|
return
|
||||||
search_edit = getattr(self._parent, 'autoInstallSearchLineEdit', None)
|
|
||||||
if focused == search_edit:
|
|
||||||
keyboard = getattr(self._parent, 'keyboard', None)
|
|
||||||
if keyboard:
|
|
||||||
keyboard.show_for_widget(focused)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Handle Y button to focus search
|
# Handle Y button to focus search
|
||||||
if button_code in BUTTONS['prev_dir']: # Y button
|
if button_code in BUTTONS['prev_dir']: # Y button
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PortProtonQt 0.1.1\n"
|
"Project-Id-Version: PortProtonQt 0.1.1\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
"POT-Creation-Date: 2026-01-12 20:59+0500\n"
|
"POT-Creation-Date: 2026-01-18 11:43+0500\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
BIN
portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.mo
Normal file
BIN
portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.mo
Normal file
Binary file not shown.
1286
portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.po
Normal file
1286
portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -33,11 +33,21 @@ LOCALE_MAP = {
|
|||||||
'el': 'greek',
|
'el': 'greek',
|
||||||
}
|
}
|
||||||
|
|
||||||
translate = gettext.translation(
|
# Try system locale directory first, fallback to local for development
|
||||||
domain="messages",
|
_system_localedir = Path("/usr/share/locale")
|
||||||
localedir = Path(__file__).parent / "locales",
|
_local_localedir = Path(__file__).parent / "locales"
|
||||||
fallback=True,
|
|
||||||
)
|
try:
|
||||||
|
translate = gettext.translation(
|
||||||
|
domain="portprotonqt",
|
||||||
|
localedir=_system_localedir,
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
translate = gettext.translation(
|
||||||
|
domain="portprotonqt",
|
||||||
|
localedir=_local_localedir,
|
||||||
|
fallback=True,
|
||||||
|
)
|
||||||
_ = translate.gettext
|
_ = translate.gettext
|
||||||
|
|
||||||
def get_system_locale():
|
def get_system_locale():
|
||||||
|
|||||||
@@ -804,16 +804,6 @@ class MainWindow(QMainWindow):
|
|||||||
self.refreshButton.setText(_("Refresh Grid"))
|
self.refreshButton.setText(_("Refresh Grid"))
|
||||||
self.update_status_message.emit(_("Game library refreshed"), 3000)
|
self.update_status_message.emit(_("Game library refreshed"), 3000)
|
||||||
|
|
||||||
def open_portproton_forum_topic(self, topic_name: str):
|
|
||||||
"""Open the PortProton forum topic or search page for this game."""
|
|
||||||
result = self.portproton_api.get_forum_topic_slug(topic_name)
|
|
||||||
base_url = "https://linux-gaming.ru/"
|
|
||||||
if result.startswith("search?q="):
|
|
||||||
url = QUrl(f"{base_url}{result}")
|
|
||||||
else:
|
|
||||||
url = QUrl(f"{base_url}t/{result}")
|
|
||||||
QDesktopServices.openUrl(url)
|
|
||||||
|
|
||||||
def loadGames(self):
|
def loadGames(self):
|
||||||
display_filter = read_display_filter()
|
display_filter = read_display_filter()
|
||||||
favorites = read_favorites()
|
favorites = read_favorites()
|
||||||
@@ -971,8 +961,8 @@ class MainWindow(QMainWindow):
|
|||||||
info.get('description', ''),
|
info.get('description', ''),
|
||||||
info.get('cover', ''),
|
info.get('cover', ''),
|
||||||
appid,
|
appid,
|
||||||
f"steam://rungameid/{appid}",
|
|
||||||
info.get('controller_support', ''),
|
info.get('controller_support', ''),
|
||||||
|
f"steam://rungameid/{appid}",
|
||||||
last_launch,
|
last_launch,
|
||||||
format_playtime(playtime_seconds),
|
format_playtime(playtime_seconds),
|
||||||
info.get('protondb_tier', ''),
|
info.get('protondb_tier', ''),
|
||||||
@@ -1121,8 +1111,8 @@ class MainWindow(QMainWindow):
|
|||||||
final_desc,
|
final_desc,
|
||||||
final_cover,
|
final_cover,
|
||||||
steam_info.get("appid", ""),
|
steam_info.get("appid", ""),
|
||||||
exec_line,
|
|
||||||
steam_info.get("controller_support", ""),
|
steam_info.get("controller_support", ""),
|
||||||
|
exec_line,
|
||||||
get_last_launch(exe_name) if exe_name else _("Never"),
|
get_last_launch(exe_name) if exe_name else _("Never"),
|
||||||
formatted_playtime,
|
formatted_playtime,
|
||||||
steam_info.get("protondb_tier", ""),
|
steam_info.get("protondb_tier", ""),
|
||||||
@@ -1440,8 +1430,8 @@ class MainWindow(QMainWindow):
|
|||||||
final_desc,
|
final_desc,
|
||||||
final_cover,
|
final_cover,
|
||||||
steam_info.get("appid", ""),
|
steam_info.get("appid", ""),
|
||||||
exec_line,
|
|
||||||
steam_info.get("controller_support", ""),
|
steam_info.get("controller_support", ""),
|
||||||
|
exec_line,
|
||||||
last_launch,
|
last_launch,
|
||||||
formatted_playtime,
|
formatted_playtime,
|
||||||
steam_info.get("protondb_tier", ""),
|
steam_info.get("protondb_tier", ""),
|
||||||
@@ -2433,6 +2423,9 @@ class MainWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
self.input_manager.gamepad_type = GamepadType.UNKNOWN
|
self.input_manager.gamepad_type = GamepadType.UNKNOWN
|
||||||
self.updateControlHints()
|
self.updateControlHints()
|
||||||
|
# Update virtual keyboard icons
|
||||||
|
if hasattr(self, 'keyboard'):
|
||||||
|
self.keyboard.update_keyboard()
|
||||||
|
|
||||||
for card in self.game_library_manager.game_card_cache.values():
|
for card in self.game_library_manager.game_card_cache.values():
|
||||||
card.update_badge_visibility(filter_key)
|
card.update_badge_visibility(filter_key)
|
||||||
|
|||||||
54
portprotonqt/meson.build
Normal file
54
portprotonqt/meson.build
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Install Python source files
|
||||||
|
install_data(
|
||||||
|
'__init__.py',
|
||||||
|
'animations.py',
|
||||||
|
'app.py',
|
||||||
|
'cli.py',
|
||||||
|
'config_utils.py',
|
||||||
|
'context_menu_manager.py',
|
||||||
|
'custom_widgets.py',
|
||||||
|
'detail_pages.py',
|
||||||
|
'dialogs.py',
|
||||||
|
'downloader.py',
|
||||||
|
'egs_api.py',
|
||||||
|
'game_card.py',
|
||||||
|
'game_library_manager.py',
|
||||||
|
'get_wine_module.py',
|
||||||
|
'howlongtobeat_api.py',
|
||||||
|
'image_utils.py',
|
||||||
|
'input_manager.py',
|
||||||
|
'keyboard_layouts.py',
|
||||||
|
'localization.py',
|
||||||
|
'logger.py',
|
||||||
|
'main_window.py',
|
||||||
|
'portproton_api.py',
|
||||||
|
'preloader.py',
|
||||||
|
'search_utils.py',
|
||||||
|
'settings_manager.py',
|
||||||
|
'steam_api.py',
|
||||||
|
'system_overlay.py',
|
||||||
|
'theme_manager.py',
|
||||||
|
'theme_security.py',
|
||||||
|
'time_utils.py',
|
||||||
|
'tray_manager.py',
|
||||||
|
'version_utils.py',
|
||||||
|
'virtual_keyboard.py',
|
||||||
|
install_dir: pkgdatadir,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install themes
|
||||||
|
install_subdir(
|
||||||
|
'themes',
|
||||||
|
install_dir: pkgdatadir,
|
||||||
|
exclude_directories: ['__pycache__'],
|
||||||
|
exclude_files: ['*.pyc'],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install locales - only .mo files (compiled translations)
|
||||||
|
# exclude_files doesn't work recursively, so we install each language manually
|
||||||
|
foreach lang : ['de', 'es', 'pt', 'ru']
|
||||||
|
install_data(
|
||||||
|
'locales' / lang / 'LC_MESSAGES' / 'portprotonqt.mo',
|
||||||
|
install_dir: get_option('localedir') / lang / 'LC_MESSAGES',
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
import tarfile
|
|
||||||
import orjson
|
import orjson
|
||||||
import requests
|
import requests
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
@@ -8,13 +7,13 @@ import glob
|
|||||||
import re
|
import re
|
||||||
import hashlib
|
import hashlib
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from PySide6.QtCore import QThread, Signal
|
from PySide6.QtCore import QThread, Signal, QUrl
|
||||||
|
from PySide6.QtGui import QDesktopServices
|
||||||
from portprotonqt.downloader import Downloader
|
from portprotonqt.downloader import Downloader
|
||||||
from portprotonqt.logger import get_logger
|
from portprotonqt.logger import get_logger
|
||||||
from portprotonqt.config_utils import get_portproton_location
|
from portprotonqt.config_utils import get_portproton_location
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
CACHE_DURATION = 30 * 24 * 60 * 60 # 30 days in seconds
|
|
||||||
AUTOINSTALL_CACHE_DURATION = 3600 # 1 hour for autoinstall cache
|
AUTOINSTALL_CACHE_DURATION = 3600 # 1 hour for autoinstall cache
|
||||||
|
|
||||||
def normalize_name(s):
|
def normalize_name(s):
|
||||||
@@ -42,6 +41,41 @@ def normalize_name(s):
|
|||||||
filtered_words = [word for word in words if word not in keywords_to_remove]
|
filtered_words = [word for word in words if word not in keywords_to_remove]
|
||||||
return " ".join(filtered_words)
|
return " ".join(filtered_words)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_exe_name(exec_line: str) -> str:
|
||||||
|
"""Extract executable name from exec_line.
|
||||||
|
|
||||||
|
Handles various exec_line formats:
|
||||||
|
- Full command: 'env VAR=val /path/to/script /path/to/game.exe' -> 'game.exe'
|
||||||
|
- Autoinstall: 'autoinstall:script_name' -> ''
|
||||||
|
- Simple path: '/path/to/game.exe' -> 'game.exe'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Executable name with .exe extension, or empty string if not found
|
||||||
|
"""
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
if not exec_line:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Handle autoinstall scripts - they don't have a direct exe
|
||||||
|
if exec_line.startswith("autoinstall:"):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
parts = shlex.split(exec_line)
|
||||||
|
|
||||||
|
# Search for the last part ending with .exe
|
||||||
|
# In PortProton format, the game exe is always the last argument
|
||||||
|
for part in reversed(parts):
|
||||||
|
if part.lower().endswith(".exe"):
|
||||||
|
game_exe = os.path.expanduser(part)
|
||||||
|
return os.path.basename(game_exe)
|
||||||
|
|
||||||
|
return ""
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
return ""
|
||||||
|
|
||||||
def get_cache_dir():
|
def get_cache_dir():
|
||||||
"""Return the cache directory path, creating it if necessary."""
|
"""Return the cache directory path, creating it if necessary."""
|
||||||
xdg_cache_home = os.getenv("XDG_CACHE_HOME", os.path.join(os.path.expanduser("~"), ".cache"))
|
xdg_cache_home = os.getenv("XDG_CACHE_HOME", os.path.join(os.path.expanduser("~"), ".cache"))
|
||||||
@@ -53,7 +87,6 @@ class PortProtonAPI:
|
|||||||
"""API to fetch game assets (cover, metadata) and forum topics from the PortProtonQt repository."""
|
"""API to fetch game assets (cover, metadata) and forum topics from the PortProtonQt repository."""
|
||||||
def __init__(self, downloader: Downloader | None = None):
|
def __init__(self, downloader: Downloader | None = None):
|
||||||
self.base_url = "https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/portprotonqt/custom_data"
|
self.base_url = "https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/portprotonqt/custom_data"
|
||||||
self.topics_url = "https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/data/linux_gaming_topics.tar.xz"
|
|
||||||
self.downloader = downloader or Downloader(max_workers=4)
|
self.downloader = downloader or Downloader(max_workers=4)
|
||||||
self.xdg_data_home = os.getenv("XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share"))
|
self.xdg_data_home = os.getenv("XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share"))
|
||||||
self.custom_data_dir = os.path.join(self.xdg_data_home, "PortProtonQt", "custom_data")
|
self.custom_data_dir = os.path.join(self.xdg_data_home, "PortProtonQt", "custom_data")
|
||||||
@@ -61,8 +94,7 @@ class PortProtonAPI:
|
|||||||
self.portproton_location = get_portproton_location()
|
self.portproton_location = get_portproton_location()
|
||||||
self.repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
self.repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
self.builtin_custom_folder = os.path.join(self.repo_root, "custom_data")
|
self.builtin_custom_folder = os.path.join(self.repo_root, "custom_data")
|
||||||
self._topics_data = None
|
self._autoinstall_cache = None # In-memory cache
|
||||||
self._autoinstall_cache = None # New: In-memory cache
|
|
||||||
|
|
||||||
def _get_game_dir(self, exe_name: str) -> str:
|
def _get_game_dir(self, exe_name: str) -> str:
|
||||||
game_dir = os.path.join(self.custom_data_dir, exe_name)
|
game_dir = os.path.join(self.custom_data_dir, exe_name)
|
||||||
@@ -503,65 +535,64 @@ class PortProtonAPI:
|
|||||||
logger.info("Started background load of autoinstall games")
|
logger.info("Started background load of autoinstall games")
|
||||||
return worker
|
return worker
|
||||||
|
|
||||||
def _load_topics_data(self):
|
def get_ppdb_url(self, game_name: str, exe_name: str) -> str:
|
||||||
"""Load and cache linux_gaming_topics_min.json from the archive."""
|
"""Get the PPDB URL for a given game.
|
||||||
if self._topics_data is not None:
|
|
||||||
return self._topics_data
|
|
||||||
|
|
||||||
cache_dir = get_cache_dir()
|
Makes an API call to ppdb.linux-gaming.ru to look up the game by exe name.
|
||||||
cache_tar = os.path.join(cache_dir, "linux_gaming_topics.tar.xz")
|
If the returned name matches the game name, returns the direct URL.
|
||||||
cache_json = os.path.join(cache_dir, "linux_gaming_topics_min.json")
|
Otherwise returns a search URL to avoid false positives (e.g., launcher.exe matches many games).
|
||||||
|
|
||||||
if os.path.exists(cache_json) and (time.time() - os.path.getmtime(cache_json) < CACHE_DURATION):
|
Args:
|
||||||
logger.info("Using cached topics JSON: %s", cache_json)
|
game_name: Display name of the game
|
||||||
try:
|
exe_name: Executable name (with or without .exe extension)
|
||||||
with open(cache_json, "rb") as f:
|
|
||||||
self._topics_data = orjson.loads(f.read())
|
|
||||||
logger.debug("Loaded %d topics from cache", len(self._topics_data))
|
|
||||||
return self._topics_data
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Error reading cached topics JSON: %s", e)
|
|
||||||
self._topics_data = []
|
|
||||||
|
|
||||||
def process_tar(result: str | None):
|
Returns:
|
||||||
if not result or not os.path.exists(result):
|
Full URL to the PPDB page or search page
|
||||||
logger.error("Failed to download topics archive")
|
"""
|
||||||
self._topics_data = []
|
base_url = "https://ppdb.linux-gaming.ru"
|
||||||
return
|
|
||||||
try:
|
|
||||||
with tarfile.open(result, mode="r:xz") as tar:
|
|
||||||
member = next((m for m in tar.getmembers() if m.name == "linux_gaming_topics_min.json"), None)
|
|
||||||
if member is None:
|
|
||||||
raise RuntimeError("linux_gaming_topics_min.json not found in archive")
|
|
||||||
fobj = tar.extractfile(member)
|
|
||||||
if fobj is None:
|
|
||||||
raise RuntimeError("Failed to extract linux_gaming_topics_min.json from archive")
|
|
||||||
raw = fobj.read()
|
|
||||||
fobj.close()
|
|
||||||
self._topics_data = orjson.loads(raw)
|
|
||||||
with open(cache_json, "wb") as f:
|
|
||||||
f.write(orjson.dumps(self._topics_data))
|
|
||||||
if os.path.exists(cache_tar):
|
|
||||||
os.remove(cache_tar)
|
|
||||||
logger.info("Archive %s deleted after extraction", cache_tar)
|
|
||||||
logger.info("Loaded %d topics from archive", len(self._topics_data))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error("Error processing topics archive: %s", e)
|
|
||||||
self._topics_data = []
|
|
||||||
|
|
||||||
self.downloader.download_async(self.topics_url, cache_tar, timeout=5, callback=process_tar)
|
# Ensure exe_name has .exe extension
|
||||||
# Wait for async download to complete if called synchronously
|
if not exe_name.lower().endswith(".exe"):
|
||||||
while self._topics_data is None:
|
exe_name = f"{exe_name}.exe"
|
||||||
time.sleep(0.1)
|
|
||||||
return self._topics_data
|
|
||||||
|
|
||||||
def get_forum_topic_slug(self, game_name: str) -> str:
|
api_url = f"{base_url}/api/lookup/exe/{urllib.parse.quote(exe_name)}"
|
||||||
"""Get the forum topic slug or search URL for a given game name."""
|
|
||||||
topics = self._load_topics_data()
|
try:
|
||||||
normalized_name = normalize_name(game_name)
|
response = requests.get(api_url, timeout=5)
|
||||||
for topic in topics:
|
if response.status_code == 200:
|
||||||
if topic["normalized_title"] == normalized_name:
|
data = response.json()
|
||||||
return topic["slug"]
|
api_name = data.get("name", "")
|
||||||
logger.debug("No forum topic found for game: %s, redirecting to search", game_name)
|
api_url_result = data.get("url", "")
|
||||||
encoded_name = urllib.parse.quote(f"#ppdb {game_name}")
|
|
||||||
return f"search?q={encoded_name}"
|
# Compare normalized names to avoid false positives
|
||||||
|
if api_name and api_url_result:
|
||||||
|
normalized_game = normalize_name(game_name)
|
||||||
|
normalized_api = normalize_name(api_name)
|
||||||
|
|
||||||
|
if normalized_game == normalized_api:
|
||||||
|
logger.debug("PPDB exact match for %s: %s", game_name, api_url_result)
|
||||||
|
return api_url_result
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"PPDB name mismatch for %s (exe: %s): API returned '%s', redirecting to search",
|
||||||
|
game_name, exe_name, api_name
|
||||||
|
)
|
||||||
|
except requests.RequestException as e:
|
||||||
|
logger.debug("PPDB API request failed for %s: %s", exe_name, e)
|
||||||
|
except (ValueError, KeyError) as e:
|
||||||
|
logger.debug("PPDB API response parsing failed for %s: %s", exe_name, e)
|
||||||
|
|
||||||
|
# Fallback to search URL
|
||||||
|
encoded_name = urllib.parse.quote(game_name)
|
||||||
|
return f"{base_url}/browse?search={encoded_name}"
|
||||||
|
|
||||||
|
def open_ppdb_page(self, game_name: str, exec_line: str) -> None:
|
||||||
|
"""Open the PPDB page for a game in the default browser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
game_name: Display name of the game
|
||||||
|
exec_line: Exec line from which to extract the exe name
|
||||||
|
"""
|
||||||
|
exe_name = extract_exe_name(exec_line)
|
||||||
|
url = self.get_ppdb_url(game_name, exe_name)
|
||||||
|
QDesktopServices.openUrl(QUrl(url))
|
||||||
|
|||||||
Reference in New Issue
Block a user