Compare commits

...

12 Commits

Author SHA1 Message Date
Stanislav 5d67511315
Merge cd70c99c67 into 3a97ba941a 2024-06-03 22:15:47 +02:00
Andrew Senetar 3a97ba941a
ci: Merge artifacts
- Merge the resulting artifacts
- Use only the .so files from build
2024-05-11 01:21:58 -07:00
Andrew Senetar e3bcf9d686
chore: Update VS Code configuration 2024-05-11 00:12:19 -07:00
Andrew Senetar a81069be61
fix: Photo matching fixes
- Correct bad query introduced in rotation matching
- Promote get_orientation from "private" on photo class
- Fix prepare_pictures to only generate the needed blocks, add check for missing blocks when rotation matchin is true
- Fix cache test inputs to match schema
2024-05-11 00:11:27 -07:00
Stanislav cd70c99c67
Merge branch 'arsenetar:master' into i18n_Russian 2023-12-07 21:52:39 +03:00
Stanislav 7eafb86c2b
Merge branch 'arsenetar:master' into i18n_Russian 2023-08-13 18:35:41 +03:00
Stanislav 71672bc2c9
Update ui.po 2023-06-25 20:39:10 +03:00
Stanislav f4e6768caf
Update core.po 2023-06-24 12:39:40 +03:00
Stanislav 8ce4080c4e
Update ui.po 2023-06-24 12:38:57 +03:00
Stanislav f191b64cca
Update columns.po 2023-06-24 12:34:42 +03:00
Stanislav a714a0860c
Update core.po 2023-06-24 12:33:16 +03:00
Stanislav 5fb8caefa9
Small fix Russian language 2023-06-24 12:04:47 +03:00
10 changed files with 83 additions and 66 deletions

View File

@ -52,4 +52,14 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: modules ${{ matrix.python-version }}
path: ${{ github.workspace }}/**/*.so
path: build/**/*.so
merge-artifacts:
needs: [test]
runs-on: ubuntu-latest
steps:
- name: Merge Artifacts
uses: actions/upload-artifact/merge@v4
with:
name: modules
pattern: modules*
delete-merged: true

2
.vscode/launch.json vendored
View File

@ -6,7 +6,7 @@
"configurations": [
{
"name": "DupuGuru",
"type": "python",
"type": "debugpy",
"request": "launch",
"program": "run.py",
"console": "integratedTerminal",

View File

@ -12,5 +12,6 @@
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "ms-python.black-formatter"
}
},
"python.testing.pytestEnabled": true
}

View File

