From 9de63a2666f56805545d646772423d64ce609792 Mon Sep 17 00:00:00 2001 From: TrigamDev Date: Fri, 5 Dec 2025 12:22:06 -0500 Subject: [PATCH 1/2] Add modal for adding a URL entry --- src/tagstudio/qt/ts_qt.py | 18 ++++++++++++++++++ .../qt/views/add_url_entry_panel_view.py | 17 +++++++++++++++++ src/tagstudio/qt/views/main_window.py | 9 +++++++++ src/tagstudio/resources/translations/en.json | 4 ++++ 4 files changed, 48 insertions(+) create mode 100644 src/tagstudio/qt/views/add_url_entry_panel_view.py diff --git a/src/tagstudio/qt/ts_qt.py b/src/tagstudio/qt/ts_qt.py index 34dec16fa..6adea0f47 100644 --- a/src/tagstudio/qt/ts_qt.py +++ b/src/tagstudio/qt/ts_qt.py @@ -97,6 +97,7 @@ from tagstudio.qt.utils.custom_runnable import CustomRunnable from tagstudio.qt.utils.file_deleter import delete_file from tagstudio.qt.utils.function_iterator import FunctionIterator +from tagstudio.qt.views.add_url_entry_panel_view import AddUrlEntryPanel from tagstudio.qt.views.main_window import MainWindow from tagstudio.qt.views.panel_modal import PanelModal from tagstudio.qt.views.splash import SplashScreen @@ -426,6 +427,10 @@ def set_open_last_loaded_on_startup(checked: bool): lambda: self.add_tag_action_callback() ) + self.main_window.menu_bar.new_url_entry_action.triggered.connect( + lambda: self.add_url_entry_action_callback() + ) + self.main_window.menu_bar.select_all_action.triggered.connect( self.select_all_action_callback ) @@ -775,6 +780,7 @@ def close_library(self, is_shutdown: bool = False): self.main_window.menu_bar.color_manager_action.setEnabled(False) self.main_window.menu_bar.ignore_modal_action.setEnabled(False) self.main_window.menu_bar.new_tag_action.setEnabled(False) + self.main_window.menu_bar.new_url_entry_action.setEnabled(False) self.main_window.menu_bar.fix_unlinked_entries_action.setEnabled(False) self.main_window.menu_bar.fix_ignored_entries_action.setEnabled(False) self.main_window.menu_bar.fix_dupe_files_action.setEnabled(False) @@ -846,6 +852,17 @@ def add_tag_action_callback(self): ) self.modal.show() + def add_url_entry_action_callback(self): + panel = AddUrlEntryPanel(self.lib) + self.modal = PanelModal( + panel, + Translations["entry.new.url"], + Translations["entry.add"], + has_save=True, + ) + + self.modal.show() + def select_all_action_callback(self): """Set the selection to all visible items.""" self.main_window.thumb_layout.select_all() @@ -1650,6 +1667,7 @@ def _init_library(self, path: Path, open_status: LibraryStatus): self.main_window.menu_bar.color_manager_action.setEnabled(True) self.main_window.menu_bar.ignore_modal_action.setEnabled(True) self.main_window.menu_bar.new_tag_action.setEnabled(True) + self.main_window.menu_bar.new_url_entry_action.setEnabled(True) self.main_window.menu_bar.fix_unlinked_entries_action.setEnabled(True) self.main_window.menu_bar.fix_ignored_entries_action.setEnabled(True) self.main_window.menu_bar.fix_dupe_files_action.setEnabled(True) diff --git a/src/tagstudio/qt/views/add_url_entry_panel_view.py b/src/tagstudio/qt/views/add_url_entry_panel_view.py new file mode 100644 index 000000000..0aa613e03 --- /dev/null +++ b/src/tagstudio/qt/views/add_url_entry_panel_view.py @@ -0,0 +1,17 @@ +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QLineEdit, QVBoxLayout + +from tagstudio.qt.views.panel_modal import PanelWidget + + +class AddUrlEntryPanel(PanelWidget): + def __init__(self, text): + super().__init__() + + self.setMinimumWidth(480) + self.root_layout = QVBoxLayout(self) + self.root_layout.setContentsMargins(6, 0, 6, 0) + self.root_layout.setAlignment(Qt.AlignmentFlag.AlignTop) + + self.url_line = QLineEdit() + self.root_layout.addWidget(self.url_line) diff --git a/src/tagstudio/qt/views/main_window.py b/src/tagstudio/qt/views/main_window.py index a4c0485a5..629d05088 100644 --- a/src/tagstudio/qt/views/main_window.py +++ b/src/tagstudio/qt/views/main_window.py @@ -69,6 +69,7 @@ class MainMenuBar(QMenuBar): edit_menu: QMenu new_tag_action: QAction + new_url_entry_action: QAction select_all_action: QAction select_inverse_action: QAction clear_select_action: QAction @@ -192,6 +193,14 @@ def setup_edit_menu(self): self.edit_menu.addSeparator() + # New URL Entry + self.new_url_entry_action = QAction(Translations["entry.new.url"], self) + + self.new_url_entry_action.setEnabled(False) + self.edit_menu.addAction(self.new_url_entry_action) + + self.edit_menu.addSeparator() + # Select All self.select_all_action = QAction(Translations["select.all"], self) self.select_all_action.setShortcut( diff --git a/src/tagstudio/resources/translations/en.json b/src/tagstudio/resources/translations/en.json index 8b84af737..554962bf6 100644 --- a/src/tagstudio/resources/translations/en.json +++ b/src/tagstudio/resources/translations/en.json @@ -69,6 +69,10 @@ "entries.unlinked.search_and_relink": "&Search && Relink", "entries.unlinked.title": "Fix Unlinked Entries", "entries.unlinked.unlinked_count": "Unlinked Entries: {count}", + "entry.add": "Add Entry", + "entry.add.url": "Add URL Entry", + "entry.new": "New Entry", + "entry.new.url": "New URL Entry", "ffmpeg.missing.description": "FFmpeg and/or FFprobe were not found. FFmpeg is required for multimedia playback and thumbnails.", "ffmpeg.missing.status": "{ffmpeg}: {ffmpeg_status}
{ffprobe}: {ffprobe_status}", "field.copy": "Copy Field", From 4c66c37337bd0e774cac40eaa748a2be2c2ec023 Mon Sep 17 00:00:00 2001 From: TrigamDev Date: Fri, 5 Dec 2025 13:12:07 -0500 Subject: [PATCH 2/2] Create URL file and add it as an entry --- src/tagstudio/core/library/alchemy/library.py | 17 +++++++++++++++++ src/tagstudio/qt/ts_qt.py | 7 +++++++ .../qt/views/add_url_entry_panel_view.py | 13 ++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/tagstudio/core/library/alchemy/library.py b/src/tagstudio/core/library/alchemy/library.py index 1ce4fc85f..475001178 100644 --- a/src/tagstudio/core/library/alchemy/library.py +++ b/src/tagstudio/core/library/alchemy/library.py @@ -991,6 +991,23 @@ def add_entries(self, items: list[Entry]) -> list[int]: return new_ids + def add_url_as_entry(self, url: str): + # Write URL file + url_file_name: str = f"{hash(url)}.url" + url_file_path: Path = Path(self.library_dir, url_file_name) + + with open(str(url_file_path), "w") as url_file: + url_file.write(f"[InternetShortcut]\nURL={url}") + + # Add to library + url_entry: Entry = Entry( + path=Path(url_file_name), + folder=unwrap(self.folder), + fields=[], + date_added=datetime.now(), + ) + self.add_entries([url_entry]) + def remove_entries(self, entry_ids: list[int]) -> None: """Remove Entry items matching supplied IDs from the Library.""" with Session(self.engine) as session: diff --git a/src/tagstudio/qt/ts_qt.py b/src/tagstudio/qt/ts_qt.py index 6adea0f47..7dd4474df 100644 --- a/src/tagstudio/qt/ts_qt.py +++ b/src/tagstudio/qt/ts_qt.py @@ -861,6 +861,13 @@ def add_url_entry_action_callback(self): has_save=True, ) + self.modal.saved.connect( + lambda: ( + self.lib.add_url_as_entry(panel.get_content()), + self.modal.hide(), + ) + ) + self.modal.show() def select_all_action_callback(self): diff --git a/src/tagstudio/qt/views/add_url_entry_panel_view.py b/src/tagstudio/qt/views/add_url_entry_panel_view.py index 0aa613e03..4e7618153 100644 --- a/src/tagstudio/qt/views/add_url_entry_panel_view.py +++ b/src/tagstudio/qt/views/add_url_entry_panel_view.py @@ -9,9 +9,12 @@ def __init__(self, text): super().__init__() self.setMinimumWidth(480) - self.root_layout = QVBoxLayout(self) - self.root_layout.setContentsMargins(6, 0, 6, 0) - self.root_layout.setAlignment(Qt.AlignmentFlag.AlignTop) + self.__root_layout = QVBoxLayout(self) + self.__root_layout.setContentsMargins(6, 0, 6, 0) + self.__root_layout.setAlignment(Qt.AlignmentFlag.AlignTop) - self.url_line = QLineEdit() - self.root_layout.addWidget(self.url_line) + self.__url_line = QLineEdit() + self.__root_layout.addWidget(self.__url_line) + + def get_content(self) -> str: + return self.__url_line.text()