Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added thumbnail rendering for PDF-files #378

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
34f347b
Fix text and RAW image handling
CyanVoxel Jun 1, 2024
3c27b37
Use chardet for character encoding detection
CyanVoxel Jun 1, 2024
7ce3519
Add support for waveform + album cover thumbnails
CyanVoxel Jun 4, 2024
6b892ce
Rename "cover" variables for MyPy
CyanVoxel Jun 4, 2024
ff17b93
Rename "audio_tags" variables for MyPy + typing
CyanVoxel Jun 4, 2024
c1cd96f
Add # type: ignore to fromstring method
CyanVoxel Jun 4, 2024
3144440
Add GIF preview support
CyanVoxel Jun 4, 2024
d339f86
Add rough check for invalid video codecs
CyanVoxel Jun 8, 2024
dc135f7
Add ".plist" to PLAINTEXT_TYPES
CyanVoxel Jun 8, 2024
10d81b3
Add readable video tester
CyanVoxel Jun 9, 2024
087176e
Add ".psd" to IMAGE_TYPES; Handle ID3NoHeaderError
CyanVoxel Jun 13, 2024
cee4254
Improve and style waveform previews
CyanVoxel Jun 15, 2024
127fed7
Add final return statement to _album_artwork()
CyanVoxel Jun 15, 2024
32257f6
Add final return statement to _audio_waveform()
CyanVoxel Jun 15, 2024
3e00a77
Tweak waveform color and size
CyanVoxel Jun 15, 2024
1529714
Fix ItemThumb label text color in light mode
CyanVoxel Jun 15, 2024
d2b5e31
Fix most theme UI legibility issues
CyanVoxel Jun 15, 2024
05a4860
Match additional UI to color scheme
CyanVoxel Jun 16, 2024
c582f3d
ruff format
CyanVoxel Jul 19, 2024
c0e56dc
feat(ui): add UI color palette dict
CyanVoxel Jul 19, 2024
598aa4f
feat(ui) center and color small font previews
CyanVoxel Jul 19, 2024
ffdfd6c
fix(ui): large font previews follow app theme
CyanVoxel Jul 20, 2024
3bfeb3c
fix(ui): blender previews follow app theme
CyanVoxel Jul 20, 2024
086fc1e
feat(ui): add resizable thumbnail options
CyanVoxel Jul 20, 2024
ef8cc6c
fix: mkv files with "[0][0][0][0]" codec load properly
CyanVoxel Jul 20, 2024
ad53f10
fix: missing audio files properly handled
CyanVoxel Jul 20, 2024
91ee242
feat(ui): use system accent color for thumb selections
CyanVoxel Jul 20, 2024
196c1ba
fix(ui): hide gif preview in multi-selections
CyanVoxel Jul 20, 2024
3932414
feat(ui): add dynamic file thumb icons
CyanVoxel Jul 21, 2024
c6a5202
fix(ui): hide previous thumbnail before resizing
CyanVoxel Jul 22, 2024
ad12d64
(fix): catch ffmpeg errors in file tester
CyanVoxel Jul 25, 2024
8d2e67d
Squashed commit of the following:
CyanVoxel Jul 25, 2024
6883f9e
feat(ui): add media types and icon resources
CyanVoxel Jul 25, 2024
447b5e6
feat(ui): add more default media types and icons
CyanVoxel Aug 21, 2024
c070f84
fix: remove leading dot in preview panel ext
CyanVoxel Aug 21, 2024
a244098
refactor: remove edge from `four_corner_gradient()`
CyanVoxel Aug 21, 2024
f91861d
fix: handle missing files in `resource_manager`
CyanVoxel Aug 21, 2024
e4f7055
fix(ui): thumb edges fading on refresh
CyanVoxel Aug 21, 2024
387baae
Merge branch 'Alpha-v9.4' into thumbnails
CyanVoxel Aug 21, 2024
81dfb50
feat(ui): add default icons for audio+vector thumbs
CyanVoxel Aug 21, 2024
a658fc4
feat(ui): apply edge to default icon thumbs
CyanVoxel Aug 21, 2024
ccf3d78
chore: remove unused code
CyanVoxel Aug 21, 2024
148f792
refactor(ui): move loading icon to `ResourceManager`
CyanVoxel Aug 21, 2024
7efde5c
Added thumbnail rendering for PDF-files
Heiholf Aug 23, 2024
c699ca1
Fixed MyPy errors in PDF thumbnail renderer
Heiholf Aug 23, 2024
afdfe61
Fixed suggestions by eivl
Heiholf Sep 10, 2024
9b72f23
Merge branch 'Alpha-v9.4' into thumbnails
CyanVoxel Sep 15, 2024
528f018
chore: fix missing import
CyanVoxel Sep 15, 2024
c0e0bf9
fix: reposition `type: ignore` for import
CyanVoxel Sep 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ numpy==1.26.4
rawpy==0.21.0
pillow-heif==0.16.0
chardet==5.2.0
pydub==0.25.1
mutagen==1.47.0
numpy==1.26.4
ffmpeg-python==0.2.0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tagstudio/resources/qt/images/file_icons/font.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tagstudio/resources/qt/images/file_icons/text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed tagstudio/resources/qt/images/thumb_border_512.png
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_broken_512.png
Binary file not shown.
Binary file not shown.
Binary file added tagstudio/resources/qt/images/thumb_loading.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed tagstudio/resources/qt/images/thumb_loading_512.png
Binary file not shown.
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_mask_128.png
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_mask_512.png
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_mask_hl_512.png
Diff not rendered.
1 change: 0 additions & 1 deletion tagstudio/src/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,5 @@
"cool gray",
"olive",
]