@ -158,7 +158,7 @@ class SqliteCache:
ids = ",".join(map(str, rowids))
sql = (
"select rowid, blocks, blocks2, blocks3, blocks4, blocks5, blocks6, blocks7, blocks8 "
f"from pictures where rowid in {ids}"
f"from pictures where rowid in ({ids})"
)
cur = self.con.execute(sql)
return (

View File

@ -54,7 +54,7 @@ def get_cache(cache_path, readonly=False):
return SqliteCache(cache_path, readonly=readonly)
def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob):
def prepare_pictures(pictures, cache_path, with_dimensions, match_rotated, j=job.nulljob):
# The MemoryError handlers in there use logging without first caring about whether or not
# there is enough memory left to carry on the operation because it is assumed that the
# MemoryError happens when trying to read an image file, which is freed from memory by the
@ -76,8 +76,14 @@ def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob):
if with_dimensions:
picture.dimensions # pre-read dimensions
try:
if picture.unicode_path not in cache:
blocks = [picture.get_blocks(BLOCK_COUNT_PER_SIDE, orientation) for orientation in range(1, 9)]
if picture.unicode_path not in cache or (
match_rotated and any(block == [] for block in cache[picture.unicode_path])
):
if match_rotated:
blocks = [picture.get_blocks(BLOCK_COUNT_PER_SIDE, orientation) for orientation in range(1, 9)]
else:
blocks = [[]] * 8
blocks[max(picture.get_orientation() - 1, 0)] = picture.get_blocks(BLOCK_COUNT_PER_SIDE)
cache[picture.unicode_path] = blocks
prepared.append(picture)
except (OSError, ValueError) as e:
@ -187,7 +193,7 @@ def getmatches(pictures, cache_path, threshold, match_scaled=False, match_rotate
j.set_progress(comparison_count, progress_msg)
j = j.start_subjob([3, 7])
pictures = prepare_pictures(pictures, cache_path, with_dimensions=not match_scaled, j=j)
pictures = prepare_pictures(pictures, cache_path, not match_scaled, match_rotated, j=j)
j = j.start_subjob([9, 1], tr("Preparing for matching"))
cache = get_cache(cache_path)
id2picture = {}

View File

@ -37,7 +37,7 @@ class Photo(fs.File):
def _plat_get_blocks(self, block_count_per_side, orientation):
raise NotImplementedError()
def _get_orientation(self):
def get_orientation(self):
if not hasattr(self, "_cached_orientation"):
try:
with self.path.open("rb") as fp:
@ -95,13 +95,13 @@ class Photo(fs.File):
fs.File._read_info(self, field)
if field == "dimensions":
self.dimensions = self._plat_get_dimensions()
if self._get_orientation() in {5, 6, 7, 8}:
if self.get_orientation() in {5, 6, 7, 8}:
self.dimensions = (self.dimensions[1], self.dimensions[0])
elif field == "exif_timestamp":
self.exif_timestamp = self._get_exif_timestamp()
def get_blocks(self, block_count_per_side, orientation: int = None):
if orientation is None:
return self._plat_get_blocks(block_count_per_side, self._get_orientation())
return self._plat_get_blocks(block_count_per_side, self.get_orientation())
else:
return self._plat_get_blocks(block_count_per_side, orientation)

View File

@ -59,13 +59,13 @@ class BaseTestCaseCache:
def test_set_then_retrieve_blocks(self):
c = self.get_cache()
b = [(0, 0, 0), (1, 2, 3)]
b = [[(0, 0, 0), (1, 2, 3)]] * 8
c["foo"] = b
eq_(b, c["foo"])
def test_delitem(self):
c = self.get_cache()
c["foo"] = ""
c["foo"] = [[]] * 8
del c["foo"]
assert "foo" not in c
with raises(KeyError):
@ -74,16 +74,16 @@ class BaseTestCaseCache:
def test_persistance(self, tmpdir):
DBNAME = tmpdir.join("hstest.db")
c = self.get_cache(str(DBNAME))
c["foo"] = [(1, 2, 3)]
c["foo"] = [[(1, 2, 3)]] * 8
del c
c = self.get_cache(str(DBNAME))
eq_([(1, 2, 3)], c["foo"])
eq_([[(1, 2, 3)]] * 8, c["foo"])
def test_filter(self):
c = self.get_cache()
c["foo"] = ""
c["bar"] = ""
c["baz"] = ""
c["foo"] = [[]] * 8
c["bar"] = [[]] * 8
c["baz"] = [[]] * 8
c.filter(lambda p: p != "bar") # only 'bar' is removed
eq_(2, len(c))
assert "foo" in c
@ -92,9 +92,9 @@ class BaseTestCaseCache:
def test_clear(self):
c = self.get_cache()
c["foo"] = ""
c["bar"] = ""
c["baz"] = ""
c["foo"] = [[]] * 8
c["bar"] = [[]] * 8
c["baz"] = [[]] * 8
c.clear()
eq_(0, len(c))
assert "foo" not in c
@ -104,7 +104,7 @@ class BaseTestCaseCache:
def test_by_id(self):
# it's possible to use the cache by referring to the files by their row_id
c = self.get_cache()
b = [(0, 0, 0), (1, 2, 3)]
b = [[(0, 0, 0), (1, 2, 3)]] * 8
c["foo"] = b
foo_id = c.get_id("foo")
eq_(c[foo_id], b)
@ -127,10 +127,10 @@ class TestCaseSqliteCache(BaseTestCaseCache):
fp.write("invalid sqlite content")
fp.close()
c = self.get_cache(dbname) # should not raise a DatabaseError
c["foo"] = [(1, 2, 3)]
c["foo"] = [[(1, 2, 3)]] * 8
del c
c = self.get_cache(dbname)
eq_(c["foo"], [(1, 2, 3)])
eq_(c["foo"], [[(1, 2, 3)]] * 8)
class TestCaseCacheSQLEscape:
@ -152,7 +152,7 @@ class TestCaseCacheSQLEscape:
def test_delitem(self):
c = self.get_cache()
c["foo'bar"] = []
c["foo'bar"] = [[]] * 8
try:
del c["foo'bar"]
except KeyError:

View File

@ -112,7 +112,7 @@ msgstr "Размеры"
#: core\pe\result_table.py:21 core\se\result_table.py:21
msgid "Size (KB)"
msgstr "Размер (КБ)"
msgstr "Размер (кБ)"
#: core\pe\result_table.py:24
msgid "EXIF Timestamp"

View File

@ -100,15 +100,15 @@ msgstr ""
#: core\app.py:469
msgid "Select a directory to copy marked files to"
msgstr "Выберите каталог, в который вы хотите скопировать отмеченные файлы"
msgstr "Выберите каталог, в который Вы хотите скопировать отмеченные файлы"
#: core\app.py:471
msgid "Select a directory to move marked files to"
msgstr "Выберите каталог, в который вы хотите переместить отмеченные файлы"
msgstr "Выберите каталог для перемещения отмеченных файлов"
#: core\app.py:510
msgid "Select a destination for your exported CSV"
msgstr "Выберите назначение для экспортируемого "
msgstr "Выберите назначение для Вашего экспортируемого CSV"
#: core\app.py:516 core\app.py:777 core\app.py:787
msgid "Couldn't write to file: {}"
@ -124,7 +124,7 @@ msgstr "Вы собираетесь удалить %d файлов из резу
#: core\app.py:749
msgid "{} duplicate groups were changed by the re-prioritization."
msgstr "{} групп дубликатов было изменено при реприоритезации."
msgstr "{} групп дубликатов было изменено при обновлении приоритета."
#: core\app.py:797
msgid "The selected directories contain no scannable file."
@ -136,7 +136,7 @@ msgstr "Сбор файлов для сканирования"
#: core\app.py:863
msgid "%s (%d discarded)"
msgstr "%s. (%d отменено)"
msgstr "%s (%d отменено)"
#: core\directories.py:191
msgid "Collected {} files to scan"
@ -148,7 +148,7 @@ msgstr "Собрано {} каталогов для сканирования"
#: core\engine.py:27
msgid "%d matches found from %d groups"
msgstr "Найдено %d совпадений из %d групп"
msgstr "%d совпадений найдено из %d групп"
#: core\gui\deletion_options.py:71
msgid "You are sending {} file(s) to the Trash."
@ -249,7 +249,7 @@ msgstr "%d / %d (%s / %s) дубликатов отмечено."
#: core\results.py:142
msgid " filter: %s"
msgstr "фильтр: %s"
msgstr " фильтр: %s"
#: core\scanner.py:114
msgid "Read metadata of %d/%d files"
@ -262,3 +262,4 @@ msgstr "Почти готово! Вожусь с результатами..."
#: core\se\scanner.py:18
msgid "Folders"
msgstr "Папки"

View File

@ -93,8 +93,8 @@ msgid ""
"Instead of sending files to trash, delete them directly. This option is "
"usually used as a workaround when the normal deletion method doesn't work."
msgstr ""
"Удалить файлы с диска вместо отправки в Корзину. Используйте если нормальный"
" метод удаления не работает."
"Удалить файлы с диска вместо отправки в Корзину. Используйте, если нормальный "
"метод удаления не работает."
#: qt/deletion_options.py:59 cocoa/en.lproj/Localizable.strings:0
msgid "Proceed"
@ -189,7 +189,7 @@ msgstr "Несохранённые результаты"
#: qt/directories_dialog.py:231 cocoa/en.lproj/Localizable.strings:0
msgid "You have unsaved results, do you really want to quit?"
msgstr "Имеются несохранённые результаты, вы действительно хотите выйти?"
msgstr "Имеются несохранённые результаты, Вы действительно хотите выйти?"
#: qt/directories_dialog.py:239 cocoa/en.lproj/Localizable.strings:0
msgid "Select a folder to add to the scanning list"
@ -205,7 +205,7 @@ msgstr "Все файлы (*.*)"
#: qt/directories_dialog.py:267 qt/result_window.py:311
msgid "dupeGuru Results (*.dupeguru)"
msgstr "Результаты dupeGuru (*. dupeguru)"
msgstr "Результаты dupeGuru (*.dupeguru)"
#: qt/directories_dialog.py:278
msgid "Start a new scan"
@ -213,7 +213,7 @@ msgstr "Начать новую проверку"
#: qt/directories_dialog.py:279 cocoa/en.lproj/Localizable.strings:0
msgid "You have unsaved results, do you really want to continue?"
msgstr "Имеются несохранённые результаты, вы действительно хотите продолжить?"
msgstr "Имеются несохранённые результаты, Вы действительно хотите продолжить?"
#: qt/directories_model.py:23 cocoa/en.lproj/Localizable.strings:0
msgid "Name"
@ -256,7 +256,7 @@ msgstr "Теги для проверки:"
#: qt/me/preferences_dialog.py:36 cocoa/en.lproj/Localizable.strings:0
msgid "Track"
msgstr "Трек"
msgstr "Дорожка"
#: qt/me/preferences_dialog.py:38 cocoa/en.lproj/Localizable.strings:0
msgid "Artist"
@ -323,7 +323,7 @@ msgstr "Уровень фильтрации:"
#: qt/preferences_dialog.py:69
msgid "More Results"
msgstr "Дополнительные результаты"
msgstr "Больше результатов"
#: qt/preferences_dialog.py:74
msgid "Fewer Results"
@ -379,7 +379,7 @@ msgstr ""
#: qt/problem_dialog.py:33 cocoa/en.lproj/Localizable.strings:0
msgid "Problems!"
msgstr "Проблемка!"
msgstr "Проблема!"
#: qt/problem_dialog.py:37 cocoa/en.lproj/Localizable.strings:0
msgid ""
@ -409,15 +409,15 @@ msgstr "Показать значения разницы"
#: qt/result_window.py:60
msgid "Send Marked to Recycle Bin..."
msgstr "Переместить отмеченные в Корзину"
msgstr "Переместить отмеченные в Корзину..."
#: qt/result_window.py:61 cocoa/en.lproj/Localizable.strings:0
msgid "Move Marked to..."
msgstr "Переместить отмеченные в"
msgstr "Переместить отмеченные в..."
#: qt/result_window.py:62 cocoa/en.lproj/Localizable.strings:0
msgid "Copy Marked to..."
msgstr "Скопировать отмеченные в"
msgstr "Скопировать отмеченные в..."
#: qt/result_window.py:63 cocoa/en.lproj/Localizable.strings:0
msgid "Remove Marked from Results"
@ -477,7 +477,7 @@ msgstr "Экспорт в CSV"
#: qt/result_window.py:89 cocoa/en.lproj/Localizable.strings:0
msgid "Save Results..."
msgstr "Сохранить результаты"
msgstr "Сохранить результаты..."
#: qt/result_window.py:90 cocoa/en.lproj/Localizable.strings:0
msgid "Invoke Custom Command"
@ -509,15 +509,15 @@ msgstr "Значения разницы"
#: qt/result_window.py:310 cocoa/en.lproj/Localizable.strings:0
msgid "Select a file to save your results to"
msgstr "Выберите файл, чтобы сохранить ваши результаты"
msgstr "Выберите файл для сохранения Ваших результатов"
#: qt/se/preferences_dialog.py:41
msgid "Ignore files smaller than"
msgstr "Игнорировать файлы меньше чем"
msgstr "Игнорировать файлы меньше, чем"
#: qt/se/preferences_dialog.py:52 cocoa/en.lproj/Localizable.strings:0
msgid "KB"
msgstr "КБ"
msgstr "кБ"
#: cocoa/en.lproj/Localizable.strings:0
msgid "%@ Results"
@ -549,7 +549,7 @@ msgstr "Все на передний план"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Check for update..."
msgstr "Проверка обновлений"
msgstr "Проверка обновлений..."
#: cocoa/en.lproj/Localizable.strings:0
msgid "Close Window"
@ -594,7 +594,7 @@ msgstr "Настройки dupeGuru"
#: cocoa/en.lproj/Localizable.strings:0
msgid "dupeGuru Results"
msgstr "Результаты dupeGuru "
msgstr "Результаты dupeGuru"
#: cocoa/en.lproj/Localizable.strings:0
msgid "dupeGuru Website"
@ -626,7 +626,7 @@ msgstr "Уровень фильтрации:"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Filter Results..."
msgstr "Отфильтровать результаты"
msgstr "Отфильтровать результаты..."
#: cocoa/en.lproj/Localizable.strings:0
msgid "Folder Selection Window"
@ -646,11 +646,11 @@ msgstr "Скрыть остальные"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Ignore files smaller than:"
msgstr "Пропускать файлы меньше чем:"
msgstr "Пропускать файлы меньше, чем:"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Load from file..."
msgstr "Загрузить из файла"
msgstr "Загрузить из файла..."
#: cocoa/en.lproj/Localizable.strings:0
msgid "Minimize"
@ -674,7 +674,7 @@ msgstr "Вставить"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Preferences..."
msgstr "Настройки"
msgstr "Настройки..."
#: cocoa/en.lproj/Localizable.strings:0
msgid "Quick Look"
@ -686,7 +686,7 @@ msgstr "Выйти из dupeGuru"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Reset to Default"
msgstr "Восстановить значения по умолчанию"
msgstr "Восстановить значение по умолчанию"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Reset To Defaults"
@ -706,7 +706,7 @@ msgstr "Выбрать все"
#: cocoa/en.lproj/Localizable.strings:0
msgid "Send Marked to Trash..."
msgstr "Переместить отмеченные в Корзину"
msgstr "Переместить отмеченные в Корзину..."
#: cocoa/en.lproj/Localizable.strings:0
msgid "Services"
@ -754,11 +754,11 @@ msgstr "Выберите файл каталогов для загрузки"
#: qt\directories_dialog.py:338
msgid "dupeGuru Results (*.dupegurudirs)"
msgstr "Каталоги dupeGuru (*.dupegurudirs)"
msgstr "Результаты dupeGuru (*.dupegurudirs)"
#: qt\directories_dialog.py:347
msgid "Select a file to save your directories to"
msgstr "Выберите файл для сохранения каталогов"
msgstr "Выберите файл для сохранения Ваших каталогов"
#: qt\directories_dialog.py:348
msgid "dupeGuru Directories (*.dupegurudirs)"
@ -798,7 +798,7 @@ msgstr ""
#: qt\exclude_list_table.py:36
msgid "Compilation error: "
msgstr "Ошибка компиляции:"
msgstr "Ошибка компиляции: "
#: qt\pe\image_viewer.py:56
msgid "Increase zoom"
@ -835,7 +835,7 @@ msgstr "Переопределить значки темы на панели и
#: qt\pe\preferences_dialog.py:58
msgid ""
"Use our own internal icons instead of those provided by the theme engine"
msgstr "Используйте внутренние значки вместо значков, встроенных в тему"
msgstr "Использовать наши внутренние значки вместо значков, встроенных в тему"
#: qt\pe\preferences_dialog.py:66
msgid "Show scrollbars in image viewers"
@ -965,7 +965,7 @@ msgstr ""
#: qt\se\preferences_dialog.py:68
msgid "Ignore files larger than"
msgstr "Игнорировать файлы больше чем"
msgstr "Игнорировать файлы больше, чем"
#: qt\app.py:135 qt\app.py:293
msgid "Clear Cache"
@ -981,7 +981,7 @@ msgstr ""
#: qt\app.py:299
msgid "Cache cleared."
msgstr "Кэш очищен "
msgstr "Кэш очищен."
#: qt\preferences_dialog.py:173
msgid "Use dark style"
@ -994,8 +994,7 @@ msgstr "Сохранить профиль сканирования"
#: qt\preferences_dialog.py:242
msgid "Profile the scan operation and save logs for optimization."
msgstr ""
"В папке установленной или портативной программы, есть папка Data в которую "
"сохраняется логи и файл с раширением *.profile для оптимизации."
"Настройте операцию сканирования и сохраните журналы для оптимизации."
#: qt\preferences_dialog.py:246
msgid "Logs located in: <a href=\"{}\">{}</a>"
@ -1023,11 +1022,11 @@ msgstr "Под лицензией GPLv3"
#: qt\about_box.py:68
msgid "No update available."
msgstr "У вас самая свежая версия"
msgstr "Обновления недоступны."
#: qt\about_box.py:71
msgid "New version {} available, download <a href=\"{}\">here</a>."
msgstr "Обнаружена новая {} версия, загружать <a href=\"{}\">тут</a>."
msgstr "Обнаружена новая {} версия, загрузить <a href=\"{}\">тут</a>."
#: qt\error_report_dialog.py:50
msgid "Error Report"