Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/build-flatpak.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# .github/workflows/build-flatpak.yml
name: reusable linux flatpak build

on:
workflow_call:
outputs:
artifact_name:
description: "the name of the flatpak artifact"
value: ${{ jobs.build-flatpak.outputs.artifact_name }}

jobs:
build-flatpak:
runs-on: ubuntu-22.04
outputs:
artifact_name: ${{ steps.build_flatpak.outputs.bundle }}
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10
options: --privileged
steps:
- uses: actions/checkout@v4

- name: generate python flatpak dependencies
run: |
python3 flatpak/gen_deps.py

- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
id: build_flatpak
with:
bundle: meikipop.flatpak
manifest-path: flatpak/manifest.yml
Comment thread
rtr46 marked this conversation as resolved.
2 changes: 2 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jobs:

build-linux:
uses: ./.github/workflows/build-linux.yml
build-flatpak:
uses: ./.github/workflows/build-flatpak.yml

build-macos:
uses: ./.github/workflows/build-macos.yml
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ jobs:
uses: ./.github/workflows/build-windows.yml
build-linux:
uses: ./.github/workflows/build-linux.yml
build-flatpak:
uses: ./.github/workflows/build-flatpak.yml
build-macos:
Comment thread
rtr46 marked this conversation as resolved.
uses: ./.github/workflows/build-macos.yml

release:
needs: [ build-windows, build-linux, build-macos ]
needs: [ build-windows, build-linux, build-flatpak, build-macos ]
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ dictionary.pkl
.DS_Store

src/meikipop.egg-info/

# Flatpak
bundle-repo
**.flatpak
python3-deps.json
106 changes: 106 additions & 0 deletions flatpak/gen_deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""
Generate python3-deps.json for a flatpak build.

