mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-02-02 08:09:13 +00:00
Merge branch 'main' into test-support-2
This commit is contained in:
50
.github/workflows/apprun.yaml
vendored
Normal file
50
.github/workflows/apprun.yaml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: PySide App Test
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
# dont run update, it is slow
|
||||
# sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libxkbcommon-x11-0 \
|
||||
x11-utils \
|
||||
libyaml-dev \
|
||||
libegl1-mesa \
|
||||
libxcb-icccm4 \
|
||||
libxcb-image0 \
|
||||
libxcb-keysyms1 \
|
||||
libxcb-randr0 \
|
||||
libxcb-render-util0 \
|
||||
libxcb-xinerama0 \
|
||||
libopengl0 \
|
||||
libxcb-cursor0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Run TagStudio app and check exit code
|
||||
run: |
|
||||
xvfb-run --server-args="-screen 0, 1920x1200x24 -ac +extension GLX +render -noreset" python tagstudio/tag_studio.py --ci -o /tmp/
|
||||
exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
echo "TagStudio ran successfully"
|
||||
else
|
||||
echo "TagStudio failed with exit code $exit_code"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,3 +1,4 @@
|
||||
ruff==0.4.2
|
||||
pre-commit==3.7.0
|
||||
pytest==8.2.0
|
||||
pytest==8.2.0
|
||||
Pyinstaller==6.6.0
|
||||
|
||||
@@ -7,4 +7,3 @@ PySide6_Addons>=6.5.1.1,<=6.6.3.1
|
||||
PySide6_Essentials>=6.5.1.1,<=6.6.3.1
|
||||
typing_extensions>=3.10.0.0,<=4.11.0
|
||||
ujson>=5.8.0,<=5.9.0
|
||||
Pyinstaller==6.6.0
|
||||
|
||||
BIN
tagstudio/resources/icon.icns
Normal file
BIN
tagstudio/resources/icon.icns
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 628 KiB After Width: | Height: | Size: 148 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 992 KiB After Width: | Height: | Size: 677 KiB |
@@ -12,6 +12,7 @@ import subprocess
|
||||
import sys
|
||||
import time
|
||||
from PIL import Image, ImageOps, ImageChops, UnidentifiedImageError
|
||||
from PIL.Image import DecompressionBombError
|
||||
import pillow_avif
|
||||
from pathlib import Path
|
||||
import traceback
|
||||
@@ -84,7 +85,7 @@ class CliDriver:
|
||||
|
||||
self.external_preview_size: tuple[int, int] = (960, 960)
|
||||
epd_path = os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent}/resources/cli/images/external_preview.png"
|
||||
f"{Path(__file__).parents[2]}/resources/cli/images/external_preview.png"
|
||||
)
|
||||
self.external_preview_default: Image = (
|
||||
Image.open(epd_path)
|
||||
@@ -93,7 +94,7 @@ class CliDriver:
|
||||
)
|
||||
self.external_preview_default.thumbnail(self.external_preview_size)
|
||||
epb_path = os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent}/resources/cli/images/no_preview.png"
|
||||
f"{Path(__file__).parents[2]}/resources/cli/images/no_preview.png"
|
||||
)
|
||||
self.external_preview_broken: Image = (
|
||||
Image.open(epb_path)
|
||||
@@ -643,8 +644,12 @@ class CliDriver:
|
||||
# raw.thumbnail((512, 512))
|
||||
raw.thumbnail(self.external_preview_size)
|
||||
raw.save(external_preview_path)
|
||||
except:
|
||||
print(f'{ERROR} Could not load image "{filepath}"')
|
||||
except (
|
||||
UnidentifiedImageError,
|
||||
FileNotFoundError,
|
||||
DecompressionBombError,
|
||||
) as e:
|
||||
print(f'{ERROR} Could not load image "{filepath} due to {e}"')
|
||||
if self.args.external_preview:
|
||||
self.set_external_preview_broken()
|
||||
elif file_type in VIDEO_TYPES:
|
||||
@@ -1109,24 +1114,34 @@ class CliDriver:
|
||||
# sys.stdout.write(f'\r{INFO} Combining [{i+1}/{len(self.lib.entries)}]: {self.get_file_color(file_type)}{entry.path}{os.sep}{entry.filename}{RESET}')
|
||||
# sys.stdout.flush()
|
||||
if file_type in IMAGE_TYPES:
|
||||
with Image.open(
|
||||
os.path.normpath(
|
||||
f"{self.lib.library_dir}/{entry.path}/{entry.filename}"
|
||||
)
|
||||
) as pic:
|
||||
if keep_aspect:
|
||||
pic.thumbnail((thumb_size, thumb_size))
|
||||
else:
|
||||
pic = pic.resize((thumb_size, thumb_size))
|
||||
if data_tint_mode and color:
|
||||
pic = pic.convert(mode="RGB")
|
||||
pic = ImageChops.hard_light(
|
||||
pic,
|
||||
Image.new(
|
||||
"RGB", (thumb_size, thumb_size), color
|
||||
),
|
||||
try:
|
||||
with Image.open(
|
||||
os.path.normpath(
|
||||
f"{self.lib.library_dir}/{entry.path}/{entry.filename}"
|
||||
)
|
||||
collage.paste(pic, (y * thumb_size, x * thumb_size))
|
||||
) as pic:
|
||||
if keep_aspect:
|
||||
pic.thumbnail((thumb_size, thumb_size))
|
||||
else:
|
||||
pic = pic.resize((thumb_size, thumb_size))
|
||||
if data_tint_mode and color:
|
||||
pic = pic.convert(mode="RGB")
|
||||
pic = ImageChops.hard_light(
|
||||
pic,
|
||||
Image.new(
|
||||
"RGB",
|
||||
(thumb_size, thumb_size),
|
||||
color,
|
||||
),
|
||||
)
|
||||
collage.paste(
|
||||
pic, (y * thumb_size, x * thumb_size)
|
||||
)
|
||||
except DecompressionBombError as e:
|
||||
print(
|
||||
f"[ERROR] One of the images was too big ({e})"
|
||||
)
|
||||
|
||||
elif file_type in VIDEO_TYPES:
|
||||
video = cv2.VideoCapture(filepath)
|
||||
video.set(
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
from .file_opener import open_file, FileOpenerHelper, FileOpenerLabel
|
||||
from .function_iterator import FunctionIterator
|
||||
from .custom_runnable import CustomRunnable
|
||||
@@ -17,8 +17,8 @@ from PySide6.QtCore import (QCoreApplication, QMetaObject, QRect,
|
||||
from PySide6.QtGui import (QFont, QAction)
|
||||
from PySide6.QtWidgets import (QComboBox, QFrame, QGridLayout,
|
||||
QHBoxLayout, QVBoxLayout, QLayout, QLineEdit, QMainWindow,
|
||||
QMenuBar, QPushButton, QScrollArea, QSizePolicy,
|
||||
QStatusBar, QWidget, QSplitter, QMenu)
|
||||
QPushButton, QScrollArea, QSizePolicy,
|
||||
QStatusBar, QWidget, QSplitter)
|
||||
from src.qt.pagination import Pagination
|
||||
|
||||
|
||||
@@ -167,10 +167,10 @@ class Ui_MainWindow(QMainWindow):
|
||||
self.statusbar.setSizePolicy(sizePolicy1)
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
menu_bar = self.menuBar()
|
||||
self.setMenuBar(menu_bar)
|
||||
# menu_bar = self.menuBar()
|
||||
# self.setMenuBar(menu_bar)
|
||||
# self.gridLayout.addWidget(menu_bar, 4, 0, 1, 1, Qt.AlignRight)
|
||||
self.frame_layout.addWidget(menu_bar)
|
||||
# self.frame_layout.addWidget(menu_bar)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
|
||||
@@ -201,26 +201,26 @@ class Ui_MainWindow(QMainWindow):
|
||||
# time.sleep(0.02) # sleep for 20ms
|
||||
pass
|
||||
|
||||
def _createMenuBar(self, main_window):
|
||||
menu_bar = QMenuBar(main_window)
|
||||
file_menu = QMenu('&File', main_window)
|
||||
edit_menu = QMenu('&Edit', main_window)
|
||||
tools_menu = QMenu('&Tools', main_window)
|
||||
macros_menu = QMenu('&Macros', main_window)
|
||||
help_menu = QMenu('&Help', main_window)
|
||||
# def _createMenuBar(self, main_window):
|
||||
# menu_bar = QMenuBar(main_window)
|
||||
# file_menu = QMenu('&File', main_window)
|
||||
# edit_menu = QMenu('&Edit', main_window)
|
||||
# tools_menu = QMenu('&Tools', main_window)
|
||||
# macros_menu = QMenu('&Macros', main_window)
|
||||
# help_menu = QMenu('&Help', main_window)
|
||||
|
||||
file_menu.addAction(QAction('&New Library', main_window))
|
||||
file_menu.addAction(QAction('&Open Library', main_window))
|
||||
file_menu.addAction(QAction('&Save Library', main_window))
|
||||
file_menu.addAction(QAction('&Close Library', main_window))
|
||||
# file_menu.addAction(QAction('&New Library', main_window))
|
||||
# file_menu.addAction(QAction('&Open Library', main_window))
|
||||
# file_menu.addAction(QAction('&Save Library', main_window))
|
||||
# file_menu.addAction(QAction('&Close Library', main_window))
|
||||
|
||||
file_menu.addAction(QAction('&Refresh Directories', main_window))
|
||||
file_menu.addAction(QAction('&Add New Files to Library', main_window))
|
||||
# file_menu.addAction(QAction('&Refresh Directories', main_window))
|
||||
# file_menu.addAction(QAction('&Add New Files to Library', main_window))
|
||||
|
||||
menu_bar.addMenu(file_menu)
|
||||
menu_bar.addMenu(edit_menu)
|
||||
menu_bar.addMenu(tools_menu)
|
||||
menu_bar.addMenu(macros_menu)
|
||||
menu_bar.addMenu(help_menu)
|
||||
# menu_bar.addMenu(file_menu)
|
||||
# menu_bar.addMenu(edit_menu)
|
||||
# menu_bar.addMenu(tools_menu)
|
||||
# menu_bar.addMenu(macros_menu)
|
||||
# menu_bar.addMenu(help_menu)
|
||||
|
||||
main_window.setMenuBar(menu_bar)
|
||||
# main_window.setMenuBar(menu_bar)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
from .tag_search import TagSearchPanel
|
||||
from .build_tag import BuildTagPanel
|
||||
from .tag_database import TagDatabasePanel
|
||||
from .add_field import AddFieldModal
|
||||
from .file_extension import FileExtensionModal
|
||||
from .delete_unlinked import DeleteUnlinkedEntriesModal
|
||||
from .relink_unlinked import RelinkUnlinkedEntries
|
||||
from .fix_unlinked import FixUnlinkedEntriesModal
|
||||
from .mirror_entities import MirrorEntriesModal
|
||||
from .fix_dupes import FixDupeFilesModal
|
||||
from .folders_to_tags import FoldersToTagsModal
|
||||
@@ -21,8 +21,9 @@ from PySide6.QtWidgets import (
|
||||
from src.core.library import Library, Tag
|
||||
from src.core.palette import ColorType, get_tag_color
|
||||
from src.core.constants import TAG_COLORS
|
||||
from src.qt.widgets import PanelWidget, PanelModal, TagWidget
|
||||
from src.qt.modals import TagSearchPanel
|
||||
from src.qt.widgets.panel import PanelWidget, PanelModal
|
||||
from src.qt.widgets.tag import TagWidget
|
||||
from src.qt.modals.tag_search import TagSearchPanel
|
||||
|
||||
|
||||
ERROR = f"[ERROR]"
|
||||
|
||||
@@ -16,8 +16,9 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from src.core.library import ItemType, Library
|
||||
from src.qt.helpers import CustomRunnable, FunctionIterator
|
||||
from src.qt.widgets import ProgressWidget
|
||||
from src.qt.helpers.custom_runnable import CustomRunnable
|
||||
from src.qt.helpers.function_iterator import FunctionIterator
|
||||
from src.qt.widgets.progress import ProgressWidget
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if typing.TYPE_CHECKING:
|
||||
|
||||
@@ -7,7 +7,7 @@ from PySide6.QtCore import Signal, Qt
|
||||
from PySide6.QtWidgets import QVBoxLayout, QPushButton, QTableWidget, QTableWidgetItem
|
||||
|
||||
from src.core.library import Library
|
||||
from src.qt.widgets import PanelWidget
|
||||
from src.qt.widgets.panel import PanelWidget
|
||||
|
||||
|
||||
class FileExtensionModal(PanelWidget):
|
||||
|
||||
@@ -17,7 +17,7 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from src.core.library import Library
|
||||
from src.qt.modals import MirrorEntriesModal
|
||||
from src.qt.modals.mirror_entities import MirrorEntriesModal
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if typing.TYPE_CHECKING:
|
||||
|
||||
@@ -10,9 +10,11 @@ from PySide6.QtCore import QThread, Qt, QThreadPool
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton
|
||||
|
||||
from src.core.library import Library
|
||||
from src.qt.helpers import FunctionIterator, CustomRunnable
|
||||
from src.qt.modals import DeleteUnlinkedEntriesModal, RelinkUnlinkedEntries
|
||||
from src.qt.widgets import ProgressWidget
|
||||
from src.qt.helpers.function_iterator import FunctionIterator
|
||||
from src.qt.helpers.custom_runnable import CustomRunnable
|
||||
from src.qt.modals.delete_unlinked import DeleteUnlinkedEntriesModal
|
||||
from src.qt.modals.relink_unlinked import RelinkUnlinkedEntries
|
||||
from src.qt.widgets.progress import ProgressWidget
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if typing.TYPE_CHECKING:
|
||||
|
||||
@@ -18,8 +18,9 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from src.core.library import Library
|
||||
from src.qt.helpers import FunctionIterator, CustomRunnable
|
||||
from src.qt.widgets import ProgressWidget
|
||||
from src.qt.helpers.function_iterator import FunctionIterator
|
||||
from src.qt.helpers.custom_runnable import CustomRunnable
|
||||
from src.qt.widgets.progress import ProgressWidget
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if typing.TYPE_CHECKING:
|
||||
|
||||
@@ -7,8 +7,9 @@ import typing
|
||||
from PySide6.QtCore import QObject, Signal, QThreadPool
|
||||
|
||||
from src.core.library import Library
|
||||
from src.qt.helpers import FunctionIterator, CustomRunnable
|
||||
from src.qt.widgets import ProgressWidget
|
||||
from src.qt.helpers.function_iterator import FunctionIterator
|
||||
from src.qt.helpers.custom_runnable import CustomRunnable
|
||||
from src.qt.widgets.progress import ProgressWidget
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if typing.TYPE_CHECKING:
|
||||
|
||||
@@ -13,8 +13,9 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from src.core.library import Library
|
||||
from src.qt.widgets import PanelWidget, PanelModal, TagWidget
|
||||
from src.qt.modals import BuildTagPanel
|
||||
from src.qt.widgets.panel import PanelWidget, PanelModal
|
||||
from src.qt.widgets.tag import TagWidget
|
||||
from src.qt.modals.build_tag import BuildTagPanel
|
||||
|
||||
|
||||
class TagDatabasePanel(PanelWidget):
|
||||
|
||||
@@ -19,7 +19,8 @@ from PySide6.QtWidgets import (
|
||||
|
||||
from src.core.library import Library
|
||||
from src.core.palette import ColorType, get_tag_color
|
||||
from src.qt.widgets import PanelWidget, TagWidget
|
||||
from src.qt.widgets.panel import PanelWidget
|
||||
from src.qt.widgets.tag import TagWidget
|
||||
|
||||
|
||||
ERROR = f"[ERROR]"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,13 @@
|
||||
<!-- TO COMPILE: pyside6-rcc resources.qrc -o resources.py (in \tagstudio\src\qt)-->
|
||||
<!-- TO COMPILE: pyside6-rcc resources.qrc -o resources_rc.py (in \tagstudio\src\qt)-->
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias = "images/star_icon_empty_128.png">../../resources/qt/images/star_icon_empty_128.png</file>
|
||||
<file alias = "images/star_icon_filled_128.png">../../resources/qt/images/star_icon_filled_128.png</file>
|
||||
<file alias = "images/box_icon_empty_128.png">../../resources/qt/images/box_icon_empty_128.png</file>
|
||||
<file alias = "images/box_icon_filled_128.png">../../resources/qt/images/box_icon_filled_128.png</file>
|
||||
<file alias = "images/edit_icon_128.png">../../resources/qt/images/edit_icon_128.png</file>
|
||||
<file alias = "images/trash_icon_128.png">../../resources/qt/images/trash_icon_128.png</file>
|
||||
<file alias = "images/clipboard_icon_128.png">../../resources/qt/images/clipboard_icon_128.png</file>
|
||||
<!-- <file alias = "images/edit_icon_128.png">../../resources/qt/images/edit_icon_128.png</file> -->
|
||||
<!-- <file alias = "images/trash_icon_128.png">../../resources/qt/images/trash_icon_128.png</file> -->
|
||||
<!-- <file alias = "images/clipboard_icon_128.png">../../resources/qt/images/clipboard_icon_128.png</file> -->
|
||||
<file alias = "images/splash.png">../../resources/qt/images/splash.png</file>
|
||||
<!-- <file>../../CommonClasses/PNGWriter/report_text/GothamBlackRegular.otf</file>
|
||||
<file>../../CommonClasses/PNGWriter/report_text/GothamBold.otf</file>
|
||||
<file>../../CommonClasses/PNGWriter/report_text/GothamBook.otf</file>
|
||||
<file>../../CommonClasses/PNGWriter/report_text/GothamLight.otf</file>
|
||||
<file>../../CommonClasses/PNGWriter/report_text/GothamMedium.otf</file>
|
||||
<file>../../CommonClasses/PNGWriter/report_text/Spanish</file>
|
||||
<file>../../CommonClasses/PNGWriter/report_text/viewmind.png</file> -->
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -6,451 +6,6 @@
|
||||
from PySide6 import QtCore
|
||||
|
||||
qt_resource_data = b"\
|
||||
\x00\x00\x0b\x9e\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\
|
||||
\x00\x00\x04\xb3iTXtXML:com.\
|
||||
adobe.xmp\x00\x00\x00\x00\x00<?\
|
||||
xpacket begin=\x22\xef\
|
||||
\xbb\xbf\x22 id=\x22W5M0MpCe\
|
||||
hiHzreSzNTczkc9d\
|
||||
\x22?>\x0a<x:xmpmeta x\
|
||||
mlns:x=\x22adobe:ns\
|
||||
:meta/\x22 x:xmptk=\
|
||||
\x22XMP Core 5.5.0\x22\
|
||||
>\x0a <rdf:RDF xmln\
|
||||
s:rdf=\x22http://ww\
|
||||
w.w3.org/1999/02\
|
||||
/22-rdf-syntax-n\
|
||||
s#\x22>\x0a <rdf:Desc\
|
||||
ription rdf:abou\
|
||||
t=\x22\x22\x0a xmlns:e\
|
||||
xif=\x22http://ns.a\
|
||||
dobe.com/exif/1.\
|
||||
0/\x22\x0a xmlns:ti\
|
||||
ff=\x22http://ns.ad\
|
||||
obe.com/tiff/1.0\
|
||||
/\x22\x0a xmlns:pho\
|
||||
toshop=\x22http://n\
|
||||
s.adobe.com/phot\
|
||||
oshop/1.0/\x22\x0a \
|
||||
xmlns:xmp=\x22http:\
|
||||
//ns.adobe.com/x\
|
||||
ap/1.0/\x22\x0a xml\
|
||||
ns:xmpMM=\x22http:/\
|
||||
/ns.adobe.com/xa\
|
||||
p/1.0/mm/\x22\x0a x\
|
||||
mlns:stEvt=\x22http\
|
||||
://ns.adobe.com/\
|
||||
xap/1.0/sType/Re\
|
||||
sourceEvent#\x22\x0a \
|
||||
exif:PixelXDime\
|
||||
nsion=\x22128\x22\x0a e\
|
||||
xif:PixelYDimens\
|
||||
ion=\x22128\x22\x0a exi\
|
||||
f:ColorSpace=\x221\x22\
|
||||
\x0a tiff:ImageWi\
|
||||
dth=\x22128\x22\x0a tif\
|
||||
f:ImageLength=\x221\
|
||||
28\x22\x0a tiff:Reso\
|
||||
lutionUnit=\x222\x22\x0a \
|
||||
tiff:XResoluti\
|
||||
on=\x2296.0\x22\x0a tif\
|
||||
f:YResolution=\x229\
|
||||
6.0\x22\x0a photosho\
|
||||
p:ColorMode=\x223\x22\x0a\
|
||||
photoshop:ICC\
|
||||
Profile=\x22sRGB IE\
|
||||
C61966-2.1\x22\x0a x\
|
||||
mp:ModifyDate=\x222\
|
||||
023-09-26T10:26:\
|
||||
39-07:00\x22\x0a xmp\
|
||||
:MetadataDate=\x222\
|
||||
023-09-26T10:26:\
|
||||
39-07:00\x22>\x0a <x\
|
||||
mpMM:History>\x0a \
|
||||
<rdf:Seq>\x0a \
|
||||
<rdf:li\x0a s\
|
||||
tEvt:action=\x22pro\
|
||||
duced\x22\x0a stE\
|
||||
vt:softwareAgent\
|
||||
=\x22Affinity Photo\
|
||||
1.8.5\x22\x0a st\
|
||||
Evt:when=\x222023-0\
|
||||
9-26T10:26:39-07\
|
||||
:00\x22/>\x0a </rdf\
|
||||
:Seq>\x0a </xmpMM\
|
||||
:History>\x0a </rd\
|
||||
f:Description>\x0a \
|
||||
</rdf:RDF>\x0a</x:x\
|
||||
mpmeta>\x0a<?xpacke\
|
||||
t end=\x22r\x22?>{r\xf7\xcc\x00\
|
||||
\x00\x01\x81iCCPsRGB IEC6\
|
||||
1966-2.1\x00\x00(\x91u\x91\xcbK\
|
||||
BA\x14\x87\xbf\xb4\xb2'\x06E\xb4h!Q\xad4\
|
||||
\xac@j\xd3B\xe9\x05\xd5B\x0d\xb2\xda\xe8\xf5\x15\xa8\
|
||||
]\xeeUB\xda\x06m\x83\x82\xa8M\xafE\xfd\x05\xb5\
|
||||
\x0dZ\x07AQ\x04\xd1\xb6\xd6EmJn\xe7j\xa0\
|
||||
D\xce0\xe7|\xf3\x9bs\x0e3g\xc0\x12L)i\
|
||||
\xbd\xd6\x0d\xe9LV\xf3Oz\x1d\x0b\xa1E\x87\xed\x85\
|
||||
F:\xa9\x17k\x09+\xba:\x1b\x98\x08Ru|\xde\
|
||||
Sc\xfa[\x97Y\xabz\xdc\xbf\xa39\x1a\xd3\x15\xa8\
|
||||
i\x10\x1eST-+<%<\xb3\x96UM\xde\x11\
|
||||
\xeeP\x92\xe1\xa8\xf0\x99\xb0S\x93\x0b\x0a\xdf\x99z\xa4\
|
||||
\xc4\xaf&'J\xfcm\xb2\x16\xf4\xfb\xc0\xd2&\xecH\
|
||||
Tp\xa4\x82\x95\xa4\x96\x16\x96\x97\xd3\x9bN\xe5\x94\xdf\
|
||||
\xfb\x98/i\x89e\xe6\x03\xe2{du\xa3\xe3g\x12\
|
||||
/\x0e\xa6\x19\xc7\x87\x87AF\xc5zp1\xc4\x80\xec\
|
||||
\xa8\x92\xef.\xe6\xcf\xb1*\xb9\x8aX\x95<\x1a+$\
|
||||
H\x92\xc5)jN\xaa\xc7\xc4\xc7E\x8f\xc9L\x917\
|
||||
\xfb\xff\xb7\xafz|x\xa8T\xbd\xc5\x0bu\xcf\x86\xf1\
|
||||
\xde\x07\xb6m(l\x19\xc6\xd7\x91a\x14\x8e\xc1\xfa\x04\
|
||||
\x97\x99r\xfe\xea!\x8c|\x88\xbeU\xd6z\x0f\xc0\xbe\
|
||||
\x01\xe7We-\xb2\x0b\x17\x9b\xd0\xf5\xa8\x86\xb5pQ\
|
||||
\xb2\xca\xb2\xc4\xe3\xf0v\x0a\xad!h\xbf\x81\xa6\xa5R\
|
||||
\xcf~\xcf9y\x80\xe0\xba|\xd55\xec\xedC\xbf\xc4\
|
||||
\xdb\x97\x7f\x00\x00<g\xb7\xa5uLy\x00\x00\x00\x09\
|
||||
pHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95+\x0e\
|
||||
\x1b\x00\x00\x05\x04IDATx\x9c\xed\x9cAh\x5c\
|
||||
U\x14\x86\xbf\xa36\x22&\xee*i\x1apaAQ\
|
||||
W\xa9\xb1\xba\xb3\xed\xa6\xb4\xa4\xb8,\xb8\x127\xba\xd2\
|
||||
\xb5n\xac\xe8\xb2\xcb\xba\x88A(\x18\xb7\x16E\xc5U\
|
||||
\xdcU\xadYU\x05\xa5\x82\xd0v\xaavUMA\xa3\
|
||||
\xcdq1/A\xa4\x99yi\xef\xcb\xb9\x93\xf3\x7f\xf0\
|
||||
$\xc3\xff\xce\x9c\xfb\xcd}3\xf7\xdd\x09\xec@\xdc\
|
||||
}\xda\xdd?p\xf7\x9e\xdf9\xbd&k:\xfayu\
|
||||
\x81E\x17P\x1aw\xbf\x0f\xf8\x19x\xb0p\xf4o\xc0\
|
||||
Cf\xf6g\xe1\xdcP\xee\x8a.\xa0\x03NP~\xf0\
|
||||
i2Ot\x90+J\xe2\xee\xe7\x0bL\xfb\x9b\xf1u\
|
||||
\xf4\xf3+\xcdN\xbc\x04x\x97\xf9f\xb6\xa3z\xb6\x13\
|
||||
/\x01b\x0bH\x80\xe4H\x80\xe4t~=k>?\
|
||||
?\x07\x1c\x05\xf6\x01{\x80\xf1\xae\xcf;\xa2\xac\x00W\
|
||||
\x81\x8b\xc0\xa7\xc0Y3\xbb\xdc\xe5\x09;\x13\xc0\xdd\xa7\
|
||||
\x80\x93\xc0\x0b\xc0\xdd]\x9dg\x87s\x13x\x0fx\xc3\
|
||||
\xccz]\x9c\xa0\x13\x01\xdc\xfd8\xf0>0\xd1E~\
|
||||
B\xfe\x00\x9e7\xb3\x8fK\x07\x17\x7f\x0f\xe0\xee/\x03\
|
||||
\x1f\xa2\xc1/\xc9\x04p\xd6\xdd_*\x1d\x5ct\x06p\
|
||||
\xf7'\x80e`\xacd\xae\xd8`\x15\xd8of\xdf\x96\
|
||||
\x0a,6\x03\xb8\xfb=\xc0\x194\xf8]2\x06\x9ci\
|
||||
z]\x84\x92\x97\x80\xd7\x81\x99\x82y\xe2\xd6\xcc\x00\xaf\
|
||||
\x95\x0a+r\x09p\xf7\xdd\xc0\x15`W\x89<1\x94\
|
||||
\xbf\x81\xbdfv\xedN\x83J\xcd\x00O\xa1\xc1\xdfN\
|
||||
v\x01\xb3%\x82J\x09\xf0d\xa1\x1c\xd1\x9e\x22=\x97\
|
||||
\x00\xa3KU\x02\xec/\x94#\xdaS\x95\x00bD)\
|
||||
%\xc0r\xa1\x1c\xd1\x9eoJ\x84\x94\x12\xa0H1b\
|
||||
KH\x80\xe4\x14\xe9\xb9\x16\x82F\x93\xba\x16\x82\x9aB\
|
||||
\xde.\x91%Z\xf1V\x89\xc1\x87\x82w\x03\x9b\x1b\x14\
|
||||
_\xa1\xfb\x01]\xb3\x0c<mf\xff\x94\x08\xd3\xed\xe0\
|
||||
\xd1b\x15\x981\xb3\xefJ\x05\x16]\x07h\xeeS\xbf\
|
||||
\x0a\xac\x95\xcc\x15@\xbf\xa7\xaf\x94\x1c|\xe8nK\xd8\
|
||||
\x1c\xb0\x88v\x05\x95bt\xb6\x84\x014\x85>\x0a\xbc\
|
||||
K\x7fc\xa3\xb8=n\x02\xf3\xc0#]\x0c>l\xcf\
|
||||
\xb6\xf0\xbd\xf4\xb7\x85\x1f\x03\x1e\x06\xa6\xd0\xb6\xf0\xcdX\
|
||||
\x01z\xc0O\xc0'\xf4\xb7\x85_\xe9\xf2\x84\xe1\xdfs\
|
||||
\xeb\xfa\xbb|\xb5\x13\xfd]C\xdd\x0cJ\x8e\x04H\x8e\
|
||||
\x04H\xce(\x08\xe0\xc0\x020G\xff\x0d\xe4T\xf3\xf3\
|
||||
B\xf3\xb7\x9d\x96\x97\x8b!\xff\x91\xa3\xe7\xee\x87\xdc\x9d\
|
||||
M\x8e\xc3\xbe\xb5\x7f\x04U]^t\xff\xc3\x19\xd0\x9b\
|
||||
\xb5!\xcd\xfdo\x93\xd7Z\x0cV\x95y\xd1\xfd\x0fg\
|
||||
@\x83\xe7[4w\xfd\x98o1`U\xe6E\xf7?\
|
||||
\x9c\x01\x0d>\xb0\x85\x06\x1fh1`U\xe6E\xf7\xbf\
|
||||
\xe6\x85\xa0\x09\xfa+cm\x18\xa7\xbf^>\x88*\xf3\
|
||||
\xa2\x17\x82j\x16`\xab\xb5\x0d{5U\x99\x17-\xc0\
|
||||
(|\x0c\x14\x1d\x22\x01\x92#\x01\x92#\x01\x92#\x01\
|
||||
\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92\
|
||||
#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\
|
||||
\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\
|
||||
\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92\
|
||||
#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\
|
||||
\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\
|
||||
\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92\
|
||||
#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\
|
||||
\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\
|
||||
\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92\
|
||||
#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\x01\x92#\
|
||||
\x01\x92#\x01\x92#\x01\x92#\x01\x92S\xb3\x00\xe3\x85\
|
||||
\x1f[{^\x085\x0b\xf0x\xe1\xc7\xd6\x9e\x17B\x0d\
|
||||
\x02\xacl\xf2\xfb\x17\xb7\x90\xd1\xe6\xb15\xe6m\xf6\xdc\
|
||||
\xf3\xe0\xee?\xfa\xadYs\xf7C\xee\xce\x90\xe3p\xf3\
|
||||
\xd8a\xd4\x98\xf7Ct\xff\xc3q\xf7/\x064\xb9\xd7\
|
||||
4pPs\xaf\xb6\x18\xacZ\xf3\x96\xa2\xfb\x1f\x8e\xbb\
|
||||
\x9f\x1e\xd2\xe45w_p\xf79w\xdf\xd3\x1cs\xcd\
|
||||
\xef\xda\xbcRk\xce;\x1d\xdd\x7f\x8b.\xc0\xdd\x8f\x00\
|
||||
\x9fE\xd7\x11\xc4\x113\xfb<\xb2\x80\x1a\x04\x18\x03\xae\
|
||||
\x01\x0fD\xd7\xb2\xcd\xfc\x0e\xec6\xb3\xd5\xc8\x22\xc2?\
|
||||
\x054\x0dX\x8c\xae#\x80\xc5\xe8\xc1\x87\x0af\x00\x00\
|
||||
w\x9f\x04.\x02\xf7G\xd7\xb2M\xdc\x00\xf6\x99\xd9/\
|
||||
\xd1\x85\x84\xcf\x00\x00M#NE\xd7\xb1\x8d\x9c\xaaa\
|
||||
\xf0\xa1\x92\x19\x00\xc0\xdd\xef\x05\x96\x80g\xa2k\xe9\x98\
|
||||
s\xc0A3\xfb+\xba\x10\xa8H\x00\xd8\xb8\x14\x9c\x07\
|
||||
\xa6\xa3k\xe9\x88K\xc0\xac\x99\xfd\x1a]\xc8:U\x5c\
|
||||
\x02\xd6i\xa6\xc5\xa3\xc0\xe5\xe8Z:\xe0\x12p\xac\xa6\
|
||||
\xc1\x87\xca\x04\x000\xb3\x0b\xc0,\xf0et-\x059\
|
||||
G\xff\x95\x7f!\xba\x90\xffS\x9d\x00\xb01\x13<\x0b\
|
||||
\xbcI\xff\x1d\xf3\xa8r\x038I\xff\x9a_\xd5+\x7f\
|
||||
dp\xf7Iw\x7f\xc7\xdd\xaf\xdf\xc62m\x14\xd7\xbd\
|
||||
_\xf3dt\xff\x86Q\xd5\x9b\xc0Ax\x7f\xc5\xf0 \
|
||||
p\x1cx\x0c\x98j\x8e\xe8\xcd\x16+@\xaf9\xbe\x07\
|
||||
>\x02\x96jX\xe4i\xc3\xbf\xe5\xe8\x86\x9f\xd1G`\
|
||||
\xf3\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x0f\xeb\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\
|
||||
\x00\x00\x04\xb3iTXtXML:com.\
|
||||
adobe.xmp\x00\x00\x00\x00\x00<?\
|
||||
xpacket begin=\x22\xef\
|
||||
\xbb\xbf\x22 id=\x22W5M0MpCe\
|
||||
hiHzreSzNTczkc9d\
|
||||
\x22?>\x0a<x:xmpmeta x\
|
||||
mlns:x=\x22adobe:ns\
|
||||
:meta/\x22 x:xmptk=\
|
||||
\x22XMP Core 5.5.0\x22\
|
||||
>\x0a <rdf:RDF xmln\
|
||||
s:rdf=\x22http://ww\
|
||||
w.w3.org/1999/02\
|
||||
/22-rdf-syntax-n\
|
||||
s#\x22>\x0a <rdf:Desc\
|
||||
ription rdf:abou\
|
||||
t=\x22\x22\x0a xmlns:e\
|
||||
xif=\x22http://ns.a\
|
||||
dobe.com/exif/1.\
|
||||
0/\x22\x0a xmlns:ti\
|
||||
ff=\x22http://ns.ad\
|
||||
obe.com/tiff/1.0\
|
||||
/\x22\x0a xmlns:pho\
|
||||
toshop=\x22http://n\
|
||||
s.adobe.com/phot\
|
||||
oshop/1.0/\x22\x0a \
|
||||
xmlns:xmp=\x22http:\
|
||||
//ns.adobe.com/x\
|
||||
ap/1.0/\x22\x0a xml\
|
||||
ns:xmpMM=\x22http:/\
|
||||
/ns.adobe.com/xa\
|
||||
p/1.0/mm/\x22\x0a x\
|
||||
mlns:stEvt=\x22http\
|
||||
://ns.adobe.com/\
|
||||
xap/1.0/sType/Re\
|
||||
sourceEvent#\x22\x0a \
|
||||
exif:PixelXDime\
|
||||
nsion=\x22128\x22\x0a e\
|
||||
xif:PixelYDimens\
|
||||
ion=\x22128\x22\x0a exi\
|
||||
f:ColorSpace=\x221\x22\
|
||||
\x0a tiff:ImageWi\
|
||||
dth=\x22128\x22\x0a tif\
|
||||
f:ImageLength=\x221\
|
||||
28\x22\x0a tiff:Reso\
|
||||
lutionUnit=\x222\x22\x0a \
|
||||
tiff:XResoluti\
|
||||
on=\x2296.0\x22\x0a tif\
|
||||
f:YResolution=\x229\
|
||||
6.0\x22\x0a photosho\
|
||||
p:ColorMode=\x223\x22\x0a\
|
||||
photoshop:ICC\
|
||||
Profile=\x22sRGB IE\
|
||||
C61966-2.1\x22\x0a x\
|
||||
mp:ModifyDate=\x222\
|
||||
023-09-26T10:35:\
|
||||
58-07:00\x22\x0a xmp\
|
||||
:MetadataDate=\x222\
|
||||
023-09-26T10:35:\
|
||||
58-07:00\x22>\x0a <x\
|
||||
mpMM:History>\x0a \
|
||||
<rdf:Seq>\x0a \
|
||||
<rdf:li\x0a s\
|
||||
tEvt:action=\x22pro\
|
||||
duced\x22\x0a stE\
|
||||
vt:softwareAgent\
|
||||
=\x22Affinity Photo\
|
||||
1.8.5\x22\x0a st\
|
||||
Evt:when=\x222023-0\
|
||||
9-26T10:35:58-07\
|
||||
:00\x22/>\x0a </rdf\
|
||||
:Seq>\x0a </xmpMM\
|
||||
:History>\x0a </rd\
|
||||
f:Description>\x0a \
|
||||
</rdf:RDF>\x0a</x:x\
|
||||
mpmeta>\x0a<?xpacke\
|
||||
t end=\x22r\x22?>=\xbdO\xa5\x00\
|
||||
\x00\x01\x81iCCPsRGB IEC6\
|
||||
1966-2.1\x00\x00(\x91u\x91\xcbK\
|
||||
BA\x14\x87\xbf\xb4\xb2'\x06E\xb4h!Q\xad4\
|
||||
\xac@j\xd3B\xe9\x05\xd5B\x0d\xb2\xda\xe8\xf5\x15\xa8\
|
||||
]\xeeUB\xda\x06m\x83\x82\xa8M\xafE\xfd\x05\xb5\
|
||||
\x0dZ\x07AQ\x04\xd1\xb6\xd6EmJn\xe7j\xa0\
|
||||
D\xce0\xe7|\xf3\x9bs\x0e3g\xc0\x12L)i\
|
||||
\xbd\xd6\x0d\xe9LV\xf3Oz\x1d\x0b\xa1E\x87\xed\x85\
|
||||
F:\xa9\x17k\x09+\xba:\x1b\x98\x08Ru|\xde\
|
||||
Sc\xfa[\x97Y\xabz\xdc\xbf\xa39\x1a\xd3\x15\xa8\
|
||||
i\x10\x1eST-+<%<\xb3\x96UM\xde\x11\
|
||||
\xeeP\x92\xe1\xa8\xf0\x99\xb0S\x93\x0b\x0a\xdf\x99z\xa4\
|
||||
\xc4\xaf&'J\xfcm\xb2\x16\xf4\xfb\xc0\xd2&\xecH\
|
||||
Tp\xa4\x82\x95\xa4\x96\x16\x96\x97\xd3\x9bN\xe5\x94\xdf\
|
||||
\xfb\x98/i\x89e\xe6\x03\xe2{du\xa3\xe3g\x12\
|
||||
/\x0e\xa6\x19\xc7\x87\x87AF\xc5zp1\xc4\x80\xec\
|
||||
\xa8\x92\xef.\xe6\xcf\xb1*\xb9\x8aX\x95<\x1a+$\
|
||||
H\x92\xc5)jN\xaa\xc7\xc4\xc7E\x8f\xc9L\x917\
|
||||
\xfb\xff\xb7\xafz|x\xa8T\xbd\xc5\x0bu\xcf\x86\xf1\
|
||||
\xde\x07\xb6m(l\x19\xc6\xd7\x91a\x14\x8e\xc1\xfa\x04\
|
||||
\x97\x99r\xfe\xea!\x8c|\x88\xbeU\xd6z\x0f\xc0\xbe\
|
||||
\x01\xe7We-\xb2\x0b\x17\x9b\xd0\xf5\xa8\x86\xb5pQ\
|
||||
\xb2\xca\xb2\xc4\xe3\xf0v\x0a\xad!h\xbf\x81\xa6\xa5R\
|
||||
\xcf~\xcf9y\x80\xe0\xba|\xd55\xec\xedC\xbf\xc4\
|
||||
\xdb\x97\x7f\x00\x00<g\xb7\xa5uLy\x00\x00\x00\x09\
|
||||
pHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95+\x0e\
|
||||
\x1b\x00\x00\x09QIDATx\x9c\xed\x9dm\xac\x1d\
|
||||
E\x1d\xc6\x7f\xd3\xd2\xa4\x5cL\xe9[\xf4\xb6@bK\
|
||||
jR+\x82\x97\x06M\xd4\x8a\xa0I\x05y\x11\xd2(\
|
||||
*\xa9\x16\x1a4\xdaJ\xd5o\xb6A\x81\xa4FD\x10\
|
||||
\xd1\x1a4HDI\x08E\xaa\xb5\x85/J\x14\x14$\
|
||||
\x08\xb4\xe5\xe5\x03m\xb1\xb1\xedM\x0d\xa5\xd0\xda\x97\xdb\
|
||||
\xda>~\x98\xad\x9eN\xf7\xbc\xdc=\xb3\xbbs\xce\xcc\
|
||||
/\xd9\xe4\xec\x9eyy\xe6\xbf\xcf\x9e\x99\x9d\xdd\xb3k\
|
||||
\xe8c$M\x06\x16\x01s\xb3\xe5\xec\x0e\xb3n\x01\x9e\
|
||||
\xcd\x96{\x8d1o\x94\xa30Q\x1a\x92.\x934\xac\
|
||||
\xee\x19\x96tY\xdd\xedI\x8c\x02I\xb7z\xd8\xf1.\
|
||||
\xb7\xd4\xdd\xaeD\x07H\x9a'\xe9X\x09\x068&i\
|
||||
^\xdd\xed\xf3\x8d\xa9[\x80O$\x0d\x00\x9b\x80\x99%\
|
||||
U\xb1\x158\xc7\x18s\xa0\xa4\xf2+\xe7\x94\xba\x05x\
|
||||
\xe6\x22:\xdb\xf9\xaf\x01O`\x07y`\x07\x88\xf3\x80\
|
||||
w\xb6\xc97\x13\xf8(\xb0\xae\xa0\xbeD\x99H\xfav\
|
||||
\x9b\x9f\xf1\xc3\x92n\x96t\x8a$\x9ce\x9c\xa4[\xb2\
|
||||
4\xad\xb8\xa9\xeev&\x9a im\x9b>\xfc\x92\x9c\
|
||||
\x1d\xef.\x97\xaa\xf5\x18bm\xdd\xed\xf4I\xadc\x00\
|
||||
I\xa7\x02\xb3\x81\xe9\xc04`R\x83\xa6\xd7\x80]\x0d\
|
||||
\xc9\xcf\x02>\xdc\xa6\xc8\xf7\x00\xa75\xf9n5pk\
|
||||
\x87\xd2V\x00W7\xf9n?\xf0b\x9b\xfcO\x00\xff\
|
||||
lX\x7f\x070#\xfb,`\x0f0\x0c\xec\x04^6\
|
||||
\xc6\x1c\xeaPW\xef#\xe94I\x0b%=\x22i\x7f\
|
||||
\x8b#m\xb1sd.h\xf3\xd3\xdc\x8a\xa3\x92&v\
|
||||
p\xf4\x1f_&ey\x8a\xb2\xc0)oq\x8b\xb4\xfb\
|
||||
ec\xb1PR3\xf3\x96\xc6\x98\xaa*\x92\xedw\xbf\
|
||||
\x8c\x9de\xbb\x0f\xb8\x12\x18\xa8\xa8\xfaW\x817G\x91\
|
||||
~\x0f\xb0\xb9$-.\x03\xd8X\xdc\x07l\x96\xf4%\
|
||||
I\x95\x0d\xce+1\x80\xa4w\x03\x1b\x81\x9f`\x7f\x0e\
|
||||
\xab\xe6\x85\x02y\x9e\xf7\xae\xa2=\x83\xc0*`c\x16\
|
||||
\xb3\xd2)\xddi\x92.\x05\x1e\x00&4I\xb2\x03x\
|
||||
\x09\xdb'\xbe\x0e\x1c\xcd\xb6o\xf0(c\xb0@\x9ei\
|
||||
\x1e\xeb\xdf\x00|/\xfb<\x16\x98\x9a\x95?\x078#\
|
||||
'\xfdl\xe0)I\xd7\x18c\xd6{\xd4Q-\x92>\
|
||||
\xd3\xa4/= \xe96IC\x92:\x1a\x88\xaa\xbb1\
|
||||
\xc0[\xea\xbc\xffG\x92\x91\xb4\xb7\x8b\xfa\x16t\xd8&\
|
||||
#\x1b\x83\xdbdc\xe2rT\xd2\xa7\xbb\xdb\x0b5!\
|
||||
in\x93F\xdd/iz\x81\xf2\xba1\x80dO\xef\
|
||||
:5\xc0'\xbb\xac\xab#\x038\xed\x9b.\x1b\x1b\x97\
|
||||
\x03\x92\xe6\x8e\xb6\xbcZ\x914E\xd2v\xa7!\xff\x91\
|
||||
\xb4D\x1d\x1e\xf19evk\x80\xed\xea\xecL`\xa2\
|
||||
\xa4\x1d]\xd65j\x03dm4\x92\x96\xea\xe4_\xcd\
|
||||
\xed\x92\xa6\x14)\xb3\x16$\xdd\x99\x13\x94\x85]\x96\xd9\
|
||||
\xad\x01$\xe9qIg\xa8\xf9\xce?3K\xd3-\x85\
|
||||
\x0c\xd0\xd0\xd6/\xe4\x94yG7eV\x86\xa4\x19\x92\
|
||||
F\x1c\xf1\xb7\xb7\xc93 i\xbe\xa4\xe5\x92\xd6H\xda\
|
||||
&{^\xeck\x1e\xa0\x91=\xb2G\xd9\x1cIc\xb2\
|
||||
eN\xb6m\x8f\xa7:\xdcy\x80\x85\xb2mZ#\xdb\
|
||||
\xc6\xf9\xb2\x17\xaeZ\xc5\xe4v\xa7\xcc\x11I3Z\xe5\
|
||||
\x09\x02I\xab\x1c\xe1\xdb$\x8do\x91\xfeBI[s\
|
||||
\x82\xe8s\x22\xa8\x19\xfb\xb2\xc57\x9dL\x04m\x95\xf4\
|
||||
\x91\x16q\x19/\x1b\xbbFV\xf9\xde_^\xe7\x01$\
|
||||
\x8d\x01>\xe5l^\x9e7\xd5){\xd4\xdf\x05\xfc\x91\
|
||||
\xffO\x93V\xcd\xdb\xb2\xa5\x0ef\x00\x8fK\xfa\xa1r\
|
||||
~\x0d\xb2\x98\xadp6_\xa9\x82c\xa8f\xf8\x9e\x08\
|
||||
\xfa\x00'N\xf4\xec\x05\x1el\x92\xf6~`\x09}v\
|
||||
O\xc2(1\xc0R\xe0\x97M\xbe\x7f\x10\xd8\xd7\xb0>\
|
||||
\x88\x8d\xb17|\x1b\xe0\x22g}\xbd1\xe6\xb0\x9bH\
|
||||
\xd2g\x81\xab<\xd7\xdd\xcb\x5c-\xe9\x1aw\xa31f\
|
||||
\x04p'\x82.\xf6Y\xb1o\x03\x9c\xe5\xac\xff\xd5M\
|
||||
i\x1a\xf0#\xcf\xf5\xf6\x03wK\xca\x9b\xb1tc\
|
||||
x\xa6\xcfJ}\x1b\xc0\x9d\xe0\xd9\x99\x93f\x190\xb9\
|
||||
@\xd9*\x90\xa7.\x8ah\x9d\x8c\x8d\x8d\xcb\x0eg}\
|
||||
\xd4\x93h\xad\xf0m\x80\xb7;\xeb\xc39i.(X\
|
||||
\xf6\xb6\x82\xf9\xea\xe0\x1f\x05\xf3\xe5\xc5\xc6\x8d\xa1\xd7\x8b\
|
||||
i\xbe\x0d\xe0^\x5c\x1ai\x5c\xc9F\xb0\xef+X\xf6\
|
||||
F\xe0H\xc1\xbcUr\x18\xab\xb5\x08y\xd7FF\x9c\
|
||||
u\xaf\x17\xf0*\xbb\x1f \xe3]4\xbf*\xd8\x8e\x11\
|
||||
\xe0a\x8fZ\xca\xe27X\x13\x14a\x020\xcb\xa3\x96\
|
||||
\xb6Tm\x80n\x070K8\xf16\xb1\xd0\xd8\x05|\
|
||||
\xb5\xcb2\xdc\x81t\xa9Tm\x80\xd1\x9c\xf3O\xcd\xd9\
|
||||
\xf6:\xf0q\x8a\xdd\xe0Q6/`\xb5\xed\xce\xf9.\
|
||||
\xaf-\xcd\xa8t^$\xe4\xff\x05\x0c5\xd9\xbe\x09;\
|
||||
X\xfa\x22\xf0A\xec\x98\xa2\xae\xd9\xbc\x7fc\xef\x1c\xfa\
|
||||
\x0b\xf0\x0b\x9a\x8fQ\xce\xafL\xd1(\x09\xd9\x00\x1f\x02\
|
||||
N\x05\x0e\xe6|w\x04\xb8'[Bg\x00k\xd4 \
|
||||
\xa9\xba\x0b\x18\x0d\x83\xc0\xca\xbaEx`%\xc5nI\
|
||||
\xab\x84\x90\x0d\x00v\x9e\xfc\x86\xbaEt\xc1\x0d\xd8\x81\
|
||||
k\xb0\x84n\x00\x03\xfc\x14x\x14\xb8\x90\xe2\xa7\x90U\
|
||||
2\x01\xab\xf51\xac\xf6\xa0/v\x85<\x06hd~\
|
||||
\xb6\x08\xd8N\xb8\x13B\xe3\xb0\xa7\xbaA\xef\xf4Fz\
|
||||
\xc5\x00\xc71T|\x9e\xdc\xef\x84\xde\x05$J&\x19\
|
||||
r\x92\x01\x22'\x19 r\x92\x01\x22'\x19 r\
|
||||
\x92\x01\x22'\x19 r\x92\x01\x22'\x19 r\x92\x01\
|
||||
\x22'\x19 r\x92\x01\x22'\x19 r\x92\x01\x22'\
|
||||
\xb4\xfb\x01v\x03\xdf\xa4\xf8?kB\xe7\xbd\xc0\xf7\x81\
|
||||
`\x9e\xf7\x13\x9a\x01\xee\xc4>1\xb3_y\x0e{C\
|
||||
\xcb\xcdu\x0b9Nh]\xc0cu\x0b\xa8\x80G\xeb\
|
||||
\x16\xd0Hh\x068V\xb7\x80\x0a\x08\xaa\x8d\xa1\x19 \
|
||||
Q1\xc9\x00\x91\x13\x9a\x01\xc6\xd6-\xa0\x02\x82jc\
|
||||
h\x06\xb8\xbcn\x01\x15pE\xdd\x02\x1a\x09\xed4p\
|
||||
)\xf6e\x0d\x1b\xe9\xadg\x02u\x82\xc1\xce\x03\x04\xf5\
|
||||
W\xb1\xd0\x0c0\x01h\xf9X\xd9\x84_B\xeb\x02\x12\
|
||||
\x15\x93\x0c\x109\xc9\x00\x91\x93\x0c\x109\xc9\x00\x91\x93\
|
||||
\x0c\x109\xc9\x00\x91\x13\xda<\xc0^\xe0;\xf8}g\
|
||||
`H\x9c\x0b\xdcD@\x8f\xba\x09\xcd\x00w\x01?\xa8\
|
||||
[D\x89\xfc\x01\xfbT\xf0o\xd5-\xe48\xa1u\x01\
|
||||
\xbf\xab[@\x05\xfc\xb6n\x01\x8d\x84f\x80\xa3\xed\x93\
|
||||
\xf4<A\xb514\x03$*&\x19 rB3@\
|
||||
hz\xca \xa86\x06%\x06\xfb0\xc8~\xe7\x13u\
|
||||
\x0bh$\xb4\xd3\xc0\x1b\xb1/I\xea\xe7\x1bB\xbeV\
|
||||
\xb7\x90FB3\xc0\x14\xe0\xde\xbaE\xc4Dh]@\
|
||||
\xa2b\x92\x01\x22'\x19 r\x92\x01\x22'\x19 r\
|
||||
\x92\x01\x22'\x19 rB\x9b\x078\x88\xfdcH?\
|
||||
\xdf\x10\xf2\x0d\xec\xeb\xf0\x82 4\x03\xdc\x0d\xac\xa8[\
|
||||
D\x89\xac\x06\xc6c\x1f\x83\x13\x04\xa1u\x01\xbd\xf0r\
|
||||
\xe8nY]\xb7\x80FB3@\xa8o\x03\xf3IP\
|
||||
m\x0c\xcd\x00\x89\x8aI\x06\x88\x9cd\x80\xc8\x09\xcd\x00\
|
||||
\x17\xd7-\xa0\x02>V\xb7\x80FB;\x0d\xfc:\xf0\
|
||||
*\xfd}C\xc8\xb2\xba\x854\x12\x9a\x01\x06\x81G\xea\
|
||||
\x16\x11\x13\xa1u\x01\x89\x8aI\x06\x88\x9cd\x80\xc8I\
|
||||
\x06\x88\x9cd\x80\xc8I\x06\x88\x9cd\x80\xc8I\x06\x88\
|
||||
\x9cd\x80\xc8I\x06\x88\x9cd\x80\xc8I\x06\x88\x9cd\
|
||||
\x80\xc8I\x06\x88\x9c\xaa\x0dp\xa8\xe2\xfaz\x91Jc\
|
||||
T\xb5\x01^\xa4\xffn\xf4\xf0\x89\x80MUVX\xb6\
|
||||
\x01\xcc\x09+\xc6\xbc\x09l)\xb9\xce^f\xb31\xe6\
|
||||
-g\x9b\xc9M\xe9\x09\xdf\x06\xd8\xe7\xacO\xcaI\xf3\
|
||||
w\xcfu\xf6\x13y\xb1\x99\xec\xac\xbb1\xee\x0a\xdf\x06\
|
||||
\x18v\xd6\xa7\xe7\xa4Y\xeb\xb9\xce~\x22/6n\x0c\
|
||||
w\xfa\xac\xd0\xb7\x01\x5cqg\xbb\x09\x8c1\xbf\x06\xd6\
|
||||
{\xae\xb7\x1fXg\x8cy g\xbb\x1b\xc3\xa0\x0d\xf0\
|
||||
\x92\xb3~I\x93t\x8b\xb1\xef\x07LX\xf6`c\x92\
|
||||
\x87\x1b\xc3\x97}V\xec\xdb\x00\xeb8\xf1\xed\xd8\xe7K\
|
||||
\x9a\xe1&2\xc6\xec\x04\x16\x01\x07<\xd7\xdf\x8b\xec\x07\
|
||||
\x16\x19c\xdc\xee\x93,vC\x0d\x9b\x8e\x01\xbf\xf7Y\
|
||||
\xb9W\x03\x18cv\x01O;\x9b\x977I\xbb\x06{\
|
||||
\x9f\xfc\x93>5\xf4\x18O\x02\xe7f\xb1\xc8\xc3\xfd\xab\
|
||||
\xfcS\xc6\x98\x7f\x95\xac\xa9;$]\xa7\x139*\xe9\
|
||||
\xbc\x16\xe9\xc7HZ&\xe9yIG\xd4\xff\x1c\x91m\
|
||||
\xeb\x8d\x92\x9a\x1e\x80\x92\xce\x93t\xcc\xc9{\x9d\xef\xfd\
|
||||
\xe5\xfd\x1cS\xd2X\xecd\xc6\xec\x86\xcd[\x80\xf7\x1b\
|
||||
cv\xb7\xc9;\x1e\xfb\x14\x8d!\x02z\xad\x8a'\xf6\
|
||||
\x02\xcf\x01\x1b\x8c1-g\xfb$M\x01\x9e\x01f6\
|
||||
l~\x058\xc7\x18\x13\xd4\xfb\x06r\x91ty\x8e\xf3\
|
||||
\xff$\xe9\xf4\xba\xb5\x85\x8e\xa4\xd3\xb3X\xb9\xf4\xd6\x9b\
|
||||
\xd5%\xfd,\xa7\x11\xafH\x9aU\xb7\xb6P\x914+\
|
||||
\x8b\x91\xcb=uk\x1b5\x92\xc6I\xfasNc\x0e\
|
||||
JZ)ib\xdd\x1aCA\xd2DI\xdf\xcdb\x93\
|
||||
\xf7\xcb9\xaen\x8d\x85\x904U\xd2\xdfr\x1a%I\
|
||||
\xfb$=$\xe9ZIC\x92\x06e\xc7\x0f}\x8d\xa4\
|
||||
\xb1Y[\x87\xb2\xb6?\x94\xc5\x22\x8f\xa7%M-S\
|
||||
O\xa9\x17\x1a\xe0\x7f\x03\xbb\x9f\x03\x9f+\xbb\xae>\xe3\
|
||||
W\xc0\xe2v\x03\xc6n)\xfdr\xb01\xe6\x901\xe6\
|
||||
\xf3\xc0\xf5\x9c|\xad q2\xc3\xc0\xf5\xc6\x98k\xcb\
|
||||
\xde\xf9P\xc1/@#\x92\x06\xb0\x0fH\xf8\x0a0\xad\
|
||||
\xca\xba{\x80a\xe0\xc7\xc0\x1d\xc6\x98\xcafH+5\
|
||||
\xc0q$\x19\xe0\x02\xe0\x0a`.\xf6\x8a\xd74N\xbe\
|
||||
\xf4\xd9\xaf\xbc\x81\xdd\xe1;\x81g\xb1/\x93|\xc6\x18\
|
||||
S\xf9\xcd2\xff\x05\xbdH\xeb\x8d\xef9$\xce\x00\x00\
|
||||
\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x22\x7f\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
@@ -1005,198 +560,6 @@ z\x03\xff\x0a\xf8\x5cc\xfexFf\xe7uI\xc6\xc7\
|
||||
=\x82\xf7\x0b\xc0\xe3\xc0\xcd\xc0y\xe0%\xe0\xcf\xb7\xfa\
|
||||
\xb8\x8a\xab\xb8\x8a\xcd\x82\xff\x0fR\xb9\xc7\x85|\xfd\
|
||||
\xc5\xb0\x00\x00\x00\x00IEND\xaeB`\x82\
|
||||
\x00\x00\x0b\xd2\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
\x00\x00\x80\x00\x00\x00\x80\x08\x06\x00\x00\x00\xc3>a\xcb\
|
||||
\x00\x00\x04\xb3iTXtXML:com.\
|
||||
adobe.xmp\x00\x00\x00\x00\x00<?\
|
||||
xpacket begin=\x22\xef\
|
||||
\xbb\xbf\x22 id=\x22W5M0MpCe\
|
||||
hiHzreSzNTczkc9d\
|
||||
\x22?>\x0a<x:xmpmeta x\
|
||||
mlns:x=\x22adobe:ns\
|
||||
:meta/\x22 x:xmptk=\
|
||||
\x22XMP Core 5.5.0\x22\
|
||||
>\x0a <rdf:RDF xmln\
|
||||
s:rdf=\x22http://ww\
|
||||
w.w3.org/1999/02\
|
||||
/22-rdf-syntax-n\
|
||||
s#\x22>\x0a <rdf:Desc\
|
||||
ription rdf:abou\
|
||||
t=\x22\x22\x0a xmlns:e\
|
||||
xif=\x22http://ns.a\
|
||||
dobe.com/exif/1.\
|
||||
0/\x22\x0a xmlns:ti\
|
||||
ff=\x22http://ns.ad\
|
||||
obe.com/tiff/1.0\
|
||||
/\x22\x0a xmlns:pho\
|
||||
toshop=\x22http://n\
|
||||
s.adobe.com/phot\
|
||||
oshop/1.0/\x22\x0a \
|
||||
xmlns:xmp=\x22http:\
|
||||
//ns.adobe.com/x\
|
||||
ap/1.0/\x22\x0a xml\
|
||||
ns:xmpMM=\x22http:/\
|
||||
/ns.adobe.com/xa\
|
||||
p/1.0/mm/\x22\x0a x\
|
||||
mlns:stEvt=\x22http\
|
||||
://ns.adobe.com/\
|
||||
xap/1.0/sType/Re\
|
||||
sourceEvent#\x22\x0a \
|
||||
exif:PixelXDime\
|
||||
nsion=\x22128\x22\x0a e\
|
||||
xif:PixelYDimens\
|
||||
ion=\x22128\x22\x0a exi\
|
||||
f:ColorSpace=\x221\x22\
|
||||
\x0a tiff:ImageWi\
|
||||
dth=\x22128\x22\x0a tif\
|
||||
f:ImageLength=\x221\
|
||||
28\x22\x0a tiff:Reso\
|
||||
lutionUnit=\x222\x22\x0a \
|
||||
tiff:XResoluti\
|
||||
on=\x2296.0\x22\x0a tif\
|
||||
f:YResolution=\x229\
|
||||
6.0\x22\x0a photosho\
|
||||
p:ColorMode=\x223\x22\x0a\
|
||||
photoshop:ICC\
|
||||
Profile=\x22sRGB IE\
|
||||
C61966-2.1\x22\x0a x\
|
||||
mp:ModifyDate=\x222\
|
||||
023-09-26T10:22:\
|
||||
11-07:00\x22\x0a xmp\
|
||||
:MetadataDate=\x222\
|
||||
023-09-26T10:22:\
|
||||
11-07:00\x22>\x0a <x\
|
||||
mpMM:History>\x0a \
|
||||
<rdf:Seq>\x0a \
|
||||
<rdf:li\x0a s\
|
||||
tEvt:action=\x22pro\
|
||||
duced\x22\x0a stE\
|
||||
vt:softwareAgent\
|
||||
=\x22Affinity Photo\
|
||||
1.8.5\x22\x0a st\
|
||||
Evt:when=\x222023-0\
|
||||
9-26T10:22:11-07\
|
||||
:00\x22/>\x0a </rdf\
|
||||
:Seq>\x0a </xmpMM\
|
||||
:History>\x0a </rd\
|
||||
f:Description>\x0a \
|
||||
</rdf:RDF>\x0a</x:x\
|
||||
mpmeta>\x0a<?xpacke\
|
||||
t end=\x22r\x22?>\x84\x19G\xf4\x00\
|
||||
\x00\x01\x81iCCPsRGB IEC6\
|
||||
1966-2.1\x00\x00(\x91u\x91\xcbK\
|
||||
BA\x14\x87\xbf\xb4\xb2'\x06E\xb4h!Q\xad4\
|
||||
\xac@j\xd3B\xe9\x05\xd5B\x0d\xb2\xda\xe8\xf5\x15\xa8\
|
||||
]\xeeUB\xda\x06m\x83\x82\xa8M\xafE\xfd\x05\xb5\
|
||||
\x0dZ\x07AQ\x04\xd1\xb6\xd6EmJn\xe7j\xa0\
|
||||
D\xce0\xe7|\xf3\x9bs\x0e3g\xc0\x12L)i\
|
||||
\xbd\xd6\x0d\xe9LV\xf3Oz\x1d\x0b\xa1E\x87\xed\x85\
|
||||
F:\xa9\x17k\x09+\xba:\x1b\x98\x08Ru|\xde\
|
||||
Sc\xfa[\x97Y\xabz\xdc\xbf\xa39\x1a\xd3\x15\xa8\
|
||||
i\x10\x1eST-+<%<\xb3\x96UM\xde\x11\
|
||||
\xeeP\x92\xe1\xa8\xf0\x99\xb0S\x93\x0b\x0a\xdf\x99z\xa4\
|
||||
\xc4\xaf&'J\xfcm\xb2\x16\xf4\xfb\xc0\xd2&\xecH\
|
||||
Tp\xa4\x82\x95\xa4\x96\x16\x96\x97\xd3\x9bN\xe5\x94\xdf\
|
||||
\xfb\x98/i\x89e\xe6\x03\xe2{du\xa3\xe3g\x12\
|
||||
/\x0e\xa6\x19\xc7\x87\x87AF\xc5zp1\xc4\x80\xec\
|
||||
\xa8\x92\xef.\xe6\xcf\xb1*\xb9\x8aX\x95<\x1a+$\
|
||||
H\x92\xc5)jN\xaa\xc7\xc4\xc7E\x8f\xc9L\x917\
|
||||
\xfb\xff\xb7\xafz|x\xa8T\xbd\xc5\x0bu\xcf\x86\xf1\
|
||||
\xde\x07\xb6m(l\x19\xc6\xd7\x91a\x14\x8e\xc1\xfa\x04\
|
||||
\x97\x99r\xfe\xea!\x8c|\x88\xbeU\xd6z\x0f\xc0\xbe\
|
||||
\x01\xe7We-\xb2\x0b\x17\x9b\xd0\xf5\xa8\x86\xb5pQ\
|
||||
\xb2\xca\xb2\xc4\xe3\xf0v\x0a\xad!h\xbf\x81\xa6\xa5R\
|
||||
\xcf~\xcf9y\x80\xe0\xba|\xd55\xec\xedC\xbf\xc4\
|
||||
\xdb\x97\x7f\x00\x00<g\xb7\xa5uLy\x00\x00\x00\x09\
|
||||
pHYs\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95+\x0e\
|
||||
\x1b\x00\x00\x058IDATx\x9c\xed\x9bK\x8b\x1c\
|
||||
U\x00\x85\xcf\xcd\xc4<@D\xc4\x8d\x10q\xa3bD\
|
||||
\xc5\x08\x12D0\x1b]\x88n\xb2PA\x08\x82[E\
|
||||
\xd0\x9d\x0b\x17\xfa\x07\x5cI@\xb7\x09b\x16\xd9\xa8\x88\
|
||||
Y\x08&\x111\x88\x1a\x18P#\x86(D\x02\xf1\x11\
|
||||
\xc1\xbc\x93\xf9\x5c\xd4\xb43\xd3Vu\xdf\x9ez\xdc\xaa\
|
||||
\xdb\xe7\x83\xda\xf4\xf4t\xdf9\xdf\xa9{\xeftW\x05\
|
||||
\x99\xe4\x007H\xda.\xe9\x1eI?KZ\x0c!\x5c\
|
||||
N;*\xd3:\xc0\xdd\xc0!\xe0\x12k\xb9\x0a\x1c\x05\
|
||||
v\xa4\x1e\xa3i\x01`\x01x\x0d\xb8\xc8d\xae\x02o\
|
||||
\x01\x9bR\x8f\xd94\xc4\xb2\xfc}S\xc4\x8f\xf3\x09\xb0\
|
||||
%\xf5\xd8MM\xd6)\xdf%\xc8\x81\x9a\xf2]\x82!\
|
||||
\xd3\x90|\x97`\x884,\xdf%\x18\x12-\xc9w\x09\
|
||||
\x86\xc0\x8c\xf2\x7f\x04\xf6\x00\xf7\x01\x8f\x03\xef\x02\xd7]\
|
||||
\x82\x812\xa3\xfc\x03\xc0\x16@c\xc7S\xc0\x05\x97`\
|
||||
`\xcc(\xff\x83\xe5\xe7\x8f\xcb\x1f\x1dO\xb8\x04\x03\xa2\
|
||||
a\xf9.\xc1\x90hI\xbeK0\x04Z\x96\xef\x12\xf4\
|
||||
\x99\x8e\xe4\xbb\x04}\xa4c\xf9\xa3\xe3\xe5\xc8\xf7s\x09\
|
||||
\xda$\x91\xfc\xd1q\xca%HHb\xf9\x02\x0eF\xbe\
|
||||
\xb7K\xd04=\x90/\xe0\xf0\x0c\x05p\x09\x9a\xa2'\
|
||||
\xf2\xb7\x01\xe7g,\x80KP\x97\x9e\xc8_\xa0\xb8\x86\
|
||||
p\xbd\xb8\x04\xeb\xa1G\xf2\x0f\xd4\x90\xbf\xba\x04\xbe\xc6\
|
||||
0\x96\xcc\xe4\x8fx3u\xae\x83\x80<\xe5Cq\xb5\
|
||||
\xb1/9\x9f\x04\xf9\xca\x1fq$u\xc6\xbd\x85\xfc\xe5\
|
||||
\x03\x5c\x016\xa7\xce\xbaw0\x1f\xf2G<\x94:\xef\
|
||||
^\xc1|\xc9\x07x6u\xe6\xbd\x81\xf9\x93\x0fp\x7f\
|
||||
\xea\xdc{\x01\xf3)\xff\x12\xc5]\xc9\xf3\x0d\xf3)\x1f\
|
||||
\xe0\xd3\xd4\xd9'\x87\xf9\x95\x7f\x11\xb8+u\xfeIa\
|
||||
~\xe5\x03\xbc\x9a:\xff\xa40\xdf\xf2\xf7\x01\x0b\xa9\x1d\
|
||||
$\x03\xcb\xb7\xfc\xc8\xb0,?'\xb0|\xcb\x8f\x0c\xcb\
|
||||
\xf2s\x02\xcb\xb7\xfc\xc8\xb0,?'\xb0|\xcb\x8f\x0c\
|
||||
\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0,?'\xb0|\xcb\x8f\
|
||||
\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0,?'\xb0|\xcb\
|
||||
\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0,?'\xb0|\
|
||||
\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0,?'\xb0\
|
||||
|\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0,?'\
|
||||
\xb0|\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0,?\
|
||||
'\xb0|\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0,\
|
||||
?'\xb0|\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\xb0\
|
||||
,?'\xb0|\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\xc8\
|
||||
\xb0,?'\xb0|\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\xfc\
|
||||
\xc8\xb0,?'\xb0|\xcb\x8f\x0c\xcb\xf2s\x02\xcb\xb7\
|
||||
\xfc\xc8\xb0,?7\x80\x87#\xc3\xb2\xfc\x81\xb0a\xc6\
|
||||
\xe7\xef\x8ax\xce\x01I\xcfK\xba>\xfbp&\xb2 \
|
||||
\xe9}I\xcf4\xfc\xba\xd3\xd8/\xe9\x85\x10B\xd3\x7f\
|
||||
\xcf\xf0\x00>\x9cr\xa6\xf8\xcc\xcf\x95e\x09\xe7&\x84\
|
||||
e\xf99\x03<8!,\xcb\xcf\x1d\xe0\x95\x8a\xb0N\
|
||||
a\xf9\x83e\x96M`\xd5\x06\xf0#y\xc3\x977@\
|
||||
\x00\xceV\x9c1O\xe23?o\x80{+B;\x0f\
|
||||
l\xc1\xf2\x07K\xec\x12\xf0X\xc5\xe3\x9fI\xba\xd4\xd0\
|
||||
X<\xed' \xb6\x00U\xeb\xff\xc7\x0d\x8d\xc3\xf2\xfb\
|
||||
\x0a\xc5\xfa\x7f\xbad\xda\x5c\x02n\xc7\xd3~\xde\x00w\
|
||||
V\x04x\x1c\xcb\x1f<1K@\xd5\xfa_w\xfa\xf7\
|
||||
\xb4\xdf\x03b\x0a\xd0\xc6\xfao\xf9C\x81\xe2\x93\xbeq\
|
||||
~\x076\xe0i?o\x80;*\xc2\xdc\xcf\xfa\xe4\xdf\
|
||||
\x02\x1clQr\x15\x96_\xc1\xb4%\xa0\xa9\xf5\x7f\x83\
|
||||
\xa4\x97$\xfd$i\xf7\x8c\xbf[\x17O\xfb\xeb\x05x\
|
||||
\xaf\xe4l\xbaFq&\xc7\x9e\xf5\xbb(\xfecH\x81\
|
||||
\xcf\xfc:\x00'JB=J\x9c\xf8m\x14_\x13/\
|
||||
u \xba\x0c\xcb\xaf\x03p[E\xb0\xaf3Y\xfcf\
|
||||
\xe0\x0d\xe0\x9f\xd6\x15Wc\xf9u\x01\x9e\xab\x08\xf7\x01\
|
||||
\xaa\xe5\xef\x06Nv x\x12\x96\xdf\x04\xc0;%\xe1\
|
||||
\xfeB\xb9\xf8\xed\xc0\xa1\xae\x0cO\xc0\xf2\x9b\x02X,\
|
||||
\x09x/k\xc5\xdf\x04\xbc\x0d\x5c\xe9Lq5\x96\xdf\
|
||||
\x14\xc0\xad\x15!?M!>\x00/\x02g:\x92;\
|
||||
\x0d\xcbo\x12\x8a\xb5|\x9c\x0b\xc0V`'p\xacK\
|
||||
\xbbS\xb0\xfc\x1al\xacx\xbc\xec\xf3\xff\xef%\xed\x95\
|
||||
\xb4G\xb3\xdfP\xd2\x16\xfe\x90\xa7&\xa1\xecA\xe0\x1b\
|
||||
I;:\x1e\xcb\xacX~\x03\xfc\xaf\x00\xc0\xcd\x92\xfe\
|
||||
,\xfbY\x8f\xb0\xfc\x86(\x9b\xca\x1f\x95\xe5\xcf\x0de\
|
||||
\x05\x88\xb9\x014\x15\x96\xdf0e\x9b\xc0\xaao\x00S\
|
||||
\xf0\x97\xa4#\x92\x0eK\xfa\x5c\xd2\xb7\x96\xdf,k\xa6\
|
||||
z\xe0FI\xe7T\x5c\xb1\x93\x82\xb3*D\x8f\x84/\
|
||||
\x86\x10\x96\x12\x8de.\x18\x9f\x01\x1eQ\xb7\xf2\x7fS\
|
||||
!z$\xfd\x87\x10\x02\x1d\xbe\xff\xdc3^\x80\xb6\xd7\
|
||||
\xffSZ+\xfc\xa4\x85\xa7e\xbc\x00M\xaf\xff'\xb4\
|
||||
\x22\xfbp\x08\xe1\xd7\x86_\xdf\xd4\xe4\xbf=\x00\xb0U\
|
||||
\xc5\xfa\xbf\xa9\xc6\xeb-j\xad\xf03\xf5\x86g\xdaf\
|
||||
\xf5\x0c\xb0S\xb3\xc9_\x92\xf4\x9dV\x84\x1f\x09!\xfc\
|
||||
\xd1\xe0\xd8L\x07\xac.\xc0\xb4\xe9\xff\x9a\xa4\xaf\xb5\xb2\
|
||||
C\xff\x22\x84\xf0w[\x033\xdd\xb0\xba\x00\xe3\x1b\xc0\
|
||||
\xcb\x92\xbe\xd2\x8a\xf0/C\x08\xe7\xbb\x1a\x98\xe9\x86 \
|
||||
I\xc0&I\xa7%\x1d\xd7\xca.\xfdX\x08\xa1\xa9[\
|
||||
\xbfM\x9f\x016.\x97\xc0\x183O\xfc\x0b` \xde\
|
||||
Vl\x1c\xb1\x12\x00\x00\x00\x00IEND\xaeB`\
|
||||
\x82\
|
||||
\x00\x03\x95\x93\
|
||||
\x89\
|
||||
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
|
||||
@@ -16837,26 +16200,11 @@ qt_resource_name = b"\
|
||||
\x07\x03}\xc3\
|
||||
\x00i\
|
||||
\x00m\x00a\x00g\x00e\x00s\
|
||||
\x00\x12\
|
||||
\x01M\xb0\xa7\
|
||||
\x00t\
|
||||
\x00r\x00a\x00s\x00h\x00_\x00i\x00c\x00o\x00n\x00_\x001\x002\x008\x00.\x00p\x00n\
|
||||
\x00g\
|
||||
\x00\x16\
|
||||
\x05\xc3U\xa7\
|
||||
\x00c\
|
||||
\x00l\x00i\x00p\x00b\x00o\x00a\x00r\x00d\x00_\x00i\x00c\x00o\x00n\x00_\x001\x002\
|
||||
\x008\x00.\x00p\x00n\x00g\
|
||||
\x00\x18\
|
||||
\x07\xf3~\xc7\
|
||||
\x00s\
|
||||
\x00t\x00a\x00r\x00_\x00i\x00c\x00o\x00n\x00_\x00f\x00i\x00l\x00l\x00e\x00d\x00_\
|
||||
\x001\x002\x008\x00.\x00p\x00n\x00g\
|
||||
\x00\x11\
|
||||
\x0e9\xdd\x87\
|
||||
\x00e\
|
||||
\x00d\x00i\x00t\x00_\x00i\x00c\x00o\x00n\x00_\x001\x002\x008\x00.\x00p\x00n\x00g\
|
||||
\
|
||||
\x00\x0a\
|
||||
\x08\x94\x19\x07\
|
||||
\x00s\
|
||||
@@ -16881,24 +16229,18 @@ qt_resource_name = b"\
|
||||
qt_resource_struct = b"\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x1a\x00\x00\x00\x00\x00\x01\x00\x03\xf2%\
|
||||
\x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x03\xca\xbe\
|
||||
\x00\x00\x01\x8a\xfb\xb4\xd6\xbe\
|
||||
\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x8a\xd2\x87V\xef\
|
||||
\x00\x00\x00\xe6\x00\x00\x00\x00\x00\x01\x00\x03\xdf\x81\
|
||||
\x00\x00\x00b\x00\x00\x00\x00\x00\x01\x00\x03\xb8\x1a\
|
||||
\x00\x00\x01\x8a\xfb\xc6t\x9f\
|
||||
\x00\x00\x01N\x00\x00\x00\x00\x00\x01\x00\x04\x0c\x00\
|
||||
\x00\x00\x00\xca\x00\x00\x00\x00\x00\x01\x00\x03\xe4\x99\
|
||||
\x00\x00\x01\x8a\xfb\xb4\xc1\x95\
|
||||
\x00\x00\x00<\x00\x00\x00\x00\x00\x01\x00\x00\x0b\xa2\
|
||||
\x00\x00\x01\x8a\xd2\x8f\xdf\xf1\
|
||||
\x00\x00\x00n\x00\x00\x00\x00\x00\x01\x00\x00\x1b\x91\
|
||||
\x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x01\x8a\xfb\xc6\x86\xda\
|
||||
\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00I\xea\
|
||||
\x00\x00\x00H\x00\x00\x00\x00\x00\x01\x00\x00\x22\x83\
|
||||
\x00\x00\x01\x8e\xfd%\xc3\xc7\
|
||||
\x00\x00\x00\xa4\x00\x00\x00\x00\x00\x01\x00\x00>\x14\
|
||||
\x00\x00\x01\x8a\xd2\x83?\x9d\
|
||||
"
|
||||
|
||||
def qInitResources():
|
||||
|
||||
@@ -42,6 +42,7 @@ from PySide6.QtWidgets import (
|
||||
QFileDialog,
|
||||
QSplashScreen,
|
||||
QMenu,
|
||||
QMenuBar,
|
||||
)
|
||||
from humanfriendly import format_timespan
|
||||
|
||||
@@ -73,23 +74,20 @@ from src.core.constants import (
|
||||
from src.core.utils.web import strip_web_protocol
|
||||
from src.qt.flowlayout import FlowLayout
|
||||
from src.qt.main_window import Ui_MainWindow
|
||||
from src.qt.helpers import FunctionIterator, CustomRunnable
|
||||
from src.qt.widgets import (
|
||||
CollageIconRenderer,
|
||||
ThumbRenderer,
|
||||
PanelModal,
|
||||
ProgressWidget,
|
||||
PreviewPanel,
|
||||
ItemThumb,
|
||||
)
|
||||
from src.qt.modals import (
|
||||
BuildTagPanel,
|
||||
TagDatabasePanel,
|
||||
FileExtensionModal,
|
||||
FixUnlinkedEntriesModal,
|
||||
FixDupeFilesModal,
|
||||
FoldersToTagsModal,
|
||||
)
|
||||
from src.qt.helpers.function_iterator import FunctionIterator
|
||||
from src.qt.helpers.custom_runnable import CustomRunnable
|
||||
from src.qt.widgets.collage_icon import CollageIconRenderer
|
||||
from src.qt.widgets.panel import PanelModal
|
||||
from src.qt.widgets.thumb_renderer import ThumbRenderer
|
||||
from src.qt.widgets.progress import ProgressWidget
|
||||
from src.qt.widgets.preview_panel import PreviewPanel
|
||||
from src.qt.widgets.item_thumb import ItemThumb
|
||||
from src.qt.modals.build_tag import BuildTagPanel
|
||||
from src.qt.modals.tag_database import TagDatabasePanel
|
||||
from src.qt.modals.file_extension import FileExtensionModal
|
||||
from src.qt.modals.fix_unlinked import FixUnlinkedEntriesModal
|
||||
from src.qt.modals.fix_dupes import FixDupeFilesModal
|
||||
from src.qt.modals.folders_to_tags import FoldersToTagsModal
|
||||
import src.qt.resources_rc
|
||||
|
||||
# SIGQUIT is not defined on Windows
|
||||
@@ -193,6 +191,9 @@ class QtDriver(QObject):
|
||||
)
|
||||
|
||||
max_threads = os.cpu_count()
|
||||
if args.ci:
|
||||
# spawn only single worker in CI environment
|
||||
max_threads = 1
|
||||
for i in range(max_threads):
|
||||
# thread = threading.Thread(target=self.consumer, name=f'ThumbRenderer_{i}',args=(), daemon=True)
|
||||
# thread.start()
|
||||
@@ -232,12 +233,11 @@ class QtDriver(QObject):
|
||||
# QPalette.ColorRole.Window, QColor('#110F1B'))
|
||||
# app.setPalette(pal)
|
||||
home_path = os.path.normpath(f"{Path(__file__).parent}/ui/home.ui")
|
||||
icon_path = os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent}/resources/icon.png"
|
||||
)
|
||||
icon_path = os.path.normpath(f"{Path(__file__).parents[2]}/resources/icon.png")
|
||||
|
||||
# Handle OS signals
|
||||
self.setup_signals()
|
||||
# allow to process input from console, eg. SIGTERM
|
||||
timer = QTimer()
|
||||
timer.start(500)
|
||||
timer.timeout.connect(lambda: None)
|
||||
@@ -261,15 +261,22 @@ class QtDriver(QObject):
|
||||
|
||||
splash_pixmap = QPixmap(":/images/splash.png")
|
||||
splash_pixmap.setDevicePixelRatio(self.main_window.devicePixelRatio())
|
||||
self.splash = QSplashScreen(splash_pixmap)
|
||||
self.splash = QSplashScreen(splash_pixmap, Qt.WindowStaysOnTopHint)
|
||||
# self.splash.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||
self.splash.show()
|
||||
|
||||
menu_bar = self.main_window.menuBar()
|
||||
if os.name == "nt":
|
||||
appid = "cyanvoxel.tagstudio.9"
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid)
|
||||
|
||||
# Allow the use of the native macOS menu bar.
|
||||
if sys.platform != "darwin":
|
||||
menu_bar.setNativeMenuBar(False)
|
||||
icon = QIcon()
|
||||
icon.addFile(icon_path)
|
||||
app.setWindowIcon(icon)
|
||||
|
||||
menu_bar = QMenuBar(self.main_window)
|
||||
self.main_window.setMenuBar(menu_bar)
|
||||
menu_bar.setNativeMenuBar(True)
|
||||
|
||||
file_menu = QMenu("&File", menu_bar)
|
||||
edit_menu = QMenu("&Edit", menu_bar)
|
||||
@@ -435,29 +442,13 @@ class QtDriver(QObject):
|
||||
menu_bar.addMenu(macros_menu)
|
||||
menu_bar.addMenu(help_menu)
|
||||
|
||||
# self.main_window.setMenuBar(menu_bar)
|
||||
# self.main_window.centralWidget().layout().addWidget(menu_bar, 0,0,1,1)
|
||||
# self.main_window.tb_layout.addWidget(menu_bar)
|
||||
|
||||
icon = QIcon()
|
||||
icon.addFile(icon_path)
|
||||
self.main_window.setWindowIcon(icon)
|
||||
|
||||
self.preview_panel = PreviewPanel(self.lib, self)
|
||||
l: QHBoxLayout = self.main_window.splitter
|
||||
l.addWidget(self.preview_panel)
|
||||
# self.preview_panel.update_widgets()
|
||||
# l.setEnabled(False)
|
||||
# self.entry_panel.setWindowIcon(icon)
|
||||
|
||||
if os.name == "nt":
|
||||
appid = "cyanvoxel.tagstudio.9"
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid)
|
||||
app.setWindowIcon(icon)
|
||||
|
||||
QFontDatabase.addApplicationFont(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
f"{Path(__file__).parents[2]}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -466,7 +457,6 @@ class QtDriver(QObject):
|
||||
self.item_thumbs: list[ItemThumb] = []
|
||||
self.thumb_renderers: list[ThumbRenderer] = []
|
||||
self.collation_thumb_size = math.ceil(self.thumb_size * 2)
|
||||
# self.filtered_items: list[tuple[SearchItemType, int]] = []
|
||||
|
||||
self._init_thumb_grid()
|
||||
|
||||
@@ -474,7 +464,7 @@ class QtDriver(QObject):
|
||||
# so the resource isn't being used, then store the specific size variations
|
||||
# in a global dict for methods to access for different DPIs.
|
||||
# adj_font_size = math.floor(12 * self.main_window.devicePixelRatio())
|
||||
# self.ext_font = ImageFont.truetype(os.path.normpath(f'{Path(__file__).parent.parent.parent}/resources/qt/fonts/Oxanium-Bold.ttf'), adj_font_size)
|
||||
# self.ext_font = ImageFont.truetype(os.path.normpath(f'{Path(__file__).parents[2]}/resources/qt/fonts/Oxanium-Bold.ttf'), adj_font_size)
|
||||
|
||||
search_button: QPushButton = self.main_window.searchButton
|
||||
search_button.clicked.connect(
|
||||
@@ -538,7 +528,11 @@ class QtDriver(QObject):
|
||||
)
|
||||
self.open_library(lib)
|
||||
|
||||
app.exec_()
|
||||
if self.args.ci:
|
||||
# gracefully terminate the app in CI environment
|
||||
self.thumb_job_queue.put((self.SIGTERM.emit, []))
|
||||
|
||||
app.exec()
|
||||
|
||||
self.shutdown()
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
from .fields import FieldContainer, FieldWidget
|
||||
from .collage_icon import CollageIconRenderer
|
||||
from .thumb_button import ThumbButton
|
||||
from .thumb_renderer import ThumbRenderer
|
||||
from .panel import PanelWidget, PanelModal
|
||||
from .text_box_edit import EditTextBox
|
||||
from .text_line_edit import EditTextLine
|
||||
from .progress import ProgressWidget
|
||||
from .tag import TagWidget
|
||||
from .tag_box import TagBoxWidget
|
||||
from .text import TextWidget
|
||||
from .item_thumb import ItemThumb
|
||||
from .preview_panel import PreviewPanel
|
||||
@@ -9,6 +9,7 @@ from pathlib import Path
|
||||
|
||||
import cv2
|
||||
from PIL import Image, ImageChops, UnidentifiedImageError
|
||||
from PIL.Image import DecompressionBombError
|
||||
from PySide6.QtCore import (
|
||||
QObject,
|
||||
QThread,
|
||||
@@ -95,22 +96,25 @@ class CollageIconRenderer(QObject):
|
||||
# sys.stdout.write(f'\r{INFO} Combining [{i+1}/{len(self.lib.entries)}]: {self.get_file_color(file_type)}{entry.path}{os.sep}{entry.filename}{RESET}')
|
||||
# sys.stdout.flush()
|
||||
if file_type in IMAGE_TYPES:
|
||||
with Image.open(
|
||||
os.path.normpath(
|
||||
f"{self.lib.library_dir}/{entry.path}/{entry.filename}"
|
||||
)
|
||||
) as pic:
|
||||
if keep_aspect:
|
||||
pic.thumbnail(size)
|
||||
else:
|
||||
pic = pic.resize(size)
|
||||
if data_tint_mode and color:
|
||||
pic = pic.convert(mode="RGB")
|
||||
pic = ImageChops.hard_light(
|
||||
pic, Image.new("RGB", size, color)
|
||||
try:
|
||||
with Image.open(
|
||||
os.path.normpath(
|
||||
f"{self.lib.library_dir}/{entry.path}/{entry.filename}"
|
||||
)
|
||||
# collage.paste(pic, (y*thumb_size, x*thumb_size))
|
||||
self.rendered.emit(pic)
|
||||
) as pic:
|
||||
if keep_aspect:
|
||||
pic.thumbnail(size)
|
||||
else:
|
||||
pic = pic.resize(size)
|
||||
if data_tint_mode and color:
|
||||
pic = pic.convert(mode="RGB")
|
||||
pic = ImageChops.hard_light(
|
||||
pic, Image.new("RGB", size, color)
|
||||
)
|
||||
# collage.paste(pic, (y*thumb_size, x*thumb_size))
|
||||
self.rendered.emit(pic)
|
||||
except DecompressionBombError as e:
|
||||
logging.info(f"[ERROR] One of the images was too big ({e})")
|
||||
elif file_type in VIDEO_TYPES:
|
||||
video = cv2.VideoCapture(filepath)
|
||||
video.set(
|
||||
@@ -142,7 +146,7 @@ class CollageIconRenderer(QObject):
|
||||
)
|
||||
with Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent}/resources/qt/images/thumb_broken_512.png"
|
||||
f"{Path(__file__).parents[2]}/resources/qt/images/thumb_broken_512.png"
|
||||
)
|
||||
) as pic:
|
||||
pic.thumbnail(size)
|
||||
|
||||
@@ -16,24 +16,24 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushBu
|
||||
|
||||
|
||||
class FieldContainer(QWidget):
|
||||
# TODO: reference a resources folder rather than path.parent.parent.parent.parent?
|
||||
# TODO: reference a resources folder rather than path.parents[3]?
|
||||
clipboard_icon_128: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/clipboard_icon_128.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/clipboard_icon_128.png"
|
||||
)
|
||||
).resize((math.floor(24 * 1.25), math.floor(24 * 1.25)))
|
||||
clipboard_icon_128.load()
|
||||
|
||||
edit_icon_128: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/edit_icon_128.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/edit_icon_128.png"
|
||||
)
|
||||
).resize((math.floor(24 * 1.25), math.floor(24 * 1.25)))
|
||||
edit_icon_128.load()
|
||||
|
||||
trash_icon_128: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/trash_icon_128.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/trash_icon_128.png"
|
||||
)
|
||||
).resize((math.floor(24 * 1.25), math.floor(24 * 1.25)))
|
||||
trash_icon_128.load()
|
||||
|
||||
@@ -27,11 +27,12 @@ from PySide6.QtWidgets import (
|
||||
from src.core.library import ItemType, Library, Entry
|
||||
from src.core.constants import AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.qt.flowlayout import FlowWidget
|
||||
from src.qt.helpers import FileOpenerHelper
|
||||
from src.qt.widgets import ThumbRenderer, ThumbButton
|
||||
from src.qt.helpers.file_opener import FileOpenerHelper
|
||||
from src.qt.widgets.thumb_renderer import ThumbRenderer
|
||||
from src.qt.widgets.thumb_button import ThumbButton
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from src.qt.widgets import PreviewPanel
|
||||
from src.qt.widgets.preview_panel import PreviewPanel
|
||||
|
||||
ERROR = f"[ERROR]"
|
||||
WARNING = f"[WARNING]"
|
||||
@@ -53,14 +54,14 @@ class ItemThumb(FlowWidget):
|
||||
|
||||
collation_icon_128: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/collation_icon_128.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/collation_icon_128.png"
|
||||
)
|
||||
)
|
||||
collation_icon_128.load()
|
||||
|
||||
tag_group_icon_128: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/tag_group_icon_128.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/tag_group_icon_128.png"
|
||||
)
|
||||
)
|
||||
tag_group_icon_128.load()
|
||||
|
||||
@@ -11,6 +11,7 @@ from datetime import datetime as dt
|
||||
|
||||
import cv2
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
from PIL.Image import DecompressionBombError
|
||||
from PySide6.QtCore import Signal, Qt, QSize
|
||||
from PySide6.QtGui import QResizeEvent, QAction
|
||||
from PySide6.QtWidgets import (
|
||||
@@ -29,18 +30,16 @@ from humanfriendly import format_size
|
||||
|
||||
from src.core.library import Entry, ItemType, Library
|
||||
from src.core.constants import VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.qt.helpers import FileOpenerLabel, FileOpenerHelper, open_file
|
||||
from src.qt.modals import AddFieldModal
|
||||
from src.qt.widgets import (
|
||||
ThumbRenderer,
|
||||
FieldContainer,
|
||||
TagBoxWidget,
|
||||
TextWidget,
|
||||
PanelModal,
|
||||
EditTextBox,
|
||||
EditTextLine,
|
||||
ItemThumb,
|
||||
)
|
||||
from src.qt.helpers.file_opener import FileOpenerLabel, FileOpenerHelper, open_file
|
||||
from src.qt.modals.add_field import AddFieldModal
|
||||
from src.qt.widgets.thumb_renderer import ThumbRenderer
|
||||
from src.qt.widgets.fields import FieldContainer
|
||||
from src.qt.widgets.tag_box import TagBoxWidget
|
||||
from src.qt.widgets.text import TextWidget
|
||||
from src.qt.widgets.panel import PanelModal
|
||||
from src.qt.widgets.text_box_edit import EditTextBox
|
||||
from src.qt.widgets.text_line_edit import EditTextLine
|
||||
from src.qt.widgets.item_thumb import ItemThumb
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -405,8 +404,15 @@ class PreviewPanel(QWidget):
|
||||
)
|
||||
raise UnidentifiedImageError
|
||||
|
||||
except (UnidentifiedImageError, FileNotFoundError, cv2.error):
|
||||
pass
|
||||
except (
|
||||
UnidentifiedImageError,
|
||||
FileNotFoundError,
|
||||
cv2.error,
|
||||
DecompressionBombError,
|
||||
) as e:
|
||||
logging.info(
|
||||
f"[PreviewPanel][ERROR] Couldn't Render thumbnail for {filepath} (because of {e})"
|
||||
)
|
||||
|
||||
try:
|
||||
self.preview_img.clicked.disconnect()
|
||||
|
||||
@@ -25,7 +25,7 @@ INFO = f"[INFO]"
|
||||
class TagWidget(QWidget):
|
||||
edit_icon_128: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/edit_icon_128.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/edit_icon_128.png"
|
||||
)
|
||||
).resize((math.floor(14 * 1.25), math.floor(14 * 1.25)))
|
||||
edit_icon_128.load()
|
||||
|
||||
@@ -12,8 +12,11 @@ from PySide6.QtWidgets import QPushButton
|
||||
|
||||
from src.core.library import Library, Tag
|
||||
from src.qt.flowlayout import FlowLayout
|
||||
from src.qt.widgets import FieldWidget, TagWidget, PanelModal
|
||||
from src.qt.modals import BuildTagPanel, TagSearchPanel
|
||||
from src.qt.widgets.fields import FieldWidget
|
||||
from src.qt.widgets.tag import TagWidget
|
||||
from src.qt.widgets.panel import PanelModal
|
||||
from src.qt.modals.build_tag import BuildTagPanel
|
||||
from src.qt.modals.tag_search import TagSearchPanel
|
||||
|
||||
# Only import for type checking/autocompletion, will not be imported at runtime.
|
||||
if typing.TYPE_CHECKING:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import QHBoxLayout, QLabel
|
||||
from src.qt.widgets import FieldWidget
|
||||
from src.qt.widgets.fields import FieldWidget
|
||||
|
||||
|
||||
class TextWidget(FieldWidget):
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
from PySide6.QtWidgets import QVBoxLayout, QPlainTextEdit
|
||||
|
||||
from src.qt.widgets import PanelWidget
|
||||
from src.qt.widgets.panel import PanelWidget
|
||||
|
||||
|
||||
class EditTextBox(PanelWidget):
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Callable
|
||||
|
||||
from PySide6.QtWidgets import QVBoxLayout, QLineEdit
|
||||
|
||||
from src.qt.widgets import PanelWidget
|
||||
from src.qt.widgets.panel import PanelWidget
|
||||
|
||||
|
||||
class EditTextLine(PanelWidget):
|
||||
|
||||
@@ -21,6 +21,7 @@ from PIL import (
|
||||
ImageOps,
|
||||
ImageFile,
|
||||
)
|
||||
from PIL.Image import DecompressionBombError
|
||||
from PySide6.QtCore import QObject, Signal, QSize
|
||||
from PySide6.QtGui import QPixmap
|
||||
from src.core.constants import PLAINTEXT_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
@@ -44,48 +45,48 @@ class ThumbRenderer(QObject):
|
||||
|
||||
thumb_mask_512: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/thumb_mask_512.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/thumb_mask_512.png"
|
||||
)
|
||||
)
|
||||
thumb_mask_512.load()
|
||||
|
||||
thumb_mask_hl_512: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/thumb_mask_hl_512.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/thumb_mask_hl_512.png"
|
||||
)
|
||||
)
|
||||
thumb_mask_hl_512.load()
|
||||
|
||||
thumb_loading_512: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/thumb_loading_512.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/thumb_loading_512.png"
|
||||
)
|
||||
)
|
||||
thumb_loading_512.load()
|
||||
|
||||
thumb_broken_512: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/thumb_broken_512.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/thumb_broken_512.png"
|
||||
)
|
||||
)
|
||||
thumb_broken_512.load()
|
||||
|
||||
thumb_file_default_512: Image.Image = Image.open(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/images/thumb_file_default_512.png"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/images/thumb_file_default_512.png"
|
||||
)
|
||||
)
|
||||
thumb_file_default_512.load()
|
||||
|
||||
# thumb_debug: Image.Image = Image.open(os.path.normpath(
|
||||
# f'{Path(__file__).parent.parent.parent}/resources/qt/images/temp.jpg'))
|
||||
# f'{Path(__file__).parents[2]}/resources/qt/images/temp.jpg'))
|
||||
# thumb_debug.load()
|
||||
|
||||
# TODO: Make dynamic font sized given different pixel ratios
|
||||
font_pixel_ratio: float = 1
|
||||
ext_font = ImageFont.truetype(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
),
|
||||
math.floor(12 * font_pixel_ratio),
|
||||
)
|
||||
@@ -110,7 +111,7 @@ class ThumbRenderer(QObject):
|
||||
ThumbRenderer.font_pixel_ratio = pixelRatio
|
||||
ThumbRenderer.ext_font = ImageFont.truetype(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
),
|
||||
math.floor(12 * ThumbRenderer.font_pixel_ratio),
|
||||
)
|
||||
@@ -138,17 +139,22 @@ class ThumbRenderer(QObject):
|
||||
try:
|
||||
# Images =======================================================
|
||||
if extension in IMAGE_TYPES:
|
||||
image = Image.open(filepath)
|
||||
# image = self.thumb_debug
|
||||
if image.mode == "RGBA":
|
||||
# logging.info(image.getchannel(3).tobytes())
|
||||
new_bg = Image.new("RGB", image.size, color="#1e1e1e")
|
||||
new_bg.paste(image, mask=image.getchannel(3))
|
||||
image = new_bg
|
||||
if image.mode != "RGB":
|
||||
image = image.convert(mode="RGB")
|
||||
try:
|
||||
image = Image.open(filepath)
|
||||
# image = self.thumb_debug
|
||||
if image.mode == "RGBA":
|
||||
# logging.info(image.getchannel(3).tobytes())
|
||||
new_bg = Image.new("RGB", image.size, color="#1e1e1e")
|
||||
new_bg.paste(image, mask=image.getchannel(3))
|
||||
image = new_bg
|
||||
if image.mode != "RGB":
|
||||
image = image.convert(mode="RGB")
|
||||
|
||||
image = ImageOps.exif_transpose(image)
|
||||
image = ImageOps.exif_transpose(image)
|
||||
except DecompressionBombError as e:
|
||||
logging.info(
|
||||
f"[ThumbRenderer][ERROR] Couldn't Render thumbnail for {filepath} (because of {e})"
|
||||
)
|
||||
|
||||
# Videos =======================================================
|
||||
elif extension in VIDEO_TYPES:
|
||||
@@ -292,7 +298,7 @@ class ThumbRenderer(QObject):
|
||||
ThumbRenderer.font_pixel_ratio = pixelRatio
|
||||
ThumbRenderer.ext_font = ImageFont.truetype(
|
||||
os.path.normpath(
|
||||
f"{Path(__file__).parent.parent.parent.parent}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
f"{Path(__file__).parents[3]}/resources/qt/fonts/Oxanium-Bold.ttf"
|
||||
),
|
||||
math.floor(12 * ThumbRenderer.font_pixel_ratio),
|
||||
)
|
||||
@@ -321,17 +327,22 @@ class ThumbRenderer(QObject):
|
||||
try:
|
||||
# Images =======================================================
|
||||
if extension in IMAGE_TYPES:
|
||||
image = Image.open(filepath)
|
||||
# image = self.thumb_debug
|
||||
if image.mode == "RGBA":
|
||||
# logging.info(image.getchannel(3).tobytes())
|
||||
new_bg = Image.new("RGB", image.size, color="#1e1e1e")
|
||||
new_bg.paste(image, mask=image.getchannel(3))
|
||||
image = new_bg
|
||||
if image.mode != "RGB":
|
||||
image = image.convert(mode="RGB")
|
||||
try:
|
||||
image = Image.open(filepath)
|
||||
# image = self.thumb_debug
|
||||
if image.mode == "RGBA":
|
||||
# logging.info(image.getchannel(3).tobytes())
|
||||
new_bg = Image.new("RGB", image.size, color="#1e1e1e")
|
||||
new_bg.paste(image, mask=image.getchannel(3))
|
||||
image = new_bg
|
||||
if image.mode != "RGB":
|
||||
image = image.convert(mode="RGB")
|
||||
|
||||
image = ImageOps.exif_transpose(image)
|
||||
image = ImageOps.exif_transpose(image)
|
||||
except DecompressionBombError as e:
|
||||
logging.info(
|
||||
f"[ThumbRenderer][ERROR] Couldn't Render thumbnail for {filepath} (because of {e})"
|
||||
)
|
||||
|
||||
# Videos =======================================================
|
||||
elif extension in VIDEO_TYPES:
|
||||
|
||||
@@ -45,6 +45,11 @@ def main():
|
||||
type=str,
|
||||
help="User interface option for TagStudio. Options: qt, cli (Default: qt)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ci",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Exit the application after checking it starts without any problem. Meant for CI check.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
core = TagStudioCore() # The TagStudio Core instance. UI agnostic.
|
||||
|
||||
Reference in New Issue
Block a user