From 051d12eb3c4bca553d31b76093ef33a78b5c1f2a Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 29 Jun 2026 15:36:02 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20--selected=20mode=20=E2=80=94=20process?= =?UTF-8?q?=20individual=20files=20(context=20menu=20+=20SendTo)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 +++++-- install.bat | 90 ++++++++++++++++++++++++++------------ organize.py | 79 +++++++++++++++++++++++---------- raskidator.bat | 51 +++++++++------------ uninstall-context-menu.bat | 27 +++++++----- 5 files changed, 167 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 437e021..9fa5a11 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,14 @@ install.bat uninstall-context-menu.bat ``` -После установки в контекстном меню два пункта: -- **«Raskidator-3000»** — сортировка (реальное перемещение) -- **«Raskidator-3000 (dry-run)»** — только показать что будет перемещено -- **Работает в библиотеках** (Pictures, Music, Videos, Documents) +После установки: + +| Действие | Результат | +|----------|-----------| +| **ПКМ на папке** → Raskidator-3000 | Сортировка всей папки | +| **ПКМ на папке** → Raskidator-3000 (dry-run) | Только показать что будет | +| **ПКМ на файле** → Raskidator-3000 | Сортировать только этот файл | +| **Выделить файлы** → Отправить → Raskidator-3000 | Сортировать только выделенные файлы | +| Работает в библиотеках (Pictures, Music, Videos) | | + +При сортировке отдельных файлов (`--selected`) лимит `MAX_FILES` игнорируется — обрабатываются все переданные файлы. diff --git a/install.bat b/install.bat index 4773e31..d0679ce 100644 --- a/install.bat +++ b/install.bat @@ -18,54 +18,90 @@ echo. set "BAT_PATH=%BAT_DIR%\raskidator.bat" set "ICON=%BAT_DIR%\raskidator.bat,0" -:: Directory\Background\shell - right-click in empty folder space -echo [1/3] Adding to folder background context menu... +:: ── 1. Папки (правая кнопка на папке) ───────────────────────────── +echo [1/4] Folder context menu... :: Обычный запуск reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000" /ve /t REG_SZ /d "Raskidator-3000" /f reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000" /v Icon /t REG_SZ /d "%ICON%" /f reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" \"%%V\"" /f -:: Пробный запуск (dry-run) -reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000DryRun" /ve /t REG_SZ /d "Raskidator-3000 (dry-run)" /f -reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000DryRun" /v Icon /t REG_SZ /d "%ICON%" /f -reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000DryRun\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" --dry-run \"%%V\"" /f - -echo [+] Directory\Background - done (2 entries) -echo. - -:: Directory\shell - right-click on folder -echo [2/3] Adding to folder right-click menu... - reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000" /ve /t REG_SZ /d "Raskidator-3000" /f reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000" /v Icon /t REG_SZ /d "%ICON%" /f reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" \"%%1\"" /f -reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000DryRun" /ve /t REG_SZ /d "Raskidator-3000 (dry-run)" /f -reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000DryRun" /v Icon /t REG_SZ /d "%ICON%" /f -reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000DryRun\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" --dry-run \"%%1\"" /f - -echo [+] Directory\shell - done (2 entries) -echo. - -:: LibraryFolder\Background - Pictures, Music, Videos... -echo [3/3] Adding to libraries (Pictures, Music, Videos)... - reg add "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000" /ve /t REG_SZ /d "Raskidator-3000" /f reg add "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000" /v Icon /t REG_SZ /d "%ICON%" /f reg add "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" \"%%V\"" /f +:: Пробный запуск (dry-run) +reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000DryRun" /ve /t REG_SZ /d "Raskidator-3000 (dry-run)" /f +reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000DryRun" /v Icon /t REG_SZ /d "%ICON%" /f +reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000DryRun\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" --dry-run \"%%V\"" /f + +reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000DryRun" /ve /t REG_SZ /d "Raskidator-3000 (dry-run)" /f +reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000DryRun" /v Icon /t REG_SZ /d "%ICON%" /f +reg add "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000DryRun\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" --dry-run \"%%1\"" /f + reg add "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000DryRun" /ve /t REG_SZ /d "Raskidator-3000 (dry-run)" /f reg add "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000DryRun" /v Icon /t REG_SZ /d "%ICON%" /f reg add "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000DryRun\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" --dry-run \"%%V\"" /f -echo [+] LibraryFolder\Background - done (2 entries) +echo [+] Folder context menu — done +echo. + +:: ── 2. Файлы (правая кнопка на 1 файле) ────────────────────────── +echo [2/4] File context menu... + +reg add "HKEY_CLASSES_ROOT\*\shell\Raskidator3000" /ve /t REG_SZ /d "Raskidator-3000" /f +reg add "HKEY_CLASSES_ROOT\*\shell\Raskidator3000" /v Icon /t REG_SZ /d "%ICON%" /f +reg add "HKEY_CLASSES_ROOT\*\shell\Raskidator3000\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" --selected \"%%1\"" /f + +reg add "HKEY_CLASSES_ROOT\*\shell\Raskidator3000DryRun" /ve /t REG_SZ /d "Raskidator-3000 (dry-run)" /f +reg add "HKEY_CLASSES_ROOT\*\shell\Raskidator3000DryRun" /v Icon /t REG_SZ /d "%ICON%" /f +reg add "HKEY_CLASSES_ROOT\*\shell\Raskidator3000DryRun\command" /ve /t REG_SZ /d "\"%BAT_PATH%\" --selected --dry-run \"%%1\"" /f + +echo [+] File context menu — done +echo. + +:: ── 3. SendTo (несколько выделенных файлов) ────────────────────── +echo [3/4] SendTo (multi-file).. + +set "SENDTO=%APPDATA%\Microsoft\Windows\SendTo" + +:: Основной +del "%SENDTO%\Raskidator-3000.bat" 2>nul +> "%SENDTO%\Raskidator-3000.bat" ( + echo @echo off + echo chcp 65001 ^>nul + echo call "%BAT_PATH%" --selected %%* +) + +:: Пробный (dry-run) +del "%SENDTO%\Raskidator-3000 (dry-run).bat" 2>nul +> "%SENDTO%\Raskidator-3000 (dry-run).bat" ( + echo @echo off + echo chcp 65001 ^>nul + echo call "%BAT_PATH%" --selected --dry-run %%* +) + +echo [+] SendTo — done +echo. + +:: ── 4. Удаление старых версий ────────────────────────────────────── +echo [4/4] Cleaning up old entries... + +reg delete "HKEY_CLASSES_ROOT\*\shell\Raskidator3000" /f >nul 2>&1 +reg delete "HKEY_CLASSES_ROOT\*\shell\Raskidator3000DryRun" /f >nul 2>&1 + +echo [+] Cleanup — done echo. echo ======================================== echo Installation complete! -echo Right-click any folder -> -echo Raskidator-3000 — сортировка -echo Raskidator-3000 (dry-run) — пробный запуск +echo. +echo ПКМ на папке — Raskidator-3000 +echo ПКМ на файле — Raskidator-3000 +echo Отправить ^> файлы — Raskidator-3000 (любое кол-во) echo ======================================== pause diff --git a/organize.py b/organize.py index 8883d77..087dc30 100644 --- a/organize.py +++ b/organize.py @@ -317,6 +317,7 @@ def organize( group_by: str = 'day', quiet: bool = False, max_files: int | None = None, + selected_files: list[Path] | None = None, ) -> int: """Scan *folder* and sort images into year/month[/day] subfolders. @@ -327,10 +328,13 @@ def organize( return 1 # ── Scan ──────────────────────────────────────────────────────────────── - files: list[Path] = [ - f for f in folder.iterdir() - if f.is_file() and f.suffix.lower() in EXTENSIONS - ] + if selected_files is not None: + files = [f for f in selected_files if f.suffix.lower() in EXTENSIONS] + else: + files: list[Path] = [ + f for f in folder.iterdir() + if f.is_file() and f.suffix.lower() in EXTENSIONS + ] files.sort() if max_files is not None and len(files) > max_files: print(_c(f' First {max_files} of {len(files)} files ' @@ -626,10 +630,16 @@ def main() -> None: add_help=True, ) parser.add_argument( - 'path', - nargs='?', + 'paths', + nargs='*', default=None, - help='Folder to scan (default: current directory)', + help='Folder to scan, or file(s) with --selected', + ) + parser.add_argument( + '--selected', + action='store_true', + default=False, + help='Process specified files instead of scanning a folder', ) parser.add_argument( '--no-color', @@ -666,23 +676,44 @@ def main() -> None: # If --max-files not set, use MAX_FILES constant max_files = args.max_files if args.max_files is not None else MAX_FILES - folder = Path(args.path) if args.path else Path.cwd() - print(_c('Raskidator-3000', _BOLD, no_color=args.no_color)) - print(f' Folder: {folder.resolve()}') - if args.dry_run: - print(_c(' Mode: DRY-RUN', _CYAN, no_color=args.no_color)) - if max_files: - print(f' Max files: {max_files}') - print() - - rc = organize( - folder, - dry_run=args.dry_run, - no_color=args.no_color, - group_by=args.group_by, - quiet=args.quiet, - max_files=max_files, - ) + if args.selected: + # Process specified files (from SendTo or file selection) + file_paths = [Path(p) for p in args.paths if Path(p).is_file()] + if not file_paths: + print(_c(' No valid files specified.', _YELLOW, no_color=args.no_color)) + sys.exit(1) + max_files = len(file_paths) # process exactly the selected files + print(_c('Raskidator-3000', _BOLD, no_color=args.no_color)) + print(f' Selected files: {len(file_paths)}') + if args.dry_run: + print(_c(' Mode: DRY-RUN', _CYAN, no_color=args.no_color)) + print() + rc = organize( + file_paths[0].parent, # use parent folder as base + dry_run=args.dry_run, + no_color=args.no_color, + group_by=args.group_by, + quiet=args.quiet, + max_files=max_files, + selected_files=file_paths, + ) + else: + folder = Path(args.paths[0]) if args.paths else Path.cwd() + print(_c('Raskidator-3000', _BOLD, no_color=args.no_color)) + print(f' Folder: {folder.resolve()}') + if args.dry_run: + print(_c(' Mode: DRY-RUN', _CYAN, no_color=args.no_color)) + if max_files: + print(f' Max files: {max_files}') + print() + rc = organize( + folder, + dry_run=args.dry_run, + no_color=args.no_color, + group_by=args.group_by, + quiet=args.quiet, + max_files=max_files, + ) sys.exit(rc) diff --git a/raskidator.bat b/raskidator.bat index 14ceda0..944a029 100644 --- a/raskidator.bat +++ b/raskidator.bat @@ -10,41 +10,32 @@ echo. :: Сохраняем путь к папке с батником ДО shift (shift меняет %0) set "BAT_DIR=%~dp0" -:: Определяем режим (--dry-run или обычный) +:: Определяем режимы set "DRY=" -if /i "%~1"=="--dry-run" ( - set "DRY=--dry-run" - shift -) +set "SELECTED=" +if /i "%~1"=="--selected" set "SELECTED=--selected" & shift +if /i "%~1"=="--dry-run" set "DRY=--dry-run" & shift -:: Определяем папку, которую обрабатываем -if "%~1"=="" ( - set "TARGET=%CD%" +if not "%SELECTED%"=="" ( + :: Режим выбранных файлов (SendTo / контекстное меню) + echo Выбранные файлы: %* + echo. + python "%BAT_DIR%organize.py" --selected %DRY% %* + if %errorlevel% equ 0 goto :end + py -3 "%BAT_DIR%organize.py" --selected %DRY% %* + if %errorlevel% equ 0 goto :end ) else ( - set "TARGET=%~1" + :: Режим папки + if "%~1"=="" (set "TARGET=%CD%") else (set "TARGET=%~1") + echo Папка: %TARGET% + if not "%DRY%"=="" echo Режим: ПРОБНЫЙ (dry-run) + echo. + python "%BAT_DIR%organize.py" %DRY% "%TARGET%" + if %errorlevel% equ 0 goto :end + py -3 "%BAT_DIR%organize.py" %DRY% "%TARGET%" + if %errorlevel% equ 0 goto :end ) -echo Папка: %TARGET% -if not "%DRY%"=="" echo Режим: ПРОБНЫЙ (dry-run) -echo. - -:: Проверяем, существует ли скрипт -if exist "%BAT_DIR%organize.py" ( - set "SCRIPT=%BAT_DIR%organize.py" -) else ( - echo [ОШИБКА] Не найден organize.py рядом с батником. - echo Ожидаемый путь: %BAT_DIR%organize.py - goto :end -) - -:: Пробуем python (наиболее распространён) -python "%SCRIPT%" %DRY% "%TARGET%" -if %errorlevel% equ 0 goto :end - -:: Если python не сработал — пробуем py -3 (Python Launcher) -py -3 "%SCRIPT%" %DRY% "%TARGET%" -if %errorlevel% equ 0 goto :end - :: Если оба не сработали echo. echo [ОШИБКА] Python не найден. Установи Python с python.org diff --git a/uninstall-context-menu.bat b/uninstall-context-menu.bat index e006acb..7b514eb 100644 --- a/uninstall-context-menu.bat +++ b/uninstall-context-menu.bat @@ -8,22 +8,29 @@ echo Raskidator-3000 echo ======================================== echo. -echo [1/3] Removing from folder background context menu... +echo [1/4] Removing from folder context menus... reg delete "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000" /f >nul 2>&1 reg delete "HKEY_CLASSES_ROOT\Directory\Background\shell\Raskidator3000DryRun" /f >nul 2>&1 -if %errorlevel% equ 0 (echo [+] Directory\Background - removed) else (echo [+] Directory\Background - done) -echo. - -echo [2/3] Removing from folder right-click menu... reg delete "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000" /f >nul 2>&1 reg delete "HKEY_CLASSES_ROOT\Directory\shell\Raskidator3000DryRun" /f >nul 2>&1 -if %errorlevel% equ 0 (echo [+] Directory\shell - removed) else (echo [+] Directory\shell - done) -echo. - -echo [3/3] Removing from libraries (Pictures, Music, Videos)... reg delete "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000" /f >nul 2>&1 reg delete "HKEY_CLASSES_ROOT\LibraryFolder\Background\shell\Raskidator3000DryRun" /f >nul 2>&1 -if %errorlevel% equ 0 (echo [+] LibraryFolder\Background - removed) else (echo [+] LibraryFolder\Background - done) +echo [+] Folder menus — done +echo. + +echo [2/4] Removing from file context menus... +reg delete "HKEY_CLASSES_ROOT\*\shell\Raskidator3000" /f >nul 2>&1 +reg delete "HKEY_CLASSES_ROOT\*\shell\Raskidator3000DryRun" /f >nul 2>&1 +echo [+] File menus — done +echo. + +echo [3/4] Removing SendTo entries... +del "%APPDATA%\Microsoft\Windows\SendTo\Raskidator-3000.bat" 2>nul +del "%APPDATA%\Microsoft\Windows\SendTo\Raskidator-3000 (dry-run).bat" 2>nul +echo [+] SendTo — done +echo. + +echo [4/4] Cleanup complete echo. echo ========================================