TAG_FAVORITE = 1
TAG_ARCHIVED = 0
4 changes: 3 additions & 1 deletion tagstudio/src/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class SettingItems(str, enum.Enum):


class Theme(str, enum.Enum):
COLOR_BG = "#65000000"
COLOR_BG_DARK = "#65000000"
COLOR_BG_LIGHT = "#22000000"
COLOR_DARK_LABEL = "#DD000000"
COLOR_HOVER = "#65AAAAAA"
COLOR_PRESSED = "#65EEEEEE"
COLOR_DISABLED = "#65F39CAA"
Expand Down
60 changes: 49 additions & 11 deletions tagstudio/src/core/media_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
class MediaType(str, Enum):
"""Names of media types."""

ADOBE_PHOTOSHOP: str = "adobe_photoshop"
AFFINITY_PHOTO: str = "affinity_photo"
ARCHIVE: str = "archive"
AUDIO_MIDI: str = "audio_midi"
AUDIO: str = "audio"
BLENDER: str = "blender"
DATABASE: str = "database"
Expand All @@ -27,7 +30,7 @@ class MediaType(str, Enum):
MATERIAL: str = "material"
MODEL: str = "model"
PACKAGE: str = "package"
PHOTOSHOP: str = "photoshop"
PDF: str = "pdf"
PLAINTEXT: str = "plaintext"
PRESENTATION: str = "presentation"
PROGRAM: str = "program"
Expand Down Expand Up @@ -67,6 +70,12 @@ class MediaCategories:
# These sets are used either individually or together to form the final sets
# for the MediaCategory(s).
# These sets may be combined and are NOT 1:1 with the final categories.
_ADOBE_PHOTOSHOP_SET: set[str] = {
".pdd",
".psb",
".psd",
}
_AFFINITY_PHOTO_SET: set[str] = {".afphoto"}
_ARCHIVE_SET: set[str] = {
".7z",
".gz",
Expand All @@ -76,6 +85,10 @@ class MediaCategories:
".tgz",
".zip",
}
_AUDIO_MIDI_SET: set[str] = {
".mid",
".midi",
}
_AUDIO_SET: set[str] = {
".aac",
".aif",
Expand Down Expand Up @@ -182,6 +195,7 @@ class MediaCategories:
".jpg_large",
".jpg",
".jpg2",
".jxl",
".png",
".psb",
".psd",
Expand All @@ -192,11 +206,17 @@ class MediaCategories:
_INSTALLER_SET: set[str] = {".appx", ".msi", ".msix"}
_MATERIAL_SET: set[str] = {".mtl"}
_MODEL_SET: set[str] = {".3ds", ".fbx", ".obj", ".stl"}
_PACKAGE_SET: set[str] = {".pkg"}
_PHOTOSHOP_SET: set[str] = {
".pdd",
".psb",
".psd",
_PACKAGE_SET: set[str] = {
".aab",
".akp",
".apk",
".apkm",
".apks",
".pkg",
".xapk",
}
_PDF_SET: set[str] = {
".pdf",
}
_PLAINTEXT_SET: set[str] = {
".bat",
Expand Down Expand Up @@ -247,14 +267,29 @@ class MediaCategories:
".wmv",
}

ADOBE_PHOTOSHOP_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.ADOBE_PHOTOSHOP,
extensions=_ADOBE_PHOTOSHOP_SET,
is_iana=False,
)
AFFINITY_PHOTO_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AFFINITY_PHOTO,
extensions=_AFFINITY_PHOTO_SET,
is_iana=False,
)
ARCHIVE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.ARCHIVE,
extensions=_ARCHIVE_SET,
is_iana=False,
)
AUDIO_MIDI_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AUDIO_MIDI,
extensions=_AUDIO_MIDI_SET,
is_iana=False,
)
AUDIO_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AUDIO,
extensions=_AUDIO_SET,
extensions=_AUDIO_SET | _AUDIO_MIDI_SET,
is_iana=True,
)
BLENDER_TYPES: MediaCategory = MediaCategory(
Expand Down Expand Up @@ -317,9 +352,9 @@ class MediaCategories:
extensions=_PACKAGE_SET,
is_iana=False,
)
PHOTOSHOP_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PHOTOSHOP,
extensions=_PHOTOSHOP_SET,
PDF_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PDF,
extensions=_PDF_SET,
is_iana=False,
)
PLAINTEXT_TYPES: MediaCategory = MediaCategory(
Expand Down Expand Up @@ -359,7 +394,10 @@ class MediaCategories:
)