Uses pip's resolver to produce a dry-run report of all transitive deps.
Then emits a single-module flatpak manifest that installs the wheels/sdists offline.
Must be run on a flatpak target Linux x86_64 host.
"""

import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path

import tomllib

PROJECT_PATH = Path("pyproject.toml")
OUT_PATH = Path("flatpak/python3-deps.json")

WHEEL_NO_DEPS = ["pynput>=1.8"]
SDIST_PACKAGES = ["evdev"]

PYTHON_VERSION = "3.13"
PLATFORMS = ["manylinux2014_x86_64", "manylinux_2_28_x86_64"]

os.environ["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"


def run_resolve(packages: list[str], extra_args: list[str]) -> list[dict]:
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as tf:
report_path = Path(tf.name)
try:
cmd = [
sys.executable,
"-m",
"pip",
"install",
"--dry-run",
"--ignore-installed",
"--python-version",
PYTHON_VERSION,
"--report",
str(report_path),
*extra_args,
*packages,
]
subprocess.check_call(cmd)
return json.loads(report_path.read_text())["install"]
finally:
report_path.unlink(missing_ok=True)


def main() -> None:
platform_args = [f"--platform={p}" for p in PLATFORMS]

deps = [
dep
for dep in tomllib.loads(PROJECT_PATH.read_text())["project"]["dependencies"]
if not dep.startswith("pynput")
]
resolved_packages = [
*run_resolve(deps, ["--only-binary=:all:", *platform_args]),
*run_resolve(
WHEEL_NO_DEPS, ["--only-binary=:all:", "--no-deps", *platform_args]
),
*run_resolve(SDIST_PACKAGES, ["--no-binary=:all:", "--no-deps"]),
]

seen = set()
sources = []
for pkg in resolved_packages:
info = pkg["download_info"]
url = info["url"]
if url in seen:
continue
seen.add(url)
sources.append(
{
"type": "file",
"url": url,
"sha256": info["archive_info"]["hashes"]["sha256"],
}
)

OUT_PATH.write_text(
json.dumps(
{
"name": "python3-deps",
"buildsystem": "simple",
"build-commands": [
(
"pip3 install --no-index --find-links=. --prefix=/app --no-build-isolation --ignore-installed --no-deps *.whl *.tar.gz"
)
],
"cleanup": ["/bin", "/share/man"],
"sources": sources,
},
indent=2,
)
)


if __name__ == "__main__":
main()
48 changes: 48 additions & 0 deletions flatpak/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
id: com.github.rtr46.meikipop
runtime: org.kde.Platform
runtime-version: "6.10"
sdk: org.kde.Sdk
base: com.riverbankcomputing.PyQt.BaseApp
base-version: "6.10"
command: "meikipop"
finish-args:
- "--socket=x11"
- "--socket=wayland"
- "--device=dri"
- "--share=ipc"
- "--share=network"
- --talk-name=org.kde.StatusNotifierWatcher
- "--filesystem=xdg-config/meikipop:rw"
- "--filesystem=xdg-config/screen_ai:rw"
- "--filesystem=xdg-data/meikipop:rw"
modules:
- name: pycairo
buildsystem: meson
sources:
- type: archive
url: https://files.pythonhosted.org/packages/22/d9/1728840a22a4ef8a8f479b9156aa2943cd98c3907accd3849fb0d5f82bfd/pycairo-1.29.0.tar.gz
sha256: f3f7fde97325cae80224c09f12564ef58d0d0f655da0e3b040f5807bd5bd3142
- name: pygobject
buildsystem: meson
sources:
- type: archive
url: https://download.gnome.org/sources/pygobject/3.56/pygobject-3.56.1.tar.gz
sha256: 2ec1cc8c55c7ffeebb97e58a9bba7aa1e74611f1173628084685446804a8881a
- python3-deps.json
- name: meikipop
buildsystem: simple
build-commands:
- |
python3 - << 'EOF'
from PIL import Image
img = Image.open("src/meikipop/resources/icon.ico")
img.save("icon.png")
EOF
- pip3 install --no-index --find-links=. --prefix=/app --no-build-isolation
--no-deps .
- install -Dm644 flatpak/meikipop.desktop /app/share/applications/com.github.rtr46.meikipop.desktop
- install -Dm644 flatpak/metainfo.xml /app/share/metainfo/com.github.rtr46.meikipop.metainfo.xml
- install -Dm644 icon.png /app/share/icons/hicolor/256x256/apps/com.github.rtr46.meikipop.png
sources:
- type: dir
path: ..
9 changes: 9 additions & 0 deletions flatpak/meikipop.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Desktop Entry]
Name=Meikipop
Comment=Japanese OCR Popup Dictionary
Exec=meikipop
Icon=com.github.rtr46.meikipop
Terminal=false
Type=Application
Categories=Education;Languages;Utility;
Keywords=Japanese;OCR;Dictionary;Popup;
32 changes: 32 additions & 0 deletions flatpak/metainfo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<component type="desktop-application">
<id>com.github.rtr46.meikipop</id>

<name>meikipop</name>
<summary>Instantly look up Japanese words anywhere on your screen</summary>
<developer id="com.github.rtr46">
<name>
rtr46
</name>
</developer>
<url type="homepage">https://github.com/rtr46/meikipop</url>
<content_rating type="oars-1.1" />

<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-or-later</project_license>

<description>
<p>
Instantly look up japanese words anywhere on your screen. Meikipop uses optical character recognition (ocr) to read text from websites, games, scanned manga, or even hard-coded video subtitles, giving you effortless dictionary lookups with the press of a key (or even without)!
</p>
</description>

<launchable type="desktop-id">com.github.rtr46.meikipop.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>
https://github.com/user-attachments/assets/a1834197-3059-438c-a2dc-716e8ec9078f
</image>
</screenshot>
</screenshots>
</component>
4 changes: 3 additions & 1 deletion src/meikipop/screenshot/wayland_mss_shim.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
gi.require_version('Gst', '1.0')
from gi.repository import GLib, Gst, Gio

from meikipop.utils.paths import paths

import mss as real_mss
from mss.exception import ScreenShotError
from mss.screenshot import ScreenShot, Size
Expand All @@ -20,7 +22,7 @@
screencast = None
screencast_lock = threading.Lock()

token_file = Path('~/.cache/.ocr_screencapture_token').expanduser() # todo this should probably use utils/path.py
token_file = Path(paths.cache_dir) / '.ocr_screencapture_token'
persist_token = str(uuid.UUID(int=0))

if token_file.exists():
Expand Down
Loading