From c789d09b07575c8cba1fe93b14a46728b3d735a5 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Fri, 26 Apr 2024 17:33:02 -0700 Subject: [PATCH 1/3] 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) --- tagstudio/src/cli/ts_cli.py | 2 +- tagstudio/src/core/ts_core.py | 6 ++-- tagstudio/src/qt/ts_qt.py | 57 +++++++++++++++++------------------ 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/tagstudio/src/cli/ts_cli.py b/tagstudio/src/cli/ts_cli.py index 560d22b2..4f78d750 100644 --- a/tagstudio/src/cli/ts_cli.py +++ b/tagstudio/src/cli/ts_cli.py @@ -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 diff --git a/tagstudio/src/core/ts_core.py b/tagstudio/src/core/ts_core.py index 0ea35039..cc5418bd 100644 --- a/tagstudio/src/core/ts_core.py +++ b/tagstudio/src/core/ts_core.py @@ -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'] diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index 7e397f90..83c8cd73 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -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 From 5b4d35b5c07283a05a1f3f6c604cfbb9a601fbd7 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Fri, 26 Apr 2024 22:13:10 -0700 Subject: [PATCH 2/3] Added default thumbnail for files --- .../qt/images/thumb_file_default_512.png | Bin 0 -> 12661 bytes tagstudio/src/qt/ts_qt.py | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 tagstudio/resources/qt/images/thumb_file_default_512.png diff --git a/tagstudio/resources/qt/images/thumb_file_default_512.png b/tagstudio/resources/qt/images/thumb_file_default_512.png new file mode 100644 index 0000000000000000000000000000000000000000..28dfbd433568600ee6397a0baec5ad25791308ab GIT binary patch literal 12661 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 G-})bN^c95w literal 0 HcmV?d00001 diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index 83c8cd73..48f6b920 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -3349,6 +3349,10 @@ class ThumbRenderer(QObject): f'{Path(__file__).parent.parent.parent}/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}/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')) # thumb_debug.load() @@ -3429,12 +3433,10 @@ class ThumbRenderer(QObject): 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( - # (adj_size, adj_size), resample=Image.Resampling.BILINEAR) + # No Rendered Thumbnail ======================================== + else: + image = ThumbRenderer.thumb_file_default_512.resize( + (adj_size, adj_size), resample=Image.Resampling.BILINEAR) if not image: raise UnidentifiedImageError @@ -3586,6 +3588,10 @@ class ThumbRenderer(QObject): draw = ImageDraw.Draw(bg) draw.text((16,16), text, file=(255,255,255)) image = bg + # No Rendered Thumbnail ======================================== + else: + image = ThumbRenderer.thumb_file_default_512.resize( + (adj_size, adj_size), resample=Image.Resampling.BILINEAR) if not image: raise UnidentifiedImageError From 8541fc59d136a292628cee15f28f177a9df76989 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sat, 27 Apr 2024 16:25:49 -0700 Subject: [PATCH 3/3] Increased plaintext types; Exception handling --- tagstudio/src/core/ts_core.py | 3 ++- tagstudio/src/qt/ts_qt.py | 36 ++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/tagstudio/src/core/ts_core.py b/tagstudio/src/core/ts_core.py index cc5418bd..05acb62a 100644 --- a/tagstudio/src/core/ts_core.py +++ b/tagstudio/src/core/ts_core.py @@ -28,7 +28,8 @@ AUDIO_TYPES: list[str] = ['mp3', 'mp4', 'mpeg4', 'm4a', 'aac', 'wav', 'flac', 'alac', 'wma', 'ogg', 'aiff'] 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'] +PLAINTEXT_TYPES: list[str] = ['txt', 'md', 'css', 'html', 'xml', 'json', 'js', + 'ts', 'ini', 'htm', 'csv', 'php', 'sh', 'bat'] 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'] diff --git a/tagstudio/src/qt/ts_qt.py b/tagstudio/src/qt/ts_qt.py index 48f6b920..bfb90ec5 100644 --- a/tagstudio/src/qt/ts_qt.py +++ b/tagstudio/src/qt/ts_qt.py @@ -3425,14 +3425,17 @@ class ThumbRenderer(QObject): 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 + elif extension in PLAINTEXT_TYPES: + try: + text: str = extension + with open(filepath, 'r', encoding='utf-8') as text_file: + text = text_file.read(256) + bg = Image.new('RGB',(256,256), color='#222222') + draw = ImageDraw.Draw(bg) + draw.text((16,16), text, file=(255,255,255)) + image = bg + except: + logging.info(f'[ThumbRenderer][ERROR]: Coulnd\'t render thumbnail for {filepath}') # No Rendered Thumbnail ======================================== else: image = ThumbRenderer.thumb_file_default_512.resize( @@ -3581,13 +3584,16 @@ class ThumbRenderer(QObject): 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 + try: + text: str = extension + with open(filepath, 'r', encoding='utf-8') as text_file: + text = text_file.read(256) + bg = Image.new('RGB',(256,256), color='#222222') + draw = ImageDraw.Draw(bg) + draw.text((16,16), text, file=(255,255,255)) + image = bg + except: + logging.info(f'[ThumbRenderer][ERROR]: Coulnd\'t render thumbnail for {filepath}') # No Rendered Thumbnail ======================================== else: image = ThumbRenderer.thumb_file_default_512.resize(