* fix: drop type_key column from text_fields and datetime_fields tables
* fix: commit empty DB_VERSION 200 library
* fix: use recommended renaming order for field tables
* ci: initial version of pyright workflow
* ci: remove invalid pyright settings
* debug: return ALL results from pyright
* ci: remove unnecessary reviewdog install
* Revert "debug: return ALL results from pyright"
This reverts commit 471e39d29f.
* ci: choose reporter based on event type
* ci: choose correct reporter for non-pr event
* ci: small cleanup
* ci: also install pytest deps
* doc: add license info for new workflow
* chore: bump pyright patch version
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* fix: add back settings for basedpyright
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* doc: add REUSE comment headers
* doc: add REUSE comment headers for mkDocs overrides
The original license text was moved to the LICENSES directory
and the MIT license identifier was used since only encoding differences
to the MIT license were present.
Further, the existing copyright notice was moved to SPDX-FileCopyrightText
and one for the TagStudio Contributors was added since the files are not
entirely the same as the originals in the mkdocs-material project.
* doc: add license information files for the translations
* doc: add licence file for oxanium font
As stated on [Google Fonts](https://fonts.google.com/specimen/Oxanium/license), this font is licensed under OFL-1.1.
* doc: re-license flow_layout.py to GPL-3.0-only
This is possible because the Qt example this is based on can be licensed
under BSD-3-Clause which is GPL-3.0 compatible.
* doc: add REUSE comment header for singleton.py
Comparing our implementation with the one it is based on, they really
don't have much more in common than the general idea of the
implementation, which afaik can't be copyrighted.
* doc: Add REUSE comment header for blender_renderer.py
We can plainly license this under GPL-3.0 according to https://www.blender.org/about/license/
* doc: add REUSE comment headers to vendored files
* doc: add REUSE comment header for rounded_pixmap_style.py
Source of name for copyright notice:
Answer by eyllanesc: https://stackoverflow.com/users/6622587/eyllanesc
Profile links to Github: https://github.com/eyllanesc
Stackoverflow licensing: https://stackoverflow.com/help/licensing
* doc: add REUSE comment headers
* doc: add license information file for icon_mono.svg
* doc: add REUSE comment header for files in .github
* doc: add REUSE.toml to give license information for various files without spamming .license files
* fix: use type parameter instead of subclass
* doc: add entries for remaining files in REUSE.toml
According to info from github comment by CyanVoxel: https://github.com/TagStudioDev/TagStudio/pull/1361#issuecomment-4423632570
* ci: add REUSE check workflow
Src: 'https://reuse.software/dev/#github-actions'
* doc: add missing license
* doc: fix license for material-design-icons
* license: change license to MIT for two files only changes by Computerdores and CyanVoxel
* doc: remove unnecessary .license files
* doc: fix incorrect license entries
* doc: add missing license doc
* perf: create sqlite indexes for common columns
* perf: optimize Library.search_tags
* fix(tag_search): do ordering before applying limit
* tag_search: order shorter tag names first
* update tests
* cleanup
* tag_search: use same sorting order when returning all tags
* use dict for deduplicating tags
* fix(tag_search): return descendants instead of ancestors
* perf(tag_search): remove slow calls to method `Library.tags`
* feat: add ability to configure automatic loading of new files over 10000
* Renaming things
* rename and reorder
* fix: remove duplicated add_new_files_callback() call
* feat: add test for failing GitHub API call.
* fix: don't raise exceptions in get_most_recent_release_version.
* style: apply ruff.
* That not should not have been there.
* refactor: cleanup parameters of open_library and open_sqlite_library
* doc: notes on what tables are affected by which migration steps
* refactor(migration order): move DBv6 repairs
* refactor(migration order): move DBv8 repairs
* refactor(migration order): move DBv9 repairs
* refactor(migration order): move DBv100 repairs
* refactor(migration order): move DBv102 repairs
* refactor: merge migration methods
* doc: final comment changes
* fix: query tag ids independent of future DB changes
* feat: remove preferences table
* refactor: various references to LibraryPrefs
* fix: update josn migration UI
* refactor: remove last vestiges of preferences table
* fix: remove newly unnecessary translations
* doc: document library format changes
* refactor: merge the two methods used for migration 104
* fix: typo in sql statement
* fix: add back support for preferences table in get_version
* fix: properly remove directory in test
* fix: incorrect schema check in get_version
* fix: update search lib via migration
* fix: update assert in test
* fix: ignore element order in assert in test
* fix: use correct path
* fix: better test output
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
Replace duplicate double-quote with single-quote so that
escaped single quotes inside single-quoted strings are
handled correctly (e.g. 'O''Brien' now tokenizes as O'Brien
instead of O'Brien).
Discovered during automated code review.
The addition of the update notification added requests and semver to the
dependencies. They were however not added to the nix package, breaking
the build.
* Add `is_hidden` field to the `tags` table
* Add hidden checkbox to the edit tag panel
* Fix formatting
* Exclude hidden tags from search results
* Fix formatting (I should probably actually check before committing? lmao?)
* Bit of cleanup
* Add toggle for excluding hidden entries below search bar
* That might be important maybe
* Update Save Format Changes page in docs (and include updated test database)
* Simplify query and invert name+logic
* chore: remove unused code, tweak strings
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
wrapGAppsHook has been aliased to wrapGAppsHook3 for a long time.
Recently this alias was converted to a throw, breaking the build with
newer nixpkgs versions.
Currently translated at 98.5% (352 of 357 strings)
Translated using Weblate (Italian)
Currently translated at 98.5% (352 of 357 strings)
Translated using Weblate (Italian)
Currently translated at 91.5% (327 of 357 strings)
Translated using Weblate (Italian)
Currently translated at 49.5% (177 of 357 strings)
Translate-URL: https://hosted.weblate.org/projects/tagstudio/strings/it/
Translation: TagStudio/Strings
Co-authored-by: Omni <blackbass175@gmail.com>
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
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
A common workflow is to have a local .envrc, allow contributors to do
so. Still provide the recommended Nix-based setup, for those who wish to
use it.
That file can then be copied to or symlinked to `.envrc`.
Use path as an input that can be overriden automatically when direnv is
in use.
nix-direnv version present in .envrc has been updated, using watch_file on the flake is already handled.
* Enable hardware acceleration for Nix devenv
Section "nativeBuildInputs" needs the dependency "qt6.full" to provide hardware acceleration in QT.
Library "wayland" is needed to create a proper OpenGL context under Wayland as well.
Provide a check for open AMD driver and use it for VDPAU video hardware acceleration.
Move "wrapQtAppsHook" to it's correct place under "nativeBuildInputs".
* Add xorg.libXrandr for hardware accelerated video playback.
Using libva and openssl libraries eliminates the need for a dependency on the qt6.full library.
Not perfect, and mostly a port of the previous edition. Hours have
already been sunk into this, and need to get this out for consumption,
and for ironing out.
For more information see: https://devenv.sh
NOTE: impure is used only because of the devenv-managed state, do not be
alarmed!
Moves the previous updated blurb from the README to the new CONTRIBUTING
file. Also reworks some wording to link to the Flake nix wiki page for
nix users who haven't enable flakes yet.
When using the nix flake to generate a development shell, the python
virtual environment will now automatically be created and dependecies
from both requirements.txt and requirements-dev.txt will be installed.
This removes the need for using the setup script after entering the
dev shell. Exec bash must be the last thing called, as any other
commands past it will not get executed by the shell hook. Also removes
some duplicate dependencies that I found.
* Update to pyside6.7.1
* Fix Ruff
* Fix MyPy
* Update mypy job to also use PySide6 6.7.1
* Remove unused imports
* Add Description to class
* Ruff format
* Fix Warning in pagination.py
* Probably fix Pyside app test
* Rename CustomQPushButton to QPushButtonWrapper
also renamed custom_qbutton.py to qbutton_wrapper.py
* Initial bug report
* Images can be attached in description
* Enclose label in quotes
* Wikis are no longer being used
* Use footnote to clarify what is up-to-date
* Footnotes not supported in checklist
* Initial feature request form
* Fixup grammar/wording
* Add missing libraries for video player
* Pin qt6 package version to 6.6.3
Currently, this succesfully launches the program. Pinning qt seperatly
allows the rest of unstable nixpkgs to be updated even after the qt
package version has been bumped. This fixes vim failing to launch in the
nix shell because of a bad gcc version. Bumping the package version to
qt6.7.1 also will require bumping PySide to 6.7.1, otherwise it will
fail to find qt. Qt 6.7.1 nixpkg commit is 47da0aee5616a063015f10ea593688646f2377e4
* fixup: Pin Qtcreator also
QtCreator was still against nixpkgs not the specific qt variant.
* Add landing page when no library is open
- Add landing page when no library is open
- Add linear_gradient method
- Reformat main_window.py with spaces instead of tabs because apparently it wasn't formatted already?
* Add color_overlay methods, ClickableLabel widget
- Add color_overlay helper methods
- Add clickable_label widget
- Add docstrings to landing.py methods
- Add logo easter egg
- Refactor landing.py content
* Fix redefinition
* Fix macOS shortcut text
* Add option to use a whitelist instead of a blacklist
* maybe fix mypy?
* Fix Mypy and rename ignored_extensions
* This should fix mypy
* Update checkbox text
* Update window title
* shorten if statment and update text
* update variable names
* Fix Mypy
* hopefully fix mypy
* Fix mypy
* deprecate ignored_extensions
Co-authored-by: Jiri <yedpodtrzitko@users.noreply.github.com>
* polishing
* polishing
* Fix mypy
* finishing touches
Co-authored-by: Jiri <yedpodtrzitko@users.noreply.github.com>
* Fix boolean loading
* UI/UX + ext list loading tweaks
- Change extension list mode setting from Checkbox to ComboBox to help better convey its purpose
- Change and simplify wording
- Add type hints to extension variables and change loading to use `get()` with default values
- Sanitize older extension lists that don't use extensions with a leading "."
- Misc. code organization and docstrings
---------
Co-authored-by: Jiri <yedpodtrzitko@users.noreply.github.com>
Co-authored-by: Travis Abendshien <lvnvtravis@gmail.com>
* Attempted fix at mismatched hashes
Due to the seemingly random nature of the bug, this cannot be tested
* macos-11 runner has been deprecated, bump to 12
* multi search mode system
A way to change the search from requiring all tags to and of the tags
* better wording
* Update start_win.bat
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Fix home_ui.py using PySide6 instead of PyQt5
* Refresh search on mode change
* Search mode selections naming fix
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* converted SearchMode from constants to enums
* Prevent Automatic opening of a Library if the ".TagStudio" folder has been deleted.
If the library no longer has a `.TagStudio` folder clear the Last_Library value
* Add disabling recent libraries that are missing or have missing `.TagStudio` folders
* Fix bug where this would crash if an empty library was passed
* Grabbed the wrong color
* Fix text and RAW image handling
- Fix RAW images not being loaded correctly in the preview panel
- Fix trying to read size data from null images
- Refactor `os.stat` to `<Path object>.stat()`
- Remove unnecessary upper/lower conversions
- Improve encoding compatibility beyond UTF-8 when reading text files
- Code cleanup
* Use chardet for character encoding detection
* Basic Video Player
* Fixes and Comments
* Fixed Bug Where Video's Audio did not stop when switching to a Image.
* Redo on VideoPlayer. Now with rounded corners.
* Fixed size not being correct when first starting video player.
* Ruff Check Fix
* Fixed Sizing Issue, and added Autoplay option in right click menu.
* Autoplay Toggle and Fixed Issue with video not stoping after closing library.
* Ruff Format
* Suggested Changes Done
* Commented out useless code that cause first warning.
* Fixed Album Art Error
* Might have found solution to Autoplay Inconsistency
* Ruff Format
* Finally Fixed Autoplay Inconsistency
* Fixed Merge Conficts
* Requested Changes and Ruff Format
* Test for new check
* Fix for PySide App Test
* More typing fixes and a few other changes.
* Ruff Format
* MyPy Fix
* MyPy Fix
* Ruff Format
* MyPy Fix
* MyPy and Ruff Fix
* Code Clean-Up and Requests completed.
* Conflict Fixes
* MyPy Fix
* Confict Fix
It appears one of the commits from main fixed the autoplay issue.
* Replace usage of os.path with usage of pathlib.Path in ts_cli.py
* Replace use of os.path with pathlib in Library.py
* Replace use of os.path with pathlib in ts_core.py
* resolve entry path/filename on creation/update
* Fix errors and bugs related to move from os.path to pathlib.
* Remove most uses of '.resolve()' as it didn't do what I thought it did
* Fix filtering in refresh directories to not need to cast to string.
* Some work on ts_qt, thumbnails don't load...
* Fixed the thumbnail issue, things seem to be working.
* Fix some bugs
* Replace some isfile with is_file ts_cli.py
* Update tagstudio/src/core/library.py
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
* Update library.py
* Update library.py
* Update library.py
* Update ts_cli.py
* Update library.py
* Update ts_qt
* Fix path display in delete unlinked entries modal
* Ruff formatting
* Builds and opens/creates library now
* Fix errors
* Fix ruff and mypy issues (hopefully)
* Fixed some things, broke some things
* Fixed the thumbnails not working
* Fix some new os.path instances in qt files
* Fix MyPy issues
* Fix ruff and mypy issues
* Fix some issues
* Update tagstudio/src/qt/widgets/preview_panel.py
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Update tagstudio/src/qt/widgets/preview_panel.py
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Update tagstudio/src/qt/widgets/preview_panel.py
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Update tagstudio/src/qt/widgets/preview_panel.py
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Update tagstudio/src/qt/widgets/thumb_renderer.py
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Update tagstudio/src/qt/widgets/thumb_renderer.py
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
* Fix refresh_dupe_files issue
* Ruff format
* Tweak filepaths
- Suffix comparisons are now case-insensitive
- Restore original thumbnail extension label behavior
- Fix preview panel trying to read file size from missing files
---------
Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
Co-authored-by: Travis Abendshien <lvnvtravis@gmail.com>
- Running "Fix Unlinked Entries" will no longer result in duplicate entries if the directory was refreshed after the original entries became unlinked.
- A "Duplicate Entries" section is added to the "Fix Unlinked Entries" modal to help repair existing affected libraries.
- Add support for HEIC/HEIF image thumbnails and previews
- Replace dependency "pillow_avif_plugin" with "pi-heif"
- Remove unused dependencies in ts_cli.py
- Add thumbnail and preview support for RAW images ["raw", "dng", "rw2", "nef", "arw", "crw", "cr3"]
- Optimize the preview panel's dimension calculations (still need to move this elsewhere)
- Refactored use of "Path" in thumb_renderer.py
When loading an image for thumbnails and previews, the resampling method is now determined by the size of the original image. Now low resolution images use "nearest neighbor" sampling while higher resolution images continue to use "bilinear" sampling.
* Refactor: remove __init__ meant for Python versions before 3.3
This does mess with a large amount of imports, as the system was being
misused to re-export submodules. This change is necessary if PyInstaller
is to work at all.
* Add MacOS icon
* Create PyInstaller spec file
* Create Release workflow
Creates executable with PyInstaller, leveraging tag_studio.spec
* Support both nonportable and portable in tag_studio.spec
* Rename spec-file to create consistently-named directories
* Only ignore other spec files
* Swap exclusion option
* Use windowed application
* Ensure environment variables are strings
* Cleanup visual order on GitHub interface
* Use app for MacOS
* Only cycle through MacOS version
* All executables generated for MacOS are portable
* Use up-to-date packages
Should resolve caching issues
* Correct architecture naming for MacOS
* Refactor: remove __init__ meant for Python versions before 3.3
This does mess with a large amount of imports, as the system was being
misused to re-export submodules. This change is necessary if PyInstaller
is to work at all.
* Thanks Ruff
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
---------
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
commit 094b6d50d975ec8feaa24af6a8a7b121fe87bce3
Author: Travis Abendshien <lvnvtravis@gmail.com>
Date: Sun May 12 19:52:25 2024 -0700
Formatted using Ruff
Co-Authored-By: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
commit 088ef5263e38c948dda9a875fcc56af97762a73e
Author: Travis Abendshien <lvnvtravis@gmail.com>
Date: Sun May 12 19:51:03 2024 -0700
Reduced QResource Usage + Path Refactor
- Removed unused or redundant QResources
- Removed unreliable uses of the Qt resource system in favor of direct paths
- Refactored paths with "parent.parent.parent" to use ".parents[index]"
Co-Authored-By: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
- Updated application icons
- Added .icns file for macOS
- (Potentially) stopped macOS dock icon from being updated with a different icon during runtime
- Removed legacy style experiments, including strange translucent widgets and indigo default tags
- Renamed "Folders To Tags" to "Create Tags From Folders"
- Edited formatting of "Create Tags From Folders" modal
- Renamed "Tag Database" to "Manage Tags"/"Library Tags"
- Tweaked names of other menubar actions
- Removed some commented-out code
* chore: add TagStudio.spec to gitignore
Prevent TagStudio.spec to be added to repo in the future
* chore: add Build Script for macos
Create script using pyinstaller to generate a macos app for tagstudio
* chore: revert duplicated files
* chore: rename build file naming
Fixed images that were considered to be "truncated" from breaking the thumbnail renderer when trying to transpose via an EXIF flag. This was happening with certain panorama photos.
- Catches additional exception in the sorting process of new files added to the library
- Skips this sorting process if there are more than 100,000 files (this is a temporary sorting measure, anyway)
- Possibly improved performance during the functionless (and cursed) "Applying Configured Macros" step of loading new files
I changed it to output an executable and a _internals dir because this makes it, atleast from my experience, much faster (12s -> 1-2s). This is probably because the decompression that has to happen before startup if its in one file
- All file types (minus JSON, XMP, and AAE) are now shown by default in the library (existing libraries will need to refresh)
- Added the Edit -> "Ignore File Extensions" option, providing the user with a way to blacklist certain file extensions from their library
- The targeted version number has been updated to 9.2.0 (this is not the final 9.2.0 release, commits will still be added before that release is packaged up)
**BASIC** support for rendering thumbnails for certain plaintext types (txt, md, html, etc.) using PIL.
Notable issues include:
- Long draw times (entire files are read)
- No text wrapping
- Hardcoded style
- Blurry text in preview pane images
- No cached thumbnails (I call dibs on the thumbnail caching system)
Added rudimentary support for audio types, text types, spreadsheets, presentations, archives, and programs. These files will now be scanned and picked up when refreshing the library.
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.
[^1]: This can mean latest release, pre-release, or git commit.
- type:checkboxes
attributes:
label:Checklist
description:Make sure you've checked (and actually did) all of the below.
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/docs/index.md).
required:true
- label:I have searched existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
required:true
- type:input
attributes:
label:TagStudio Version
placeholder:Alpha 9.1.0
validations:
required:true
- type:input
attributes:
label:Operating System & Version
placeholder:TagOS 3.14
validations:
required:true
- type:textarea
attributes:
label:Description
description:Clear and concise description of the problem. Attach screenshots if needed, and include any errors you see.
validations:
required:true
- type:textarea
attributes:
label:Expected Behavior
description:Clear and concise description of what you think should happen.
validations:
required:true
- type:textarea
attributes:
label:Steps to Reproduce
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/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.
[^1]: This can mean latest release, pre-release, or git commit.
- type:checkboxes
attributes:
label:Checklist
description:Make sure you've checked (and actually did) all of the below.
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/docs/index.md).
required:true
- label:I have searched existing [issues](https://github.com/TagStudioDev/TagStudio/issues).
required:true
- type:textarea
attributes:
label:Description
description:Clear and concise description of the problem or missing capability. Attach screenshots if needed, and explain your own use case.
validations:
required:true
- type:textarea
attributes:
label:Solution
description:Clear and concise description of what you think should happen.
- type:textarea
attributes:
label:Alternatives
description:Any considered alternative solutions or workarounds. If undesirable, why?
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)!**
<i>TagStudio Alpha v9.5.5 running on macOS Sequoia.</i>
</p>
## Contents
- [Feature Highlights](#feature-highlights)
- [Basic Usage](#basic-usage)
- [Installation](#installation)
- [Goals & Priorities](#goals--priorities)
- [FAQ](#faq)
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!
## Feature Highlights
### Libraries
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.
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.
### File Entries
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.
For a generalized list of what's currently supported:
- **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!
### [Tags](https://docs.tagstud.io/tags) and [Fields](https://docs.tagstud.io/fields)
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.
Tags currently consist of the following attributes:
- **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.
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.
Field types currently include:
- **Text Lines**: Single lines of text.
- **Text Boxes**: Multi-line pieces of text.
- **Datetimes**: Dates and times.
### [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
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 or by pressing <kbd>Ctrl</kbd></kbd>+<kbd>R</kbd> (macOS: <kbd>⌘ Command</kbd>+<kbd>R</kbd>).
### 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> (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.
#### 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, click on it inside the preview panel or right-click the tag and select "Edit Tag" from the context 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> (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 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 high priorities for future versions.
See instructions in the "[Creating Development Environment](/CONTRIBUTING.md/#creating-a-development-environment)" section from the [contributing](https://docs.tagstud.io/contributing) page.
## Installation
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 **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.
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]
> **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!
## Goals & Priorities
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.
See the [**Roadmap**](docs/roadmap.md) on the documentation site for a complete list of planned features and estimated timeline.
### Overall Goals
- 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.
### Project Priorities
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
### Will TagStudio move, modify, or mess with my files?
**No**, outside of _explicit_ functionality such as "Move File(s) to Trash".
### Will TagStudio require me to recreate my tags or library in future updates?
**No.** It's our highest priority to ensure that your data safely and smoothly transfers over to newer versions.
### What state is the project currently in?
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.
### What features are you planning on adding?
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.
The most important remaining features before I consider the program to be "feature complete" are:
- 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
### What features will NOT be added?
- 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.
### Is a Rust port coming?
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.
### 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.
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=function(){varr=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=newfunction(){functione(){vare,r=f.cursor+3;if(d=f.limit,0<=r&&r<=f.limit){for(a=r;;){if(e=f.cursor,f.in_grouping(w,97,248)){f.cursor=e;break}if(f.cursor=e,e>=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d<a&&(d=a)}}functionn(){vare,r;if(f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case1:f.slice_del();break;case2:f.in_grouping_b(p,97,229)&&f.slice_del()}}functiont(){vare,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}functions(){vare,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case2:f.slice_from("løs")}}functiono(){vare;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}vara,d,u,c=[newr("hed",-1,1),newr("ethed",0,1),newr("ered",-1,1),newr("e",-1,1),newr("erede",3,1),newr("ende",3,1),newr("erende",5,1),newr("ene",3,1),newr("erne",3,1),newr("ere",3,1),newr("en",-1,1),newr("heden",10,1),newr("eren",10,1),newr("er",-1,1),newr("heder",13,1),newr("erer",13,1),newr("s",-1,2),newr("heds",16,1),newr("es",16,1),newr("endes",18,1),newr("erendes",19,1),newr("enes",18,1),newr("ernes",18,1),newr("eres",18,1),newr("ens",16,1),newr("hedens",24,1),newr("erens",24,1),newr("ers",16,1),newr("ets",16,1),newr("erets",28,1),newr("et",-1,1),newr("eret",30,1)],l=[newr("gd",-1,-1),newr("dt",-1,-1),newr("gt",-1,-1),newr("kt",-1,-1)],m=[newr("ig",-1,1),newr("lig",0,1),newr("elig",1,1),newr("els",-1,1),newr("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=newi;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){returnf.getCurrent()},this.stem=function(){varr=f.cursor;returne(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};returnfunction(e){return"function"==typeofe.update?e.update(function(e){returnn.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}});
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hi=function(){this.pipeline.reset(),this.pipeline.add(e.hi.trimmer,e.hi.stopWordFilter,e.hi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hi.stemmer))},e.hi.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿa-zA-Za-zA-Z0-90-9",e.hi.trimmer=e.trimmerSupport.generateTrimmer(e.hi.wordCharacters),e.Pipeline.registerFunction(e.hi.trimmer,"trimmer-hi"),e.hi.stopWordFilter=e.generateStopWordFilter("अत अपना अपनी अपने अभी अंदर आदि आप इत्यादि इन इनका इन्हीं इन्हें इन्हों इस इसका इसकी इसके इसमें इसी इसे उन उनका उनकी उनके उनको उन्हीं उन्हें उन्हों उस उसके उसी उसे एक एवं एस ऐसे और कई कर करता करते करना करने करें कहते कहा का काफ़ी कि कितना किन्हें किन्हों किया किर किस किसी किसे की कुछ कुल के को कोई कौन कौनसा गया घर जब जहाँ जा जितना जिन जिन्हें जिन्हों जिस जिसे जीधर जैसा जैसे जो तक तब तरह तिन तिन्हें तिन्हों तिस तिसे तो था थी थे दबारा दिया दुसरा दूसरे दो द्वारा न नके नहीं ना निहायत नीचे ने पर पहले पूरा पे फिर बनी बही बहुत बाद बाला बिलकुल भी भीतर मगर मानो मे में यदि यह यहाँ यही या यिह ये रखें रहा रहे ऱ्वासा लिए लिये लेकिन व वग़ैरह वर्ग वह वहाँ वहीं वाले वुह वे वो सकता सकते सबसे सभी साथ साबुत साभ सारा से सो संग ही हुआ हुई हुए है हैं हो होता होती होते होना होने".split(" ")),e.hi.stemmer=function(){returnfunction(e){return"function"==typeofe.update?e.update(function(e){returne}):e}}();varr=e.wordcut;r.init(),e.hi.tokenizer=function(i){if(!arguments.length||null==i||void0==i)return[];if(Array.isArray(i))returni.map(function(r){returnisLunr2?newe.Token(r.toLowerCase()):r.toLowerCase()});vart=i.toString().toLowerCase().replace(/^\s+/,"");returnr.cut(t).split("|")},e.Pipeline.registerFunction(e.hi.stemmer,"stemmer-hi"),e.Pipeline.registerFunction(e.hi.stopWordFilter,"stopWordFilter-hi")}});
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hy=function(){this.pipeline.reset(),this.pipeline.add(e.hy.trimmer,e.hy.stopWordFilter)},e.hy.wordCharacters="[A-Za-z-֏ff-ﭏ]",e.hy.trimmer=e.trimmerSupport.generateTrimmer(e.hy.wordCharacters),e.Pipeline.registerFunction(e.hy.trimmer,"trimmer-hy"),e.hy.stopWordFilter=e.generateStopWordFilter("դու և եք էիր էիք հետո նաև նրանք որը վրա է որ պիտի են այս մեջ ն իր ու ի այդ որոնք այն կամ էր մի ես համար այլ իսկ էին ենք հետ ին թ էինք մենք նրա նա դուք եմ էի ըստ որպես ում".split(" ")),e.Pipeline.registerFunction(e.hy.stopWordFilter,"stopWordFilter-hy"),e.hy.stemmer=function(){returnfunction(e){return"function"==typeofe.update?e.update(function(e){returne}):e}}(),e.Pipeline.registerFunction(e.hy.stemmer,"stemmer-hy")}});
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.kn=function(){this.pipeline.reset(),this.pipeline.add(e.kn.trimmer,e.kn.stopWordFilter,e.kn.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.kn.stemmer))},e.kn.wordCharacters="ಀ-಄ಅ-ಔಕ-ಹಾ-ೌ಼-ಽೕ-ೖೝ-ೞೠ-ೡೢ-ೣ೦-೯ೱ-ೳ",e.kn.trimmer=e.trimmerSupport.generateTrimmer(e.kn.wordCharacters),e.Pipeline.registerFunction(e.kn.trimmer,"trimmer-kn"),e.kn.stopWordFilter=e.generateStopWordFilter("ಮತ್ತು ಈ ಒಂದು ರಲ್ಲಿ ಹಾಗೂ ಎಂದು ಅಥವಾ ಇದು ರ ಅವರು ಎಂಬ ಮೇಲೆ ಅವರ ತನ್ನ ಆದರೆ ತಮ್ಮ ನಂತರ ಮೂಲಕ ಹೆಚ್ಚು ನ ಆ ಕೆಲವು ಅನೇಕ ಎರಡು ಹಾಗು ಪ್ರಮುಖ ಇದನ್ನು ಇದರ ಸುಮಾರು ಅದರ ಅದು ಮೊದಲ ಬಗ್ಗೆ ನಲ್ಲಿ ರಂದು ಇತರ ಅತ್ಯಂತ ಹೆಚ್ಚಿನ ಸಹ ಸಾಮಾನ್ಯವಾಗಿ ನೇ ಹಲವಾರು ಹೊಸ ದಿ ಕಡಿಮೆ ಯಾವುದೇ ಹೊಂದಿದೆ ದೊಡ್ಡ ಅನ್ನು ಇವರು ಪ್ರಕಾರ ಇದೆ ಮಾತ್ರ ಕೂಡ ಇಲ್ಲಿ ಎಲ್ಲಾ ವಿವಿಧ ಅದನ್ನು ಹಲವು ರಿಂದ ಕೇವಲ ದ ದಕ್ಷಿಣ ಗೆ ಅವನ ಅತಿ ನೆಯ ಬಹಳ ಕೆಲಸ ಎಲ್ಲ ಪ್ರತಿ ಇತ್ಯಾದಿ ಇವು ಬೇರೆ ಹೀಗೆ ನಡುವೆ ಇದಕ್ಕೆ ಎಸ್ ಇವರ ಮೊದಲು ಶ್ರೀ ಮಾಡುವ ಇದರಲ್ಲಿ ರೀತಿಯ ಮಾಡಿದ ಕಾಲ ಅಲ್ಲಿ ಮಾಡಲು ಅದೇ ಈಗ ಅವು ಗಳು ಎ ಎಂಬುದು ಅವನು ಅಂದರೆ ಅವರಿಗೆ ಇರುವ ವಿಶೇಷ ಮುಂದೆ ಅವುಗಳ ಮುಂತಾದ ಮೂಲ ಬಿ ಮೀ ಒಂದೇ ಇನ್ನೂ ಹೆಚ್ಚಾಗಿ ಮಾಡಿ ಅವರನ್ನು ಇದೇ ಯ ರೀತಿಯಲ್ಲಿ ಜೊತೆ ಅದರಲ್ಲಿ ಮಾಡಿದರು ನಡೆದ ಆಗ ಮತ್ತೆ ಪೂರ್ವ ಆತ ಬಂದ ಯಾವ ಒಟ್ಟು ಇತರೆ ಹಿಂದೆ ಪ್ರಮಾಣದ ಗಳನ್ನು ಕುರಿತು ಯು ಆದ್ದರಿಂದ ಅಲ್ಲದೆ ನಗರದ ಮೇಲಿನ ಏಕೆಂದರೆ ರಷ್ಟು ಎಂಬುದನ್ನು ಬಾರಿ ಎಂದರೆ ಹಿಂದಿನ ಆದರೂ ಆದ ಸಂಬಂಧಿಸಿದ ಮತ್ತೊಂದು ಸಿ ಆತನ ".split(" ")),e.kn.stemmer=function(){returnfunction(e){return"function"==typeofe.update?e.update(function(e){returne}):e}}();varr=e.wordcut;r.init(),e.kn.tokenizer=function(t){if(!arguments.length||null==t||void0==t)return[];if(Array.isArray(t))returnt.map(function(r){returnisLunr2?newe.Token(r.toLowerCase()):r.toLowerCase()});varn=t.toString().toLowerCase().replace(/^\s+/,"");returnr.cut(n).split("|")},e.Pipeline.registerFunction(e.kn.stemmer,"stemmer-kn"),e.Pipeline.registerFunction(e.kn.stopWordFilter,"stopWordFilter-kn")}});
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){varr=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=newfunction(){functione(){vare,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a<s&&(a=s)}}functioni(){vare,r,n;if(w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case1:w.slice_del();break;case2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case3:w.slice_from("er")}}functiont(){vare,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}functiono(){vare,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}vars,a,m=[newr("a",-1,1),newr("e",-1,1),newr("ede",1,1),newr("ande",1,1),newr("ende",1,1),newr("ane",1,1),newr("ene",1,1),newr("hetene",6,1),newr("erte",1,3),newr("en",-1,1),newr("heten",9,1),newr("ar",-1,1),newr("er",-1,1),newr("heter",12,1),newr("s",-1,2),newr("as",14,1),newr("es",14,1),newr("edes",16,1),newr("endes",16,1),newr("enes",16,1),newr("hetenes",19,1),newr("ens",14,1),newr("hetens",21,1),newr("ers",14,1),newr("ets",14,1),newr("et",-1,1),newr("het",25,1),newr("ert",-1,3),newr("ast",-1,1)],u=[newr("dt",-1,-1),newr("vt",-1,-1)],l=[newr("leg",-1,1),newr("eleg",0,1),newr("ig",-1,1),newr("eig",2,1),newr("lig",2,1),newr("elig",4,1),newr("els",-1,1),newr("lov",-1,1),newr("elov",7,1),newr("slov",7,1),newr("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=newn;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){returnw.getCurrent()},this.stem=function(){varr=w.cursor;returne(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};returnfunction(e){return"function"==typeofe.update?e.update(function(e){returni.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}});
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){varr=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=newfunction(){functione(){vare,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o<a&&(o=a)}}functiont(){vare,r=w.limit_backward;if(w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case1:w.slice_del();break;case2:w.in_grouping_b(d,98,121)&&w.slice_del()}}functioni(){vare=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}functions(){vare,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case1:w.slice_del();break;case2:w.slice_from("lös");break;case3:w.slice_from("full")}w.limit_backward=r}}vara,o,u=[newr("a",-1,1),newr("arna",0,1),newr("erna",0,1),newr("heterna",2,1),newr("orna",0,1),newr("ad",-1,1),newr("e",-1,1),newr("ade",6,1),newr("ande",6,1),newr("arne",6,1),newr("are",6,1),newr("aste",6,1),newr("en",-1,1),newr("anden",12,1),newr("aren",12,1),newr("heten",12,1),newr("ern",-1,1),newr("ar",-1,1),newr("er",-1,1),newr("heter",18,1),newr("or",-1,1),newr("s",-1,2),newr("as",21,1),newr("arnas",22,1),newr("ernas",22,1),newr("ornas",22,1),newr("es",21,1),newr("ades",26,1),newr("andes",26,1),newr("ens",21,1),newr("arens",29,1),newr("hetens",29,1),newr("erns",21,1),newr("at",-1,1),newr("andet",-1,1),newr("het",-1,1),newr("ast",-1,1)],c=[newr("dd",-1,-1),newr("gd",-1,-1),newr("nn",-1,-1),newr("dt",-1,-1),newr("gt",-1,-1),newr("kt",-1,-1),newr("tt",-1,-1)],m=[newr("ig",-1,1),newr("lig",0,1),newr("els",-1,1),newr("fullt",-1,3),newr("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=newn;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){returnw.getCurrent()},this.stem=function(){varr=w.cursor;returne(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};returnfunction(e){return"function"==typeofe.update?e.update(function(e){returnt.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}});
!function(e,t){"function"==typeofdefine&&define.amd?define(t):"object"==typeofexports?module.exports=t():t()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ta=function(){this.pipeline.reset(),this.pipeline.add(e.ta.trimmer,e.ta.stopWordFilter,e.ta.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ta.stemmer))},e.ta.wordCharacters="-உஊ-ஏஐ-ஙச-ட-னப-யர-ஹ-ிீ-ொ-ௐ---௩௪-௯௰-௹௺-a-zA-Za-zA-Z0-90-9",e.ta.trimmer=e.trimmerSupport.generateTrimmer(e.ta.wordCharacters),e.Pipeline.registerFunction(e.ta.trimmer,"trimmer-ta"),e.ta.stopWordFilter=e.generateStopWordFilter("அங்கு அங்கே அது அதை அந்த அவர் அவர்கள் அவள் அவன் அவை ஆக ஆகவே ஆகையால் ஆதலால் ஆதலினால் ஆனாலும் ஆனால் இங்கு இங்கே இது இதை இந்த இப்படி இவர் இவர்கள் இவள் இவன் இவை இவ்வளவு உனக்கு உனது உன் உன்னால் எங்கு எங்கே எது எதை எந்த எப்படி எவர் எவர்கள் எவள் எவன் எவை எவ்வளவு எனக்கு எனது எனவே என் என்ன என்னால் ஏது ஏன் தனது தன்னால் தானே தான் நாங்கள் நாம் நான் நீ நீங்கள்".split(" ")),e.ta.stemmer=function(){returnfunction(e){return"function"==typeofe.update?e.update(function(e){returne}):e}}();vart=e.wordcut;t.init(),e.ta.tokenizer=function(r){if(!arguments.length||null==r||void0==r)return[];if(Array.isArray(r))returnr.map(function(t){returnisLunr2?newe.Token(t.toLowerCase()):t.toLowerCase()});vari=r.toString().toLowerCase().replace(/^\s+/,"");returnt.cut(i).split("|")},e.Pipeline.registerFunction(e.ta.stemmer,"stemmer-ta"),e.Pipeline.registerFunction(e.ta.stopWordFilter,"stopWordFilter-ta")}});
!function(e,t){"function"==typeofdefine&&define.amd?define(t):"object"==typeofexports?module.exports=t():t()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.te=function(){this.pipeline.reset(),this.pipeline.add(e.te.trimmer,e.te.stopWordFilter,e.te.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.te.stemmer))},e.te.wordCharacters="ఀ-ఄఅ-ఔక-హా-ౌౕ-ౖౘ-ౚౠ-ౡౢ-ౣ౦-౯౸-౿఼ఽ్ౝ౷",e.te.trimmer=e.trimmerSupport.generateTrimmer(e.te.wordCharacters),e.Pipeline.registerFunction(e.te.trimmer,"trimmer-te"),e.te.stopWordFilter=e.generateStopWordFilter("అందరూ అందుబాటులో అడగండి అడగడం అడ్డంగా అనుగుణంగా అనుమతించు అనుమతిస్తుంది అయితే ఇప్పటికే ఉన్నారు ఎక్కడైనా ఎప్పుడు ఎవరైనా ఎవరో ఏ ఏదైనా ఏమైనప్పటికి ఒక ఒకరు కనిపిస్తాయి కాదు కూడా గా గురించి చుట్టూ చేయగలిగింది తగిన తర్వాత దాదాపు దూరంగా నిజంగా పై ప్రకారం ప్రక్కన మధ్య మరియు మరొక మళ్ళీ మాత్రమే మెచ్చుకో వద్ద వెంట వేరుగా వ్యతిరేకంగా సంబంధం".split(" ")),e.te.stemmer=function(){returnfunction(e){return"function"==typeofe.update?e.update(function(e){returne}):e}}();vart=e.wordcut;t.init(),e.te.tokenizer=function(r){if(!arguments.length||null==r||void0==r)return[];if(Array.isArray(r))returnr.map(function(t){returnisLunr2?newe.Token(t.toLowerCase()):t.toLowerCase()});vari=r.toString().toLowerCase().replace(/^\s+/,"");returnt.cut(i).split("|")},e.Pipeline.registerFunction(e.te.stemmer,"stemmer-te"),e.Pipeline.registerFunction(e.te.stopWordFilter,"stopWordFilter-te")}});
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");varr="2"==e.version[0];e.th=function(){this.pipeline.reset(),this.pipeline.add(e.th.trimmer),r?this.tokenizer=e.th.tokenizer:(e.tokenizer&&(e.tokenizer=e.th.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.th.tokenizer))},e.th.wordCharacters="[-]",e.th.trimmer=e.trimmerSupport.generateTrimmer(e.th.wordCharacters),e.Pipeline.registerFunction(e.th.trimmer,"trimmer-th");vart=e.wordcut;t.init(),e.th.tokenizer=function(i){if(!arguments.length||null==i||void0==i)return[];if(Array.isArray(i))returni.map(function(t){returnr?newe.Token(t):t});varn=i.toString().replace(/^\s+/,"");returnt.cut(n).split("|")}}});
!function(e,r){"function"==typeofdefine&&define.amd?define(r):"object"==typeofexports?module.exports=r():r()(e.lunr)}(this,function(){returnfunction(e){if(void0===e)thrownewError("Lunr is not present. Please include / require Lunr before this script.");if(void0===e.stemmerSupport)thrownewError("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}});
File diff suppressed because one or more lines are too long
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.