Plaintext Thumbs (Proof of Concept)

**BASIC** support for rendering thumbnails for certain plaintext types (txt, md, html, etc.) using PIL.
Notable issues include:
- Long draw times (entire files are read)
- No text wrapping
- Hardcoded style
- Blurry text in preview pane images
- No cached thumbnails (I call dibs on the thumbnail caching system)
This commit is contained in:
Travis Abendshien
2024-04-26 17:33:02 -07:00
parent 039c574cf4
commit c789d09b07
3 changed files with 33 additions and 32 deletions

View File

@@ -191,7 +191,7 @@ class CliDriver:
return WHITE_FG
elif ext.lower().replace('.','',1) in VIDEO_TYPES:
return BRIGHT_CYAN_FG
elif ext.lower().replace('.','',1) in TEXT_TYPES:
elif ext.lower().replace('.','',1) in DOC_TYPES:
return BRIGHT_GREEN_FG
else:
return BRIGHT_WHITE_FG

View File

@@ -18,6 +18,7 @@ BACKUP_FOLDER_NAME: str = 'backups'
COLLAGE_FOLDER_NAME: str = 'collages'
LIBRARY_FILENAME: str = 'ts_library.json'
# TODO: Turn this whitelist into a user-configurable blacklist.
IMAGE_TYPES: list[str] = ['png', 'jpg', 'jpeg', 'jpg_large', 'jpeg_large',
'jfif', 'gif', 'tif', 'tiff', 'heic', 'heif', 'webp',
'bmp', 'svg', 'avif', 'apng', 'jp2', 'j2k', 'jpg2']
@@ -25,8 +26,9 @@ VIDEO_TYPES: list[str] = ['mp4', 'webm', 'mov', 'hevc', 'mkv', 'avi', 'wmv',
'flv', 'gifv', 'm4p', 'm4v', '3gp']
AUDIO_TYPES: list[str] = ['mp3', 'mp4', 'mpeg4', 'm4a', 'aac', 'wav', 'flac',
'alac', 'wma', 'ogg', 'aiff']
TEXT_TYPES: list[str] = ['txt', 'rtf', 'md',
DOC_TYPES: list[str] = ['txt', 'rtf', 'md',
'doc', 'docx', 'pdf', 'tex', 'odt', 'pages']
PLAINTEXT_TYPES: list[str] = ['txt', 'md', 'css', 'html', 'xml', 'json', 'js', 'ts']
SPREADSHEET_TYPES: list[str] = ['csv', 'xls', 'xlsx', 'numbers', 'ods']
PRESENTATION_TYPES: list[str] = ['ppt', 'pptx', 'key', 'odp']
ARCHIVE_TYPES: list[str] = ['zip', 'rar', 'tar', 'tar.gz', 'tgz', '7z']
@@ -34,7 +36,7 @@ PROGRAM_TYPES: list[str] = ['exe', 'app']
SHORTCUT_TYPES: list[str] = ['lnk', 'desktop', 'url']
ALL_FILE_TYPES: list[str] = IMAGE_TYPES + VIDEO_TYPES + AUDIO_TYPES + \
TEXT_TYPES + SPREADSHEET_TYPES + PRESENTATION_TYPES + \
DOC_TYPES + SPREADSHEET_TYPES + PRESENTATION_TYPES + \
ARCHIVE_TYPES + PROGRAM_TYPES + SHORTCUT_TYPES
BOX_FIELDS = ['tag_box', 'text_box']

View File

@@ -38,9 +38,9 @@ from humanfriendly import format_timespan, format_size
from src.core.library import Collation, Entry, ItemType, Library, Tag
from src.core.palette import ColorType, get_tag_color
from src.core.ts_core import (TagStudioCore, TAG_COLORS, DATE_FIELDS, TEXT_FIELDS, BOX_FIELDS, ALL_FILE_TYPES,
from src.core.ts_core import (PLAINTEXT_TYPES, TagStudioCore, TAG_COLORS, DATE_FIELDS, TEXT_FIELDS, BOX_FIELDS, ALL_FILE_TYPES,
SHORTCUT_TYPES, PROGRAM_TYPES, ARCHIVE_TYPES, PRESENTATION_TYPES,
SPREADSHEET_TYPES, TEXT_TYPES, AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES,
SPREADSHEET_TYPES, DOC_TYPES, AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES,
LIBRARY_FILENAME, COLLAGE_FOLDER_NAME, BACKUP_FOLDER_NAME, TS_FOLDER_NAME,
VERSION_BRANCH, VERSION)
from src.core.utils.web import strip_web_protocol
@@ -3321,7 +3321,7 @@ class CollageIconRenderer(QObject):
return '\033[37m'
elif ext.lower().replace('.','',1) in VIDEO_TYPES:
return '\033[96m'
elif ext.lower().replace('.','',1) in TEXT_TYPES:
elif ext.lower().replace('.','',1) in DOC_TYPES:
return '\033[92m'
else:
return '\033[97m'
@@ -3393,6 +3393,7 @@ class ThumbRenderer(QObject):
extension = os.path.splitext(filepath)[1][1:].lower()
try:
# Images =======================================================
if extension in IMAGE_TYPES:
image = Image.open(filepath)
# image = self.thumb_debug
@@ -3403,11 +3404,8 @@ class ThumbRenderer(QObject):
image = new_bg
if image.mode != 'RGB':
image = image.convert(mode='RGB')
# raise ValueError
# except (UnidentifiedImageError, FileNotFoundError):
# image = Image.open(os.path.normpath(f'{Path(__file__).parent.parent.parent}/resources/cli/images/no_preview.png'))
# image.thumbnail((adj_size,adj_size))
# Videos =======================================================
elif extension in VIDEO_TYPES:
video = cv2.VideoCapture(filepath)
video.set(cv2.CAP_PROP_POS_FRAMES,
@@ -3421,7 +3419,18 @@ class ThumbRenderer(QObject):
success, frame = video.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = Image.fromarray(frame)
# Plain Text ===================================================
elif extension in ['txt', 'md']:
text: str = extension
with open(filepath, 'r', encoding='utf-8') as text_file:
text = text_file.read()
bg = Image.new('RGB',(256,256), color='#222222')
draw = ImageDraw.Draw(bg)
draw.text((16,16), text, file=(255,255,255))
image = bg
# Other Files ==================================================
# TODO: Create placeholder thumbnails for non-media files.
# else:
# image: Image.Image = ThumbRenderer.thumb_loading_512.resize(
@@ -3440,9 +3449,7 @@ class ThumbRenderer(QObject):
new_y = adj_size
new_x = math.ceil(adj_size * (orig_x / orig_y))
img_ratio = new_x / new_y
# logging.info(f'[TR] {(new_x / new_y)}')
# self.updated_ratio.emit(new_x / new_y)
# img_ratio = new_x / new_y
image = image.resize(
(new_x, new_y), resample=Image.Resampling.BILINEAR)
@@ -3495,21 +3502,6 @@ class ThumbRenderer(QObject):
final = ThumbRenderer.thumb_broken_512.resize(
(adj_size, adj_size), resample=Image.Resampling.BILINEAR)
# if file_type in VIDEO_TYPES + ['gif', 'apng'] or broken_thumb:
# idk = ImageDraw.Draw(final)
# # idk.textlength(file_type)
# ext_offset_x = idk.textlength(
# text=file_type.upper(), font=ThumbRenderer.ext_font) / 2
# ext_offset_x = math.floor(ext_offset_x * (1/pixelRatio))
# x_margin = math.floor(
# (adj_size-((base_size[0]//6)+ext_offset_x) * pixelRatio))
# y_margin = math.floor(
# (adj_size-((base_size[0]//8)) * pixelRatio))
# stroke_width = round(2 * pixelRatio)
# fill = 'white' if not broken_thumb else '#E32B41'
# idk.text((x_margin, y_margin), file_type.upper(
# ), fill=fill, font=ThumbRenderer.ext_font, stroke_width=stroke_width, stroke_fill=(0, 0, 0))
qim = ImageQt.ImageQt(final)
if image:
image.close()
@@ -3559,6 +3551,7 @@ class ThumbRenderer(QObject):
extension = os.path.splitext(filepath)[1][1:].lower()
try:
# Images =======================================================
if extension in IMAGE_TYPES:
image = Image.open(filepath)
# image = self.thumb_debug
@@ -3569,11 +3562,8 @@ class ThumbRenderer(QObject):
image = new_bg
if image.mode != 'RGB':
image = image.convert(mode='RGB')
# raise ValueError
# except (UnidentifiedImageError, FileNotFoundError):
# image = Image.open(os.path.normpath(f'{Path(__file__).parent.parent.parent}/resources/cli/images/no_preview.png'))
# image.thumbnail((adj_size,adj_size))
# Videos =======================================================
elif extension in VIDEO_TYPES:
video = cv2.VideoCapture(filepath)
video.set(cv2.CAP_PROP_POS_FRAMES,
@@ -3587,6 +3577,15 @@ class ThumbRenderer(QObject):
success, frame = video.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image = Image.fromarray(frame)
# Plain Text ===================================================
elif extension in PLAINTEXT_TYPES:
text: str = extension
with open(filepath, 'r', encoding='utf-8') as text_file:
text = text_file.read()
bg = Image.new('RGB',(256,256), color='#222222')
draw = ImageDraw.Draw(bg)
draw.text((16,16), text, file=(255,255,255))
image = bg
if not image:
raise UnidentifiedImageError