mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-07-10 16:01:58 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 08da66aa07 | |||
| 4397e087ef | |||
| fee9480cc3 | |||
| 1f6aae98ed | |||
| 8edbb7a258 | |||
| 9c3f76ce16 | |||
| c755894c84 | |||
| 377593b190 |
@@ -0,0 +1,59 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: Setup Python
|
||||
description: Combination of Python setup actions
|
||||
|
||||
inputs:
|
||||
skip-setup:
|
||||
default: 'false'
|
||||
description: Whether to skip setup actions and only execute installs
|
||||
groups:
|
||||
description: Newline-separated list of dependency groups to install
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup Python install
|
||||
if: inputs.skip-setup != 'true'
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Setup uv install
|
||||
if: inputs.skip-setup != 'true'
|
||||
uses: astral-sh/setup-uv@v8.2.0
|
||||
|
||||
- name: Run pip install (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
env:
|
||||
INPUT_GROUPS: ${{ inputs.groups }}
|
||||
UV_PYTHON_DOWNLOADS: never
|
||||
UV_SYSTEM_PYTHON: '1'
|
||||
shell: pwsh
|
||||
run: |
|
||||
if (![string]::IsNullOrEmpty(${env:INPUT_GROUPS})) {
|
||||
$groupArgs = -split ${env:INPUT_GROUPS} -replace '^', '--group='
|
||||
}
|
||||
|
||||
uv pip install . ${groupArgs}
|
||||
|
||||
- name: Run pip install (non-Windows)
|
||||
if: runner.os != 'Windows'
|
||||
env:
|
||||
INPUT_GROUPS: ${{ inputs.groups }}
|
||||
UV_PYTHON_DOWNLOADS: never
|
||||
UV_SYSTEM_PYTHON: '1'
|
||||
shell: bash
|
||||
run: |
|
||||
extra_args=()
|
||||
|
||||
if [ -n "${INPUT_GROUPS}" ]; then
|
||||
while IFS= read -r group; do
|
||||
if [ -n "${group}" ]; then
|
||||
extra_args+=("--group=${group}")
|
||||
fi
|
||||
done <<<"${INPUT_GROUPS}"
|
||||
fi
|
||||
|
||||
uv pip install . "${extra_args[@]}"
|
||||
@@ -0,0 +1,203 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# TODO: This will be invoked by a 'Release' workflow in the future.
|
||||
---
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
schedule:
|
||||
# From: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule
|
||||
# To decrease the chance of delay, schedule your workflow to run at a different time of the hour.
|
||||
- cron: 42 3 * * *
|
||||
timezone: America/Los_Angeles
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: sh
|
||||
|
||||
jobs:
|
||||
meta:
|
||||
name: Metadata
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
type: ${{ steps.meta.outputs.type }}
|
||||
version: ${{ steps.meta.outputs.version }}
|
||||
steps:
|
||||
- name: Set metadata
|
||||
id: meta
|
||||
run: |
|
||||
type=Nightly
|
||||
if [ "${GITHUB_REF_TYPE}" = tag ]; then
|
||||
case ${GITHUB_REF_NAME} in
|
||||
v*)
|
||||
type=Release
|
||||
case ${GITHUB_REF_NAME} in
|
||||
*-pr*) type=Pre-${type} ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ ${type} = Nightly ]; then
|
||||
version=dev+$(date --iso-8601 | tr -d -)
|
||||
else
|
||||
version=${GITHUB_REF_NAME}
|
||||
fi
|
||||
|
||||
cat <<EOF >>"${GITHUB_OUTPUT}"
|
||||
type=${type}
|
||||
version=${version}
|
||||
EOF
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: ${{ needs.meta.outputs.type != 'Nightly' }}
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
os-label: Linux x86
|
||||
|
||||
- os: macos-15-intel
|
||||
os-label: macOS x86
|
||||
|
||||
- os: macos-15
|
||||
os-label: macOS ARM
|
||||
|
||||
- os: windows-2022
|
||||
os-label: Windows x86
|
||||
|
||||
name: Build${{ needs.meta.outputs.type && format(' {0}', needs.meta.outputs.type) }}${{ matrix.os-label && format(' ({0})', matrix.os-label) }}
|
||||
needs: meta
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
BUILD_PREFIX: tagstudio_${{ needs.meta.outputs.version }}
|
||||
steps:
|
||||
- name: Prepare build (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$dir = "${env:RUNNER_TEMP}/upload"
|
||||
New-Item -Path ${dir} -ItemType Directory -Force
|
||||
|
||||
$os = ${env:RUNNER_OS}.ToLower()
|
||||
$arch = uname -m
|
||||
|
||||
$commonPath = "${dir}/${env:BUILD_PREFIX}_${os}_${arch}"
|
||||
Add-Content -Path ${env:GITHUB_ENV} -Value @"
|
||||
BUILD_ARTIFACT_DEFAULT=${commonPath}.zip
|
||||
BUILD_ARTIFACT_PORTABLE=${commonPath}_portable.zip
|
||||
"@ -Encoding utf8
|
||||
|
||||
- name: Prepare build (non-Windows)
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
dir=${RUNNER_TEMP}/upload
|
||||
mkdir -p "${dir}"
|
||||
os=$(
|
||||
tr '[:upper:]' '[:lower:]' <<EOF
|
||||
${RUNNER_OS}
|
||||
EOF
|
||||
)
|
||||
arch=$(uname -m)
|
||||
|
||||
common_path=${dir}/${BUILD_PREFIX}_${os}_${arch}
|
||||
artifact_default=${common_path}.tar.gz
|
||||
if [ "${RUNNER_OS}" = macOS ]; then
|
||||
artifact_portable=
|
||||
else
|
||||
artifact_portable=${common_path}_portable.tar.gz
|
||||
fi
|
||||
cat <<EOF >>"${GITHUB_ENV}"
|
||||
BUILD_ARTIFACT_DEFAULT=${artifact_default}
|
||||
${artifact_portable:+BUILD_ARTIFACT_PORTABLE=${artifact_portable}}
|
||||
EOF
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-python
|
||||
with:
|
||||
groups: build
|
||||
|
||||
- parallel:
|
||||
- name: Run PyInstaller (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
pyinstaller --distpath dist/default --workpath build/default scripts/tagstudio.spec
|
||||
tar czf "${BUILD_ARTIFACT_DEFAULT}" -C dist/default/tagstudio .
|
||||
|
||||
- name: Run PyInstaller (Linux portable)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
pyinstaller --distpath dist/portable --workpath build/portable scripts/tagstudio.spec -- --portable
|
||||
tar czf "${BUILD_ARTIFACT_PORTABLE}" -C dist/portable tagstudio
|
||||
|
||||
- name: Run PyInstaller (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
scripts/tagstudio.spec
|
||||
tar czf "${BUILD_ARTIFACT_DEFAULT}" -C dist TagStudio.app
|
||||
|
||||
- name: Run PyInstaller (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
PyInstaller --distpath dist/default --workpath build/default scripts/tagstudio.spec
|
||||
Compress-Archive -Path dist/default/TagStudio -DestinationPath ${env:BUILD_ARTIFACT_DEFAULT}
|
||||
|
||||
- name: Run PyInstaller (Windows portable)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
PyInstaller --distpath dist/portable --workpath build/portable scripts/tagstudio.spec -- --portable
|
||||
Compress-Archive -Path dist/portable/TagStudio.exe -DestinationPath ${env:BUILD_ARTIFACT_PORTABLE}
|
||||
|
||||
- parallel:
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: ${{ env.BUILD_ARTIFACT_DEFAULT }}
|
||||
if-no-files-found: error
|
||||
# Short retention for 'Pre-Release' and 'Release' because they get uploaded to a GitHub release.
|
||||
retention-days: &upload-artifact-retention-days ${{ case(needs.meta.outputs.type == 'Nightly', '7', '1') }}
|
||||
archive: false
|
||||
|
||||
- name: Upload build artifact (portable)
|
||||
if: env.BUILD_ARTIFACT_PORTABLE != ''
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: ${{ env.BUILD_ARTIFACT_PORTABLE }}
|
||||
if-no-files-found: error
|
||||
retention-days: *upload-artifact-retention-days
|
||||
archive: false
|
||||
|
||||
upload:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
name: Upload Builds to Release
|
||||
needs: [meta, build]
|
||||
if: needs.meta.outputs.type != 'Nightly'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: ${{ runner.temp }}/download
|
||||
merge-multiple: 'true'
|
||||
skip-decompress: 'true'
|
||||
|
||||
- name: Upload build artifacts to release
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
files: ${{ runner.temp }}/download/*
|
||||
@@ -0,0 +1,56 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# TODO: In the future, docs will be built (but not published) for PRs.
|
||||
---
|
||||
name: Build Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/actions/setup-python/action.yml
|
||||
- .github/workflows/build_docs.yml
|
||||
- docs/**
|
||||
- mkdocs.yml
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: sh
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
name: Publish Docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-python
|
||||
with:
|
||||
groups: docs
|
||||
|
||||
- name: Use cache
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
key: mkdocs-material-${{ github.run_id }}
|
||||
path: .cache
|
||||
restore-keys: |
|
||||
mkdocs-material-
|
||||
- name: Run mkdocs
|
||||
env:
|
||||
DISABLE_MKDOCS_2_WARNING: 'true'
|
||||
run: mkdocs gh-deploy --force
|
||||
@@ -0,0 +1,168 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: Checks (Python)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths: &on_paths
|
||||
- .github/actions/setup-python/action.yml
|
||||
- .github/workflows/checks_python.yml
|
||||
- .editorconfig
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- '**.py'
|
||||
- '**.pyi'
|
||||
- 'src/tagstudio/resources/**'
|
||||
push:
|
||||
paths: *on_paths
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: sh
|
||||
|
||||
jobs:
|
||||
run-conditions:
|
||||
name: Run Conditions
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
pyright: ${{ steps.run-conditions.outputs.pyright }}
|
||||
pytest: ${{ steps.run-conditions.outputs.pytest }}
|
||||
ruff: ${{ steps.run-conditions.outputs.ruff }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v47.0.6
|
||||
with:
|
||||
files_yaml: |
|
||||
generic:
|
||||
- .github/workflows/checks_python.yml
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- '**.py'
|
||||
- '**.pyi'
|
||||
pyright:
|
||||
- .github/actions/setup-python/action.yml
|
||||
pytest:
|
||||
- .github/actions/setup-python/action.yml
|
||||
- 'src/tagstudio/resources/**'
|
||||
ruff:
|
||||
- .editorconfig
|
||||
|
||||
- name: Set run conditions
|
||||
id: run-conditions
|
||||
run: |
|
||||
cat <<EOF >>"${GITHUB_OUTPUT}"
|
||||
pyright=${{ steps.changed-files.outputs.generic_any_changed || steps.changed-files.outputs.pyright_any_changed }}
|
||||
pytest=${{ steps.changed-files.outputs.generic_any_changed || steps.changed-files.outputs.pytest_any_changed }}
|
||||
ruff=${{ steps.changed-files.outputs.generic_any_changed || steps.changed-files.outputs.ruff_any_changed }}
|
||||
EOF
|
||||
|
||||
check-pyright:
|
||||
name: Pyright
|
||||
needs: run-conditions
|
||||
if: needs.run-conditions.outputs.pyright == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- &checkout
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-python
|
||||
with:
|
||||
groups: |
|
||||
pyright
|
||||
pytest
|
||||
|
||||
- name: Execute Pyright
|
||||
run: pyright
|
||||
|
||||
check-pytest:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
os-label: Linux
|
||||
|
||||
- os: windows-2022
|
||||
os-label: Windows
|
||||
|
||||
name: pytest${{ matrix.os-label && format(' ({0})', matrix.os-label) }}
|
||||
needs: run-conditions
|
||||
if: needs.run-conditions.outputs.pytest == 'true'
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- *checkout
|
||||
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-python
|
||||
with:
|
||||
groups: pytest
|
||||
|
||||
- name: Install system dependencies (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libegl1 \
|
||||
libgl1 \
|
||||
libopengl0 \
|
||||
libpulse0 \
|
||||
libxcb-cursor0 \
|
||||
libxcb-icccm4 \
|
||||
libxcb-image0 \
|
||||
libxcb-keysyms1 \
|
||||
libxcb-randr0 \
|
||||
libxcb-render-util0 \
|
||||
libxcb-xinerama0 \
|
||||
libxkbcommon-x11-0 \
|
||||
libyaml-dev \
|
||||
ripgrep \
|
||||
x11-utils
|
||||
|
||||
- name: Install system dependencies (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install ripgrep
|
||||
|
||||
- name: Install system dependencies (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: choco install ripgrep
|
||||
|
||||
- name: Execute pytest (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: pytest
|
||||
|
||||
- name: Execute pytest (non-Windows)
|
||||
if: runner.os != 'Windows'
|
||||
run: pytest
|
||||
|
||||
check-ruff:
|
||||
name: Ruff
|
||||
needs: run-conditions
|
||||
if: needs.run-conditions.outputs.ruff == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- *checkout
|
||||
|
||||
- parallel:
|
||||
- name: Run Ruff linter
|
||||
uses: astral-sh/ruff-action@v4.0.0
|
||||
|
||||
- name: Run Ruff formatter
|
||||
uses: astral-sh/ruff-action@v4.0.0
|
||||
with:
|
||||
args: format --check
|
||||
@@ -1,51 +0,0 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
name: Publish Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/publish_docs.yaml
|
||||
- docs/**
|
||||
- mkdocs.yml
|
||||
- CHANGELOG.md
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: publish-docs
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade uv
|
||||
uv pip install --system . --group mkdocs
|
||||
|
||||
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
key: mkdocs-material-${{ env.cache_id }}
|
||||
path: .cache
|
||||
restore-keys: |
|
||||
mkdocs-material-
|
||||
|
||||
- name: Execute mkdocs
|
||||
run: mkdocs gh-deploy --force
|
||||
@@ -1,31 +0,0 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
name: Pyright
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
pyright:
|
||||
name: Run Pyright
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade uv
|
||||
uv pip install --system . --group pyright --group pytest
|
||||
|
||||
- name: Execute Pyright
|
||||
uses: jordemort/action-pyright@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
reporter: ${{ github.event_name == 'pull_request' && 'github-pr-review' || 'github-check' }}
|
||||
@@ -1,70 +0,0 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
name: pytest
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
pytest-linux:
|
||||
name: Run pytest (Linux)
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- &checkout
|
||||
name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- &setup-python
|
||||
name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
|
||||
- &install-dependencies
|
||||
name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade uv
|
||||
uv pip install --system . --group pytest
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libegl1 \
|
||||
libgl1 \
|
||||
libopengl0 \
|
||||
libpulse0 \
|
||||
libxcb-cursor0 \
|
||||
libxcb-icccm4 \
|
||||
libxcb-image0 \
|
||||
libxcb-keysyms1 \
|
||||
libxcb-randr0 \
|
||||
libxcb-render-util0 \
|
||||
libxcb-xinerama0 \
|
||||
libxkbcommon-x11-0 \
|
||||
libyaml-dev \
|
||||
ripgrep \
|
||||
x11-utils
|
||||
|
||||
- name: Execute pytest
|
||||
run: |
|
||||
xvfb-run pytest --cov-report xml --cov=tagstudio
|
||||
|
||||
pytest-windows:
|
||||
name: Run pytest (Windows)
|
||||
runs-on: windows-2025
|
||||
|
||||
steps:
|
||||
- *checkout
|
||||
- *setup-python
|
||||
- *install-dependencies
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
choco install ripgrep
|
||||
|
||||
- name: Execute pytest
|
||||
run: |
|
||||
pytest
|
||||
@@ -1,156 +0,0 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v[0-9]+.[0-9]+.[0-9]+*
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
strategy:
|
||||
matrix:
|
||||
build-type: ["", portable]
|
||||
include:
|
||||
- build-type: ""
|
||||
build-flag: ""
|
||||
suffix: ""
|
||||
- build-type: portable
|
||||
build-flag: --portable
|
||||
suffix: _portable
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade uv
|
||||
uv pip install --system . --group pyinstaller
|
||||
|
||||
- name: Execute PyInstaller
|
||||
run: |
|
||||
pyinstaller tagstudio.spec -- ${{ matrix.build-flag }}
|
||||
tar czfC dist/tagstudio_linux_x86_64${{ matrix.suffix }}.tar.gz dist tagstudio
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tagstudio_linux_x86_64${{ matrix.suffix }}
|
||||
path: dist/tagstudio_linux_x86_64${{ matrix.suffix }}.tar.gz
|
||||
|
||||
macos:
|
||||
strategy:
|
||||
matrix:
|
||||
os-version: ["14", "15"]
|
||||
include:
|
||||
- os-version: "14"
|
||||
arch: x86_64
|
||||
- os-version: "15"
|
||||
arch: aarch64
|
||||
|
||||
runs-on: macos-${{ matrix.os-version }}
|
||||
|
||||
env:
|
||||
# INFO: Even though we run on 14, target towards compatibility
|
||||
MACOSX_DEPLOYMENT_TARGET: "11.0"
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade uv
|
||||
uv pip install --system . --group pyinstaller
|
||||
|
||||
- name: Execute PyInstaller
|
||||
run: |
|
||||
pyinstaller tagstudio.spec
|
||||
tar czfC dist/tagstudio_macos_${{ matrix.arch }}.tar.gz dist TagStudio.app
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tagstudio_macos_${{ matrix.arch }}
|
||||
path: dist/tagstudio_macos_${{ matrix.arch }}.tar.gz
|
||||
|
||||
windows:
|
||||
strategy:
|
||||
matrix:
|
||||
build-type: ["", portable]
|
||||
include:
|
||||
- build-type: ""
|
||||
build-flag: ""
|
||||
suffix: ""
|
||||
file-end: ""
|
||||
- build-type: portable
|
||||
build-flag: --portable
|
||||
suffix: _portable
|
||||
file-end: .exe
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade uv
|
||||
uv pip install --system . --group pyinstaller
|
||||
|
||||
- name: Execute PyInstaller
|
||||
run: |
|
||||
PyInstaller tagstudio.spec -- ${{ matrix.build-flag }}
|
||||
Compress-Archive -Path dist/TagStudio${{ matrix.file-end }} -DestinationPath dist/tagstudio_windows_x86_64${{ matrix.suffix }}.zip
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tagstudio_windows_x86_64${{ matrix.suffix }}
|
||||
path: dist/tagstudio_windows_x86_64${{ matrix.suffix }}.zip
|
||||
|
||||
publish:
|
||||
needs: [linux, macos, windows]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Publish release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
tagstudio_linux_x86_64/*
|
||||
tagstudio_linux_x86_64_portable/*
|
||||
tagstudio_macos_x86_64/*
|
||||
tagstudio_macos_aarch64/*
|
||||
tagstudio_windows_x86_64/*
|
||||
tagstudio_windows_x86_64_portable/*
|
||||
@@ -1,12 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2020 Free Software Foundation Europe e.V.
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
name: REUSE compliance check
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: REUSE Compliance Check
|
||||
uses: fsfe/reuse-action@v6
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: REUSE Compliance Check
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: sh
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: REUSE
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Run REUSE
|
||||
uses: fsfe/reuse-action@v6
|
||||
@@ -1,33 +0,0 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
---
|
||||
name: Ruff
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ruff-format:
|
||||
name: Run Ruff format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Execute Ruff format
|
||||
uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
version: 0.15.17
|
||||
args: format --check
|
||||
|
||||
ruff-check:
|
||||
name: Run Ruff check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Execute Ruff check
|
||||
uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
version: 0.15.17
|
||||
args: check
|
||||
@@ -8,3 +8,8 @@ bracketSpacing = true
|
||||
objectWrap = "preserve"
|
||||
quoteProps = "as-needed"
|
||||
singleQuote = false
|
||||
|
||||
[[overrides]]
|
||||
files = [".github/**/*.yml", ".github/**/*.yaml"]
|
||||
[overrides.options]
|
||||
singleQuote = true
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
# TagStudio: A User-Focused Photo & File Management System
|
||||
|
||||
[](https://github.com/TagStudioDev/TagStudio/releases)
|
||||
[](https://github.com/TagStudioDev/TagStudio/releases)
|
||||
[](https://hosted.weblate.org/projects/tagstudio/strings/)
|
||||
[](https://github.com/TagStudioDev/TagStudio/actions/workflows/pytest.yaml)
|
||||
[](https://github.com/TagStudioDev/TagStudio/actions/workflows/mypy.yaml)
|
||||
[](https://github.com/TagStudioDev/TagStudio/actions/workflows/ruff.yaml)
|
||||
[](https://api.reuse.software/info/github.com/TagStudioDev/TagStudio)
|
||||
[](https://github.com/TagStudioDev/TagStudio/actions/workflows/checks_python.yml)
|
||||
|
||||
<p align="center">
|
||||
<img width="60%" src="src/tagstudio/resources/qt/images/tagstudio_logo-text_color.png">
|
||||
|
||||
+8
-2
@@ -9,7 +9,11 @@ toc_depth: 2
|
||||
|
||||
# :material-script-text: Changelog
|
||||
|
||||
## 9.6.1 <small>July 6th, 2026</small>
|
||||
## 9.6.1 <small>July 9th, 2026</small>
|
||||
|
||||
### Added
|
||||
|
||||
- feat(ui): render .pxd thumbnails by @Sola-ris in #1430
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -30,13 +34,15 @@ toc_depth: 2
|
||||
#### Internal Changes
|
||||
|
||||
- fix(nix): add pyright to devshell by @Computerdores in #1415
|
||||
- fix(deps): replace optional dependencies with dependency groups by @Xarvex in #1435
|
||||
- feat(ci)!: complete workflows revamp by @Xarvex in #1437
|
||||
|
||||
#### Translations
|
||||
|
||||
- **French** updated by @kitsumed
|
||||
- **Hungarian** updated by @smileyhead
|
||||
- **Russian** updated by @romandobra
|
||||
- **Spanish** updated by @JCC1998
|
||||
- **Spanish** updated by @JCC1998, @2004milenadiaz-source
|
||||
|
||||
---
|
||||
|
||||
|
||||
+2
-2
@@ -236,13 +236,13 @@ direnv allow
|
||||
To build your own executables of TagStudio, first follow the steps in "[Installing Dependencies](#installing-dependencies)." Once that's complete, run the following PyInstaller command:
|
||||
|
||||
```
|
||||
pyinstaller tagstudio.spec
|
||||
pyinstaller scripts/tagstudio.spec
|
||||
```
|
||||
|
||||
If you're on Windows or Linux and wish to build a portable executable, then pass the following flag:
|
||||
|
||||
```
|
||||
pyinstaller tagstudio.spec -- --portable
|
||||
pyinstaller scripts/tagstudio.spec -- --portable
|
||||
```
|
||||
|
||||
The resulting executable file(s) will be located in a new folder named "dist".
|
||||
|
||||
+20
-23
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Installation
|
||||
title: Installing
|
||||
icon: material/download
|
||||
---
|
||||
|
||||
<!-- SPDX-FileCopyrightText: (c) TagStudio Contributors -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-only -->
|
||||
|
||||
# :material-download: Installation
|
||||
# :material-download: Installing
|
||||
|
||||
TagStudio provides executable [releases](https://github.com/TagStudioDev/TagStudio/releases) as well as full access to its [source code](https://github.com/TagStudioDev/TagStudio) under the [GPLv3](https://github.com/TagStudioDev/TagStudio/blob/main/LICENSE) license.
|
||||
|
||||
@@ -17,8 +17,8 @@ To download executable builds of TagStudio, visit the [Releases](https://github.
|
||||
TagStudio has builds for :fontawesome-brands-windows: **Windows**, :fontawesome-brands-apple: **macOS** _(Apple Silicon & Intel)_, and :material-penguin: **Linux**. We also offer portable releases for Windows and Linux which are self-contained and easier to move around.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! info "Third-Party Dependencies"
|
||||
You may need to install [third-party dependencies](#third-party-dependencies) such as [FFmpeg](https://ffmpeg.org/download.html) to use the full feature set of TagStudio.
|
||||
!!! info "Optional Dependencies"
|
||||
You may need to install [optional third-party dependencies](#optional-dependencies) such as [FFmpeg](https://ffmpeg.org/download.html) to use the full feature set of TagStudio.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! warning ":fontawesome-brands-apple: macOS "Privacy & Security" Popup"
|
||||
@@ -82,6 +82,19 @@ Some external dependencies are required for TagStudio to execute. Below is a tab
|
||||
| [qt-multimedia](https://repology.org/project/qt) | required |
|
||||
| [qt-wayland](https://repology.org/project/qt) | Wayland support |
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! bug "Missing Linux Dependency Example"
|
||||
An error message such as the following indicates that you're missing the `libxcb-cursor` or `xcb-util-cursor` library, depending on your distro:
|
||||
```
|
||||
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
|
||||
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "/tmp/_MEIayuTiW/cv2/qt/plugins" even though it was found.
|
||||
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
|
||||
|
||||
Available platform plugins are: vnc, wayland-egl, offscreen, wayland, linuxfb, minimalegl, eglfs, minimal, vkkhrdisplay, xcb.
|
||||
|
||||
Aborted (core dumped)
|
||||
```
|
||||
|
||||
### :material-nix: Nix(OS)
|
||||
|
||||
For [Nix(OS)](https://nixos.org/), the TagStudio repository includes a [flake](https://wiki.nixos.org/wiki/Flakes) that provides some outputs such as a development shell and package.
|
||||
@@ -212,7 +225,9 @@ Finally, `inputs` can be used in a module to add the package to your packages li
|
||||
|
||||
Don't forget to rebuild!
|
||||
|
||||
## Third-Party Dependencies
|
||||
## Optional Dependencies
|
||||
|
||||
Some TagStudio functionality such as multimedia thumbnails and playback, RAR archive thumbnails, and improved directory scanning performance will require installing optional third-party dependencies. Depending on your system, you may already have one or more of these installed. To check
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
!!! tip
|
||||
@@ -242,21 +257,3 @@ To generate thumbnails for RAR-based files (like `.cbr`) you'll need an extracto
|
||||
### ripgrep
|
||||
|
||||
A recommended tool to improve the performance of directory scanning is [`ripgrep`](https://github.com/BurntSushi/ripgrep), a Rust-based directory walker that natively integrates with our [`.ts_ignore`](ignore.md) (`.gitignore`-style) pattern matching system for excluding files and directories. Ripgrep is already pre-installed on some Linux distributions and also available from several package managers.
|
||||
|
||||
## Common Error Messages
|
||||
|
||||
### Could not load the Qt platform plugin "xcb"
|
||||
|
||||
If you get an error message like this one:
|
||||
|
||||
```
|
||||
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
|
||||
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "/tmp/_MEIayuTiW/cv2/qt/plugins" even though it was found.
|
||||
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
|
||||
|
||||
Available platform plugins are: vnc, wayland-egl, offscreen, wayland, linuxfb, minimalegl, eglfs, minimal, vkkhrdisplay, xcb.
|
||||
|
||||
Aborted (core dumped)
|
||||
```
|
||||
|
||||
Make sure you installed `libxcb-cursor` or `xcb-util-cursor`.
|
||||
|
||||
@@ -196,8 +196,8 @@ Migration from the legacy JSON format is provided via a walkthrough when opening
|
||||
|
||||
#### Version 202
|
||||
|
||||
| Added in Commit | Introduced in Release | Format |
|
||||
| --------------- | ----------------------------------------------------------------------- | ------ |
|
||||
| | [v9.6.1](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.6.1) | SQLite |
|
||||
| Added in Commit | Introduced in Release | Format |
|
||||
| ---------------------------------------- | ----------------------------------------------------------------------- | ------ |
|
||||
| 95e2fe7b4449951c385e35a2e13f0c1925f1f98e | [v9.6.1](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.6.1) | SQLite |
|
||||
|
||||
- Applies repairs to the `tag_parents` table, removing rows that reference child tags that have been deleted.
|
||||
|
||||
+20
-19
@@ -47,7 +47,7 @@ Images will generate thumbnails the first time they are viewed or since the last
|
||||
|
||||
### :material-movie-open: Videos
|
||||
|
||||
Video thumbnails will default to the closest viable frame from the middle of the video. Both thumbnail generation and video playback in the Preview Panel requires [FFmpeg](install.md#third-party-dependencies) installed on your system.
|
||||
Video thumbnails will default to the closest viable frame from the middle of the video. Both thumbnail generation and video playback in the Preview Panel requires [FFmpeg](install.md#optional-dependencies) installed on your system.
|
||||
|
||||
| Filetype | Extensions | Dependencies |
|
||||
| --------------------- | ----------------------- | :----------: |
|
||||
@@ -65,7 +65,7 @@ Video thumbnails will default to the closest viable frame from the middle of the
|
||||
|
||||
### :material-sine-wave: Audio
|
||||
|
||||
Audio thumbnails will default to embedded cover art (if any) and fallback to generated waveform thumbnails. Audio file playback is supported in the Preview Panel if you have [FFmpeg](install.md#third-party-dependencies) installed on your system. Audio waveforms are currently not cached.
|
||||
Audio thumbnails will default to embedded cover art (if any) and fallback to generated waveform thumbnails. Audio file playback is supported in the Preview Panel if you have [FFmpeg](install.md#optional-dependencies) installed on your system. Audio waveforms are currently not cached.
|
||||
|
||||
| Filetype | Extensions | Dependencies |
|
||||
| ------------------- | ------------------------ | :----------: |
|
||||
@@ -82,23 +82,24 @@ Audio thumbnails will default to embedded cover art (if any) and fallback to gen
|
||||
|
||||
Preview support for office documents or well-known project file formats varies by the format and whether or not embedded thumbnails are available to be read from. OpenDocument-based files are typically supported.
|
||||
|
||||
| Filetype | Extensions | Preview Type |
|
||||
| ------------------------------------ | --------------------- | -------------------------------------------------------------------------- |
|
||||
| Blender | `.blend`, `.blend<#>` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Clip Studio Paint | `.clip` | Embedded thumbnail |
|
||||
| Keynote (Apple iWork) | `.key` | Embedded thumbnail |
|
||||
| Krita[^3] | `.kra`, `.krz` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Mdipack (FireAlpaca, Medibang Paint) | `.mdp` | Embedded thumbnail |
|
||||
| MuseScore | `.mscz` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Numbers (Apple iWork) | `.numbers` | Embedded thumbnail |
|
||||
| OpenDocument Presentation | `.odp`, `.fodp` | Embedded thumbnail |
|
||||
| OpenDocument Spreadsheet | `.ods`, `.fods` | Embedded thumbnail |
|
||||
| OpenDocument Text | `.odt`, `.fodt` | Embedded thumbnail |
|
||||
| Pages (Apple iWork) | `.pages` | Embedded thumbnail |
|
||||
| Paint.NET | `.pdn` | Embedded thumbnail |
|
||||
| PDF | `.pdf` | First page render |
|
||||
| Photoshop | `.psd` | Flattened image render |
|
||||
| PowerPoint (Microsoft Office) | `.pptx`, `.ppt` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Filetype | Extensions | Preview Type |
|
||||
| ------------------------------------- | --------------------- | -------------------------------------------------------------------------- |
|
||||
| Blender | `.blend`, `.blend<#>` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Clip Studio Paint | `.clip` | Embedded thumbnail |
|
||||
| Keynote (Apple iWork) | `.key` | Embedded thumbnail |
|
||||
| Krita[^3] | `.kra`, `.krz` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Mdipack (FireAlpaca, Medibang Paint) | `.mdp` | Embedded thumbnail |
|
||||
| MuseScore | `.mscz` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
| Numbers (Apple iWork) | `.numbers` | Embedded thumbnail |
|
||||
| OpenDocument Presentation | `.odp`, `.fodp` | Embedded thumbnail |
|
||||
| OpenDocument Spreadsheet | `.ods`, `.fods` | Embedded thumbnail |
|
||||
| OpenDocument Text | `.odt`, `.fodt` | Embedded thumbnail |
|
||||
| Pages (Apple iWork) | `.pages` | Embedded thumbnail |
|
||||
| Paint.NET | `.pdn` | Embedded thumbnail |
|
||||
| PDF | `.pdf` | First page render |
|
||||
| Photoshop | `.psd` | Flattened image render |
|
||||
| Pixelmator Pro (Apple Creator Studio) | `.pxd` | Embedded thumbnail |
|
||||
| PowerPoint (Microsoft Office) | `.pptx`, `.ppt` | Embedded thumbnail :material-alert-circle:{ title="If available in file" } |
|
||||
|
||||
### :material-archive: Archives
|
||||
|
||||
|
||||
+2
-2
@@ -32,9 +32,8 @@ extra:
|
||||
nav:
|
||||
- Home:
|
||||
- index.md
|
||||
- Getting Started:
|
||||
- Installing:
|
||||
- install.md
|
||||
- usage.md
|
||||
- Developers:
|
||||
- developing.md
|
||||
- contributing.md
|
||||
@@ -42,6 +41,7 @@ nav:
|
||||
- Help:
|
||||
- help/ffmpeg.md
|
||||
- Using TagStudio:
|
||||
- usage.md
|
||||
- libraries.md
|
||||
- entries.md
|
||||
- preview-support.md
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
{
|
||||
buildPythonPackage,
|
||||
cmake,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
{
|
||||
autoPatchelfHook,
|
||||
buildPythonPackage,
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ ruff = ["ruff==0.15.17"]
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/tagstudio"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
[tool.pytest]
|
||||
qt_api = "pyside6"
|
||||
pythonpath = ["src"]
|
||||
filterwarnings = [
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env perl
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use feature 'say';
|
||||
|
||||
# From: https://regex101.com/r/vkijKf/1
|
||||
if (($ARGV[0] // <STDIN>) =~ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/) {
|
||||
foreach my $index (0 .. $#{^CAPTURE}) {
|
||||
say ${^CAPTURE}[$index] // "";
|
||||
}
|
||||
}
|
||||
Regular → Executable
+14
-8
@@ -1,17 +1,22 @@
|
||||
#!/usr/bin/env -S python -m PyInstaller
|
||||
# vi: ft=python
|
||||
# SPDX-FileCopyrightText: (c) TagStudio Contributors
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
|
||||
import argparse
|
||||
import platform
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from tomllib import load
|
||||
|
||||
from PyInstaller.building.api import COLLECT, EXE, PYZ
|
||||
from PyInstaller.building.build_main import Analysis
|
||||
from PyInstaller.building.osx import BUNDLE
|
||||
from tomllib import load
|
||||
|
||||
parser = ArgumentParser()
|
||||
# HACK: Without this, the script will fail if empty arguments are passed.
|
||||
parser.add_argument("_", nargs="*", help=argparse.SUPPRESS)
|
||||
parser.add_argument("--portable", action="store_true")
|
||||
options = parser.parse_args()
|
||||
|
||||
@@ -20,23 +25,24 @@ with open("pyproject.toml", "rb") as file:
|
||||
|
||||
system = platform.system()
|
||||
|
||||
project_root = Path("..", "src/tagstudio")
|
||||
name = pyproject["name"] if system == "Windows" else "tagstudio"
|
||||
icon = None
|
||||
if system == "Windows":
|
||||
icon = "src/tagstudio/resources/icon.ico"
|
||||
icon = Path(project_root, "resources/icon.ico")
|
||||
elif system == "Darwin":
|
||||
icon = "src/tagstudio/resources/icon.icns"
|
||||
icon = Path(project_root, "resources/icon.icns")
|
||||
|
||||
|
||||
datafiles = [
|
||||
("src/tagstudio/qt/*.json", "tagstudio/qt"),
|
||||
("src/tagstudio/qt/*.qrc", "tagstudio/qt"),
|
||||
("src/tagstudio/resources", "tagstudio/resources"),
|
||||
(f"{project_root}/qt/*.json", "tagstudio/qt"),
|
||||
(f"{project_root}/qt/*.qrc", "tagstudio/qt"),
|
||||
(f"{project_root}/resources", "tagstudio/resources"),
|
||||
]
|
||||
|
||||
a = Analysis(
|
||||
["src/tagstudio/main.py"],
|
||||
pathex=["src"],
|
||||
[Path(project_root, "main.py")],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=datafiles,
|
||||
hiddenimports=[],
|
||||
@@ -12,14 +12,14 @@ import structlog
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
FILETYPE_EQUIVALENTS = [
|
||||
set(["aif", "aiff", "aifc"]),
|
||||
set(["html", "htm", "xhtml", "shtml", "dhtml"]),
|
||||
set(["jfif", "jpeg_large", "jpeg", "jpg_large", "jpg"]),
|
||||
set(["json", "jsonc", "json5"]),
|
||||
set(["md", "markdown", "mkd", "rmd"]),
|
||||
set(["tar.gz", "tgz"]),
|
||||
set(["xml", "xul"]),
|
||||
set(["yaml", "yml"]),
|
||||
{"aif", "aiff", "aifc"},
|
||||
{"html", "htm", "xhtml", "shtml", "dhtml"},
|
||||
{"jfif", "jpeg_large", "jpeg", "jpg_large", "jpg"},
|
||||
{"json", "jsonc", "json5"},
|
||||
{"md", "markdown", "mkd", "rmd"},
|
||||
{"tar.gz", "tgz"},
|
||||
{"xml", "xul"},
|
||||
{"yaml", "yml"},
|
||||
]
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class MediaCategory:
|
||||
extensions (set[str]): The set of file extensions associated with this category.
|
||||
Includes leading ".", all lowercase, and does not need to be unique to this category.
|
||||
|
||||
is_iana (bool): Represents whether or not this is an IANA registered category.
|
||||
is_iana (bool): Represents whether this is an IANA registered category.
|
||||
"""
|
||||
|
||||
media_type: MediaType
|
||||
@@ -257,6 +257,7 @@ class MediaCategories:
|
||||
".odt",
|
||||
".pages",
|
||||
".pdf",
|
||||
".pxd",
|
||||
".rtf",
|
||||
".tex",
|
||||
".wpd",
|
||||
@@ -336,7 +337,7 @@ class MediaCategories:
|
||||
".webp",
|
||||
}
|
||||
_INSTALLER_SET: set[str] = {".appx", ".msi", ".msix"}
|
||||
_IWORK_SET: set[str] = {".key", ".pages", ".numbers"}
|
||||
_IWORK_SET: set[str] = {".key", ".numbers", ".pages"}
|
||||
_MATERIAL_SET: set[str] = {".mtl"}
|
||||
_MDIPACK_SET: set[str] = {".mdp"}
|
||||
_MODEL_SET: set[str] = {".3ds", ".fbx", ".obj", ".stl"}
|
||||
|
||||
@@ -623,7 +623,7 @@ class ThumbRenderer(QObject):
|
||||
image (Image.Image): The image to apply the edge to.
|
||||
edge (tuple[Image.Image, Image.Image]): The edge images to apply.
|
||||
Item 0 is the inner highlight, and item 1 is the outer shadow.
|
||||
faded (bool): Whether or not to apply a faded version of the edge.
|
||||
faded (bool): Whether to apply a faded version of the edge.
|
||||
Used for light themes.
|
||||
"""
|
||||
opacity: float = 1.0 if not faded else 0.8
|
||||
@@ -847,7 +847,7 @@ class ThumbRenderer(QObject):
|
||||
|
||||
@staticmethod
|
||||
def _krita_thumb(filepath: Path) -> Image.Image | None:
|
||||
"""Extract and render a thumbnail for an Krita file.
|
||||
"""Extract and render a thumbnail for a Krita file.
|
||||
|
||||
Args:
|
||||
filepath (Path): The path of the file.
|
||||
@@ -1218,14 +1218,18 @@ class ThumbRenderer(QObject):
|
||||
return im
|
||||
|
||||
@staticmethod
|
||||
def _iwork_thumb(filepath: Path) -> Image.Image | None:
|
||||
"""Extract and render a thumbnail for an Apple iWork (Pages, Numbers, Keynote) file.
|
||||
def _apple_embedded_thumb(filepath: Path) -> Image.Image | None:
|
||||
"""Extract and render an apple embedded thumbnail (iWork, Apple Creative Studio).
|
||||
|
||||
Args:
|
||||
filepath (Path): The path of the file.
|
||||
"""
|
||||
preview_thumb_dir = "preview.jpg"
|
||||
quicklook_thumb_dir = "QuickLook/Thumbnail.jpg"
|
||||
thumb_files: list[str] = [
|
||||
"preview.jpg",
|
||||
"QuickLook/Preview.heic",
|
||||
"QuickLook/Thumbnail.jpg",
|
||||
"QuickLook/Thumbnail.heic",
|
||||
]
|
||||
im: Image.Image | None = None
|
||||
|
||||
def get_image(path: str) -> Image.Image | None:
|
||||
@@ -1240,10 +1244,10 @@ class ThumbRenderer(QObject):
|
||||
thumb: Image.Image | None = None
|
||||
|
||||
# Check if the file exists in the zip
|
||||
if preview_thumb_dir in zip_file.namelist():
|
||||
thumb = get_image(preview_thumb_dir)
|
||||
elif quicklook_thumb_dir in zip_file.namelist():
|
||||
thumb = get_image(quicklook_thumb_dir)
|
||||
for thumb_file in thumb_files:
|
||||
if thumb_file in zip_file.namelist():
|
||||
thumb = get_image(thumb_file)
|
||||
break
|
||||
else:
|
||||
logger.error("Couldn't render thumbnail", filepath=filepath)
|
||||
|
||||
@@ -1314,7 +1318,7 @@ class ThumbRenderer(QObject):
|
||||
page_size *= size / page_size.height()
|
||||
else:
|
||||
page_size *= size / page_size.width()
|
||||
# Enlarge image for antialiasing
|
||||
# Enlarge image for anti-aliasing
|
||||
scale_factor = 2.5
|
||||
page_size *= scale_factor
|
||||
# Render image with no anti-aliasing for speed
|
||||
@@ -1865,8 +1869,11 @@ class ThumbRenderer(QObject):
|
||||
):
|
||||
image = self._open_doc_thumb(_filepath)
|
||||
# Apple iWork Suite ============================================
|
||||
elif MediaCategories.is_ext_in_category(ext, MediaCategories.IWORK_TYPES):
|
||||
image = self._iwork_thumb(_filepath)
|
||||
elif (
|
||||
MediaCategories.is_ext_in_category(ext, MediaCategories.IWORK_TYPES)
|
||||
or ext == ".pxd"
|
||||
):
|
||||
image = self._apple_embedded_thumb(_filepath)
|
||||
# Plain Text ===================================================
|
||||
elif MediaCategories.is_ext_in_category(
|
||||
ext, MediaCategories.PLAINTEXT_TYPES, mime_fallback=True
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"about.description": "TagStudio es una aplicación para organizar fotografías y archivos que utiliza un sistema de etiquetas subyacentes centrado en dar libertad y flexibilidad al usuario. Sin programas ni formatos propios, ni un mar de archivos y sin trastornar completamente la estructura de tu sistema de archivos.",
|
||||
"about.documentation": "Documentación",
|
||||
"about.module.found": "Encontrado",
|
||||
"about.modules.title": "Modulos Opcionales",
|
||||
"about.title": "Acerca de TagStudio",
|
||||
"about.version": "Versión",
|
||||
"about.version.latest": "{built_version} (Última versión: {latest_version})",
|
||||
@@ -68,7 +69,7 @@
|
||||
"entries.unlinked.remove": "Eliminar Entradas No Vinculadas",
|
||||
"entries.unlinked.remove_alt": "Quit&ar entradas desvinculadas",
|
||||
"entries.unlinked.scanning": "Buscando entradas no enlazadas en la biblioteca...",
|
||||
"entries.unlinked.search_and_relink": "&Buscar && volver a vincular",
|
||||
"entries.unlinked.search_and_relink": "&Buscar && Revincular",
|
||||
"entries.unlinked.title": "Corregir entradas no vinculadas",
|
||||
"entries.unlinked.unlinked_count": "Entradas no vinculadas: {count}",
|
||||
"ffmpeg.missing.status": "{ffmpeg}: {ffmpeg_status}<br>{ffprobe}: {ffprobe_status}",
|
||||
@@ -196,6 +197,36 @@
|
||||
"json_migration.title.new_lib": "<h2>v9.5+ biblioteca</h2>",
|
||||
"json_migration.title.old_lib": "<h2>v9.4 biblioteca</h2>",
|
||||
"landing.open_create_library": "Abrir/Crear biblioteca {shortcut}",
|
||||
"language.am": "Idioma amhárico",
|
||||
"language.ceb": "Cebuano",
|
||||
"language.cs": "Checo",
|
||||
"language.da": "Danés",
|
||||
"language.de": "Alemán",
|
||||
"language.el": "Griego",
|
||||
"language.en": "Inglés",
|
||||
"language.es": "Español",
|
||||
"language.fi": "Finlandés",
|
||||
"language.fil": "Filipino",
|
||||
"language.fr": "Francés",
|
||||
"language.hu": "Húngaro",
|
||||
"language.is": "Islandés",
|
||||
"language.it": "Italiano",
|
||||
"language.ja": "Japonés",
|
||||
"language.nb_NO": "Noruego (bokmål)",
|
||||
"language.nl": "Neerlandés",
|
||||
"language.pl": "Polaco",
|
||||
"language.pt": "Portugués",
|
||||
"language.pt_BR": "Portugués (Brasil)",
|
||||
"language.qpv": "Viossa",
|
||||
"language.ro": "Rumano",
|
||||
"language.ru": "Ruso",
|
||||
"language.sv": "Sueco",
|
||||
"language.ta": "Tamil",
|
||||
"language.th": "Tailandés",
|
||||
"language.tok": "Toki Pona",
|
||||
"language.tr": "Turco",
|
||||
"language.zh_Hans": "Chino (simplificado)",
|
||||
"language.zh_Hant": "Chino (tradicional)",
|
||||
"library.missing": "Falta la ubicación",
|
||||
"library.name": "Biblioteca",
|
||||
"library.refresh.scanning.plural": "Escaneando directorios en busca de nuevos archivos...\n{searched_count} archivos buscados, {found_count} nuevos archivos encontrados",
|
||||
@@ -277,6 +308,7 @@
|
||||
"select.all": "Seleccionar todo",
|
||||
"select.clear": "Borrar selección",
|
||||
"select.inverse": "Invertir selección",
|
||||
"settings.appearance": "Apariencia",
|
||||
"settings.cached_thumb_resolution.label": "Resolución Miniaturas de vídeo en caché",
|
||||
"settings.clear_thumb_cache.title": "Borrar cache de las miniaturas",
|
||||
"settings.dateformat.english": "Inglés",
|
||||
@@ -293,6 +325,8 @@
|
||||
"settings.infinite_scroll": "Desplazamiento infinito",
|
||||
"settings.language": "Idioma",
|
||||
"settings.library": "Ajustes de la biblioteca",
|
||||
"settings.localization": "Localización",
|
||||
"settings.media": "Contenido",
|
||||
"settings.open_library_on_start": "Abrir biblioteca al iniciar",
|
||||
"settings.page_size": "Tamaño de la página",
|
||||
"settings.restart_required": "Por favor, reinicia TagStudio para que se los cambios surtan efecto.",
|
||||
|
||||
Reference in New Issue
Block a user