ALL_CATEGORIES: list[MediaCategory] = [
ADOBE_PHOTOSHOP_TYPES,
AFFINITY_PHOTO_TYPES,
ARCHIVE_TYPES,
AUDIO_MIDI_TYPES,
AUDIO_TYPES,
BLENDER_TYPES,
DATABASE_TYPES,
Expand All @@ -373,7 +411,7 @@ class MediaCategories:
MATERIAL_TYPES,
MODEL_TYPES,
PACKAGE_TYPES,
PHOTOSHOP_TYPES,
PDF_TYPES,
PLAINTEXT_TYPES,
PRESENTATION_TYPES,
PROGRAM_TYPES,
Expand Down
35 changes: 34 additions & 1 deletion tagstudio/src/core/palette.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ColorType(int, Enum):
DARK_ACCENT = 4


_TAG_COLORS = {
_TAG_COLORS: dict = {
"": {
ColorType.PRIMARY: "#1e1e1e",
ColorType.TEXT: ColorType.LIGHT_ACCENT,
Expand Down Expand Up @@ -277,6 +277,33 @@ class ColorType(int, Enum):
},
}

_UI_COLORS: dict = {
"": {
ColorType.PRIMARY: "#333333",
ColorType.BORDER: "#555555",
ColorType.LIGHT_ACCENT: "#FFFFFF",
ColorType.DARK_ACCENT: "#1e1e1e",
},
"red": {
ColorType.PRIMARY: "#e22c3c",
ColorType.BORDER: "#e54252",
ColorType.LIGHT_ACCENT: "#f39caa",
ColorType.DARK_ACCENT: "#440d12",
},
"green": {
ColorType.PRIMARY: "#28bb48",
ColorType.BORDER: "#43c568",
ColorType.LIGHT_ACCENT: "#DDFFCC",
ColorType.DARK_ACCENT: "#0d3828",
},
"purple": {
ColorType.PRIMARY: "#C76FF3",
ColorType.BORDER: "#c364f2",
ColorType.LIGHT_ACCENT: "#EFD4FB",
ColorType.DARK_ACCENT: "#3E1555",
},
}


def get_tag_color(type, color):
color = color.lower()
Expand All @@ -287,3 +314,9 @@ def get_tag_color(type, color):
return _TAG_COLORS[color][type]
except KeyError:
return "#FF00FF"


def get_ui_color(type: ColorType, color: str):
"""Returns a hex value given a color name and ColorType."""
color = color.lower()
return _UI_COLORS.get(color).get(type)
13 changes: 9 additions & 4 deletions tagstudio/src/qt/helpers/color_overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,28 @@

# TODO: Consolidate the built-in QT theme values with the values
# here, in enums.py, and in palette.py.
_THEME_DARK_FG: str = "#FFFFFF55"
_THEME_DARK_FG: str = "#FFFFFF77"
_THEME_LIGHT_FG: str = "#000000DD"
_THEME_DARK_BG: str = "#000000DD"
_THEME_LIGHT_BG: str = "#FFFFFF55"


def theme_fg_overlay(image: Image.Image) -> Image.Image:
def theme_fg_overlay(image: Image.Image, use_alpha: bool = True) -> Image.Image:
"""
Overlay the foreground theme color onto an image.

Args:
image (Image): The PIL Image object to apply an overlay to.
"""
dark_fg: str = _THEME_DARK_FG[:-2] if not use_alpha else _THEME_DARK_FG
light_fg: str = _THEME_LIGHT_FG[:-2] if not use_alpha else _THEME_LIGHT_FG

overlay_color = (
_THEME_DARK_FG
dark_fg
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
else _THEME_LIGHT_FG
else light_fg
)

im = Image.new(mode="RGBA", size=image.size, color=overlay_color)
return _apply_overlay(image, im)

Expand Down
29 changes: 29 additions & 0 deletions tagstudio/src/qt/helpers/file_tester.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio


import ffmpeg
from pathlib import Path


def is_readable_video(filepath: Path | str):
"""Test if a video is in a readable format. Examples of unreadable videos
include files with undetermined codecs and DRM-protected content.

Args:
filepath (Path | str):
"""
try:
probe = ffmpeg.probe(Path(filepath))
for stream in probe["streams"]:
# DRM check
if stream.get("codec_tag_string") in [
"drma",
"drms",
"drmi",
]:
return False
except ffmpeg.Error:
return False
return True
41 changes: 23 additions & 18 deletions tagstudio/src/qt/helpers/gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio

from PIL import Image, ImageEnhance, ImageChops
from PIL import Image


def four_corner_gradient_background(
image: Image.Image, adj_size, mask, hl
def four_corner_gradient(
image: Image.Image, size: tuple[int, int], mask: Image.Image
) -> Image.Image:
if image.size != (adj_size, adj_size):
if image.size != size:
# Old 1 color method.
# bg_col = image.copy().resize((1, 1)).getpixel((0,0))
# bg = Image.new(mode='RGB',size=(adj_size,adj_size),color=bg_col)
# bg = Image.new(mode='RGB',size=size,color=bg_col)
# bg.thumbnail((1, 1))
# bg = bg.resize((adj_size,adj_size), resample=Image.Resampling.NEAREST)
# bg = bg.resize(size, resample=Image.Resampling.NEAREST)

# Small gradient background. Looks decent, and is only a one-liner.
# bg = image.copy().resize((2, 2), resample=Image.Resampling.BILINEAR).resize((adj_size,adj_size),resample=Image.Resampling.BILINEAR)
# bg = image.copy().resize((2, 2), resample=Image.Resampling.BILINEAR).resize(size,resample=Image.Resampling.BILINEAR)

# Four-Corner Gradient Background.
# Not exactly a one-liner, but it's (subjectively) really cool.
Expand All @@ -29,26 +29,31 @@ def four_corner_gradient_background(
bg.paste(tr, (1, 0, 2, 2))
bg.paste(bl, (0, 1, 2, 2))
bg.paste(br, (1, 1, 2, 2))
bg = bg.resize((adj_size, adj_size), resample=Image.Resampling.BICUBIC)

bg = bg.resize(size, resample=Image.Resampling.BICUBIC)
bg.paste(
image,
box=(
(adj_size - image.size[0]) // 2,
(adj_size - image.size[1]) // 2,
(size[0] - image.size[0]) // 2,
(size[1] - image.size[1]) // 2,
),
)

bg.putalpha(mask)
final = bg
final = Image.new("RGBA", bg.size, (0, 0, 0, 0))
final.paste(bg, mask=mask.getchannel(0))

# bg.putalpha(mask)
# final = bg

else:
image.putalpha(mask)
final = image
# image.putalpha(mask)
# final = image

final = Image.new("RGBA", size, (0, 0, 0, 0))
final.paste(image, mask=mask.getchannel(0))

if final.mode != "RGBA":
final = final.convert("RGBA")

hl_soft = hl.copy()
hl_soft.putalpha(ImageEnhance.Brightness(hl.getchannel(3)).enhance(0.5))
final.paste(ImageChops.soft_light(final, hl_soft), mask=hl_soft.getchannel(3))
return final


Expand Down
31 changes: 31 additions & 0 deletions tagstudio/src/qt/helpers/rounded_pixmap_style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Based on the implementation by eyllanesc:
# https://stackoverflow.com/questions/54230005/qmovie-with-border-radius
# Licensed under the Creative Commons CC BY-SA 4.0 License:
# https://creativecommons.org/licenses/by-sa/4.0/
# Modified for TagStudio: https://github.com/CyanVoxel/TagStudio

from PySide6.QtGui import QPixmap, QPainter, QBrush
from PySide6.QtWidgets import (
QProxyStyle,
)


class RoundedPixmapStyle(QProxyStyle):
def __init__(self, radius=8):
super().__init__()
self._radius = radius

def drawItemPixmap(self, painter, rectangle, alignment, pixmap):
painter.save()
pix = QPixmap(pixmap.size())
pix.fill("#00000000")
p = QPainter(pix)
p.setBrush(QBrush(pixmap))
p.setPen("#00000000")
p.setRenderHint(QPainter.RenderHint.Antialiasing)
p.drawRoundedRect(pixmap.rect(), self._radius, self._radius)
p.end()
super(RoundedPixmapStyle, self).drawItemPixmap(
painter, rectangle, alignment, pix
)
painter.restore()
Loading