Swapped `--prefix` with `--suffix` according to the NixOS manual: https://nixos.org/manual/nixpkgs/stable/#fun-makeWrapper
Using prefix essentially hardcodes dependencies without a fallback, and these dependencies are only needed during runtime.
* perf: remove unnecessary path conversions
* perf: search_library if no limit set don't do extra count
* perf: improve responsiveness of ui when rendering thumbnails
* feat: infinite scrolling thumbnail grid
* fix: update tests
* perf: don't run update for thumb grid if rows haven't changed
* fix: update blank thumbnails on initial page load
* fix: do partial updates when selecting items
* fix: remove badges on loading thumbnails
* fix: move all extra item_thumbs off screen
* load a few hidden rows when scrolling
* cleanup
* update imports
* remove todo
* support pagination
* allow setting page_size to 0 for no limit
* add ui setting for infinite scrolling
* undo render thread affinity changes
* always load a few off-screen rows
* feat: add thumb cache size setting to settings panel
* refactor: change names in cache_manager.py to be less ambiguous, more descriptive
* refactor: store cache size in MiB instead of bytes
Currently translated at 100.0% (355 of 355 strings)
Translated using Weblate (Hungarian)
Currently translated at 99.7% (354 of 355 strings)
Translated using Weblate (Hungarian)
Currently translated at 99.4% (353 of 355 strings)
Translate-URL: https://hosted.weblate.org/projects/tagstudio/strings/hu/
Translation: TagStudio/Strings
Co-authored-by: Szíjártó Levente Pál <szijartoleventepal@gmail.com>
* perf: optimize mapping of category->tags
* perf: one less db call for Library.tag_display_name
* fix: include joins in Library.get_tag_hierarchy
* fix: remove category if empty in preview panel
* fix: add missing imports and remove unneeded dict
* fix: add tags that are categories to their own category
* fix: flip parent_id/child_id in get_tag_hierarchy
* fix: prevent trying to save duplicate TagParents
* refactor: store DB version inside `versions` table
* tests: update search_library db file
* chore: add copyright info to library constants.py
* fix: only backup db if loaded version is lower
* chore: mark Preferences as @deprecated
* fix: render folder entries as unlinked
* fix: hide file size for linked folders
* fix: linked folders can be revealed in explorer
* fix: linked folders can be opened
* fix: linked folders can be deleted
* fix: skip rendering thumbnails in `ThumbRenderer._render`
* fix: skip getting image metadata for folders
* fix: conflicts
* style: ruff
commit c1346e7df36b137cf88be284a96329fee9605a6a
Author: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
Date: Sat Aug 23 18:04:58 2025 -0700
docs: update DB v100 with tag_parents flip
commit 7e5d9381759b000533c809df9d9bc4f9d984e363
Author: HeikoWasTaken <heikowastaken@protonmail.com>
Date: Sun Aug 24 00:31:21 2025 +0100
fix: swap IDs in parent_tags DB table (#998)
* fix: reorder child and parent IDs in TagParent constructor call
* feat: add db10 migration
* fix: SQL query returning parent IDs instead of children IDs
* fix: stop assigning child tags as parents
* fix: select and remove parent tags, instead of child tags
* test/fix: correctly reorder child/parent args in broken test
* fix: migrate json subtags as parent tags, instead of child tags (I see where it went wrong now lol)
* fix: query parent tags instead of children
* refactor: scooching this down below db9 migrations
* test: add DB10 migration test
---------
Co-authored-by: heiko <heiko_was_taken@protonmail.com>
commit 1ce02699ad9798800f9d98832b2a6377e3d79ed4
Author: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
Date: Sat Aug 23 14:47:39 2025 -0700
feat: add db minor versioning, bump to 100
* feat: add `.ts_ignore` pattern ignoring system
* fix: add wcmatch dependency
* search: add ".TemporaryItems" to GLOBAL_IGNORE
* add `desktop.ini` and `.localized` to global ignore
* add ".fhdx" and ".ts" filetypes
* chore: remove logging statement
* chore: format with ruff
* feat: use ripgrep for scanning if available
* docs: add ignore.md
* search: remove ts_ignore filtering on queries
* feat: detect if files are added but ignored
* fix: render edges on all unlinked thumbs
* perf: don't search for cached unlinked thumbs
* fix(ui): ensure newlines in file stats
* fix: use ignore_to_glob for wcmatch
* fix(tests): remove inconsistent test
The test hinged on the timing of refresh_dir()'s yield's rather than actual values
* ui: change ignored icon and color
* ui: tweak media player style and behavior
* ui: change player overlay to gradient
* ui: add slight outline to player bar
* feat: realtime playback seeking
* fix(ui): click to jump to unbuffered media
* chore: organize imports
* refactor: remove unused code
* translation - almost done
* almost done
* small stuft left
* finish, the only untranslated text "The \"Favorite\" and \"Archived\" tags now inherit from a new 「Meta Tags」 tag which is marked as a category by default." of json_migration.info.description
* small changes - done
* refactor: basic split
* fix: renaming and usage test didn't work for the tests
* fix: tests
* refactor: restructuring
* refactor: further separation and lots of related changes
* refactor: remove last reference to a widget from controller
* refactor: address todo
* fix: failing tests and mypy compaint
* refactor: move control logic to controller
* refactor: more readable button style
* refactor: move existing code to view
* refactor: move existing code to controller
* fix: imports
* refactor: make methods private by default
* refactor: privatise fields
* refactor: reduce code duplication
* refactor: consolidate and sort display methods
* refactor: remove needless setting of delete action text
* refactor: extract control logic from _display_file
* refactor: use MediaType for __switch_preview
* fix: import in preview_panel_view.py
* refactor: remove unnecessary wrapper on view side
* refactor: move image data retrieval to control side
* refactor: move audio / video specific code to the respective method
* refactor: remove superfluos methods
* refactor: this and that
* refactor: use proper type instead of dict for file stats
* refactor: extract gif parsing to controller
* refactor: extract video size extraction to controller
* doc: add rule of thumb to Qt MVC Style Guide
* doc: change rule of thumb from note to tip
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* refactor: basic split
* fix: renaming and usage test didn't work for the tests
* fix: tests
* refactor: restructuring
* refactor: further separation and lots of related changes
* refactor: remove last reference to a widget from controller
* refactor: address todo
* fix: failing tests and mypy compaint
* refactor: move control logic to controller
* refactor: more readable button style
* fix: set_selection was called with invalid argument
* feat: add settings field
* feat: change click behaviour based on settings value
* fix: make ignore comment pyright specific to shut up mypy
* fix: add german and english translations for new strings
* fix: settings dropdowns were always english not matter the selected language
* feat: custom modal making use of QDateTimeEdit
* fix: add back license notice
* refactor: remove unnecessary line
* feat: use date and hour format from settings for date time picker
* fix: incorrect qt enum accesses
* refactor: clean up main_window.py and reenable ruff checks for it
* refactor: completely reorganise main_window.py
* refactor: factor out regions into separate methods
* refactor: move preview_panel to MainWindow
* refactor: move file menu construction to MainMenuBar
* refactor: move edit menu construction to MainMenuBar
* refactor: move view menu construction to MainMenuBar
* refactor: move tools menu construction to MainMenuBar
* refactor: move macros menu construction to MainMenuBar
* refactor: move help menu construction to MainMenuBar
* refactor: move ui construction code out of QtDriver and into MainWindow
* fix: remove unnecessary imports
* fix: tests were failing
* refactor: store browsing history for navigation purposes
* refactor: remove page_size from FilterState
* refactor: move on from the term "filter" in favor of "BrowsingState"
* fix: refactors didn't propagate to the tests
* fix: ruff complaints
* fix: remaing refactoring errors
* fix: navigation works again
* fix: also store and restore query
* fix: tests were overwriting the settings.toml
* fix(GlobalSettings): add default value for _loaded_from
* fix: a case in read_settings didn't set loaded_from correctly
* fix(GlobalSettings): proper serialisation
* refactor: simplify datetime widget creation
* refactor: fix warnings
* feat: text modal to enter date
* chore: change ignore comment to only apply to pyright not mypy
* feat(ui): expand file and thumbnail support
* feat: add iwork and powerpoint thumb support
Note: a lot of the zip-based code is becoming duplicated - this should be consolidated in the future.
* fix: remove decompression bomb check and catch others
* feat: add .aiff file equivalencies
* ui: update database icon
* feat: add .effect and .shader to shader set
* fix: correct malformed or missing media types
* feat: add misc code/plaintext types to media types
* fix: catch BadZipFile error for iWork thumbs
* chore: add type hints to thumb_renderer dicts
* refactor: change most internal render methods to static
* feat: merge media controls.
Initial commit to merge audio/video files. There are
still a few bugs around widget sizing that need fixing.
* fix: center widgets in preview area
Add widgets to a sublayout to allow for centering
in a QStackedLayout.
Remove references to the legacy video player in
the thumb preview.
* fix: resolve commit suggestions.
Subclass QSlider to handle click events
and allow for easier seeking.
Implement context menu along with autoplay
setting for the media widget.
Pause video when media player is clicked
instead of opening file.
* fix: start media muted
Start video/audio muted on initial load of
the media player.
Remove code causing mypy issue.
Add new method for getting slider click state.
* refactor: use layouts instead of manual positioning.
Add various layouts for positioning widgets instead
of manually moving widgets.
Change the volume slider orientation at smaller
media sizes.
* fix: color position label white
Fix position label color to white so it stays visible
regardless of theme.
* fix: allow dragging slider after click
* Apply suggestions from code review
fix: apply suggestions from code review.
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* fix: remove references to legacy video player.
Combine the stats logic for video/audio into one method.
Fix several issues after incorrectly implementing suggestions.
* fix: add loop setting and other actions.
* refactor: simplify widget state management.
Make a single method to control widget state.
Works with the main QStackLayout and cleans up
widget state if it is needed (i.e., stopping the media
player when switching to a different preview).
* fix: add pages to QStackLayout to fix widget position.
Fixes a regression in commit 4c6934. We need the pages
to properly center the widgets in the QStackLayout.
* fix: ensure media_player doesn't exceed maximum size if thumbnail.
Fix and issue where the media_player would expand past the
thumbnail on resize.
* refactor: move settings to new system
* build: add "src" to pathex.
Instead of having to pre-install tagstudio, this will have PyInstaller directly build it from source.
* build: be more selective about the data files.
Since PyInstaller builds from source now there is no need to manually add it.
---------
Co-authored-by: Léonard <Leeooonaard@gmail.com>
* feat: create QMovie with file path instead of copying image around
* fix: make ignore pyright specific so mypy doesn't complain
* fix: restore ability to delete file while viewing it
* feat: add tab widget
* refactor: move languages dict to translations.py
* refactor: move build of Settings Modal to SettingsPanel class
* feat: hide title label
* feat: global settings class
* fix: initialise settings
* fix: properly store grid files changes
* fix: placeholder text for library settings
* feat: add ui elements for remaining global settings
* feat: add page size setting
* fix: version mismatch between pydantic and typing_extensions
* fix: update test_driver.py
* fix(test_file_path_options): replace patch with change of settings
* feat: setting for dark mode
* fix: only show restart_label when necessary
* fix: change modal from "done" type to "Save/Cancel" type
* feat: add test for GlobalSettings
* docs: mark roadmap item as completed
* fix(test_filepath_setting): Mock the app field of QtDriver
* Update src/tagstudio/main.py
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* fix: address review suggestions
* fix: page size setting
* feat: change dark mode option to theme dropdown
* fix: test was expecting wrong behaviour
* fix: test was testing for correct behaviour, fix behaviour instead
* fix: test fr fr
* fix: tests fr fr fr
* fix: tests fr fr fr fr
* fix: update test
* fix: tests fr fr fr fr fr
* fix: select all was selecting hidden entries
* fix: create more thumbitems as necessary
Fixes: #200Fixes: #365Fixes: #512Fixes: #800
fix(pyproject): resolve mix-up of mypy and pytest
chore(ci): remove legacy scripts
chore: format with new mypy rules; fix translation test
wip(ci/mypy): remove config flag
fix(pyinstaller): use correct dict access
fix(resources): usage in ts_qt.py
feat(nix/package): validate tests with pytest hook
fix(nix/package): remove old dependency patch
feat(nix): support Darwin
fix(nix/package): move check deps to checkInputs
fix(nix/shell): typo
fix(nix/shell): correctly wrap Python with Qt args
fix(pyproject): specify mypy-extensions
feat(nix/package): provide pillow-jxl-plugin
nix(nix/package): split into multiple files, allow overriding of JXL and vtf2img
fix(nix/shell): provide FFmpeg on runtime
feat(flake): output pillow-jxl-plugin and vtf2img
fix(nix/package): load pipewire
feat(nix/package): run tests on pillow-jxl-plugin
fix: remove extra noqa comment
docs: update installation docs
docs: shrink table size on docs site
nit(nix/package): pipewire not needed in buildInputs
docs: update commands, environment, setup
fix: use consistent possessives
chore: format with prettier, add ignore flags
fix(pyinstaller): consume from pyproject
Revert "fix(pyinstaller): consume from pyproject"
This reverts commit 398cd4e5630a3e83d22d15286d7ac59b4c07c5d6.
refactor: use icon from resource manager
Also fixes incorrect path currently used in ts_qt.py.
nix(pyinstaller): replace use of sys.platform with platform.system
docs: add build section
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* refactor: simplify pathlib imports
* feat: add tests for invalid format keys and missing / unnecessary translations
* feat: parametrise format key test
* feat: parametrise completeness test
* fix: include more useful information in the error message
* refactor: remove unused method
* refactor: extract translation loading to a separate function
* fix: include more useful information in the error message
* fix: don't fail test when language hasn't been completely translated
* feat: implement first class for translated widget
* feat: add further translated widget types
* refactor: replace usages of translate_qobject with appropriate classes
* refactor: remove wrapper classes
* refactor: remove unnecessary used of Translations.formatted
* refactor: remove further unnecessary format calls
* refactor: replace calls for Translations.formatted with calls to str.format and remove some unused code
* refactor: remove TranslatedString class
* refactor: replace translate_with_setter with direct calls to the setter
* ui: show more informative library error messages
* tests: add sqlite db migration tests
* tests: fix and refactor migration tests
* fix: apply db8 schema changes before repairing db6
* docs: add save file format change log
* chore: remove db version explanations from docstrings
* feat(ui): add language setting
* translations: implement remaining todos
* ui: show language names in settings instead of codes
* translations: add Dutch setting, anticipating #798
* feat: custom tag colors
* ui: minor ui polish
* ui: add confirmation for deleting colors
* ui: match tag_color_preview focused style
* ui: reduce spacing between color swatch groups
* ui!: change default behavior of secondary color
The secondary color now acts as only the text color by default, with the new `color_border` bool serving to optionally restore the previous text + colored border behavior.
* ui: adjust focused tag/color button styles
* fix: avoid namespace collision
* fix: make reserved namespace check case-insensitive
* ui: add namespace description + prompt
* fix: don't reset tag color if none are chosen
* refactor(ui): use form layout for build_color
* fix(ui): dynamically scale field title widget
* feat(ui): add additional tag shade colors
Add "burgundy", "dark-teal", and "dark_lavender" tag colors.
* fix: don't check for self in collision checks
* fix: update tag references on color update
* fix(ui): stop fields widgets expanding indefinitely
* fix(ui): don't update preview for non-selected badge toggles
* ui: optimize badge updating
NOTE: In theory this approach opens the doors for a visual state mismatch between the tags shown and the tags on the actual entry. The performance increase comes from the fact that `get_entry_full()` isn't called, which is the real bottleneck here.
* perf: optimize query methods
* fix(ui): expand usage of esc for closing modals
* chore: remove log statements
* refactor: use Qt enum in place of magic number
* ui: use enter key to save panel widgets
* docs: refer to pyenv in CONTRIBUTING.md
This commit was made in order to account for users who may already have
a Python installation that isn't 3.12.
A good real world scenario where this may be a problem is users running
on rolling release Linux distributions, such as Arch Linux which
provides bleeding edge updates in a weekly basis.
Continuing with Arch Linux as an example, it provides Python 3.13, which
the current version of TagStudio doesn't seem to work with, an issue I
encountered myself this morning.
In some systems like Windows, this may not matter, but it is an issue
nonetheless, and users using a package manager centric system can't be
expected to downgrade/upgrade their Python version for TagStudio, as
this may cause issues in their system.
The easiest way to solve this is with pyenv, which enables users to
install other Python versions independent from each other, aside from
the system install, and allows specifying a version to use for a
directory (And its subdirectories), the current terminal session, or the
entire system if desired.
Here is a short, technical and objective summary of the changes:
- Updated `.gitignore` to to ignore `.python-version` which pyenv uses.
- Updated CONTRIBUTING.md to refer to pyenv for versioning issues.
- pyenv is listed as a prerequisite, specifying in parenthesis that it's only for when your Python install isn't 3.12.
- The "Creating a Python Virtual Environment" section now has an additional step at the beginning, to check that the current Python installation is 3.12.
- As part of this added step, there are steps to use pyenv to install Python 3.12 separately from the existing Python installation, and activate it for the TagStudio root folder, in case it's not the appropriate version.
- The numbering of the other steps was offset accordingly.
- The "Manually Launching (Outside of an IDE)" section now refers to the above first step, should the user encounter some kind of error.
* docs: refer to alternate shells in CONTRIBUTING.md
Some users may not be using the default shell for their system, and
Linux users are the most likely to do this, though this can be seen in
macOS as well. People will often use shells like fish which is not POSIX
compliant but works very well and has great auto-completion, it's very
user friendly.
The reason why I care about this is that the instructions specify to use
.venv/bin/activate which is a bash script and will not run on fish or
csh, and if you take a look at the script, indeed it says it will only
run with bash.
Python will provide alternate scripts for other shells, and at least on
my system (CachyOS), those are scripts for Powershell, bash, fish and
csh. People using these alternate shells, who don't know this may be
confused when running the script referenced in CONTRIBUTING.md and
seeing it fail completely.
The only real change in this commit was adding under the "Linux/macOS"
command in step 3 (step 2 prior to my last commit) of the "Creating a
Python Virtual Environment" section, a short instruction to switch to
the default shell, or use the appropriate script for the user's
preferred shell if any (Since, at least on my system, there aren't
scripts for every shell, for example there is no activate.zsh file).
* fix: modal windows now have the Qt.Dialog flag
Set the Qt.Dialog flag for modal windows, such as "Add Field" so that
they will be seen as dialogs in the backend. This change is unlikely to
be at all noticeable in systems like Windows, but for users running
tiling window managers like bspwm or Sway, this will prevent these modal
windows from being tiled alongside the main window, instead they will be
floating on top, which is the expected behaviour seen on floating window
managers, like the ones used by Windows and macOS.
Added self.setWindowFlag(Qt.Dialog, on=True) # type: ignore to the
following files:
- tagstudio/src/qt/modals/add_field.py
- tagstudio/src/qt/modals/delete_unlinked.py
- tagstudio/src/qt/modals/drop_import.py
- tagstudio/src/qt/modals/file_extension.py
- tagstudio/src/qt/modals/fix_dupes.py
- tagstudio/src/qt/modals/fix_unlinked.py
- tagstudio/src/qt/modals/folders_to_tags.py
- tagstudio/src/qt/modals/mirror_entities.py
- tagstudio/src/qt/widgets/paged_panel/paged_panel.py
- tagstudio/src/qt/widgets/panel.py
- tagstudio/src/qt/widgets/progress.py
Note that without adding # type: ignore, MyPy *will* give out an error,
presumably because PySide6 is missing type hints for several things, and
this may *or* may not be a justifiable use of # type: ignore, which
throws the MyPy type checking for that line out the window.
Ref: #392, #464
* fix: I forgot to run ruff format
* revert: changes to docs and gitignore
This revert is for moving the referenced changes to their own pull request.
Refs: 15f4f38, a25ef8c
* docs: other shells and pyenv in CONTRIBUTING.md
This commit changes CONTRIBUTING.md to refer to pyenv in the case of
issues with Python versions, as well as downloading the correct version
off of the Python website alternatively. It also now elaborates on the
process of running the Python virtual environment on Linux and macOS, by
referencing activation scripts for alternative shells such as fish and
CSH.
The table added to CONTRIBUTING.md is taken from the official Python
documentation: https://docs.python.org/3.12/library/venv.html
* revert: accidental inclution of #464
Refs: a51550f, 5854ccc
* feat(ui): recycle tag list in `TagSearchPanel`
* chore: address mypy warnings
* fix: order results from sql before limiting
* fix(ui): check for self.exclude before remaking sets
* fix(ui): only init tag manager and file ext manager once
* fix(ui:): remove redundant tag search panel updates
* update code comments and docstrings
* feat(ui): add tag view limit dropdown
* ensure disconnection of file_extension_panel.saved
* fix(ui): always reset tag search panel when opened
* feat: return parent tags in tag search
Known issue: this bypasses the tag_limit
* refactor: use consistant `datetime` imports
* refactor: sort by base tag name to improve performance
* fix: escape `&` when displaying tag names
* ui: show "create and add" tag with other results
* fix: optimize and fix tag result sorting
* feat(ui): allow tags in list to be selected and added by keyboard
* ui: use `esc` to reset search focus and/or close modal
* fix(ui): add pressed+focus styling to "create tag" button
* ui: use `esc` key to close `PanelWidget`
* ui: move disambiguation button to right side
* ui: expand clickable area of "-" tag button, improve styling
* ui: add "Ctrl+M" shortcut to open tag manager
* fix(ui): show "add tags" window title when accessing from home
* fix: move `path_strings` var inside `with` block
* refactor: move lambda to local function
* fix: catch `ParsingError` and return no results
* refactor: move `ParsingError` handling out to `QtDriver`
Reverts changes made to `test_search.py` and `enums.py`.
* chore: change version branch to "Pre-Release 1"
* docs: update docstring for JSON `open_library()`
Update method docstring with exact starting and ending versions now that they exist.
* docs: update README, CONTRIBUTING.md, & index.md
* docs: update roadmap
* docs: fix typo in README, change callout
* docs: update docs site with v9.5 features
* docs: remove warning from index.md
* implement copy/paste for fields and tags
* fix tests
* fixed badge refresh on pasting
* renamed translation field
* ignore duplicate tags on insert
* fixes
* break into several statements to satisfy tests
* chore: format with ruff
* fix: relink unlinked entry to existing entry without sql error (#720)
* edited and added db functions get_entry_full_by_path & merge_entries
* implemented edge case for entry existing on relinking
* added test for merge_entries
* fix: don't remove item while iterating over list
* fix: catch `FileNotFoundError` for unlinked raw files
* refactor: rename methods and variables in missing_files.py
* refactor: rename `missing_files_count` to `missing_file_entries_count`
---------
Co-authored-by: mashed5894 <mashed5894@gmail.com>
* fix: always catch db mismatch
* chore: remove/modify log statements
* translations: translate library version mismatch
* style: fix line over col limit
* attempt at adding `TagColor` table
* store both columns of `TagColor` inside `Tag` table
* fix: fix tag color relationships
* refactor: replace TagColor enums with TagColorGroup system
* ui: derive tag accent colors from primary
* refactor: move dynamic tag color logic, apply to "+" button
* feat(ui): add neon tag colors
* remove tag text color field
* ui: use secondary color for border and text
* ui: add TagColorPreview widget
* feat(ui): add new tag color selector
* ui: add color name tooltips
* ui: tweak tag colors and selector
* feat: add `namespaces` table
* translations: add + update translation keys
* tests: update fixtures
* chore: code cleanup
* ui: add spacing between color groups
* fix: expand refactor to create and add button
* chore: format with ruff
* feat(ui): add thumbnail caching
* feat: enforce total size limit for thumb cache
* fix: add file modification date to thumb hash
This change allows for modified files to have their cached thumbnail regenerated.
* feat: add menu option to clear thumbnail cache
* fix: check if `cached_path` is None further on
* refactor: move configurable cache vars to class level
* docs: complete roadmap items
* feat: use cache size limit from config file
TODO: Store this on a per-library basis and allow it to be configurable inside the program.
* fix: move `last_cache_folder` to class level
* chore: add/tweak type hints
* refactor: use make `CacheManager` a singleton
* Add Volume Slider
* Slider reflects muted state
* Default Volume Value
* Update roadmap to reflect current state
* Forgot a period in a comment
* Add suggestions
* Update tagstudio/src/qt/widgets/media_player.py
Co-authored-by: VasigaranAndAngel <72515046+VasigaranAndAngel@users.noreply.github.com>
* remove unwanted line
---------
Co-authored-by: VasigaranAndAngel <72515046+VasigaranAndAngel@users.noreply.github.com>
* fix: only update file preview when necessary
* fix: don't update thumb from tag_box callbacks
* fix: reset preview panel and filters when loading new library
* fix: clear library state on close; close old library on open
* fix: reset scrollbar position on library close
* Port create & add tag
* Fix Name Conflict
* Add translation keys
* unify uses of BuildTagPanel
* replace tabs with spaces
* Add tag on creation
* Add Suggestions
* Trigger on return
---------
Co-authored-by: bjorn-out <b.g.out@uva.nl>
* added about section to the help menu and modal to display information
* load image through resource manager
* cleaned up about text
* remove visit github repo menu, moved to about modal
* added function to check ffmpeg version
* fixed version formatting
* copied in ffmpeg checker code from v9.4 with a warning message on startup if ffmpeg is not detected
* mypy fixes
* about.content spacing
* about.content title formatting
* added config path to about screen
* ruff fixes
---------
Co-authored-by: Sean Krueger <skrueger2270@gmail.com>
* build_tag modal: make Tag Title selected when opening modal
* build_tag modal: Tab now changes focus when aliases field is in focus
* fix: unpredictable tab order in build tag modal
* feat: implement proper tab order
* ci: fix mypy errors
* fix: merge issue 1
* fix: merge issue 2
* refactor: TagDatabasePanel now inherits from TagSearchPanel for code deduplication
* Revert "refactor: TagDatabasePanel now inherits from TagSearchPanel for code deduplication"
This reverts commit fac589b3e3.
* feat: optimise tag constraint
* feat: use less subqueries
* refactoring: __entry_satisfies_ast was unnecessary
* feat: reduce time consumption of counting total results massively
* feat: log the time it takes to fetch the results
* Revert "feat: reduce time consumption of counting total results massively"
This reverts commit 30af514681.
* feat: log the time it takes to count the results
* feat: optimise __entry_has_all_tags
* refactor: TagDatabasePanel now inherits from TagSearchPanel for code deduplication
* refactor: rename callback method
* extract creation of row items to separate method
* feat: Delete all unlinked entries at once (#617)
This technically removes the usefulness progress indicator, but brief
testing on my end had it delete ~8000 entries in less time than it took
me to see if the progress indicator was properly indeterminate or not
* fix(fmt): remove unused import
* fix: wasn't able to delete more than 32766 entries
* chose: ruff check --fix
* fix: address review comment
---------
Co-authored-by: Tobias Berger <toby@tobot.dev>
* refactor: remove TagBoxField and TagField (NOT WORKING)
* refactor: remove tag field types
* ci: fix mypy and ruff tests
* refactor: split up preview_panel
* fix: search now uses `TagEntry` (#656)
* fix: move theme check inside class
* refactor: reimplement file previews
* refactor: modularize `file_attributes.py`
* ui: show fields in preview panel
known issues:
- fields to not visually update after being edited until the entries are reloaded from the thumbnail grid (yes, the thumbnail grid)
- add field button currently non-functional
- surprise segfaults
* search: remove TagEntry join
* fix: remove extra `self.filter` assignment
* add success return flag to `add_tags_to_entry()`
* refactor: use entry IDs instead of objects and indices
- fixes preview panel not updating after entry edits
- fixes slow selection performance
- fixes double render call
* feat: add tag categories to preview panel
* ui: add "is category" checkbox in tag panel
* fix: tags can be compared for name sorting
* fix: don't add tags to previous selections
* fix: badges now properly update
* ui: hide sizeGrip
* ui: add blue ui color
* ui: display empty selection; better multi-selection
* cleanup comments; rename tsp to tag_search_panel
* fix(ui): properly unset container callbacks
* fix: optimize queries
* fix: catch int cast exception
* fix: remove unnecessary update calls
* fix: restore try/except block in preview_panel
* fix: correct type hints for get_tag_categories
* fix: tags no longer lazy load subtags and aliases
* fix: recursively include parent tag categories
* chore: update copyright info
* chore: remove unused code
* fix: load fields for selected entry
* refactor: remove `is_connected` from AddFieldModal
* fix: include category tags under their own categories
* fix: badges now update when last tag is removed
* fix: resolve differences with main
* fix: return empty set in place of `None`
* ui: add field highlighting, tweak theming
* refactor!: eradicate use of the term "subtag"
- Removes ambiguity between the use of the term "parent tag" and "subtag"
- Fixes inconstancies between the use of the term "subtag" to refer to either parent tags or child tags
- Fixes duplicate and ambiguous subtags mapped relationship for the Tag model
- Does NOT fix tests
* fix: catch and show library load errors
* tests: fix and/or remove tests
* suppress db preference warnings
* tests: add field container tests
* tests: add tag category tests
* refactor(ui): move recent libraries list to file menu
* docs: update roadmap and docs for tag categories
* fix: restore json migration functionality
* logs: remove/update debug logs
* chore: remove unused code
* tests: remove tests related to `TagBoxWidget`
* ui: optimize selection and badge updates
* docs: update usage
* fix: change typo of `tag.id` to `tag_id`
Co-authored-by: Jann Stute <46534683+Computerdores@users.noreply.github.com>
* fix: use term "child tags" instead of "subtags" in docstring
Co-authored-by: Jann Stute <46534683+Computerdores@users.noreply.github.com>
* fix: reference `child_id` instead of `parent_id` when deleting tags
Co-Authored-By: Jann Stute <46534683+Computerdores@users.noreply.github.com>
* add TODO comment for `update_thumbs()` optimization
* fix: combine and check (most) built-in tag data from JSON
Known issue: Tag colors from built-in JSON tags are not updated. This can be seen in the failing test.
* refactor: rename `select_item()` to `toggle_item_selection()`
* add TODO to optimize `add_tags_to_entry()`
* fix: remove unnecessary joins in search
* Revert "fix: remove unnecessary joins in search"
This reverts commit 4c019ca19c.
* fix: remove unnecessary joins in search
* reremove unused method `get_all_child_tag_ids()`
* fix: migrate user-edited tag colors for built-in tags
* style: update header for contributor-created files
* fix: use absolute path in "open file" context menu
* chore: change paramater type hint
---------
Co-authored-by: python357-1 <jb2101554@gmail.com>
Co-authored-by: Jann Stute <46534683+Computerdores@users.noreply.github.com>
* ui: add sorting mode dropdown
* feat: pass sorting mode to Library.search_library
* feat: implement sorting by creation date
* ui: add dropdown for sorting direction
* ui: update shown entries after changing sorting mode / direction
* docs: mark sorting by "Date Created" as completed
* fix: remove sorting options that have not been implemented
* fix: rename sorting mode to "Date Added"
* fix: check off the right item on the roadmap
* feat: translate sorting UI
* fix: address review comments
This fix uses stylesheets instead of initializing new QFonts in the Ui_MainWindow class. This fixes the fonts appearing differently on different OSes, including a fix for text using these QFonts appearing small on macOS. The use of stylesheets also puts the styling in line with how the rest of the program operates.
* feat: implement parent tag search
* feat: add tests for parent tag search
* fix: typo
* feat: log the time it takes to build the SQL Expression
* feat: instead of hardcoding child tag ids into main query, include subquery
* Revert "feat: instead of hardcoding child tag ids into main query, include subquery"
This reverts commit 2615e7dab4.
* feat: implement Translator class
* feat: add translate_with_setter and implement formatting of translations
* feat: extend PanelModal to allow for translation
* feat: extend ProgressWidget to allow for translation
* feat: translation in ts_qt.py
* debug: set default lang to DE and show "Not Translated" when replacing untranslated stuff
* add translation todos
* feat: translate modals
* feat: translate more stuff
* fix: UI test wasn't comparing to translated strings
* feat: translations for most of the remaining stuff
* fix: replace debug changes with simpler one
* uncomment debug change
* fix: missing parameter in call
* fix: various review feedback
* fix: don't translate json migration discrepancies list
* fix: typo
* fix: various PR feedback
* fix: correctly read non-ascii characters
* fix: add missing new line at eof
* fix: comment out line of debug code
* fix: address latest review comment
* fix: KeyError that occurred when formatting translations
* fix: regression of d594e84
* fix: add newline to en.json
* fix: organize en.json, fix typo
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* updated parents and aliases to use the flowLaout in the build tag panel
added shortcuts for adding and removing aliases and updated the alias ui
to always show remove button and not cover alias
aliases use flowlayout
wrote test for buildTagPanel removeSelectedAlias
parent tags now use flowlayout in build tag panel
moved buttons for adding aliases and parents to be at the end of the
flowLayout
* added aliases and subtags to search results
* aliases now use a table, removed unnecessary keyboard shortcuts
* reverted subtags to regular list from flowlayout
* chor remove redundant lambda
* feat: added display names for tags
* fix: aliases table enter/return and backspace work as expected, display names work as expected, adding aliases outputs them in order
* format
* fix: add parent button on build tag panel
* fix: empty aliases where not being removed all the time
* fix: alias name changes would be discarded when a new alias was created or an alias was removed
* fix: removed display names, as they didn't display properly and should be added in a different PR
* fix: mypy
* fix: added missing session.expunge_all
session.expunge_all() on line 621 was removed, added it back.
* fix: parent_tags relationship in Tag class
parent_tags primaryJoin and secondaryJoin where in the wrong order. They have been switched back to the proper order.
* fix: pillow_jxl import was missing
* fix: ruff
* fix: type hint fixes
* fix: fix the type hint fixes
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* feat: implement search equivalence of "jpg" and "jpeg" filetypes in an extensible manner
* docs: update completion for search features on roadmap
* fix: move FILETYPE_EQUIVALENTS to media_types.py
* [feat] can now add a new tag from the tag library panel
* [feat] can remove tags from the tag library panel
[fix] library panel updates tag list when a new tag is create
* [test] added test for library->remove_tag
* [fix] type error
* removed redundant lambda
Co-authored-by: VasigaranAndAngel <72515046+VasigaranAndAngel@users.noreply.github.com>
* fix: tags with a reserved id could be edited or removed, now they cannot.
* fix: when a tag is removed or edited the preivew panel will update to reflect the changes
Co-authored-by: Sean Krueger <skrueger2270@gmail.com>
* fix: mypy check
* fix: aliases and subtags not being removed from DB when tag they reference was removed.
* feat: added a confirmation message box when removing tags.
* fix: mypy
---------
Co-authored-by: VasigaranAndAngel <72515046+VasigaranAndAngel@users.noreply.github.com>
Co-authored-by: Sean Krueger <skrueger2270@gmail.com>
* fix: bug where preview_panel tag was out of date compared to tag in database after edits where made using the tagDatabasePanel
* fix: changes made in the preview panel are saved to the database
* feat: Drag and drop files in and out of TagStudio (#153)
* Ability to drop local files in to TagStudio to add to library
* Added renaming option to drop import
* Improved readability and switched to pathLib
* format
* Apply suggestions from code review
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
* Revert Change
* Update tagstudio/src/qt/modals/drop_import.py
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
* Added support for folders
* formatting
* Progress bars added
* Added Ability to Drag out of window
* f
* format
* Ability to drop local files in to TagStudio to add to library
* Added renaming option to drop import
* Improved readability and switched to pathLib
* format
* Apply suggestions from code review
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
* Revert Change
* Update tagstudio/src/qt/modals/drop_import.py
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
* Added support for folders
* formatting
* Progress bars added
* Added Ability to Drag out of window
* f
* format
* format
* formatting and refactor
* format again
* formatting for mypy
* convert lambda to func for clarity
* mypy fixes
* fixed dragout only worked on selected
* Refactor typo, Add license
* Reformat QMessageBox
* Disable drops when no library is open
Co-authored-by: Sean Krueger <skrueger2270@gmail.com>
* Rebased onto SQL migration
* Updated logic to based on selected grid_idx instead of selected ids
* Add newly dragged-in files to SQL database
* Fix buttons being inconsistant across platforms
* Fix ruff formatting
* Rename "override" button to "overwrite"
---------
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
Co-authored-by: Travis Abendshien <lvnvtravis@gmail.com>
Co-authored-by: Sean Krueger <skrueger2270@gmail.com>
* refactor: Update dialog and simplify drop import logic
* Handle Qt events for main window in ts_qt.py
* Replace magic values with enums
* Match import duplicate file dialog to delete missing entry dialog
* Remove excessive progess widgets
* Add docstrings and logging
* refactor: add function for common ProgressWidget use
Extracts the create_progress_bar function from drop_import to the
ProgressWidget class. Instead of creating a ProgressWidget,
FunctionIterator, and CustomRunnable every time a thread-safe progress
widget is needed, the from_iterable function in ProgressWidget now
handles all of that.
---------
Co-authored-by: Creepler13 <denis.schlichting03@gmail.com>
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
Co-authored-by: Travis Abendshien <lvnvtravis@gmail.com>
* add files
* fix: term was parsing ANDList instead of ORList
* make mypy happy
* ruff format
* add missing todo
* add more constraint types
* add parent property to AST
* add BaseVisitor class
* make mypy happy
* add __init__.py
* Revert "make mypy happy"
This reverts commit 926d0dd2e79d06203e84e2f83c06c7fe5b33de23.
* refactoring and fixes
* rudimentary search field integration
* fix: check for None properly
* fix: Entries without Tags are now searchable
* make mypy happy
* Revert "fix: Entries without Tags are now searchable"
This reverts commit 19b40af7480b0c068b81b642b51536a9ec96d030.
* fix: changed joins to outerjoins and added missing outerjoin
* use query lang instead of tag_id FIlterState
* add todos
* fix: remove uncecessary line that broke search when searching for exact name
* fix tag search
* refactoring
* fix: path now uses GLOB operator for proper GLOBs
* refactoring: remove FilterState.id and implement Library.get_entry_full as replacement
* fix: use default value notation instead of if None statement in __post_init__
* remove obsolete Search Mode UI and related code
* ruff fixes
* remove obsolete tests
* fix: item_thumb didn't query entries correctly
* fix: search_library now correctly returns the number of *unique* entries
* make mypy happy
* implement NOT
* remove obsolete filename search
* remove summary as it is not applicable anymore
* finish refactoring of FilterState
* implement special:untagged
* fix: make mypy happy
* Revert changes to search_tags in favor of changes from #604
* fix: also port test changes
* fix: remove unneccessary import
* fix: remove unused dataclass
* fix: AND now works correctly with tags
* simplify structure of parsed AST
* add performance logging
* perf: Improve performance of search by reducing number of required joins from 4 to 1
* perf: double NOT is now optimized out of the AST
* fix: bug where pages would show less than the configured number of entries
* Revert "add performance logging"
This reverts commit c3c7d7546d.
* fix: tag_id search was broken
* somewhat adapt the existing autocompletion to this PR
* perf: Use Relational Division Queries to improve Query Execution Time
* fix: raise Exception so as to not fail silently
* fix: Parser bug broke parentheses
* little bit of clean up
* remove unnecessary comment
* add library for testing search
* feat: add basic tests
* fix: and queries containing just one tag were broken
* chore: remove debug code
* feat: more tests
* refactor: more consistent name for variable
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* fix: ruff check complaint over double import
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* port fixes from json branch
* fix: correctly pass grid_idx in autofill macro
* fix(BUILD_URL macro): only add url if url was successfully built
* fix(AUTOFILL macro): error when trying to add tag with name that already exists
* fix: test was wrongly renaming sidecar file
* Added a check to see if the library is loaded in filter_items
* Returned check to see if there is an engine
---------
Co-authored-by: gred <gred25@yandex.ru>
* feat(ui): add PagedPanel widget
* feat(ui): add MigrationModal widget
* feat: add basic json to sql conversion
* fix: chose `poolclass` based on file or memory db
* feat: migrate tag colors from json to sql
* feat: migrate entry fields from json to sql
- fix: tag name column no longer has unique constraint
- fix: tags are referenced by id in db queries
- fix: tag_search_panel no longer queries db on initialization; does not regress to empty search window when shown
- fix: tag name search no longer uses library grid FilterState object
- fix: tag name search now respects tag limit
* set default `is_new` case
* fix: limit correct tag query
* feat: migrate tag aliases and subtags from json to sql
* add migration timer
* fix(tests): fix broken tests
* rename methods, add docstrings
* revert tag id search, split tag name search
* fix: use correct type in sidecar macro
* tests: add json migration tests
* fix: drop leading dot from json extensions
* add special characters to json db test
* tests: add file path and entry field parity checks
* fix(ui): tag manager no longer starts empty
* fix: read old windows paths as posix
Addresses #298
* tests: add posix + windows paths to json library
* tests: add subtag, alias, and shorthand parity tests
* tests: ensure no none values in parity checks
* tests: add tag color test, use tag id in tag tests
* tests: fix and optimize tests
* tests: add discrepancy tracker
* refactor: reduce duplicate UI code
* fix: load non-sequential entry ids
* fix(ui): sort tags in the preview panel
* tests(fix): prioritize `None` check over equality
* fix(tests): fix multi "same tag field type" tests
* ui: increase height of migration modal
* feat: add progress bar to migration ui
* fix(ui): sql values update earlier
* refactor: use `get_color_from_str` in test
* refactor: migrate tags before aliases and subtags
* remove unused assertion
* refactor: use `json_migration_req` flag
* feat: Audio Playback
Add the ability to play audio files.
Add a slider to seek through an audio file.
Add play/pause and mute/unmute buttons for audio files.
Note: This is a continuation of a mistakenly closed PR:
Ref: https://github.com/TagStudioDev/TagStudio/pull/529
While redoing the changes, I made a couple of improvements.
When the end of the track is reached, the pause button will
swap to the play button and allow the track to be replayed.
Here is the original feature request:
Ref: https://github.com/TagStudioDev/TagStudio/issues/450
* fix: prevent autoplay on new track when paused
* refactor: Add MediaPlayer base class.
Added a MediaPlayer base class per some suggestions
in the PR comments. Hopefully this reduces duplicate code
between the audio/video player in the future.
* refactor: add controls to base MediaPlayer class
Move media controls from the AudioPlayer widget
to the MediaPlayer base class. This removes the need
for a separate AudioPlayer class, and allows the
video player to reuse the media controls.
* fix: position_label update with slider
Update the position_label when the slider is moving.
* fix: replace platform dependent time formatting
Replace the use of `-` in the time format since this
is not availabile on all platforms.
Update initial `position_label` value to '0:00'.
* fix(ui): display loading icon before rendered thumb
* fix: skip out of range thumbs
* fix: optimize library refreshing
* fix(ui): tag colors show correct names
* fix(ui): ensure inner field containers are deleted
* fix(ui): don't show default preview label text
* fix: catch all missing file thumbs; clean up logs
* fix: mypy error in ts_qt
* fix: mypy error in file_opener due to conflicting types
* fix: remove unnecessary type ignores
* refix type ignore comments
* partially revert "refix type ignore comments" due to being implemented in #608
This changes the behavior of the tag name inside `BuildTagPanel` for newly created tags:
* The default "New Tag" name is now automatically highlighted
* Blank tag names (including spaces) are no longer allowed to be created
* NOTE: This does not change the tag name column rules in the db, nor does it necessarily need to
***
* [Feature Request]: Make the create tag panel have empty tag name field
* [Feature Request]: Make the create tag panel have empty tag name field
* Revert "[Feature Request]: Make the create tag panel have empty tag name field"
This reverts commit f9c7f5d889.
* [Feature Request]: Make the create tag panel have empty tag name field
* Revert "[Feature Request]: Make the create tag panel have empty tag name field"
This reverts commit e5df3e0f15.
* Update .gitignore
* Updated as per disscussion in issue #591 (DRAFT
* Updated as per disscussion in issue #591 (DRAFT
* Added formatting
* Updated code as per discussion is #592
* Updated code as per discussion is #592 (again)
* Fixed spacing
* Add placeholder text to name field.
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Use universal red color for red border.
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* fix: add `src.core.palette` imports
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* feat: add autocomplete for mediatype, filetype, path, tag, and tag_id searches
* fix: address issues brought up in review
* fix: fix mypy issue
* fix: fix mypy issues for real this time
* fix resolution info
* Fix for Raw and Vector Image types
* Small refactor
* Create IMAGE_RASTER_TYPES and remove is_image_ext_raster
* Change if statment only for raster
* Rename _IMAGE_SET to _IMAGE_RASTER_SET
---------
Co-authored-by: gred <gred25@yandex.ru>
* feat: port v9.4 thumbnail + related feats to v9.5
Ports the following thumbnail and related PRs from the `Alpha-v9.4` branch to `main` (v9.5+):
- (#273) Blender thumbnail support
- (#307) Add font thumbnail preview support
- (#331) refactor: move type constants to new media classes
- (#390) feat(ui): expanded thumbnail and preview features
- (#370) ui: "open in explorer" action follows os name
- (#373) feat(ui): preview support for source engine files
- (#274) Refactor video_player.py (Fix#270)
- (#430) feat(ui): show file creation/modified dates + restyle path label
- (#471) fix(ui): use default audio icon if ffmpeg is absent
- (#472) fix(ui): use birthtime for creation time on mac & win
Co-Authored-By: Ethnogeny <111099761+050011-code@users.noreply.github.com>
Co-Authored-By: Theasacraft <91694323+Thesacraft@users.noreply.github.com>
Co-Authored-By: SupKittyMeow <77246128+supkittymeow@users.noreply.github.com>
Co-Authored-By: EJ Stinson <93455158+favroitegamers@users.noreply.github.com>
Co-Authored-By: Sean Krueger <71362472+seakrueger@users.noreply.github.com>
* remove vscode exceptions from `.gitignore`
* delete .vscode directory
* style: format for `ruff check`
* fix(tests): update `test_update_widgets_not_selected` test
* remove Send2Trash dependency
* refactor: use dataclass for MediaCateogry
* refactor: use enums for UI colors
* docs: add file docstring for silent_Popen
* refactor: replace logger with structlog
* use early return inside `ResourceManager.get()`
* add `is_ext_in_category()` method to `MediaCategory`
Add method to check if an extension is a member of a given MediaCategory.
* style: fix docstring style, missing type hints, rename `afm`
* fix: use structlog vars in logging
* refactor: move platform-dependent strings to PlatformStrings
* refactor: move `parents[2]` path to variable
* fix: undo logger regressions
---------
Co-authored-by: Ethnogeny <111099761+050011-code@users.noreply.github.com>
Co-authored-by: Theasacraft <91694323+Thesacraft@users.noreply.github.com>
Co-authored-by: SupKittyMeow <77246128+supkittymeow@users.noreply.github.com>
Co-authored-by: EJ Stinson <93455158+favroitegamers@users.noreply.github.com>
Co-authored-by: Sean Krueger <71362472+seakrueger@users.noreply.github.com>
* Add en.json with strings found in code
* remove unused, internal, and logging strings
This removes any string tokens for unused/unfinished features, internally facing code, and log outputs.
---------
Co-authored-by: Travis Abendshien <lvnvtravis@gmail.com>
* Fix tag search to not require wildcards
* Add partial tag check to test_tag_search
* chore: format with ruff
---------
Co-authored-by: Tyrannicodin <tyrannicodin@gmail.com>
Co-authored-by: Travis Abendshien <lvnvtravis@gmail.com>
* use sqlite + sqlalchemy as a database backend
* change entries getter
* page filterstate.page_size persistent
* add test for entry.id filter
* fix closing library
* fix tag search, adding field
* add field position
* add fields reordering
* use folder
* take field position into consideration
* fix adding tag
* fix test
* try to catch the correct exception, moron
* dont expunge subtags
* DRY models
* rename LibraryField, add is_default property
* remove field.position unique constraint
if ! use flake . --override-input devenv-root "file+file://${devenv_root_file}"; then
printf '%s\n' "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2
Before reporting, read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/doc/index.md) and search existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
Before reporting, read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/docs/index.md) and search existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
Validate that you are using an up-to-date version[^1], your issue might already be fixed!
Questions, guidance, and usage goes in [discussions](https://github.com/TagStudioDev/TagStudio/discussions). Invalid issues will be closed.
@@ -20,7 +21,7 @@ body:
options:
- label:I am using an up-to-date version.
required:true
- label:I have read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/doc/index.md).
- label:I have read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/docs/index.md).
required:true
- label:I have searched existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
required:true
@@ -51,7 +52,7 @@ body:
- type:textarea
attributes:
label:Steps to Reproduce
description:Minimal steps neded for the problem to occur.
description:Minimal steps needed for the problem to occur.
*Please add an appropriate title for this feature request.*
Before suggesting, read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/doc/index.md) and search existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
Before suggesting, read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/docs/index.md) and search existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
Validate that you are using an up-to-date version[^1], your feature might already be implemented!
Questions, guidance, and usage goes in [discussions](https://github.com/TagStudioDev/TagStudio/discussions). Invalid issues will be closed.
@@ -20,7 +21,7 @@ body:
options:
- label:I am using an up-to-date version.
required:true
- label:I have read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/doc/index.md).
- label:I have read the [documentation](https://github.com/TagStudioDev/TagStudio/blob/main/docs/index.md).
required:true
- label:I have searched existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
Thank you so much for showing interest in contributing to TagStudio! Here are a set of instructions and guidelines for contributing code or documentation to the project. This document will change over time, so make sure that your contributions still line up with the requirements here before submitting a pull request.
## Getting Started
- Check the [Planned Features](https://github.com/TagStudioDev/TagStudio/blob/main/doc/updates/planned_features.md) page, [FAQ](/README.md/#faq), as well as the open [Issues](https://github.com/TagStudioDev/TagStudio/issues) and [Pull Requests](https://github.com/TagStudioDev/TagStudio/pulls).
- If you'd like to add a feature that isn't on the roadmap or doesn't have an open issue, **PLEASE create a feature request** issue for it discussing your intentions so any feedback or important information can be given by the team first.
- We don't want you wasting time developing a feature or making a change that can't/won't be added for any reason ranging from pre-existing refactors to design philosophy differences.
### Contribution Checklist
- I've read the [Planned Features](https://github.com/TagStudioDev/TagStudio/blob/main/doc/updates/planned_features.md) page
- I've read the [FAQ](/README.md/#faq), including the "[Features I Likely Won't Add/Pull](/README.md/#features-i-likely-wont-addpull)" section
- I've checked the open [Issues](https://github.com/TagStudioDev/TagStudio/issues) and [Pull Requests](https://github.com/TagStudioDev/TagStudio/pulls)
- **I've created a new issue for my feature _before_ starting work on it**, or have at least notified others in the relevant existing issue(s) of my intention to work on it
- I've set up my development environment including Ruff and Mypy
- I've read the [Code Guidelines](#code-guidelines) and/or [Documentation Guidelines](#documentation-guidelines)
- **_I mean it, I've found or created a new issue for my feature!_**
- [Ruff](https://github.com/astral-sh/ruff) (Included in `requirements-dev.txt`)
- [Mypy](https://github.com/python/mypy) (Included in `requirements-dev.txt`)
- [PyTest](https://docs.pytest.org) (Included in `requirements-dev.txt`)
### Creating a Python Virtual Environment
If you wish to launch the source version of TagStudio outside of your IDE:
> [!IMPORTANT]
> Depending on your system, Python may be called `python`, `py`, `python3`, or `py3`. These instructions use the alias `python3` for consistency. You can check to see which alias your system uses and if it's for the correct Python version by typing `python3 --version` (or whichever alias) into your terminal.
> [!TIP]
> On Linux and macOS, you can launch the `tagstudio.sh` script to skip the following process, minus the `requirements-dev.txt` installation step. _Using the script is fine if you just want to launch the program from source._
1. In the root repository directory, create a python virtual environment:
`python3 -m venv .venv`
2. Activate your environment:
- Windows w/Powershell: `.venv\Scripts\Activate.ps1`
- Windows w/Command Prompt: `.venv\Scripts\activate.bat`
- Linux/macOS: `source .venv/bin/activate`
3. Install the required packages:
-`pip install -r requirements.txt`
- If developing (includes Ruff and Mypy): `pip install -r requirements-dev.txt`
_Learn more about setting up a virtual environment [here](https://docs.python.org/3/tutorial/venv.html)._
### Manually Launching (Outside of an IDE)
- **Windows** (start_win.bat)
- To launch TagStudio, launch the `start_win.bat` file. You can modify this .bat file or create a shortcut and add one or more additional arguments if desired.
- **Linux/macOS** (TagStudio.sh)
- Run the "TagStudio.sh" script and the program should launch! (Make sure that the script is marked as executable if on Linux). Note that launching from the script from outside of a terminal will not launch a terminal window with any debug or crash information. If you wish to see this information, just launch the shell script directly from your terminal with `./TagStudio.sh`.
- **NixOS** (Nix Flake)
> [!WARNING]
> Support for NixOS is still a work in progress.
- Use the provided [Flake](https://nixos.wiki/wiki/Flakes) to create and enter a working environment by running `nix develop`. Then, run the program via `python3 tagstudio/tag_studio.py` from the root directory.
- **Any** (No Scripts)
- Alternatively, with the virtual environment loaded, run the python file at `tagstudio\tag_studio.py` from your terminal. If you're in the project's root directory, simply run `python3 tagstudio/tag_studio.py`.
## Workflow Checks
When pushing your code, several automated workflows will check it against predefined tests and style checks. It's _highly recommended_ that you run these checks locally beforehand to avoid having to fight back-and-forth with the workflow checks inside your pull requests.
> [!TIP]
> To format the code automatically before each commit, there's a configured action available for the `pre-commit` hook. Install it by running `pre-commit install`. The hook will be executed each time on running `git commit`.
### [Ruff](https://github.com/astral-sh/ruff)
A Python linter and code formatter. Ruff uses the `pyproject.toml` as its config file and runs whenever code is pushed or pulled into the project.
#### Running Locally
- Lint code with by moving into the `/tagstudio` directory with `cd tagstudio` and running `ruff --config ../pyproject.toml`.
- Format code with `ruff format` inside the repository directory
Ruff is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff), PyCharm [plugin](https://plugins.jetbrains.com/plugin/20574-ruff), and [more](https://docs.astral.sh/ruff/integrations/).
### [Mypy](https://github.com/python/mypy)
Mypy is a static type checker for Python. It sure has a lot to say sometimes, but we recommend you take its advice when possible. Mypy also uses the `pyproject.toml` as its config file and runs whenever code is pushed or pulled into the project.
#### Running Locally
- **First time only:** Move into the `/tagstudio` directory with `cd tagstudio` and run the following:
-`mkdir -p .mypy_cache`
-`mypy --install-types --non-interactive`
- Check code by moving into the `/tagstudio` directory with `cd tagstudio`_(if you aren't already inside)_ and running `mypy --config-file ../pyproject.toml .`. _(Don't forget the `.` at the end!)_
> [!CAUTION]
> There's a known issue between PySide v6.6.3 and Mypy where Mypy will detect issues with the `.pyi` files inside of PySide and prematurely stop checking files. This issue is not present in PySide v6.6.2, which _should_ be compatible with everything else if you wish to try using that version in the meantime.
Mypy is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=matangover.mypy), PyCharm [plugin](https://plugins.jetbrains.com/plugin/11086-mypy), and [more](https://plugins.jetbrains.com/plugin/11086-mypy).
### PyTest
- Run all tests by moving into the `/tagstudio` directory with `cd tagstudio` and running `pytest tests/`.
## Code Guidelines
### Style
Most of the style guidelines can be checked, fixed, and enforced via Ruff. Older code may not be adhering to all of these guidelines, in which case _"do as I say, not as I do"..._
- Do your best to write clear, concise, and modular code.
- Try to keep a maximum column with of no more than **100** characters.
- Code comments should be used to help describe sections of code that don't speak for themselves.
- Use [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) docstrings for any classes and functions you add.
- If you're modifying an existing function that does _not_ have docstrings, you don't _have_ to add docstrings to it... but it would be pretty cool if you did ;)
- Imports should be ordered alphabetically (in newly created python files).
- When writing text for window titles, form titles, or dropdown options, use "[Title Case](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case)" capitalization. Your IDE may have a command to format this for you automatically, although some may incorrectly capitalize short prepositions. In a pinch you can use a website such as [capitalizemytitle.com](https://capitalizemytitle.com/) to check.
- If it wasn't mentioned above, then stick to [**PEP-8**](https://peps.python.org/pep-0008/)!
> [!WARNING]
> Column width limits, docstring formatting, and import sorting aren't currently checked in the Ruff workflow but likely will be in the near future.
### Implementations
- Avoid direct calls to `os`
- Use `Pathlib` library instead of `os.path`
- Use `sys.platform` instead of `os.name`
- Don't prepend local imports with `tagstudio`, stick to `src`
- Use `logging` instead of `print` statements
- Avoid nested `f-string`s
#### Runtime
- Code must function on supported versions of Windows, macOS, and Linux:
- Windows: 10, 11
- macOS: 12.0+
- Linux: TBD
- Avoid use of unnecessary logging statements in final submitted code.
- Code should not cause unreasonable slowdowns to the program outside of a progress-indicated task.
#### Git/GitHub Specifics
- Use clear and concise commit messages. If your commit does too much, either consider breaking it up into smaller commits or providing extra detail in the commit description.
- Use imperative-style present-tense commit messages. Examples:
- "Add feature foo"
- "Change method bar"
- "Fix function foobar"
- Pull Requests should have an adequate title and description which clearly outline your intentions and changes/additions. Feel free to provide screenshots, GIFs, or videos, especially for UI changes.
## Documentation Guidelines
Documentation contributions include anything inside of the `doc/` folder, as well as the `README.md` and `CONTRIBUTING.md` files.
- Use "[snake_case](https://developer.mozilla.org/en-US/docs/Glossary/Snake_case)" for file and folder names
- Follow the folder structure pattern
- Don't add images or other media with excessively large file sizes
- Provide alt text for all embedded media
- Use "[Title Case](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case)" for title capitalization
> This is still a **_very_** rough personal project of mine in its infancy. I’m open-sourcing it now in order to accept contributors sooner and to better facilitate the direction of the project from an earlier stage.
> There **_are_** bugs, and there will **_very likely_** be breaking changes!
TagStudio is a photo & file organization application with an underlying tag-based system that focuses on giving freedom and flexibility to the user. No proprietary programs or formats, no sea of sidecar files, and no complete upheaval of your filesystem structure. **Read the documentation and more at [docs.tagstud.io](https://docs.tagstud.io)!**
TagStudio is a photo & file organization application with an underlying system that focuses on giving freedom and flexibility to the user. No proprietary programs or formats, no sea of sidecar files, and no complete upheaval of your filesystem structure.
<i>TagStudio Alpha v9.5.5 running on macOS Sequoia.</i>
</p>
## Contents
-[Goals](#goals)
-[Priorities](#priorities)
-[Current Features](#current-features)
-[Contributing](#contributing)
-[Installation](#installation)
- [Usage](#usage)
- [FAQ](#faq)
- [Feature Highlights](#feature-highlights)
- [Basic Usage](#basic-usage)
- [Installation](#installation)
- [Goals & Priorities](#goals--priorities)
- [FAQ](#faq)
## Goals
Translation hosting generously provided by [Weblate](https://weblate.org/en/). Check out our [project page](https://hosted.weblate.org/projects/tagstudio/) to help translate TagStudio!
- To achieve a portable, privacy-oriented, open, extensible, and feature-rich system of organizing and rediscovering files.
- To provide powerful methods for organization, notably the concept of tag composition, or “taggable tags”.
- To create an implementation of such a system that is resilient against a user’s actions outside the program (modifying, moving, or renaming files) while also not burdening the user with mandatory sidecar files or otherwise requiring them to change their existing file structures and workflows.
- To support a wide range of users spanning across different platforms, multi-user setups, and those with large (several terabyte) libraries.
- To make the darn thing look like nice, too. It’s 2024, not 1994.
## Feature Highlights
## Priorities
### Libraries
1.**The concept.** Even if TagStudio as a project or application fails, I’d hope that the idea lives on in a superior project. The [goals](#goals) outlined above don’t reference TagStudio once - _TagStudio_ is what references the _goals._
2.**The system.** Frontends and implementations can vary, as they should. The core underlying metadata management system is what should be interoperable between different frontends, programs, and operating systems. A standard implementation for this should settle as development continues. This opens up the doors for improved and varied clients, integration with third-party applications, and more.
3.**The application.** If nothing else, TagStudio the application serves as the first (and so far only) implementation for this system of metadata management. This has the responsibility of doing the idea justice and showing just what’s possible when it comes to user file management.
4. (The name.) I think it’s fine for an app or client, but it doesn’t really make sense for a system or standard. I suppose this will evolve with time.
A TagStudio library contains all of your tags, fields for a set of files based on one of your system directories. Similar to how Obsidian [vaults](https://help.obsidian.md/vault) function, TagStudio libraries act as a layer on top of your existing folders and file structure, and don't require your to move or duplicate files.
## Current Features
TagStudio places a `.TagStudio` folder in the folder you open as a library. Files included in your library are referred to as "entries", and are kept track of inside of a SQLite database inside the `.TagStudio` folder along with tags and other library data.
- Create libraries/vaults centered around a system directory. Libraries contain a series of entries: the representations of your files combined with metadata fields. Each entry represents a file in your library’s directory, and is linked to its location.
- Add metadata to your library entries, including:
- Name, Author, Artist (Single-Line Text Fields)
- Description, Notes (Multiline Text Fields)
- Tags, Meta Tags, Content Tags (Tag Boxes)
- Create rich tags composed of a name, a list of aliases, and a list of “subtags” - being tags in which these tags inherit values from.
- Search for entries based on tags, ~~metadata~~ (TBA), or filenames/filetypes (using `filename: <query>`)
- Special search conditions for entries that are: `untagged`/`no tags` and `empty`/`no fields`.
### File Entries
> [!NOTE]
> For more information on the project itself, please see the [FAQ](#faq) section as well as the [documentation](/doc/index.md).
All file types are supported in TagStudio libraries, just not all have dedicated preview support. For a full list of filetypes with supported previews, see the "[Supported Previews](https://docs.tagstud.io/preview-support)" page on the documentation site. There's also playback support for videos, audio files, and supported animated image formats.
## Contributing
For a generalized list of what's currently supported:
If you're interested in contributing to TagStudio, please take a look at the [contribution guidelines](/CONTRIBUTING.md) for how to get started!
-**Images**
- Raster Images (JPEG, PNG, etc.)
- Vector (SVG)
- Animated (GIF, WEBP, APNG)
- RAW Formats
-**Videos**
-**Plaintext Files**
-**Documents**_(If supported)_
-**eBooks**_(If supported)_
-**Photoshop PSDs**, **Blender Projects**, **Krita Projects**, and more!
## Installation
### [Tags](https://docs.tagstud.io/tags) and [Fields](https://docs.tagstud.io/fields)
To download TagStudio, visit the [Releases](https://github.com/TagStudioDev/TagStudio/releases) section of the GitHub repository and download the latest release for your system under the "Assets" section. TagStudio is available for **Windows**, **macOS**_(Apple Silicon & Intel)_, and **Linux**. Windows and Linux builds are also available in portable versions if you want a more self-contained executable to move around.
Tags represent an object or attribute - this could be a person, place, object, concept, and more. Unlike most tagging systems, TagStudio tags are not solely represented by a line of text or a hashtag. Tags in TagStudio consist of several properties and relationships that give extra customization, searching power, and ease of tagging that cannot be achieved by string-based tags alone. TagStudio tags are designed to be as simple or as complex as you'd like, giving options to users of all skill levels and use cases.
> [!IMPORTANT]
> On macOS, you may be met with a message saying _""TagStudio" can't be opened because Apple cannot check it for malicious software."_ If you encounter this, then you'll need to go to the "Settings" app, navigate to "Privacy & Security", and scroll down to a section that says _""TagStudio" was blocked from use because it is not from an identified developer."_ Click the "Open Anyway" button to allow TagStudio to run. You should only have to do this once after downloading the application.
Tags currently consist of the following attributes:
#### Optional Arguments
-**Name**: The full name for your tag. **_This does NOT have to be unique!_**
-**Shorthand Name**: The shortest alternate name for your tag, used for abbreviations.
-**Aliases**: Alternate names your tag goes by.
-**Color**: The display color of your tag.
-**Parent Tags**: Other tags in which this tag inherits from. In practice, this means that this tag can be substituted in searches for any listed parent tags.
- Parent tags checked with the "disambiguation" checkbox next to them will be used to help disambiguate tag names that may not be unique.
- For example: If you had a tag for "Freddy Fazbear", you might add "Five Nights at Freddy's" as one of the parent tags. If the disambiguation box is checked next to "Five Nights at Freddy's" parent tag, then the tag "Freddy Fazbear" will display as "Freddy Fazbear (Five Nights at Freddy's)". Furthermore, if the "Five Nights at Freddy's" tag has a shorthand like "FNAF", then the "Freddy Fazbear" tag will display as "Freddy Fazbear (FNAF)".
-**Is Category**: A property that when checked, treats this tag as a category in the preview panel.
Optional arguments to pass to the program.
Fields, like tags, are additional pieces of custom metadata that you can add to your file entries. Fields currently have several hardcoded names (e.g. "Title", "Author", "Series") but custom field names are planned for an upcoming update.
> `--open <path>` / `-o <path>`
> Path to a TagStudio Library folder to open on start.
Field types currently include:
> `--config-file <path>` / `-c <path>`
> Path to the TagStudio config file to load.
-**Text Lines**: Single lines of text.
-**Text Boxes**: Multi-line pieces of text.
-**Datetimes**: Dates and times.
## Usage
### [Search](https://docs.tagstud.io/search)
- Search for file entries based on tags, file path (`path:`), file types (`filetype:`), and even media types! (`mediatype:`). Path searches currently use [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) syntax, so you may need to wrap your filename or filepath in asterisks while searching. This will not be strictly necessary in future versions of the program.
- Use and combine boolean operators (`AND`, `OR`, `NOT`) along with parentheses groups, quotation escaping, and underscore substitution to create detailed search queries
- Use special search conditions (`special:untagged` and `special:empty`) to find file entries without tags or fields, respectively
## Basic Usage
> [!TIP]
> For more usage instructions, see the [documentation site](https://docs.tagstud.io/libraries)!
### Creating/Opening a Library
@@ -84,161 +94,141 @@ With TagStudio opened, start by creating a new library or opening an existing on
### Refreshing the Library
In order to scan for new files or file changes, you’ll need to manually go to File -> Refresh Directories.
> [!NOTE]
> In the future, library refreshing will also be automatically done in the background, or additionally on app startup.
### Adding Metadata to Entries
To add a metadata field to a file entry, start by clicking the “Add Field” button under the file preview in the right-hand preview panel. From the dropdown menu, select the type of metadata field you’d like to add to the entry.
### Editing Metadata Fields
#### Text Line / Text Box
Hover over the field and click the pencil icon. From there, add or edit text in the dialog box popup.
#### Tag Box
Click the “+” button at the end of the Tags list, and search for tags to add inside the new dialog popup. Click the “+” button next to whichever tags you want to add. Alternatively, after you search for a tag, press the Enter/Return key to add the add the first item in the list. Press Enter/Return once more to close the dialog box
> [!WARNING]
> Keyboard control and navigation is currently _very_ buggy, but will be improved in future versions.
Libraries under 10,000 files automatically scan for new or modified files when opened. In order to refresh the library manually, select "Refresh Directories" under the File menu or by pressing <kbd>Ctrl</kbd></kbd>+<kbd>R</kbd> (macOS: <kbd>⌘ Command</kbd>+<kbd>R</kbd>).
### Creating Tags
To create a new tag, click on Edit -> New Tag from the menu bar. From there, enter a tag name, shorthand name, any tag aliases separated by newlines, any subtags, and an optional color.
Create a new tag by accessing the "New Tag" option from the Edit menu or by pressing <kbd>Ctrl</kbd>+<kbd>T</kbd> (macOS: <kbd>⌘ Command</kbd>+<kbd>T</kbd>). In the tag creation panel, enter a tag name, optional shorthand name, optional tag aliases, optional parent tags, and an optional color.
- The tag **shorthand** is a type of alias that displays in situations when screen space is more valuable (ex. as a subtag for other tags).
- **Aliases** are alternate names for a tag. These let you search for terms other than the exact tag name in order to find the tag again.
- **Subtags** are tags in which this tag is a child tag of. In other words, tags under this section are parents of this tag. For example, if you had a tag for a character from a show, you would make the show a subtag of this character. This would display as “Character (Show)” in most areas of the app. The first tag in this list is used as the tag shown in parentheses for specification.
- The **color** dropdown lets you select an optional color for this tag to display as.
#### Tag Manager
You can manage your library of tags from opening the "Tag Manager" panel from Edit -> "Tag Manager" or by pressing <kbd>Ctrl</kbd>+<kbd>M</kbd> (macOS: <kbd>⌘ Command</kbd>+<kbd>M</kbd>). From here you can create, search for, edit, and permanently delete any tags you've created in your library.
### Editing Tags
To edit a tag, right-click the tag in the tag field of the preview pane and select “Edit Tag”
To edit a tag, click on it inside the preview panel or right-click the tag and select "Edit Tag" from the context menu.
### Relinking Renamed/Moved Files
### Adding Tags to File Entries
Inevitably, some of the files inside your library will be renamed, moved, or deleted. If a file has been renamed or moved, TagStudio will display the thumbnail as a red tag with a cross through it _(this icon is also used for items with broken thumbnails)._ To relink moved files or delete these entries, go to Tools -> Manage Unlinked Entries. Click the “Refresh” button to scan your library for unlinked entries. Once complete, you can attempt to “Search & Relink” any unlinked entries to their respective files, or “Delete Unlinked Entries” in the event the original files have been deleted and you no longer wish to keep their metadata entries inside your library.
Access the "Add Tag" search box by either clicking on the "Add Tag" button at the bottom of the right sidebar, accessing the "Add Tags to Selected" option from the File menu, or by pressing <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd> (macOS: <kbd>⌘ Command</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd>).
From here you can search for existing tags or create a new one if the one you're looking for doesn't exist. Click the "+" button next to any tags you want to the currently selected file entries. To quickly add the top result, press the <kbd>Enter</kbd>/<kbd>Return</kbd> key to add the top-most tag and reset the tag search. Press <kbd>Enter</kbd>/<kbd>Return</kbd> once more to close the dialog box. By using this method, you can quickly add various tags in quick succession just by using the keyboard!
To remove a tag from a file entry, hover over the tag in the preview panel and click on the "-" icon that appears.
### Adding Fields to File Entries
To add a metadata field to a file entry, start by clicking the "Add Field" button at the bottom of the preview panel. From the dropdown menu, select the type of metadata field you’d like to add to the entry
### Editing Fields
Hover over the field and click the pencil icon. From there, add or edit text in the dialog box popup.
### Relinking Moved Files
Inevitably some of the files inside your library will be renamed, moved, or deleted. If a file has been renamed or moved, TagStudio will display the thumbnail as a red broken chain link. To relink moved files or delete these entries, select the "Manage Unlinked Entries" option under the Tools menu. Click the "Refresh" button to scan your library for unlinked entries. Once complete, you can attempt to "Search & Relink" any unlinked file entries to their respective files, or "Delete Unlinked Entries" in the event the original files have been deleted and you no longer wish to keep their entries inside your library.
> [!WARNING]
> There is currently no method to relink entries to files that have been renamed - only moved or deleted. This is a top priority for future releases.
> There is currently no method to relink entries to files that have been renamed - only moved or deleted. This is a high priority for future releases.
> [!WARNING]
> If multiple matches for a moved file are found (matches are currently defined as files with a matching filename as the original), TagStudio will currently ignore the match groups. Adding a GUI for manual selection, as well as smarter automated relinking, are top priorities for future versions.
> If multiple matches for a moved file are found (matches are currently defined as files with a matching filename as the original), TagStudio will currently ignore the match groups. Adding a GUI for manual selection, as well as smarter automated relinking, are high priorities for future versions.
### Saving the Library
See instructions in the "[Creating Development Environment](/CONTRIBUTING.md/#creating-a-development-environment)" section from the [contributing](https://docs.tagstud.io/contributing) page.
Libraries are saved upon exiting the program. To manually save, select File -> Save Library from the menu bar. To save a backup of your library, select File -> Save Library Backup from the menu bar.
## Installation
### Half-Implemented Features
To download executable builds of TagStudio, visit the [Releases](https://github.com/TagStudioDev/TagStudio/releases) page of the GitHub repository and download the latest release for your system under the "Assets" section at the bottom of the release.
#### Fix Duplicate Files
TagStudio has builds for **Windows**, **macOS**_(Apple Silicon & Intel)_, and **Linux**. We also offer portable releases for Windows and Linux which are self-contained and easier to move around.
Load in a .dupeguru file generated by [dupeGuru](https://github.com/arsenetar/dupeguru/) and mirror metadata across entries marked as duplicates. After mirroring, return to dupeGuru to manage deletion of the duplicate files. After deletion, use the “Fix Unlinked Entries” feature in TagStudio to delete the duplicate set of entries for the now-deleted files
For detailed instructions, installation help, and instructions for developing for TagStudio, please see the "[Installation](https://docs.tagstud.io/install)" page on our documentation website.
> [!IMPORTANT]
> If you're interested in contributing to TagStudio, please take a look at the [contribution guidelines](https://docs.tagstud.io/contributing) for how to get started!
### Third-Party Dependencies
For video thumbnails and playback, you'll also need [FFmpeg](https://ffmpeg.org/download.html) installed on your system. If you encounter any issues with this, please reference our [FFmpeg Help](/docs/help/ffmpeg.md) guide. For faster library scanning and refreshing, it's also recommended you install [ripgrep](https://github.com/BurntSushi/ripgrep).
<!-- prettier-ignore -->
> [!CAUTION]
> While this feature is functional, it’s a pretty roundabout process and can be streamlined in the future.
> **We do not currently publish TagStudio to any package managers. Any TagStudio distributions outside of the GitHub [Releases](https://github.com/TagStudioDev/TagStudio/releases) page are _unofficial_ and not maintained by us.**
>
> Installation support will not be given to users installing from unofficial sources. Use these versions at your own risk!
#### Image Collage
## Goals & Priorities
Create an image collage of your photos and videos.
TagStudio aims to create an **open** and **robust** format for file tagging that isn't burdened by the limitations of traditional tagging and file metadata systems. **TagStudio** is the first proof-of-concept implementation of this system.
> [!CAUTION]
> Collage sizes and options are hardcoded, and there's no GUI indicating the process of the collage creation.
See the [**Roadmap**](docs/roadmap.md) on the documentation site for a complete list of planned features and estimated timeline.
#### Macros
### Overall Goals
Apply tags and other metadata automatically depending on certain criteria. Set specific macros to run when the files are added to the library. Part of this includes applying tags automatically based on parent folders.
- To achieve a portable, private, extensible, open-format, and feature-rich system of organizing and rediscovering files.
- To provide powerful methods for organization, notably the concept of tag inheritance, or "taggable tags" _(and in the near future, the combination of composition-based tags)._
- To create an implementation of such a system that is resilient against a user’s actions outside the program (modifying, moving, or renaming files) while also not burdening the user with mandatory sidecar files or requiring them to change their existing file structures and workflows.
- To support a wide range of users spanning across different platforms, multi-user setups, and those with large (several terabyte) libraries.
- To make the dang thing look nice, too. It’s 2025, not 1995.
> [!CAUTION]
> Macro options are hardcoded, and there’s currently no way for the user to interface with this (still incomplete) system at all.
### Project Priorities
#### Gallery-dl Sidecar Importing
Import JSON sidecar data generated by [gallery-dl](https://github.com/mikf/gallery-dl).
> [!CAUTION]
> This feature is not supported or documented in any official capacity whatsoever. It will likely be rolled-in to a larger and more generalized sidecar importing feature in the future.
## Launching/Building From Source
See instructions in the "[Creating Development Environment](/CONTRIBUTING.md/#creating-a-development-environment)" section from the [contribution documentation](/CONTRIBUTING.md).
1.**The concept.** Even if TagStudio as an application fails, I’d hope that the idea lives on in a superior project. The goals outlined above don’t reference TagStudio once - _TagStudio_ is what references the _goals._
2.**The system.** Frontends and implementations can vary, as they should. The core underlying metadata management system is what should be interoperable between different frontends, programs, and operating systems. A standard implementation for this should settle as development continues. This opens up the doors for improved and varied clients, integration with third-party applications, and more.
3.**The application.** If nothing else, TagStudio the application serves as the first (and so far only) implementation for this system of metadata management. This has the responsibility of doing the idea justice and showing just what’s possible when it comes to user file management.
## FAQ
### What State Is the Project Currently In?
### Will TagStudio move, modify, or mess with my files?
As of writing (Alpha v9.3.0) the project is in a useable state, however it lacks proper testing and quality of life features.
**No**, outside of _explicit_ functionality such as "Move File(s) to Trash".
### What Features Are You Planning on Adding?
### Will TagStudio require me to recreate my tags or library in future updates?
> [!IMPORTANT]
> See the [Planned Features](/doc/updates/planned_features.md) documentation for the latest feature lists. The lists here are currently being migrated over there with individual pages for larger features.
**No.** It's our highest priority to ensure that your data safely and smoothly transfers over to newer versions.
Of the several features I have planned for the project, these are broken up into “priority” features and “future” features. Priority features were originally intended for the first public release, however are currently absent from the Alpha v9.x.x builds.
### What state is the project currently in?
#### Priority Features
As of writing (Alpha v9.5.5) the project is very usable, however there's still some quirks and missing QoL features. Several additional features and changes are still planned (see: [roadmap](https://docs.tagstud.io/roadmap)) that add even more power and flexibility to the tagging and field systems while making it easier to tag in bulk and perform automated operations. Bugfixes and polishes are constantly trickling in along with the larger feature releases.
- Improved search
- Sortable Search
- Boolean Search
- Coexisting Text + Tag Search
- Searchable File Metadata
- Comprehensive Tag management tab
- Easier ways to apply tags in bulk
- Tag Search Panel
- Recent Tags Panel
- Top Tags Panel
- Pinned Tags Panel
- Better (stable, performant) library grid view
- Improved entry relinking
- Cached thumbnails
- Tag-like Groups
- Resizable thumbnail grid
- User-defined metadata fields
- Multiple directory support
- SQLite (or similar) save files
- Reading of EXIF and XMP fields
- Improved UI/UX
- Better internal API for accessing Entries, Tags, Fields, etc. from the library.
- Proper testing workflow
- Continued code cleanup and modularization
- Exportable/importable library data including "Tag Packs"
### What features are you planning on adding?
#### Future Features
See the [roadmap](https://docs.tagstud.io/roadmap) page for the core features being planned and implemented for TagStudio. For a more up to date look on what's currently being added for upcoming releases, see our GitHub [milestones](https://github.com/TagStudioDev/TagStudio/milestones) for versioned releases.
- Support for multiple simultaneous users/clients
- Draggable files outside the program
- Comprehensive filetype whitelist
- A finished “macro system” for automatic tagging based on predetermined criteria.
- Different library views
- Date and time fields
- Entry linking/referencing
- Audio waveform previews
- 3D object previews
- Additional previews for miscellaneous file types
- Optional global tags and settings, spanning across libraries
- Importing & exporting libraries to/from other programs
- Port to a more performant language and modern frontend (Rust?, Tauri?, etc.)
- Plugin system
- Local OCR search
- Support for local machine learning-based tag suggestions for images
- Mobile version _(FAR future)_
The most important remaining features before I consider the program to be "feature complete" are:
#### Features I Likely Won’t Add/Pull
- Custom names for Fields
- List views for files
- Multiple root directory support for libraries
- Improved file entry relinking
- File entry groups
- Sorting by file date modified and created
- Macros
- Improved search bar with visualized tags and improved autocomplete
- Side panel for easier tagging (pinned tags, recent tags, tag search, tag palette)
- Improved tag management interface
- Improved and finalized Tag Categories
- Fixed and improved mixed entry data displays (see: [#337](https://github.com/TagStudioDev/TagStudio/issues/337))
- Sharable tag data
- Separate core library + API
- Native Cloud Integration
- There are plenty of services already (native or third-party) that allow you to mount your cloud drives as virtual drives on your system. Pointing TagStudio to one of these mounts should function similarly to what native integration would look like.
- Native ChatGPT/Non-Local LLM Integration
- This could mean different things depending on what you're intending. Whether it's trying to use an LLM to replace the native search, or to trying to use a model for image recognition, I'm not interested in hooking people's TagStudio libraries into non-local LLMs such as ChatGPT and/or turn the program into a "chatbot" interface (see: [Goals/Privacy](#goals)). I wouldn't, however, mind using **locally** hosted models to provide the _optional_ ability for additional searching and tagging methods (especially when it comes to facial recognition).
### What features will NOT be added?
### Why Is the Version Already v9?
- Native Cloud Integration
- There are plenty of services already (native or third-party) that allow you to mount your cloud drives as virtual drives on your system. Hosting a TagStudio library on one of these mounts should function similarly to what native integration would look like.
- Supporting native cloud integrations such as these would be an unnecessary "reinventing the wheel" burden for us that is outside the scope of this project.
- This could mean different things depending on your intentions. Whether it's trying to use an LLM to replace the native search, or to trying to use a model for image recognition, I'm not interested in hooking people's TagStudio libraries into non-local LLMs such as ChatGPT and/or turn the program into a "chatbot" interface (see: [Overall Goals/Privacy](#overall-goals)).
- With that being said, the future TagStudio API should be well-suited to connect to any sort of service you'd like, including machine learning models if so you choose. I just won't _personally_ add any native integrations with online services.
I’ve been developing this project over several years in private, and have gone through several major iterations and rewrites in that time. This “major version” is just a number at the end of the day, and if I wanted to I couldn’t released this as “Version 0” or “Version 1.0”, but I’ve decided to stick to my original version numbers to avoid needing to go in and change existing documentation and code comments. Version 10 is intended to include all of the “Priority Features” I’ve outlined in the [previous](#what-features-are-you-planning-on-adding) section. I’ve also labeled this version as an Alpha, and will likely reset the numbers when a feature-complete beta is reached.
### Is a Rust port coming?
### Wait, Is There a CLI Version?
Not from us, or at least _not quite_. There are plans to break off the core TagStudio library into its own MIT-licensed module that can be used in other applications and plugins, and ideally this would be written in Rust. While I understand there's a lot of vocal support and volunteers willing to help with this, it's something that's better off coming at my/our own pace in order to ensure it's done correctly to align with the project's intentions and to remain maintainable in the future.
As of right now, **no**. However, I _did_ have a CLI version in the recent past before dedicating my efforts to the Qt GUI version. I’ve left in the currently-inoperable CLI code just in case anyone was curious about it. Also yes, it’s just a bunch of glorified print statements (_the outlook for some form of curses on Windows didn’t look great at the time, and I just needed a driver for the newly refactored code...)._
### Windows Defender thinks TagStudio is a virus or a trojan, why?
Unfortunately, executable Python applications "compiled" with something like PyInstaller are notorious for raising false positives in anti-virus software, most commonly Windows Defender (see: [#276](https://github.com/TagStudioDev/TagStudio/issues/276) and related issues). There's really not much we can do about this on our end, as the malware matches frequently change and sample submissions to Microsoft are slow and often ineffective. If you're effected by this, you may need to allow TagStudio to bypass your anti-virus software.
### Why is TagStudio already on version 9.x?
Over the first few years of private development the project went through several major iterations and rewrites. These major version bumps came quickly, and by the time TagStudio was opened-sourced the version number had already reached v9.0. Instead of resetting to "v0.0" or "v1.0" for this public release I decided to keep my v9.x numbering scheme and reserve v10.0 for when all the core features on the [roadmap](https://docs.tagstud.io/roadmap/) are implemented. I’ve also labeled this version as an "Alpha" and will drop this once either all of the core features are implemented or the project feels stable and feature-rich enough to be considered "Beta" and beyond.
> This documentation is still a work in progress, and is intended to aide with deconstructing and understanding of the core mechanics of TagStudio and how it operates.
The "Database Migration", "DB Migration", or "SQLite Migration" is an upcoming update to TagStudio which will replace the current JSON [library](/doc/library/library.md) with a SQL-based one, and will additionally include some fundamental changes to how some features such as [tags](/doc/library/tag.md) will work.
Entries are the units that fill a [library](/doc/library/library.md). Each one corresponds to a file, holding a reference to it along with the metadata associated with it.
### Entry Object Structure
1.`id`:
- Int, Unique, **Required**
- The ID for the Entry.
- Used for internal processing
2.`filename`:
- String, **Required**
- The filename with extension of the referenced media file.
3.`path`:
- String, **Required**, OS Agnostic
- The folder path in which the media file is located in.
4. [`fields`](/doc/library/field.md):
- List of dicts, Optional
- A list of Field ID/Value dicts.
NOTE: _Entries currently have several unused optional fields intended for later features._
## Retrieving Entries based on [Tag](/doc/library/tag.md) Cluster
By default when querying Entries, each Entry's `tags` list (stored in the form of Tag `id`s) is compared against the Tag `id`s in a given Tag cluster (list of Tag `id`s) or appended clusters in the case of multi-term queries. The type of comparison depends on the type of query and whether or not it is an inclusive or exclusive query, or a combination of both. This default searching behavior is done in _O(n)_ time, but can be sped up in the future by building indexes on certain search terms. These indexes can be stored on disk and loaded back into memory in future sessions. These indexes will also need to be updated as new Tags and Entries are added or edited.
Entries can be grouped via tags marked as “groups” which when applied to different entries will signal TagStudio to treat those entries as a single group inside of searches and browsing.
Fields are the building blocks of metadata stored in [entries](/doc/library/entry.md). Fields have several base types for representing different kinds of information, including:
#### `text_line`
- A string of text, displayed as a single line.
- e.g: Title, Author, Artist, URL, etc.
#### `text_box`
- A long string of text displayed as a box of text.
- e.g: Description, Notes, etc.
#### `tag_box`
- A box of [tags](/doc/library/tag.md) defined and added by the user.
- Multiple tag boxes can be used to separate classifications of tags.
- e.g: Content Tags, Meta Tags, etc.
#### `datetime` [WIP]
- A date and time value.
- e.g: Date Created, Date Modified, Date Taken, etc.
#### `checkbox` [WIP]
- A simple two-state checkbox.
- Can be associated with a tag for quick organization.
- e.g: Archive, Favorite, etc.
#### `collation` [obsolete]
- Previously used for associating files to be used in a [collation](/doc/utilities/macro.md#create-collage), will be removed in favor of a more flexible feature in future updates.
The library is how TagStudio represents your chosen directory, with every file inside of it being displayed as an [entry](/doc/library/entry.md). You can have as many or few libraries as you wish, since each libraries' data is stored within a "`.TagStudio`" folder at its root.
Note that this means [tags](/doc/library/tag.md) you create only exist _per-library_.
Tags are user-defined attributes made up of one or more keywords, aliases, and relationships to other tags. A person, place, thing, concept, you name it! Tags allow for a more sophisticated way to organize and search [entries](/doc/library/entry.md) thanks to their aliases, parent tags, and more.
Tags can be as simple or complex as wanted, so that any user can tune TagStudio to fit their needs.
Among the things that make tags so useful, aliases give the ability to contain alternate names and spellings, making searches intuitive and expansive. Furthermore, parent-tags/subtags offer relational organization capabilities for the structuring and connection of the [library's](/doc/library/library.md) contents.
## Tag Object Structure
#### `id`
ID for the tag.
- Int, Unique, Required
- Used for internal processing
#### `name`
The normal name of the tag, with no shortening or specification.
- String, Required
- Doesn't have to be unique
- Used for display, searching, and storing
#### `shorthand`
The shorthand name for the tag. Works like an alias but is used for specific display purposes.
- String, Optional
- Doesn't have to be unique
- Used for display and searching
#### `aliases`
Alternate names for the tag.
- List of Strings, Optional
- Recommended to be unique to this tag
- Used for searching
#### `subtags`
Other Tags that make up properties of this tag. Also called "parent tags".
- List of Strings, Optional
- Used for display (first parent tag only) and searching.
#### `color`
A color name string for customizing the tag's display color
- String, Optional
- Used for display
## Tag Search Examples:
Using for example, a library of files including some tagged with the following tags:
Replaces [Tag Fields](/doc/library/field.md#tag_box). Tags are able to be marked as a “category” which then displays as tag fields currently do, with any tags inheriting from that category being displayed underneath.
Tag overrides are the ability to add or remove [parent tags](/doc/library/tag.md#subtags) from a [tag](/doc/library/tag.md) on a per- [entry](/doc/library/entry.md) basis. Relies on the [Database Migration](/doc/updates/db_migration.md) update being complete.
The database migration is an upcoming refactor to TagStudio's library data storage system. The database will be migrated from a JSON-based one to a SQLite-based one. Part of this migration will include a reworked schema, which will allow for several new features and changes to how [tags](/doc/library/tag.md) and [fields](/doc/library/field.md) operate.
Tools and macros are features that serve to create a more fluid [library](/doc/library/library.md)-managing process, or provide some extra functionality. Please note that some are still in active development and will be fleshed out in future updates.
## Tools
### Fix Unlinked Entries
This tool displays the number of unlinked [entries](/doc/library/entry.md), and some options for their resolution.
1. Refresh
- Scans through the library and updates the unlinked entry count.
2. Search & Relink
- Attempts to automatically find and reassign missing files.
3. Delete Unlinked Entries
- Displays a confirmation prompt containing the list of all missing files to be deleted before committing to or cancelling the operation.
### Fix Duplicate Files
This tool allows for management of duplicate files in the library using a [DupeGuru](https://dupeguru.voltaicideas.net/) file.
1. Load DupeGuru File
- load the "results" file created from a DupeGuru scan
2. Mirror Entries
- Duplicate entries will have their contents mirrored across all instances. This allows for duplicate files to then be deleted with DupeGuru as desired, without losing the [field](/doc/library/field.md) data that has been assigned to either. (Once deleted, the "Fix Unlinked Entries" tool can be used to clean up the duplicates)
### Create Collage
This tool is a preview of an upcoming feature. When selected, TagStudio will generate a collage of all the contents in a Library, which can be found in the Library folder ("/your-folder/.TagStudio/collages/"). Note that this feature is still in early development, and doesn't yet offer any customization options.
## Macros
### Auto-fill [WIP]
Tool is in development and will be documented in future update.
### Sort fields
Tool is in development, will allow for user-defined sorting of [fields](/doc/library/field.md).
### Folders to Tags
Creates tags from the existing folder structure in the library, which are previewed in a hierarchy view for the user to confirm. A tag will be created for each folder and applied to all entries, with each subfolder being linked to the parent folder as a [parent tag](/doc/library/tag.md#subtags). Tags will initially be named after the folders, but can be fully edited and customized afterwards.
TagStudio features a variety of built-in tag colors, alongside the ability for users to create their own custom tag color palettes.
## Tag Color Manager
The Tag Color Manager is where you can create and manage your custom tag colors and associated namespaces. You can access the Tag Color Manager from the "File -> Manage Tag Colors" option in the menu bar.

## Creating a Namespace
TagStudio uses namespaces to group colors into palettes. Namespaces are a way for you to use the same color name across multiple palettes without having to worry about [name collision](https://en.wikipedia.org/wiki/Name_collision) with other palettes. This is especially useful when sharing your color palettes with others!\*
_\* Color pack sharing coming in a future update_
To create your first namespace, either click the "New Namespace" button or the large button prompt underneath the built-in colors.

### Name
The display name of the namespace, used for presentation.
### ID Slug
An internal ID for the namespace which is automatically derived from the namespace name.
Namespaces beginning with "tagstudio" are reserved by TagStudio and will automatically have their text changed.
<!-- prettier-ignore -->
!!! note
It's currently not possible to manually edit the Namespace ID Slug. This will be possible once sharable color packs are added.
## Creating a Color
Once you've created your first namespace, click the "+" button inside the namespace section to create a color. To edit a color that you've previously created, either click on the color name or right click and select "Edit Color" from the context menu.

### Name
The display name for the color, used for presentation. You may occasionally see the color name followed by the [namespace name](#name) in parentheses to disambiguate it from other colors with the same name.
### ID Slug
Similar to [Namespace ID Slugs](#id-slug), the ID Slug is used as an internal ID and is automatically derived from the tag color name.
<!-- prettier-ignore -->
!!! note
It's currently not possible to manually edit the Color ID Slug. This will be possible once sharable color packs are added.
### Primary Color
The primary color is used as the main tag color and by default is used as the background color with the text and border colors being derived from this color.
### Secondary Color
By default, the secondary color is only used as an optional override for the tag text color. This color can be cleared by clicking the adjacent "Reset" button.

The secondary color can also be used as the tag border color by checking the "Use Secondary Color for Border" box.

## Using Colors
When editing a tag, click the tag color button to bring up the tag color selection panel. From here you can choose any built-in TagStudio color as well as any of your custom colors.

Thank you so much for showing interest in contributing to TagStudio! Here are a set of instructions and guidelines for contributing code or documentation to the project. This document will change over time, so make sure that your contributions still line up with the requirements here before submitting a pull request.
## Getting Started
- Check the [Feature Roadmap](roadmap.md) page to see what priority features there are, the [FAQ](https://github.com/TagStudioDev/TagStudio/blob/main/README.md#faq), as well as the project's [Issues](https://github.com/TagStudioDev/TagStudio/issues) and [Pull Requests](https://github.com/TagStudioDev/TagStudio/pulls).
- If you'd like to add a feature that isn't on the feature roadmap or doesn't have an open issue, **PLEASE create a feature request** issue for it discussing your intentions so any feedback or important information can be given by the team first.
- We don't want you wasting time developing a feature or making a change that can't/won't be added for any reason ranging from pre-existing refactors to design philosophy differences.
-**Please don't** create pull requests that consist of large refactors, _especially_ without discussing them with us first. These end up doing more harm than good for the project by continuously delaying progress and disrupting everyone else's work.
- If you wish to discuss TagStudio further, feel free to join the [Discord Server](https://discord.com/invite/hRNnVKhF2G)!
### Contribution Checklist
- I've read the [Feature Roadmap](roadmap.md) page
- I've read the [FAQ](https://github.com/TagStudioDev/TagStudio/blob/main/README.md#faq)
- I've checked the project's [Issues](https://github.com/TagStudioDev/TagStudio/issues) and [Pull Requests](https://github.com/TagStudioDev/TagStudio/pulls)
-**I've created a new issue for my feature/fix _before_ starting work on it**, or have at least notified others in the relevant existing issue(s) of my intention to work on it
- I've set up my development environment including Ruff, Mypy, and PyTest
- I've read the CONTRIBUTING.md/Contributing page on the documentation site as well as the and/or [Style Guide](style.md)
-**_I mean it, I've found or created an issue for my feature/fix!_**
<!-- prettier-ignore -->
!!! note
If the fix is small and self-explanatory (i.e. a typo), then it doesn't require an issue to be opened first. Issue tracking is supposed to make our lives easier, not harder. Please use your best judgement to minimize the amount of work for everyone involved.
## Creating a Development Environment
If you wish to develop for TagStudio, you'll need to create a development environment by installing the required dependencies. You have a number of options depending on your level of experience and familiarly with existing Python toolchains.
If you know what you're doing and have developed for Python projects in the past, you can get started quickly with the "Brief Instructions" below. Otherwise, please see the full instructions on the documentation website for "[Creating a Development Environment](https://docs.tagstud.io/install/#creating-a-development-environment)".
### Brief Instructions
1. Have [Python 3.12](https://www.python.org/downloads/) and PIP installed. Also have [FFmpeg](https://ffmpeg.org/download.html) installed if you wish to have audio/video playback and thumbnails.
2. Clone the repository to the folder of your choosing:
3. Use a dependency manager such as [uv](https://docs.astral.sh/uv/) or [Poetry 2.0](https://python-poetry.org/blog/category/releases/) to install the required dependencies, or alternatively create and activate a [virtual environment](https://docs.tagstud.io/install/#manual-installation) with `venv`.
4. If using a virtual environment instead of a dependency manager, install an editable version of the program and development dependencies with the following PIP command:
```
pip install -e ".[dev]"
```
Otherwise, modify the command above for use with your dependency manager of choice. For example if using uv, you may use this:
```
uv pip install -e ".[dev]"
```
## Workflow Checks
When pushing your code, several automated workflows will check it against predefined tests and style checks. It's _highly recommended_ that you run these checks locally beforehand to avoid having to fight back-and-forth with the workflow checks inside your pull requests.
<!-- prettier-ignore -->
!!! tip
To format the code automatically before each commit, there's a configured action available for the `pre-commit` hook. Install it by running `pre-commit install`. The hook will be executed each time on running `git commit`.
### [Ruff](https://github.com/astral-sh/ruff)
A Python linter and code formatter. Ruff uses the `pyproject.toml` as its config file and runs whenever code is pushed or pulled into the project.
#### Running Locally
Inside the root repository directory:
- Lint code with `ruff check`
- Some linting suggestions can be automatically formatted with `ruff check --fix`
- Format code with `ruff format`
Ruff should automatically discover the configuration options inside the [pyproject.toml](https://github.com/TagStudioDev/TagStudio/blob/main/pyproject.toml) file. For more information, see the [ruff configuration discovery docs](https://docs.astral.sh/ruff/configuration/#config-file-discovery).
Ruff is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff), PyCharm [plugin](https://plugins.jetbrains.com/plugin/20574-ruff), and [more](https://docs.astral.sh/ruff/integrations/).
### [Mypy](https://github.com/python/mypy)
Mypy is a static type checker for Python. It sure has a lot to say sometimes, but we recommend you take its advice when possible. Mypy also uses the `pyproject.toml` as its config file and runs whenever code is pushed or pulled into the project.
#### Running Locally
- **(First time only)** Run the following:
- `mkdir -p .mypy_cache`
- `mypy --install-types --non-interactive`
- You can now check code by running `mypy --config-file pyproject.toml .` in the repository root. _(Don't forget the "." at the end!)_
Mypy is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=matangover.mypy), PyCharm [plugin](https://plugins.jetbrains.com/plugin/11086-mypy), and [more](https://plugins.jetbrains.com/plugin/11086-mypy).
### PyTest
- Run all tests by running `pytest tests/` in the repository root.
## Code Style
See the [Style Guide](style.md)
### Modules & Implementations
- **Do not** modify legacy library code in the `src/core/library/json/` directory
- Avoid direct calls to `os`
- Use `Pathlib` library instead of `os.path`
- Use `platform.system()` instead of `os.name` and `sys.platform`
- Don't prepend local imports with `tagstudio`, stick to `src`
- Use the `logger` system instead of `print` statements
- Avoid nested f-strings
- Use HTML-like tags inside Qt widgets over stylesheets where possible
### Commit and Pull Request Style
- Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) as a guideline for commit messages. This allows us to easily generate changelogs for releases.
- See some [examples](https://www.conventionalcommits.org/en/v1.0.0/#examples) of what this looks like in practice.
- Use clear and concise commit messages. If your commit does too much, either consider breaking it up into smaller commits or providing extra detail in the commit description.
- Pull requests should have an adequate title and description which clearly outline your intentions and changes/additions. Feel free to provide screenshots, GIFs, or videos, especially for UI changes.
- Pull requests should ideally be limited to **a single** feature or fix.
<!-- prettier-ignore -->
!!! important
**Please do not force push if your PR is open for review!** Force pushing makes it impossible to discern which changes have already been reviewed and which haven't. This means a reviewer will then have to re-review all the already reviewed code, which is a lot of unnecessary work for reviewers.
<!-- prettier-ignore -->
!!! tip
If you're unsure where to stop the scope of your PR, ask yourself: _"If I broke this up, could any parts of it still be used by the project in the meantime?"_
### Runtime Requirements
- Final code must function on supported versions of Windows, macOS, and Linux:
- Windows: 10, 11
- macOS: 13.0+
- Linux: _Varies_
- Final code must **_NOT:_**
- Contain superfluous or unnecessary logging statements
- Cause unreasonable slowdowns to the program outside of a progress-indicated task
- Cause undesirable visual glitches or artifacts on screen
## Documentation Guidelines
Documentation contributions include anything inside of the `docs/` folder, as well as the `README.md` and `CONTRIBUTING.md` files. Documentation inside the `docs/` folder is built and hosted on our static documentation site, [docs.tagstud.io](https://docs.tagstud.io/).
- Use "[dash-case / kebab-case](https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case)" for file and folder names
- Follow the folder structure pattern
- Don't add images or other media with excessively large file sizes
- Provide alt text for all embedded media
- Use "[Title Case](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case)" for title capitalization
## Translation Guidelines
Translations are performed on the TagStudio [Weblate project](https://hosted.weblate.org/projects/tagstudio/).
If you wish to develop for TagStudio, you'll need to create a development environment by installing the required dependencies. You have a number of options depending on your level of experience and familiarity with existing Python toolchains.
<!-- prettier-ignore -->
!!! tip "Contributing"
If you wish to contribute to TagStudio's development, please read our [CONTRIBUTING.md](https://github.com/TagStudioDev/TagStudio/blob/main/CONTRIBUTING.md)!
## Installing Python
Python [3.12](https://www.python.org/downloads) is required to develop for TagStudio. Any version matching "Python 3.12.x" should work, with "x" being any number. Alternatively you can use a tool such as [pyenv](https://github.com/pyenv/pyenv) to install this version of Python without affecting any existing Python installations on your system. Tools such as [uv](#installing-with-uv) can also install Python versions.
<!-- prettier-ignore -->
!!! info "Python Aliases"
Depending on your system, Python may be called `python`, `py`, `python3`, or `py3`. These instructions use the alias `python` for consistency.
If you already have Python installed on your system, you can check the version by running the following command:
```sh
python --version
```
---
### Installing with pyenv
If you choose to install Python using pyenv, please refer to the following instructions:
1. Follow pyenv's [install instructions](https://github.com/pyenv/pyenv/?tab=readme-ov-file#installation) for your system.
2. Install the appropriate Python version with pyenv by running `pyenv install 3.12` (This will **not** mess with your existing Python installation).
3. Navigate to the repository root folder in your terminal and run `pyenv local 3.12`. You could alternatively use `pyenv shell 3.12` or `pyenv global 3.12` instead to set the Python version for the current terminal session or the entire system respectively, however using `local` is recommended.
---
## Cloning from GitHub
The repository can be cloned/downloaded via `git` in your terminal, or by downloading the zip file from the "Code" button on the [repository page](https://github.com/TagStudioDev/TagStudio).
To install the required dependencies, you can use a dependency manager such as [uv](https://docs.astral.sh/uv) or [Poetry 2.0](https://python-poetry.org). Alternatively you can create a virtual environment and manually install the dependencies yourself.
### Installing with uv
If using [uv](https://docs.astral.sh/uv), you can install the dependencies for TagStudio with the following command:
```sh
uv pip install -e ".[dev]"
```
A reference `.envrc` is provided for use with [direnv](#direnv), see [`contrib/.envrc-uv`](https://github.com/TagStudioDev/TagStudio/blob/main/contrib/.envrc-uv).
---
### Installing with Poetry
If using [Poetry](https://python-poetry.org), you can install the dependencies for TagStudio with the following command:
```sh
poetry install --with dev
```
---
### Manual Installation
If you choose to manually set up a virtual environment and install dependencies instead of using a dependency manager, please refer to the following instructions:
<!-- prettier-ignore -->
!!! tip "Virtual Environments"
Learn more about setting up a virtual environment with Python's [official tutorial](https://docs.python.org/3/tutorial/venv.html).
1. In the root repository directory, create a python virtual environment:
```sh
python -m venv .venv
```
2. Activate your environment:
- Windows w/Powershell: `.venv\Scripts\Activate.ps1`
- Windows w/Command Prompt: `.venv\Scripts\activate.bat`
- Linux/macOS: `source .venv/bin/activate`
<!-- prettier-ignore -->
!!! info "Supported Shells"
Depending on your system, the regular activation script _might_ not work on alternative shells. In this case, refer to the table below for supported shells:
| Shell | Script |
| ---------: | :------------------------ |
| Bash/ZSH | `.venv/bin/activate` |
| Fish | `.venv/bin/activate.fish` |
| CSH/TCSH | `.venv/bin/activate.csh` |
| PowerShell | `.venv/bin/activate.ps1` |
3. Use the following PIP command to create an editable installation and install the required development dependencies:
```sh
pip install -e ".[dev]"
```
## Nix(OS)
If using [Nix](https://nixos.org/), there is a development environment already provided in the [flake](https://wiki.nixos.org/wiki/Flakes) that is accessible with the following command:
```sh
nix develop
```
A reference `.envrc` is provided for use with [direnv](#direnv), see [`contrib/.envrc-nix`](https://github.com/TagStudioDev/TagStudio/blob/main/contrib/.envrc-nix).
## Tooling
### Editor Integration
The entry point for TagStudio is `src/tagstudio/main.py`. You can target this file from your IDE to run or connect a debug session. The example(s) below show off example launch scripts for different IDEs. Here you can also take advantage of [launch arguments](./usage.md/#launch-arguments) to pass your own test [libraries](libraries.md) to use while developing. You can find more editor configurations in [`contrib`](https://github.com/TagStudioDev/TagStudio/tree/main/contrib).
There is a [pre-commit](https://pre-commit.com/) configuration that will run through some checks before code is committed. Namely, mypy and the Ruff linter and formatter will check your code, catching those nits right away.
Once you have pre-commit installed, just run:
```sh
pre-commit install
```
From there, Git will automatically run through the hooks during commit actions!
### direnv
You can automatically enter this development shell, and keep your user shell, with a tool like [direnv](https://direnv.net/). Some reference `.envrc` files are provided in the repository at [`contrib`](https://github.com/TagStudioDev/TagStudio/tree/main/contrib).
Two currently available are for [Nix](#nixos) and [uv](#installing-with-uv), to use one:
```sh
ln -s .envrc-$variant .envrc
```
You will have to allow usage of it.
<!-- prettier-ignore -->
!!! warning "direnv Security Framework"
These files are generally a good idea to check, as they execute commands on directory load. direnv has a security framework to only run `.envrc` files you have allowed, and does keep track on if it has changed. So, with that being said, the file may need to be allowed again if modifications are made.
```sh
cat .envrc # You are checking them, right?
direnv allow
```
## Building
To build your own executables of TagStudio, first follow the steps in "[Installing Dependencies](#installing-dependencies)." Once that's complete, run the following PyInstaller command:
```
pyinstaller tagstudio.spec
```
If you're on Windows or Linux and wish to build a portable executable, then pass the following flag:
```
pyinstaller tagstudio.spec -- --portable
```
The resulting executable file(s) will be located in a new folder named "dist".
Entries are the individual representations of your files inside a TagStudio [library](./index.md). Each one corresponds one-to-one to a file on disk, and tracks all of the additional [tags](tags.md) and metadata that you attach to it inside TagStudio.
## Storage
File entry data is stored within the `ts_library.sqlite` file inside each library's `.TagStudio` folder. No modifications are made to your actual files on disk, and nothing like sidecar files are generated for your files.
## Appearance
File entries appear as thumbnails inside the grid display. The preview panel shows a more detailed preview of the file, along with extra file stats and all attached TagStudio tags and fields.
## Unlinked Entries
If the file that an entry is referencing has been moved, renamed, or deleted on disk, then TagStudio will display its unlinked status with a red chain-link icon instead of its thumbnail image. Certain uncached stats such as the file size and image dimensions will also be unavailable to see in the preview panel.
To fix file entries that have become unlinked, select the "Fix Unlinked Entries" option from the Tools menu. From there, refresh the unlinked entry count and choose whether to search and relink you files, and/or delete the file entries from your library. This will NOT delete or modify any files on disk.
Fields are additional types of metadata that you can attach to [file entries](./entries.md). Like [tags](tags.md), fields are not stored inside files themselves nor in sidecar files, but rather inside the respective TagStudio [library](./index.md) save file.
FFmpeg is required for thumbnail previews and playback features on audio and video files. FFmpeg is a free Open Source project dedicated to the handling of multimedia (video, audio, etc) files. For more information, see their official website at [ffmpeg.org](https://www.ffmpeg.org/).
## Installation on Windows
### Prebuilt Binaries
Pre-built binaries from trusted sources are available on the [FFmpeg website](https://www.ffmpeg.org/download.html). Under "More downloading options" click on the Windows section, then under "Windows EXE Files" select a source to download a build from. Follow any further download instructions from whichever build website you choose.
1. Download 7z or zip file and extract it (right click > Extract All)
2. Move extracted contents to a unique folder (i.e; `c:\ffmpeg` or `c:\Program Files\ffmpeg`)
3. Add FFmpeg to your system PATH
1. In Windows, search for or go to "Edit the system environment variables" under the Control Panel
2. Under "User Variables", select "Path" then edit
3. Click new and add `<Your folder>\bin` (e.g; `c:\ffmpeg\bin` or `c:\Program Files\ffmpeg\bin`)
4. Click "Okay"
### Package Managers
FFmpeg is also available from:
1. WinGet (`winget install ffmpeg`)
2. Scoop (`scoop install main/ffmpeg`)
3. Chocolatey (`choco install ffmpeg-full`)
## Installation on Mac
### Homebrew
FFmpeg is available under the macOS section of the [FFmpeg website](https://www.ffmpeg.org/download.html) or can be installed via [Homebrew](https://brew.sh/) using `brew install ffmpeg`.
## Installation on Linux
### Package Managers
FFmpeg may be installed by default on some Linux distributions, but if not, it is available via your distribution package manager of choice:
1. Debian/Ubuntu (`sudo apt install ffmpeg`)
2. Fedora (`sudo dnf install ffmpeg-free`)
3. Arch (`sudo pacman -S ffmpeg`)
# Help
For additional help, please join the [Discord](https://discord.gg/hRNnVKhF2G) or create an Issue on the [GitHub repository](https://github.com/TagStudioDev/TagStudio)
TagStudio versions prior to v9.5.4 use a different, more limited method to exclude or include file extensions from your library and subsequent searches. Opening a pre-exiting library in v9.5.4 or later will non-destructively convert this to the newer, more extensive `.ts_ignore` format.
If you're still running an older version of TagStudio in the meantime, you can access the legacy system by going to "Edit -> Manage File Extensions" in the menubar.
TagStudio offers the ability to ignore specific files and directories via a `.ts_ignore` file located inside your [library's](libraries.md) `.TagStudio` folder. This file is designed to use very similar [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>)-style pattern matching as the [`.gitignore`](https://git-scm.com/docs/gitignore) file used by Git™[^1]. It can be edited within TagStudio or opened to edit with an external program by going to the "Edit -> Ignore Files" option in the menubar.
This file is only referenced when scanning directories for new files to add to your library, and does not apply to files that have already been added to your library.
<!-- prettier-ignore -->
!!! tip
If you just want some specific examples of how to achieve common tasks with the ignore patterns (e.g. ignoring a single file type, ignoring a specific folder) then jump to the "[Use Cases](#use-cases)" section!
<!-- prettier-ignore-start -->
=== "Example .ts_ignore file"
```toml title="My Library/.TagStudio/.ts_ignore"
# TagStudio .ts_ignore file.
# Code
__pycache__
.pytest_cache
.venv
.vs
# Projects
Minecraft/**/Metadata
Minecraft/Website
!Minecraft/Website/*.png
!Minecraft/Website/*.css
# Documents
*.doc
*.docx
*.ppt
*.pptx
*.xls
*.xlsx
```
<!-- prettier-ignore-end -->
## Pattern Format
<!-- prettier-ignore -->
!!! note ""
_This section sourced and adapted from Git's[^1] `.gitignore` [documentation](https://git-scm.com/docs/gitignore)._
### Internal Processes
When scanning your library directories, the `.ts_ignore` file is read by either the [`wcmatch`](https://facelessuser.github.io/wcmatch/glob/) library or [`ripgrep`](https://github.com/BurntSushi/ripgrep) in glob mode depending if you have the later installed on your system and it's detected by TagStudio. Ripgrep is the preferred method for scanning directories due to its improved performance and identical pattern matching to `.gitignore`. This mixture of tools may lead to slight inconsistencies if not using `ripgrep`.
---
### Comments ( `#` )
A `#` symbol at the start of a line indicates that this line is a comment, and match no items. Blank lines are used to enhance readability and also match no items.
- Can be escaped by putting a backslash ("`\`") in front of the `#` symbol.
<!-- prettier-ignore-start -->
=== "Example comment"
```toml
# This is a comment! I can say whatever I want on this line.
file_that_is_being_matched.txt
# file_that_is_NOT_being_matched.png
file_that_is_being_matched.png
```
=== "Organizing with comments"
```toml
# TagStudio .ts_ignore file.
# Minecraft Stuff
Minecraft/**/Metadata
Minecraft/Website
!Minecraft/Website/*.png
!Minecraft/Website/*.css
# Microsoft Office
*.doc
*.docx
*.ppt
*.pptx
*.xls
*.xlsx
```
=== "Escape a # symbol"
```toml
# To ensure a file named '#hashtag.jpg' is ignored:
\#hashtag.jpg
```
<!-- prettier-ignore-end -->
---
### Directories ( `/` )
The forward slash "`/`" is used as the directory separator. Separators may occur at the beginning, middle or end of the `.ts_ignore` search pattern.
- If there is a separator at the beginning or middle (or both) of the pattern, then the pattern is relative to the directory level of the particular `.TagStudio` library folder itself. Otherwise the pattern may also match at any level below the `.TagStudio` folder level.
- If there is a separator at the end of the pattern then the pattern will only match directories, otherwise the pattern can match both files and directories.
<!-- prettier-ignore-start -->
=== "Example folder pattern"
```toml
# Matches "frotz" and "a/frotz" if they are directories.
frotz/
```
=== "Example nested folder pattern"
```toml
# Matches "doc/frotz" but not "a/doc/frotz".
doc/frotz/
```
<!-- prettier-ignore-end -->
---
### Negation ( `!` )
A `!` prefix before a pattern negates the pattern, allowing any files matched matched by previous patterns to be un-matched.
- Any matching file excluded by a previous pattern will become included again.
- **It is not possible to re-include a file if a parent directory of that file is excluded.**
<!-- prettier-ignore-start -->
=== "Example negation"
```toml
# All .jpg files will be ignored, except any located in the 'Photos' folder.
*.jpg
Photos/!*.jpg
```
=== "Escape a ! Symbol"
```toml
# To ensure a file named '!wowee.jpg' is ignored:
\!wowee.jpg
```
<!-- prettier-ignore-end -->
---
### Wildcards
#### Single Asterisks ( `*` )
An asterisk "`*`" matches anything except a slash.
<!-- prettier-ignore-start -->
=== "File examples"
```toml
# Matches all .png files in the "Images" folder.
Images/*.png
# Matches all .png files in all folders
*.png
```
=== "Folder examples"
```toml
# Matches any files or folders directly in "Images/" but not deeper levels.
# Matches file "Images/mario.jpg"
# Matches folder "Images/Mario"
# Does not match file "Images/Mario/cat.jpg"
Images/*
```
<!-- prettier-ignore-end -->
#### Question Marks ( `?` )
The character "`?`" matches any one character except "`/`".
<!-- prettier-ignore-start -->
=== "File examples"
```toml
# Matches any .png file starting with "IMG_" and ending in any four characters.
# Matches "IMG_0001.png"
# Matches "Photos/IMG_1234.png"
# Does not match "IMG_1.png"
IMG_????.png
# Same as above, except matches any file extension instead of only .png
IMG_????.*
```
=== "Folder examples"
```toml
# Matches all files in any direct subfolder of "Photos" beginning in "20".
# Matches "Photos/2000"
# Matches "Photos/2024"
# Matches "Photos/2099"
# Does not match "Photos/1995"
Photos/20??/
```
<!-- prettier-ignore-end -->
#### Double Asterisks ( `**` )
Two consecutive asterisks ("`**`") in patterns matched against full pathname may have special meaning:
- A leading "`**`" followed by a slash means matches in all directories.
- A trailing "`/**`" matches everything inside.
- A slash followed by two consecutive asterisks then a slash ("`/**/`") matches zero or more directories.
- Other consecutive asterisks are considered regular asterisks and will match according to the previous rules.
<!-- prettier-ignore-start -->
=== "Leading **"
```toml
# Both match file or directory "foo" anywhere
**/foo
foo
# Matches file or directory "bar" anywhere that is directly under directory "foo"
**/foo/bar
```
=== "Trailing /**"
```toml
# Matches all files inside directory "abc" with infinite depth.
abc/**
```
=== "Middle /**/"
```toml
# Matches "a/b", "a/x/b", "a/x/y/b" and so on.
a/**/b
```
<!-- prettier-ignore-end -->
#### Square Brackets ( `[a-Z]` )
Character sets and ranges are specific and powerful forms of wildcards that use characters inside of brackets (`[]`) to leverage very specific matching. The range notation, e.g. `[a-zA-Z]`, can be used to match one of the characters in a range.
<!-- prettier-ignore -->
!!! tip
For more in-depth examples and explanations on how to use ranges, please reference the [`glob`](https://man7.org/linux/man-pages/man7/glob.7.html) man page.
<!-- prettier-ignore-start -->
=== "Range examples"
```toml
# Matches all files that start with "IMG_" and end in a single numeric character.
# Matches "IMG_0.jpg", "IMG_7.png"
# Does not match "IMG_10.jpg", "IMG_A.jpg"
IMG_[0-9]
# Matches all files that start with "IMG_" and end in a single alphabetic character
IMG_[a-z]
```
=== "Set examples"
```toml
# Matches all files that start with "IMG_" and in any character in the set.
# Matches all files that start with "IMG_" and end in a single alphabetic character
IMG_[a-z]
```
<!-- prettier-ignore-end -->
---
## Use Cases
### Ignoring Files by Extension
<!-- prettier-ignore -->
=== "Ignore all .jpg files"
```toml
*.jpg
```
=== "Ignore all files EXCEPT .jpg files"
```toml
*
!*.jpg
```
=== "Ignore all .jpg files in specific folders"
```toml
./Photos/Worst Vacation/*.jpg
Music/Artwork Art/*.jpg
```
<!-- prettier-ignore -->
!!! tip "Ensuring Complete Extension Matches"
For some filetypes, it may be nessisary to specify different casing and alternative spellings in order to match with all possible variations of an extension in your library.
```toml title="Ignore (Most) Possible JPEG File Extensions"
# The JPEG Cinematic Universe
*.jpg
*.jpeg
*.jfif
*.jpeg_large
*.JPG
*.JPEG
*.JFIF
*.JPEG_LARGE
```
### Ignoring a Folder
<!-- prettier-ignore -->
=== "Ignore all "Cache" folders"
```toml
# Matches any folder called "Cache" no matter where it is in your library.
cache/
```
=== "Ignore a "Downloads" folder"
```toml
# "Downloads" must be a folder on the same level as your ".TagStudio" folder.
# Does not match with folders name "Downloads" elsewhere in your library
# Does not match with a file called "Downloads"
/Downloads/
```
=== "Ignore .jpg files in specific folders"
```toml
Photos/Worst Vacation/*.jpg
/Music/Artwork Art/*.jpg
```
[^1]: The term "Git" is a licensed trademark of "The Git Project", a member of the Software Freedom Conservancy. Git is released under the [GNU General Public License version 2.0](https://opensource.org/license/GPL-2.0), an open source license. TagStudio is not associated with the Git Project, only including systems based on some therein.
<figcaption>TagStudio Alpha v9.5.5 running on macOS Sequoia.</figcaption>
</figure>
<div class="grid" markdown>

**TagStudio** is a photo & file organization application with an underlying tag-based system that focuses on giving freedom and flexibility to the user. No proprietary programs or formats, no sea of sidecar files, and no complete upheaval of your filesystem structure.
TagStudio works with photos, videos, music, documents, and more! **All file types** are recognized by TagStudio, with most common ones having built-in preview support.
[:material-arrow-right: See Full Preview Support](preview-support.md)
- :material-tag-text:{ .lg .middle } **Create [Tags](tags.md) Your Way**
***
- :material-format-font: No character restrictions
TagStudio is licensed under the GPL-3.0 license with the source code and executable releases available on [GitHub](https://github.com/TagStudioDev/TagStudio).
[:material-arrow-right: Roadmap to MIT Core Library License](roadmap.md#core-library-api)
- :material-database:{ .lg .middle } **Central Save File**
***
Apposed to filling your drives with [sidecar files](https://en.wikipedia.org/wiki/Sidecar_file), TagStudio uses a project-like [library](libraries.md) system that stores your tags and metadata inside a single save file per-library.
[:material-arrow-right: Learn About the Format](libraries.md)
</div>
---
## :material-layers-triple: More Than an Application
TagStudio aims to create an **open** and **robust** format for file tagging that isn't burdened by the limitations of traditional tagging and file metadata systems. **TagStudio** is the first proof-of-concept implementation of this system.
<div class="grid cards" markdown>
- :material-map-check:{ .lg .middle } See the [**Roadmap**](roadmap.md) for future features and updates
TagStudio provides executable [releases](https://github.com/TagStudioDev/TagStudio/releases) as well as full access to its [source code](https://github.com/TagStudioDev/TagStudio) under the [GPLv3](https://github.com/TagStudioDev/TagStudio/blob/main/LICENSE) license.
## Executables
To download executable builds of TagStudio, visit the [Releases](https://github.com/TagStudioDev/TagStudio/releases) page of the GitHub repository and download the latest release for your system under the "Assets" section at the bottom of the release.
TagStudio has builds for :fontawesome-brands-windows: **Windows**, :fontawesome-brands-apple: **macOS**_(Apple Silicon & Intel)_, and :material-penguin: **Linux**. We also offer portable releases for Windows and Linux which are self-contained and easier to move around.
<!-- prettier-ignore -->
!!! info "Third-Party Dependencies"
You may need to install [third-party dependencies](#third-party-dependencies) such as [FFmpeg](https://ffmpeg.org/download.html) to use the full feature set of TagStudio.
On macOS, you may be met with a message saying "**"TagStudio" can't be opened because Apple cannot check it for malicious software.**" If you encounter this, then you'll need to go to the "Settings" app, navigate to "Privacy & Security", and scroll down to a section that says "**"TagStudio" was blocked from use because it is not from an identified developer.**" Click the "Open Anyway" button to allow TagStudio to run. You should only have to do this once after downloading the application.
---
## Package Managers
<!-- prettier-ignore -->
!!! danger "Unofficial Releases"
**We do not currently publish TagStudio to _remote_ package repositories. Any TagStudio distributions outside of the [GitHub repository](https://github.com/TagStudioDev/TagStudio) are _unofficial_ and not maintained by us!**
Installation support will not be given to users installing from unofficial sources. Use these versions at your own risk!
### :fontawesome-brands-python: Installing with PIP
TagStudio is installable via [PIP](https://pip.pypa.io/). Note that since we don't currently distribute on PyPI, the repository needs to be cloned and installed locally. Make sure you have Python 3.12 and PIP installed if you choose to install using this method.
The repository can be cloned/downloaded via `git` in your terminal, or by downloading the zip file from the "Code" button on the [repository page](https://github.com/TagStudioDev/TagStudio).
| [qt-wayland](https://repology.org/project/qt) | Wayland support |
### :material-nix: Nix(OS)
For [Nix(OS)](https://nixos.org/), the TagStudio repository includes a [flake](https://wiki.nixos.org/wiki/Flakes) that provides some outputs such as a development shell and package.
Two packages are provided: `tagstudio` and `tagstudio-jxl`. The distinction was made because `tagstudio-jxl` has an extra compilation step for [JPEG-XL](https://jpeg.org/jpegxl) image support. To give either of them a test run, you can execute `nix run github:TagStudioDev/TagStudio#tagstudio`. If you are in a cloned repository and wish to run a package with the context of the repository, you can simply use `nix run` with no arguments.
`nix build` can be used in place of `nix run` if you only want to build. **The packages will only build if tests pass.**
<!-- prettier-ignore -->
!!! info "Nix Support"
Support for Nix is handled on a best-effort basis by one of our maintainers. Issues related to Nix may be slower to resolve, and could require further details.
Want to add TagStudio into your configuration?
This can be done by first adding the flake input into your `flake.nix`:
You can check to see if these dependencies are correctly located by launching TagStudio and going to "About TagStudio" in the menu bar.
### FFmpeg/FFprobe
For audio/video thumbnails and playback you'll need [FFmpeg](https://ffmpeg.org/download.html) installed on your system. If you encounter any issues with this, please reference our [FFmpeg Help](./help/ffmpeg.md) guide.
### RAR Extractor
To generate thumbnails for RAR-based files (like `.cbr`) you'll need an extractor capable of handling them.
- :material-penguin: On Linux you'll need to install either `unrar` (likely in you distro's non-free repository) or `unrar-free` from your package manager.
- :fontawesome-brands-apple: On macOS `unrar` can be installed through Homebrew's [`rar`](https://formulae.brew.sh/cask/rar) formula.
On macOS, you may be met with a message similar to "**"unrar" Not Opened. Apple could not verify "unrar" is free of malware that may harm your Mac or compromise your privacy**" If you encounter this, then you'll need to go to the "Settings" app, navigate to "Privacy & Security", and scroll down to a section that says "**"unrar" was blocked from use because it is not from an identified developer.**" Click the "Open Anyway" button to allow unrar to be used.
- :fontawesome-brands-windows: On Windows you'll need to install either [`WinRAR`](https://www.rarlab.com/download.htm) or [`7-zip`](https://www.7-zip.org/) and add their folder to you `PATH`.
<!-- prettier-ignore -->
!!! tip "WinRAR License"
Both `unrar` and `WinRAR` require a license, but since the evaluation copy has no time limit you can simply dismiss the prompt.
### ripgrep
A recommended tool to improve the performance of directory scanning is [`ripgrep`](https://github.com/BurntSushi/ripgrep), a Rust-based directory walker that natively integrates with our [`.ts_ignore`](ignore.md) (`.gitignore`-style) pattern matching system for excluding files and directories. Ripgrep is already pre-installed on some Linux distributions and also available from several package managers.
This page is a work in progress and needs to be updated with additional information.
The library is how TagStudio represents your chosen directory, with every file inside being represented by a [file entry](entries.md). You can have as many or few libraries as you wish, since each libraries' data is stored within a `.TagStudio` folder at its root. From there the library save file itself is stored as `ts_library.sqlite`, with TagStudio versions 9.4 and below using a the legacy `ts_library.json` format.
Note that this means [tags](tags.md) you create only exist _per-library_. Global tags along with other library structure updates are planned for future releases on the [roadmap](roadmap.md#library).
This page outlines the various changes made to the TagStudio library save file format over time, sometimes referred to as the "database" or "database file".
---
## JSON
Legacy (JSON) library save format versions were tied to the release version of the program itself. This number was stored in a `version` key inside the JSON file.
The legacy database format for public TagStudio releases [v9.1](https://github.com/TagStudioDev/TagStudio/tree/Alpha-v9.1) through [v9.4.2](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.4.2). Variations of this format had been used privately since v1.0.0.
Replaced by the new SQLite format introduced in TagStudio [v9.5.0 Pre-Release 1](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.5.0-pr1).
---
## SQLite
Starting with TagStudio [v9.5.0-pr1](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.5.0-pr1), the library save format has been moved to a [SQLite](https://sqlite.org) format. Legacy JSON libraries are migrated (with the user's consent) to the new format when opening in current versions of the program. The save format versioning is now separate from the program's versioning number.
Versions **1-100** stored the database version in a table called `preferences` in a row with the `key` column of `"DB_VERSION"` inside the corresponding `value` column.
Versions **>101** store the database version in a table called `versions` in a row with the `key` column of `'CURRENT'` inside the corresponding `value` column. The `versions` table also stores the initial database version in which the file was created with under the `'INITIAL'` key. Databases created before this key was introduced will always have `'INITIAL'` value of `100`.
```mermaid
erDiagram
versions {
TEXT key PK "Values: ['INITIAL', 'CURRENT']"
INTEGER value
}
```
### Versions 1 - 5
These versions were used while developing the new SQLite file format, outside any official or recommended release. These versions **were never supported** in any official capacity and were actively warned against using for real libraries.
The first public version of the SQLite save file format.
Migration from the legacy JSON format is provided via a walkthrough when opening a legacy library in TagStudio [v9.5.0 Pre-Release 1](https://github.com/TagStudioDev/TagStudio/releases/tag/v9.5.0-pr1) or later.
- Adds the `color_border` column to the `tag_colors` table. Used for instructing the [secondary color](colors.md#secondary-color) to apply to a tag's border as a new optional behavior.
- Adds three new default colors: "Burgundy (TagStudio Shades)", "Dark Teal (TagStudio Shades)", and "Dark Lavender (TagStudio Shades)".
- Updates Neon colors to use the new `color_border` property.
- The version number divided by 100 (and floored) constitutes the **major** version. Major version indicate breaking changes that prevent libraries from being opened in TagStudio versions older than the ones they were created in.
- Values more precise than this ("ones" through "tens" columns) constitute the **minor** version. These indicate minor changes that don't prevent a newer library from being opened in an older version of TagStudio, as long as the major version is not also increased.
- Swaps `parent_id` and `child_id` values in the `tag_parents` table
Tools and macros are features that serve to create a more fluid [library](libraries.md)-managing process, or provide some extra functionality. Please note that some are still in active development and will be fleshed out in future updates.
## Tools
### Fix Unlinked Entries
This tool displays the number of unlinked [entries](entries.md), and some options for their resolution.
Refresh
: Scans through the library and updates the unlinked entry count.
Search & Relink
: Attempts to automatically find and reassign missing files.
Delete Unlinked Entries
: Displays a confirmation prompt containing the list of all missing files to be deleted before committing to or cancelling the operation.
### Fix Duplicate Files
This tool allows for management of duplicate files in the library using a [DupeGuru](https://dupeguru.voltaicideas.net/) file.
Load DupeGuru File
: load the "results" file created from a DupeGuru scan
Mirror Entries
: Duplicate entries will have their contents mirrored across all instances. This allows for duplicate files to then be deleted with DupeGuru as desired, without losing the [field](fields.md) data that has been assigned to either. (Once deleted, the "Fix Unlinked Entries" tool can be used to clean up the duplicates)
### Create Collage
This tool is a preview of an upcoming feature. When selected, TagStudio will generate a collage of all the contents in a Library, which can be found in the Library folder ("/your-folder/.TagStudio/collages/"). Note that this feature is still in early development, and doesn't yet offer any customization options.
## Macros
### Auto-fill [WIP]
Tool is in development and will be documented in a future update.
### Sort fields
Tool is in development. Will allow for user-defined sorting of [fields](fields.md).
### Folders to Tags
Creates tags from the existing folder structure in the library, which are previewed in a hierarchy view for the user to confirm. A tag will be created for each folder and applied to all entries, with each subfolder being linked to the parent folder as a [parent tag](tags.md#parent-tags). Tags will initially be named after the folders, but can be fully edited and customized afterwards.
TagStudio offers built-in preview and thumbnail support for a wide variety of file types. Files that don't have explicit support can still be added to your library like normal, they will just show a default icon for thumbnails and previews. TagStudio also references the file's [MIME](https://en.wikipedia.org/wiki/Media_type) type in an attempt to render previews for file types that haven't gained explicit support yet.
### :material-image-outline: Images
Images will generate thumbnails the first time they are viewed or since the last time they were modified. Thumbnails are used in the grid view, but not in the Preview Panel. Animated images will play in the Preview Panel.
| Camera Image File Format (Canon) | `.crw`, `.cr2`, `.cr3` |
| Digital Negative | `.dng` |
| Fuji RAW | `.raf` |
| Nikon RAW | `.nef`, `.nrw` |
| Olympus RAW | `.orf` |
| Panasonic RAW | `.raw`, `.rw2` |
| Sony RAW | `.arw` |
### :material-movie-open: Videos
Video thumbnails will default to the closest viable frame from the middle of the video. Both thumbnail generation and video playback in the Preview Panel requires [FFmpeg](install.md#third-party-dependencies) installed on your system.
Audio thumbnails will default to embedded cover art (if any) and fallback to generated waveform thumbnails. Audio file playback is supported in the Preview Panel if you have [FFmpeg](install.md#third-party-dependencies) installed on your system. Audio waveforms are currently not cached.
Preview support for office documents or well-known project file formats varies by the format and whether or not embedded thumbnails are available to be read from. OpenDocument-based files are typically supported.
| Comic Book Archive | `.cbr`, `.cbt``.cbz` | Embedded cover or first page |
### :material-cube-outline: 3D Models
<!-- prettier-ignore -->
!!! failure "3D Model Support"
TagStudio does not currently support previews for 3D model files *(outside of Blender project embedded thumbnails)*. This is on our [roadmap](roadmap.md#uiux) for a future release.
### :material-format-font: Fonts
Font thumbnails will use a "Aa" example preview of the font, with a full alphanumeric of the font available in the Preview Panel.
| Filetype | Extensions |
| -------------------- | ----------------- |
| OpenType Font | `.otf`, `.otc` |
| TrueType Font | `.ttf`, `.ttc` |
| Web Open Font Format | `.woff`, `.woff2` |
### :material-text-box: Text
<!-- prettier-ignore -->
!!! info "Plain Text Support"
TagStudio supports the *vast* majority of files considered to be "[plain text](https://en.wikipedia.org/wiki/Plain_text)". If an extension or format is not listed here, odds are it's still supported anyway.
Text files render the first 256 bytes of text information to an image preview for thumbnails and the Preview Panel. Improved thumbnails, full scrollable text, and syntax highlighting are on our [roadmap](roadmap.md#uiux) for future features.
The `.jpg_large` extension is unofficial and instead the byproduct of how [Google Chrome used to download images from Twitter](https://fileinfo.com/extension/jpg_large). Since this mangled extension is still in circulation, TagStudio supports it.
[^2]:
Apple Lossless traditionally uses `.m4a` and `.caf` containers, but may unofficially use the `.alac` extension. The `.m4a` container is also used for separate compressed audio codecs.
[^3]:
Krita also supports saving projects as OpenRaster `.ora` files. Support for these is listed in the "[Images](#images)" section.
This page outlines the current and planned features required for TagStudio to be considered "feature complete" (v10.0.0). Features and changes are broken up by group in order to better assess the overall state of those features. [Priority levels](#priority-levels) and [version estimates](#version-estimates) are provided in order to give a rough idea of what's planned and when it may release.
This roadmap will update as new features are planned or completed. If there's a feature you'd like to see but is not listed on this page, please check the GitHub [Issues](https://github.com/TagStudioDev/TagStudio/issues) page and submit a feature request if one does not already exist!
## Priority Levels
Planned features and changes are assigned **priority levels** to signify how important they are to the feature-complete version of TagStudio and to serve as a general guide for what should be worked on first, along with [version estimates](#version-estimates). When features are completed, their priority level icons are removed.
- :material-chevron-double-up:{ .priority-med title="Medium Priority" } **Medium Priority** - Important, but not necessary
- :material-chevron-up:{ .priority-low title="Low Priority" } **Low Priority** - Just nice to have
## Version Estimates
Features are given rough estimations for which version they will be completed in, and are listed next to their names (e.g. Feature **[v9.0.0]**). They are eventually replaced with links to the version changelog in which they were completed in, if applicable.
<!-- prettier-ignore -->
!!! tip
For a more definitive and up-to-date list of features planned for near-future updates, please reference the current GitHub [Milestones](https://github.com/TagStudioDev/TagStudio/milestones)!
---
## Core
### :material-database: SQL Library Database
An improved SQLite-based library save file format in which legacy JSON libraries are be migrated to.
Must be finalized or deemed "feature complete" before other core features are developed or finalized.
<!-- prettier-ignore -->
!!! note
See the "[Library](#library)" section for features related to the library database rather than the underlying schema.
- [x] A SQLite-based library save file format **[[v9.5.0](changelog.md#950-march-3rd-2025)]**
- [ ] Date Photo Taken :material-chevron-triple-up:{ .priority-high title="High Priority" }
- [ ] Media Duration :material-chevron-triple-up:{ .priority-high title="High Priority" }
- [ ] Media Dimensions :material-chevron-double-up:{ .priority-med title="Medium Priority" }
- [ ] Word Count :material-chevron-up:{ .priority-low title="Low Priority" }
### :material-database-cog: Core Library + API
A separated, UI agnostic core library that would be used to interface with the TagStudio library format. Would host an API for communication from outside the program. This would be licensed under the more permissive [MIT](https://en.wikipedia.org/wiki/MIT_License) license to foster wider adoption compared to the TagStudio application source code.
### :material-clipboard-text: Format Specification
A detailed written specification for the TagStudio tag and/or library format. Intended for used by third-parties to build alternative cores or protocols that can remain interoperable.
- [ ] Format Specification Established :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v10.0.0]**
Some form of official plugin support for TagStudio, likely with its own API that may connect to or encapsulate part of the the [core library API](#core-library-api).
- [ ] Ability to store TagStudio library folder separate from library files :material-chevron-triple-up:{ .priority-high title="High Priority" } **[v9.6.x]**
- [ ] Non-exclusive; Entries can be in multiple groups :material-chevron-triple-up:{ .priority-high title="High Priority" }
- [ ] Ability to number entries within group :material-chevron-triple-up:{ .priority-high title="High Priority" }
- [ ] Ability to set sorting method for group :material-chevron-triple-up:{ .priority-high title="High Priority" }
- [ ] Ability to set custom thumbnail for group :material-chevron-triple-up:{ .priority-high title="High Priority" }
- [ ] Group is treated as entry with tags and metadata :material-chevron-double-up:{ .priority-med title="Medium Priority" }
- [ ] Nested groups :material-chevron-double-up:{ .priority-med title="Medium Priority" }
### :material-tag-text: [Tags](tags.md)
Discrete library objects representing [attributes](<https://en.wikipedia.org/wiki/Property_(philosophy)>). Can be applied to library [entries](entries.md), or applied to other tags to build traversable relationships.
- [ ] Set Field Content :material-chevron-triple-up:{ .priority-high title="High Priority" }
- [ ] [...]
### :material-table-arrow-right: Sharable Data
Sharable TagStudio library data in the form of data packs (tags, colors, etc.) or other formats.
Packs are intended as an easy way to import and export specific data between libraries and users, while export-only formats are intended to be imported by other programs.
TagStudio provides various methods to search your library, ranging from TagStudio data such as tags to inherent file data such as paths or media types.
## Boolean Operators
TagStudio allows you to use common [Boolean search](https://en.wikipedia.org/wiki/Full-text_search#Boolean_queries) operators when searching your library, along with [grouping](#grouping-and-nesting), [nesting](#grouping-and-nesting), and [character escaping](#escaping-characters). Note that you may need to use grouping in order to get the desired results you're looking for.
### AND
The `AND` operator will only return results that match **both** sides of the operator. `AND` is used implicitly when no Boolean operators are given. To use the `AND` operator explicitly, simply type "and" (case insensitive) in-between items of your search.
<!-- prettier-ignore -->
!!! example
Searching for "Tag1 Tag2" will be treated the same as "Tag1 `AND` Tag2" and will only return results that contain both Tag1 and Tag2.
### OR
The `OR` operator will return results that match **either** the left or right side of the operator. To use the `OR` operator simply type "or" (case insensitive) in-between items of your search.
<!-- prettier-ignore -->
!!! example
Searching for "Tag1 `OR` Tag2" will return results that contain either "Tag1", "Tag2", or both.
### NOT
The `NOT` operator will returns results where the condition on the right is **false.** To use the `NOT` operator simply type "not" (case insensitive) in-between items of your search. You can also begin your search with `NOT` to only view results that do not contain the next term that follows.
<!-- prettier-ignore -->
!!! example
Searching for "Tag1 `NOT` Tag2" will only return results that contain "Tag1" while also not containing "Tag2".
### Grouping and Nesting
Searches can be grouped and nested by using parentheses to surround parts of your search query.
<!-- prettier-ignore -->
!!! example
Searching for "(Tag1 `OR` Tag2) `AND` Tag3" will return any results that contain Tag3, plus one or the other (or both) of Tag1 and Tag2.
### Escaping Characters
Sometimes search queries have ambiguous characters and need to be "escaped". This is most common with tag names which contain spaces, or overlap with existing search keywords such as "[path:](#filename-and-path) of exile". To escape most search terms, surround the section of your search in plain quotes. Alternatively, spaces in tag names can be replaced by underscores.
#### Valid Escaped Tag Searches
- "Tag Name With Spaces"
- Tag_Name_With_Spaces
#### Invalid Escaped Tag Searches
- Tag Name With Spaces
- Reason: Ambiguity between a tag named "Tag Name With Spaces" and four individual tags called "Tag", "Name", "With", "Spaces".
## Tags
[Tag](#tags) search is the default mode of file entry search in TagStudio. No keyword prefix is required, however using `tag:` will also work. The tag search attempts to match tag [names](tags.md#name), [shorthands](tags.md#shorthand), [aliases](tags.md#aliases), as well as allows for tags to [substitute](tags.md#intuition-via-substitution) in for any of their [parent tags](tags.md#parent-tags).
You may also see the `tag_id:` prefix keyword show up when using the right-click "Search for Tag" option on tags. This is meant for internal use, and eventually will not be displayed or accessible to the user.
## Fields
_[Field](fields.md) search is currently not in the program, however is coming in a future version._
## File Entry Search
### Filename and Path
Filename and path search is available via the `path:` keyword and comes in a few different styles. By default, any string that follows the `path:` keyword will be searched as a substring inside a file's complete filepath. This means that given a file `folder/my_file.txt`, searching for `path: my_file` or `path: folder` will both return results for that file.
#### Case Sensitivity
TagStudio uses a "[smartcase](https://neovim.io/doc/user/options.html#'smartcase')"-like system for case sensitivity. This means that a search term typed in `lowercase` will be treated as **case-insensitive**, while a term typed in any `MixedCase` will be treated as **case-sensitive**. This makes it quicker to type searches when case sensitivity isn't required, while also providing a simple option to leverage case sensitivity when desired. Note that this means there's technically no way to currently search for a lowercase term while respecting case sensitivity.
#### Glob Syntax
Optionally, you may use [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) syntax to search filepaths.
#### Examples
Given a file "Artwork/Piece.jpg", the following searches will return results for it:
Most of the style guidelines can be checked, fixed, and enforced via Ruff. Older code may not be adhering to all of these guidelines, in which case _"do as I say, not as I do"..._
- Do your best to write clear, concise, and modular code.
- This should include making methods private by default (e.g. `__method()`)
- Methods should only be protected (e.g. `_method()`) or public (e.g. `method()`) when needed and warranted
- Keep a maximum column width of no more than **100** characters.
- Code comments should be used to help describe sections of code that can't speak for themselves.
- Use [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) docstrings for any classes and functions you add.
- If you're modifying an existing function that does _not_ have docstrings, you don't _have_ to add docstrings to it... but it would be pretty cool if you did ;)
- Imports should be ordered alphabetically.
- Lists of values should be ordered using their [natural sort order](https://en.wikipedia.org/wiki/Natural_sort_order).
- Some files have their methods ordered alphabetically as well (i.e. [`thumb_renderer`](https://github.com/TagStudioDev/TagStudio/blob/main/src/tagstudio/qt/widgets/thumb_renderer.py)). If you're working in a file and notice this, please try and keep to the pattern.
- When writing text for window titles or form titles, use "[Title Case](https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case)" capitalization. Your IDE may have a command to format this for you automatically, although some may incorrectly capitalize short prepositions. In a pinch you can use a website such as [capitalizemytitle.com](https://capitalizemytitle.com/) to check.
- If it wasn't mentioned above, then stick to [**PEP-8**](https://peps.python.org/pep-0008/)!
## Qt
As of writing this section, the QT part of the code base is quite unstructured and the View and Controller parts are completely intermixed[^1]. This makes maintenance, fixes and general understanding of the code base quite challenging, because the interesting parts you are looking for are entangled in a bunch of repetitive UI setup code. To address this we are aiming to more strictly separate the view and controller aspects of the QT frontend.
The general structure of the QT code base should look like this:
```
qt
├── controllers
│ ├── widgets
│ │ └── preview_panel_controller.py
│ └── main_window_controller.py
├── views
│ ├── widgets
│ │ └── preview_panel_view.py
│ └── main_window_view.py
├── ts_qt.py
└── mixed.py
```
In this structure there are the `views` and `controllers` sub-directories. They have the exact same structure and for every `<component>_view.py` there is a `<component>_controller.py` at the same location in the other subdirectory and vice versa.
Observe the following key aspects of this example:
- The Controller is just called `MyCoolWidget` instead of `MyCoolWidgetController` as it will be directly used by other code
- The UI elements are in private variables
- This enforces that the controller shouldn't directly access UI elements
- Instead the view should provide a protected API (e.g. `_get_color()`) for things like setting/getting the value of a dropdown, etc.
- Instead of `_get_color()` there could also be a `_color` method marked with `@property`
- The callback methods are already defined as protected methods with NotImplementedErrors
- Defines the interface the callbacks
- Enforces that UI events be handled
<!-- prettier-ignore -->
!!! tip
A good (non-exhaustive) rule of thumb is: If it requires a non-UI import, then it doesn't belong in the `*_view.py` file.
[^1]: For an explanation of the Model-View-Controller (MVC) Model, checkout this article: [MVC Framework Introduction](https://www.geeksforgeeks.org/mvc-framework-introduction/).
Tags are discrete objects that represent some attribute. This could be a person, place, object, concept, and more. Unlike most tagging systems, TagStudio tags are not solely represented by a line of text or a hashtag. Tags in TagStudio consist of several properties and relationships that give extra customization, searching power, and ease of tagging that cannot be achieved by string-based tags alone. TagStudio tags are designed to be as simple or as complex as you'd like, giving options to users of all skill levels and use cases.
## Naming Tags
TagStudio tags do not share the same naming limitations of many other tagging solutions. The key standouts of tag names in TagStudio are:
- Tag names do **NOT** have to be unique
- Tag names are **NOT** limited to specific characters
- Tags can have **aliases**, a.k.a. alternate names to go by
### Name
This is the base name of a tag. It does not have to be unique, and can use any characters you wish. If your tag can go by multiple names, for example if it's the name of a person or something that's commonly shortened or abbreviated, then it's recommended that you put the full tag name here.
### Shorthand
This is a special type of alias that's used for shortening the tag name under special circumstances, mostly when screen space is limited. Tag shorthands can be searched for just like tag names and tag aliases.
### Aliases
Aliases are alternate names that the tag can go by. This may include individual first names for people, alternate spellings, shortened names, and more. If there's a common abbreviation or shortened name for your tag, it's recommended to use the [shorthand](#shorthand) field for this instead.
When searching for a tag, aliases (including the shorthand) can also be used to find the tag. This not only includes searching for tags themselves, but for tagged [file entries](entries.md) as well!
### Disambiguation
Just as in real life, sometimes there are different attributes that share the same name with one another. The process of adding specificity to something in order to not confuse it with something similar is known as [disambiguation](https://en.wikipedia.org/wiki/Word-sense_disambiguation). In TagStudio we give the option to automatically disambiguate tag names based on a specially marked [Parent Tag](#parent-tags). Parent tags are explained in further detail below, but for the purposes of tag names they can lend themselves to clarifying the name of a tag without the user needing to manually change the name or add complicated aliases.
Given a tag named "Freddy", we may confuse it with other "Freddy" tags in our library. There are lots of Freddys in the world, after all. If we're talking about Freddy from "Five Nights at Freddy's", then we may already (and likely should) have a separate "Five Nights at Freddy's" tag added as a parent tag. When the disambiguation box next to a parent tag is selected (see image below) then our tag name will automatically display its name with that parent tag's name (or shorthand if available) in parentheses.
So if the "Five Nights at Freddy's" tag is added as a parent tag on the "Freddy" tag, and the disambiguation box next to it is checked, then our tag name will automatically be displayed as "Freddy (Five Nights at Freddy's)". Better yet, if the "Five Nights at Freddy's" tag has a shorthand such as "FNAF", then our "Freddy" tag will be displayed as "Freddy (FNAF)". This process preserves our base tag name ("Freddy") and provides an option to get a clean and consistent method to display disambiguating parent categories, rather than having to type this information in manually for each applicable tag.
## Tag Relationships
One of the core properties of tags in TagStudio is their ability to form relationships with other tags, just as attributes have relationships with each other in real life. A rectangle is a square, but a square isn't a rectangle. A certain plumber with a red hat and blue overalls might be part of a well-known media franchise, developed by an equally well-known company. But how do representing these relationships help with tagging images and files? With tag relationships, we can leverage the following principles:
1. [Simplicity via Deduplication](#simplicity-via-deduplication)
2. [Intuition via Substitution](#intuition-via-substitution)
3. [Rediscovery via Linking](#rediscovery-via-linking)
### Parent Tags
#### Simplicity via Deduplication
In a system where tags have no relationships, you're required to add as many tags as you possibly can to describe every last element of an image or file. If you want to tag an image of Shrek, you need to add a tag for `Shrek` himself, a `Character` tag since he's a character, a `Movie` and perhaps `Dreamworks` tag since he's a character from a movie, or perhaps a `Book` tag if we're talking about the original character, and then of course tags for every other attribute of Shrek shown or implied. By allowing tags to have inheritance relationships, we can have a single `Shrek` tag inherit from `Character` (Shrek IS a character) as well as from a separate `Shrek (Movie Franchise)` tag that itself inherits from `Movie Franchise` and `Dreamworks`. Now by simply adding the `Shrek` tag to an image, we've effectively also added the `Character`, `Shrek (Move Franchise)`, `Movie Franchise`, and `Dreamworks` attributes all in one go. On the image entry itself we only see `Shrek`, but the rest of the attributes are implied.

#### Intuition via Substitution
Now when searching for images that have `Dreamworks` and `Character`, any images or files originally just tagged with `Shrek` will appear as you would expect. A little bit of tag setup goes a long way not only saving so much time during tagging, but also to ensure an intuitive way to search your files!
#### Rediscovery via Linking
Lastly, when searching your files with broader categories such as `Character` or `Dreamworks` you may rediscover images and files that you had simply tagged with tags such as `Barbatus` or `Tulio`, since you didn't need to manually tag those files with `Character` or `Dreamworks`, but had forgotten that they are both in fact Dreamworks characters. While you focus on tagging your files with seemingly surface level attributes, your TagStudio library is building rich connections between tags and files that may not be fully apparent until being discovered through various search queries. While you were simply tagging images with `Shrek` and `Tulio`, you may have unlocked an easy way to search for "2D Dreamworks Characters" without having to explicitly tag for that!
### Component Tags
<!-- prettier-ignore -->
!!! warning ""
**_Coming in version 9.6.x_**
Component tags will be built from a composition-based, or "HAS" type relationship between tags. This takes care of instances where an attribute may "have" another attribute, but doesn't inherit from it. Shrek may be an `Ogre`, he may be a `Character`, but he is NOT a `Leather Vest` - even if he's commonly seen _with_ it. Component tags, along with the upcoming "Tag Override" feature, are built to handle these cases in a way that still simplifies the tagging process without adding too much undue complexity for the user.
## Tag Appearance
### Color
Tags use a default uncolored appearance by default, however can take on a number of built-in and user-created colors and color palettes! Tag color palettes can be based on a single color value (see: TagStudio Standard, TagStudio Shades, TagStudio Pastels) or use an optional secondary color use for the text and optionally the tag border (e.g. TagStudio Neon).

#### User-Created Colors
Custom palettes and colors can be created via the [Tag Color Manager](colors.md). These colors will display alongside the built-in colors inside the tag selection window and are separated by their namespace names. Colors which use the secondary color for the tag border will be outlined in that color, otherwise they will only display the secondary color on the bottom of the swatch to indicate at a glance that the text colors are different.

### Icon
<!-- prettier-ignore -->
!!! warning ""
**_Coming in version 9.6.x_**
## Tag Properties
Properties are special attributes of tags that change their behavior in some way.
#### Is Category
The "Is Category" property of tags determines if a tag should be treated as a category itself when being organized inside the preview panel. If this tag or any tags inheriting from this tag (i.e. tags that have this tag as a "[Parent Tag](#parent-tags)"), then these tags will appear under a separated group that's named after this tag. Tags inheriting from multiple "category tags" will still show up under any applicable category.
This means that duplicates of tags can appear on entries if the tag inherits from multiple parent categories, however this is by design and reflects the nature of multiple inheritance. Any tags not inheriting from a category tag will simply show under a default "Tag" section.
The built-in tags "Favorite" and "Archived" inherit from the built-in "Meta Tags" category which is marked as a category by default. This behavior of default tags can be fully customized by disabling the category option and/or by adding/removing the tags' Parent Tags.
### Migrating from v9.4 Libraries
Due to the nature of how tags and Tag Felids operated prior to v9.5, the organization style of Tag Categories vs Tag Fields is not 1:1. Instead of tags being organized into fields on a per-entry basis, tags themselves determine their organizational layout via the "Is Property" flag. Any tags _(not currently inheriting from either the "Favorite" or "Archived" tags)_ will be shown under the default "Tags" header upon migrating to the v9.5+ library format. Similar organization to Tag Fields can be achieved by using the built-in "Meta Tags" tag or any other marked with "Is Category" and then setting those tags as parents for other tags to inherit from.
#### Is Hidden
<!-- prettier-ignore -->
!!! warning ""
**_Coming in version 9.6.x_**
When the "Is Hidden" property is checked, any file entries tagged with this tag will not show up in searches by default. This property comes by default with the built-in "Archived" tag.
## Tag Search Examples
The following are examples of how a set of given tags will respond to various search queries.
| Tag | Name | Shorthand | Aliases | Parent Tags |
With TagStudio opened, start by creating a new library or opening an existing one using File -> Open/Create Library from the menu bar. TagStudio will automatically create a new library from the chosen directory if one does not already exist. Upon creating a new library, TagStudio will automatically scan your folders for files and add those to your library (no files are moved during this process!).
## Refreshing the Library
Libraries under 10,000 files automatically scan for new or modified files when opened. In order to refresh the library manually, select "Refresh Directories" under the File menu.
## Adding Tags to File Entries
Access the "Add Tag" search box by either clicking on the "Add Tag" button at the bottom of the right sidebar, accessing the "Add Tags to Selected" option from the File menu, or by pressing <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>T</kbd>.
From here you can search for existing tags or create a new one if the one you're looking for doesn't exist. Click the "+" button next to any tags you want to the currently selected file entries. To quickly add the top result, press the <kbd>Enter</kbd>/<kbd>Return</kbd> key to add the topmost tag and reset the tag search. Press <kbd>Enter</kbd>/<kbd>Return</kbd> once more to close the dialog box. By using this method, you can quickly add various tags in quick succession just by using the keyboard!
To remove a tag from a file entry, hover over the tag in the preview panel and click on the "-" icon that appears.
## Adding Metadata to File Entries
To add a metadata field to a file entry, start by clicking the "Add Field" button at the bottom of the preview panel. From the dropdown menu, select the type of metadata field you’d like to add to the entry
## Editing Metadata Fields
### Text Line / Text Box
Hover over the field and click the pencil icon. From there, add or edit text in the dialog box popup.
## Creating Tags
Create a new tag by accessing the "New Tag" option from the Edit menu or by pressing <kbd>Ctrl</kbd>+<kbd>T</kbd>. In the tag creation panel, enter a tag name, optional shorthand name, optional tag aliases, optional parent tags, and an optional color.
- The tag **name** is the base name of the tag. **_This does NOT have to be unique!_**
- The tag **shorthand** is a special type of alias that displays in situations where screen space is more valuable, notably with name disambiguation.
-**Aliases** are alternate names for a tag. These let you search for terms other than the exact tag name in order to find the tag again.
-**Parent Tags** are tags in which this tag can substitute for in searches. In other words, tags under this section are parents of this tag.
- Parent tags with the disambiguation check next to them will be used to help disambiguate tag names that may not be unique.
- For example: If you had a tag for "Freddy Fazbear", you might add "Five Nights at Freddy's" as one of the parent tags. If the disambiguation box is checked next to "Five Nights at Freddy's" parent tag, then the tag "Freddy Fazbear" will display as "Freddy Fazbear (Five Nights at Freddy's)". Furthermore, if the "Five Nights at Freddy's" tag has a shorthand like "FNAF", then the "Freddy Fazbear" tag will display as "Freddy Fazbear (FNAF)".
- The **color** option lets you select an optional color palette to use for your tag.
- The **"Is Category"** property lets you treat this tag as a category under which itself and any child tags inheriting from it will be sorted by inside the preview panel.
### Tag Manager
You can manage your library of tags by opening the "Tag Manager" panel from Edit -> "Manage Tags". From here you can create, search for, edit, and permanently delete any tags you've created in your library.
## Editing Tags
To edit a tag, click on it inside the preview panel or right-click the tag and select "Edit Tag" from the context menu.
## Relinking Moved Files
Inevitably some of the files inside your library will be renamed, moved, or deleted. If a file has been renamed or moved, TagStudio will display the thumbnail as a red broken chain link. To relink moved files or delete these entries, select the "Manage Unlinked Entries" option under the Tools menu. Click the "Refresh" button to scan your library for unlinked entries. Once complete, you can attempt to "Search & Relink" any unlinked file entries to their respective files, or "Delete Unlinked Entries" in the event the original files have been deleted and you no longer wish to keep their entries inside your library.
<!-- prettier-ignore -->
!!! warning
There is currently no method to relink entries to files that have been renamed - only moved or deleted. This is a high priority for future releases.
<!-- prettier-ignore -->
!!! warning
If multiple matches for a moved file are found (matches are currently defined as files with a matching filename as the original), TagStudio will currently ignore the match groups. Adding a GUI for manual selection, as well as smarter automated relinking, are high priorities for future versions.
### Saving the Library
As of version 9.5, libraries are saved automatically as you go. To save a backup of your library, select File -> Save Library Backup from the menu bar.
## Launch Arguments
There are a handful of launch arguments you can pass to TagStudio via the command line or a desktop shortcut.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
classNoRendererError(Exception):...
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.