Compare commits
3 Commits
59f1c1c38e
...
d2239a670c
| Author | SHA1 | Date | |
|---|---|---|---|
| d2239a670c | |||
| 53430f768f | |||
| a8205447e3 |
File diff suppressed because it is too large
Load Diff
@@ -46,13 +46,13 @@
|
||||
"last_updated": "2026-03-22T18:11:22Z"
|
||||
},
|
||||
"88555ca4-9c88-4bcf-825f-dd19b19b7145": {
|
||||
"user_message_count": 35,
|
||||
"agent_message_count": 341,
|
||||
"user_message_count": 50,
|
||||
"agent_message_count": 592,
|
||||
"start_timestamp": "2026-03-21T13:01:32-06:00",
|
||||
"end_timestamp": "2026-03-21T13:01:32-06:00",
|
||||
"markdown_size_bytes": 310211,
|
||||
"markdown_size_bytes": 461149,
|
||||
"provider": "cursoride",
|
||||
"last_updated": "2026-03-22T18:11:22Z"
|
||||
"last_updated": "2026-03-22T19:25:16Z"
|
||||
},
|
||||
"8a226504-a2e0-4b10-b083-a609486034c7": {
|
||||
"user_message_count": 9,
|
||||
@@ -70,7 +70,7 @@
|
||||
"end_timestamp": "2025-06-25T22:09:21-06:00",
|
||||
"markdown_size_bytes": 38319,
|
||||
"provider": "cursoride",
|
||||
"last_updated": "2026-03-22T18:11:10Z"
|
||||
"last_updated": "2026-03-22T18:13:12Z"
|
||||
},
|
||||
"b90a47f3-3c59-4ca3-a099-690412afaab1": {
|
||||
"user_message_count": 7,
|
||||
|
||||
+90
-54
@@ -1,31 +1,44 @@
|
||||
@echo off
|
||||
setlocal
|
||||
setlocal EnableExtensions
|
||||
REM ANSI colors (needs Windows 10+ console VT or Windows Terminal)
|
||||
for /f %%a in ('powershell -nop -c "[char]0x1b"') do set "ESC=%%a"
|
||||
set "RST=%ESC%[0m"
|
||||
set "BLD=%ESC%[1m"
|
||||
set "DIM=%ESC%[2m"
|
||||
set "RED=%ESC%[91m"
|
||||
set "GRN=%ESC%[92m"
|
||||
set "YLW=%ESC%[93m"
|
||||
set "BLU=%ESC%[94m"
|
||||
set "MAG=%ESC%[95m"
|
||||
set "CYN=%ESC%[96m"
|
||||
set "GRY=%ESC%[90m"
|
||||
set "WHT=%ESC%[97m"
|
||||
|
||||
REM Always use 'nathan' as NAS username
|
||||
set NASUSER=nathan
|
||||
|
||||
REM Menu for operation selection
|
||||
echo.
|
||||
echo Select operation:
|
||||
echo 1. Generate thumbnails only
|
||||
echo 2. SSH cleanup only
|
||||
echo 3. Both (generate thumbnails + SSH cleanup)
|
||||
echo 4. Fix OneDrive thumbnail override (NEW!)
|
||||
echo %BLD%%YLW%Select operation:%RST%
|
||||
echo %CYN%1.%RST% Generate thumbnails only
|
||||
echo %CYN%2.%RST% SSH cleanup only
|
||||
echo %CYN%3.%RST% Both (generate thumbnails + SSH cleanup)
|
||||
echo %CYN%4.%RST% Fix OneDrive thumbnail override %DIM%(NEW^)%RST%
|
||||
echo.
|
||||
set /p CHOICE=Enter your choice (1-4):
|
||||
set /p CHOICE=%CYN%Enter your choice ^(1-4^): %RST%
|
||||
|
||||
if "%CHOICE%"=="1" goto THUMBS_ONLY
|
||||
if "%CHOICE%"=="2" goto SSH_ONLY
|
||||
if "%CHOICE%"=="3" goto BOTH
|
||||
if "%CHOICE%"=="4" goto FIX_ONEDRIVE
|
||||
echo Invalid choice. Exiting.
|
||||
echo %RED%Invalid choice. Exiting.%RST%
|
||||
exit /b
|
||||
|
||||
:THUMBS_ONLY
|
||||
REM Prompt for Windows path
|
||||
set /p WINPATH=Enter the full Windows path to your NAS directory (e.g., R:\YouTube\Streams\MixerTwitch):
|
||||
set /p WINPATH=%CYN%Enter the full Windows path to your NAS directory ^(e.g., R:\YouTube\Streams\MixerTwitch^): %RST%
|
||||
if "%WINPATH%"=="" (
|
||||
echo No path provided. Exiting.
|
||||
echo %RED%No path provided. Exiting.%RST%
|
||||
exit /b
|
||||
)
|
||||
|
||||
@@ -34,9 +47,9 @@ set "RELPATH=%WINPATH:~3%"
|
||||
set "RELPATH=%RELPATH:\=/%"
|
||||
set "NASPATH=/volume1/Hydra/%RELPATH%"
|
||||
|
||||
echo Uploading extract_eadir_to_tmp.sh to NAS and seeding...
|
||||
echo %GRN%Uploading extract_eadir_to_tmp.sh to NAS and seeding...%RST%
|
||||
if not exist "%~dp0extract_eadir_to_tmp.sh" (
|
||||
echo ERROR: extract_eadir_to_tmp.sh not found next to this batch file.
|
||||
echo %RED%ERROR: extract_eadir_to_tmp.sh not found next to this batch file.%RST%
|
||||
goto END
|
||||
)
|
||||
ssh Hydra "cat > ~/extract_eadir_to_tmp.sh" < "%~dp0extract_eadir_to_tmp.sh"
|
||||
@@ -48,9 +61,9 @@ goto END
|
||||
|
||||
:SSH_ONLY
|
||||
REM Prompt for Windows path
|
||||
set /p WINPATH=Enter the full Windows path to your NAS directory (e.g., R:\YouTube\Streams\MixerTwitch):
|
||||
set /p WINPATH=%CYN%Enter the full Windows path to your NAS directory ^(e.g., R:\YouTube\Streams\MixerTwitch^): %RST%
|
||||
if "%WINPATH%"=="" (
|
||||
echo No path provided. Exiting.
|
||||
echo %RED%No path provided. Exiting.%RST%
|
||||
exit /b
|
||||
)
|
||||
|
||||
@@ -59,27 +72,33 @@ set "RELPATH=%WINPATH:~3%"
|
||||
set "RELPATH=%RELPATH:\=/%"
|
||||
set "NASPATH=/volume1/Hydra/%RELPATH%"
|
||||
|
||||
echo DEBUG: WINPATH = %WINPATH%
|
||||
echo DEBUG: RELPATH = %RELPATH%
|
||||
echo DEBUG: NASPATH = %NASPATH%
|
||||
echo %GRY%DEBUG: WINPATH = %WINPATH%%RST%
|
||||
echo %GRY%DEBUG: RELPATH = %RELPATH%%RST%
|
||||
echo %GRY%DEBUG: NASPATH = %NASPATH%%RST%
|
||||
echo.
|
||||
call :WIN_REMOVE_LOCAL_EADIR
|
||||
REM Ensure nascleanup.sh exists on NAS (upload + fix line endings), then run it
|
||||
echo Uploading nascleanup.sh to NAS and running cleanup...
|
||||
echo %GRN%Uploading nascleanup.sh to NAS and running cleanup...%RST%
|
||||
if not exist "%~dp0nascleanup.sh" (
|
||||
echo ERROR: nascleanup.sh not found next to this batch file.
|
||||
echo %RED%ERROR: nascleanup.sh not found next to this batch file.%RST%
|
||||
goto END
|
||||
)
|
||||
ssh Hydra "cat > ~/nascleanup.sh" < "%~dp0nascleanup.sh"
|
||||
ssh Hydra "bash -lc 'if command -v dos2unix >/dev/null 2>&1; then dos2unix -f ~/nascleanup.sh; else tr -d \"\r\" < ~/nascleanup.sh > ~/nascleanup.sh.tmp && mv ~/nascleanup.sh.tmp ~/nascleanup.sh; fi; chmod +x ~/nascleanup.sh; ~/nascleanup.sh \"%NASPATH%\"'"
|
||||
call :WIN_REMOVE_LOCAL_EADIR_TMP
|
||||
if errorlevel 1 (
|
||||
echo %RED%ERROR: nascleanup failed on NAS - check promote ^(eaDir_tmp -^> @eaDir^) messages above.%RST%
|
||||
echo %YLW%Keeping eaDir_tmp on the share so you can retry without re-running FFmpeg.%RST%
|
||||
call :PRINT_HYDRA_CLEAR_EADIR
|
||||
) else (
|
||||
call :WIN_REMOVE_LOCAL_EADIR_TMP
|
||||
)
|
||||
goto END
|
||||
|
||||
:BOTH
|
||||
REM Prompt for Windows path
|
||||
set /p WINPATH=Enter the full Windows path to your NAS directory (e.g., R:\YouTube\Streams\MixerTwitch):
|
||||
set /p WINPATH=%CYN%Enter the full Windows path to your NAS directory ^(e.g., R:\YouTube\Streams\MixerTwitch^): %RST%
|
||||
if "%WINPATH%"=="" (
|
||||
echo No path provided. Exiting.
|
||||
echo %RED%No path provided. Exiting.%RST%
|
||||
exit /b
|
||||
)
|
||||
|
||||
@@ -88,15 +107,15 @@ set "RELPATH=%WINPATH:~3%"
|
||||
set "RELPATH=%RELPATH:\=/%"
|
||||
set "NASPATH=/volume1/Hydra/%RELPATH%"
|
||||
|
||||
echo DEBUG: WINPATH = %WINPATH%
|
||||
echo DEBUG: RELPATH = %RELPATH%
|
||||
echo DEBUG: NASPATH = %NASPATH%
|
||||
echo %GRY%DEBUG: WINPATH = %WINPATH%%RST%
|
||||
echo %GRY%DEBUG: RELPATH = %RELPATH%%RST%
|
||||
echo %GRY%DEBUG: NASPATH = %NASPATH%%RST%
|
||||
echo.
|
||||
|
||||
REM Pre-seed eaDir_tmp from @eaDir on NAS before generation
|
||||
echo Uploading extract_eadir_to_tmp.sh to NAS and seeding...
|
||||
echo %GRN%Uploading extract_eadir_to_tmp.sh to NAS and seeding...%RST%
|
||||
if not exist "%~dp0extract_eadir_to_tmp.sh" (
|
||||
echo ERROR: extract_eadir_to_tmp.sh not found next to this batch file.
|
||||
echo %RED%ERROR: extract_eadir_to_tmp.sh not found next to this batch file.%RST%
|
||||
goto END
|
||||
)
|
||||
ssh Hydra "cat > ~/extract_eadir_to_tmp.sh" < "%~dp0extract_eadir_to_tmp.sh"
|
||||
@@ -110,41 +129,47 @@ call :WIN_REMOVE_LOCAL_EADIR
|
||||
|
||||
REM SSH cleanup commands (run separately)
|
||||
REM Ensure nascleanup.sh exists on NAS (upload + fix line endings), then run it
|
||||
echo Uploading nascleanup.sh to NAS and running cleanup...
|
||||
echo %GRN%Uploading nascleanup.sh to NAS and running cleanup...%RST%
|
||||
if not exist "%~dp0nascleanup.sh" (
|
||||
echo ERROR: nascleanup.sh not found next to this batch file.
|
||||
echo %RED%ERROR: nascleanup.sh not found next to this batch file.%RST%
|
||||
goto END
|
||||
)
|
||||
ssh Hydra "cat > ~/nascleanup.sh" < "%~dp0nascleanup.sh"
|
||||
ssh Hydra "bash -lc 'if command -v dos2unix >/dev/null 2>&1; then dos2unix -f ~/nascleanup.sh; else tr -d \"\r\" < ~/nascleanup.sh > ~/nascleanup.sh.tmp && mv ~/nascleanup.sh.tmp ~/nascleanup.sh; fi; chmod +x ~/nascleanup.sh; ~/nascleanup.sh \"%NASPATH%\"'"
|
||||
call :WIN_REMOVE_LOCAL_EADIR_TMP
|
||||
if errorlevel 1 (
|
||||
echo %RED%ERROR: nascleanup failed on NAS - check promote ^(eaDir_tmp -^> @eaDir^) messages above.%RST%
|
||||
echo %YLW%Keeping eaDir_tmp on the share so you can retry without re-running FFmpeg.%RST%
|
||||
call :PRINT_HYDRA_CLEAR_EADIR
|
||||
) else (
|
||||
call :WIN_REMOVE_LOCAL_EADIR_TMP
|
||||
)
|
||||
goto END
|
||||
|
||||
:FIX_ONEDRIVE
|
||||
echo.
|
||||
echo =================================================================
|
||||
echo FIX ONEDRIVE THUMBNAIL OVERRIDE
|
||||
echo =================================================================
|
||||
echo %BLD%%MAG%=================================================================%RST%
|
||||
echo %BLD%%MAG% FIX ONEDRIVE THUMBNAIL OVERRIDE %RST%
|
||||
echo %BLD%%MAG%=================================================================%RST%
|
||||
echo.
|
||||
echo This will fix OneDrive overriding your custom thumbnails with
|
||||
echo generic file icons for unsupported formats like .blend files.
|
||||
echo %WHT%This will fix OneDrive overriding your custom thumbnails with%RST%
|
||||
echo %WHT%generic file icons for unsupported formats like .blend files.%RST%
|
||||
echo.
|
||||
echo What this does:
|
||||
echo - Disables OneDrive cloud thumbnail providers
|
||||
echo - Restores local thumbnail priority
|
||||
echo - Prevents future thumbnail cache deletion
|
||||
echo %CYN%What this does:%RST%
|
||||
echo %GRY% - Disables OneDrive cloud thumbnail providers%RST%
|
||||
echo %GRY% - Restores local thumbnail priority%RST%
|
||||
echo %GRY% - Prevents future thumbnail cache deletion%RST%
|
||||
echo.
|
||||
set /p CONFIRM=Continue? (y/n):
|
||||
set /p CONFIRM=%YLW%Continue? ^(y/n^): %RST%
|
||||
if /i not "%CONFIRM%"=="y" goto END
|
||||
|
||||
echo.
|
||||
echo Running PowerShell script to fix OneDrive thumbnail override...
|
||||
echo %GRN%Running PowerShell script to fix OneDrive thumbnail override...%RST%
|
||||
echo.
|
||||
|
||||
REM Check if PowerShell script exists
|
||||
if not exist "fix_thumbnail_override.ps1" (
|
||||
echo ERROR: fix_thumbnail_override.ps1 not found in current directory.
|
||||
echo Please ensure the PowerShell script is in the same folder as this batch file.
|
||||
echo %RED%ERROR: fix_thumbnail_override.ps1 not found in current directory.%RST%
|
||||
echo %YLW%Please ensure the PowerShell script is in the same folder as this batch file.%RST%
|
||||
pause
|
||||
goto END
|
||||
)
|
||||
@@ -153,24 +178,34 @@ REM Run PowerShell script as administrator
|
||||
powershell -ExecutionPolicy Bypass -Command "Start-Process PowerShell -ArgumentList '-ExecutionPolicy Bypass -File \"%~dp0fix_thumbnail_override.ps1\"' -Verb RunAs"
|
||||
|
||||
echo.
|
||||
echo OneDrive thumbnail override fix initiated.
|
||||
echo Please check the PowerShell window for progress and results.
|
||||
echo %GRN%OneDrive thumbnail override fix initiated.%RST%
|
||||
echo %GRY%Please check the PowerShell window for progress and results.%RST%
|
||||
echo.
|
||||
echo After the fix completes:
|
||||
echo 1. Restart your computer
|
||||
echo 2. Re-run your thumbnail generation process
|
||||
echo 3. Your custom thumbnails should now have priority!
|
||||
echo %CYN%After the fix completes:%RST%
|
||||
echo %GRY% 1. Restart your computer%RST%
|
||||
echo %GRY% 2. Re-run your thumbnail generation process%RST%
|
||||
echo %GRY% 3. Your custom thumbnails should now have priority!%RST%
|
||||
echo.
|
||||
goto END
|
||||
|
||||
REM Print ssh Hydra command to wipe @eaDir when NAS cleanup failed (matches NASPATH from pasted R:\ path).
|
||||
:PRINT_HYDRA_CLEAR_EADIR
|
||||
if "%NASPATH%"=="" goto :eof
|
||||
echo %MAG%To clear locked @eaDir on Hydra: ssh in first.%RST%
|
||||
echo %GRY%Permission denied lines above name nested paths ^(e.g. editDesc, gameplay^).%RST%
|
||||
echo %GRY%Clear every @eaDir under the SAME folder you passed to nascleanup ^(NASPATH below^),%RST%
|
||||
echo %GRY%not only the subpath from the first error line.%RST%
|
||||
echo %BLD%%YLW% sudo find "%NASPATH%" -depth -type d -name '@eaDir' -exec sudo rm -rf {} +%RST%
|
||||
goto :eof
|
||||
|
||||
REM Remove folder-level @eaDir on the pasted Windows path (same files as NAS over SMB).
|
||||
:WIN_REMOVE_LOCAL_EADIR
|
||||
if "%WINPATH%"=="" goto :eof
|
||||
if exist "%WINPATH%\@eaDir" (
|
||||
echo Removing "%WINPATH%\@eaDir" via Windows ^(SMB share^)...
|
||||
echo %CYN%Removing "%WINPATH%\@eaDir" via Windows ^(SMB share^)...%RST%
|
||||
rd /s /q "%WINPATH%\@eaDir" 2>nul
|
||||
if exist "%WINPATH%\@eaDir" (
|
||||
echo WARNING: Could not remove @eaDir; try PowerShell as admin or delete in Explorer.
|
||||
echo %YLW%WARNING: Could not remove @eaDir; try PowerShell as admin or delete in Explorer.%RST%
|
||||
)
|
||||
)
|
||||
goto :eof
|
||||
@@ -178,13 +213,14 @@ goto :eof
|
||||
REM Remove every eaDir_tmp under pasted path (including subfolders e.g. gameplay\eaDir_tmp).
|
||||
:WIN_REMOVE_LOCAL_EADIR_TMP
|
||||
if "%WINPATH%"=="" goto :eof
|
||||
echo Removing all eaDir_tmp folders under "%WINPATH%" ^(recursive, SMB^)...
|
||||
echo %CYN%Removing all eaDir_tmp folders under "%WINPATH%" ^(recursive, SMB^)...%RST%
|
||||
if not exist "%~dp0remove_eadir_tmp_recursive.ps1" (
|
||||
echo ERROR: remove_eadir_tmp_recursive.ps1 not found next to this batch file.
|
||||
echo %RED%ERROR: remove_eadir_tmp_recursive.ps1 not found next to this batch file.%RST%
|
||||
goto :eof
|
||||
)
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0remove_eadir_tmp_recursive.ps1" "%WINPATH%"
|
||||
goto :eof
|
||||
|
||||
:END
|
||||
pause
|
||||
echo %DIM%Press any key to continue . . .%RST%
|
||||
pause >nul
|
||||
|
||||
@@ -20,8 +20,8 @@ fi
|
||||
echo "=== Extracting @eaDir thumbnails to eaDir_tmp ==="
|
||||
echo "Target: $TARGET_DIR"
|
||||
|
||||
# Only TARGET_DIR/@eaDir (not nested .../@eaDir/.../@eaDir from bad prior runs; those hit chmod 555 parents)
|
||||
find "$TARGET_DIR" -mindepth 1 -maxdepth 1 -type d -name '@eaDir' -print0 | while IFS= read -r -d '' EADIR; do
|
||||
# Every .../@eaDir under TARGET (e.g. TARGET/@eaDir and TARGET/gameplay/@eaDir). Skip paths inside eaDir_tmp.
|
||||
find "$TARGET_DIR" -type d -name '@eaDir' ! -path '*/eaDir_tmp/*' -print0 | while IFS= read -r -d '' EADIR; do
|
||||
PARENT_DIR="$(dirname "$EADIR")"
|
||||
|
||||
# Each immediate subdir under @eaDir corresponds to a source filename
|
||||
|
||||
+43
-27
@@ -28,6 +28,38 @@ flatten_indexer_layout() {
|
||||
done
|
||||
}
|
||||
|
||||
# Copy one thumbgen tree: PARENT/eaDir_tmp -> PARENT/@eaDir (psthumbgen uses per-folder eaDir_tmp).
|
||||
promote_one_eadir_tmp() {
|
||||
local EADIR_TMP="$1"
|
||||
local PARENT TARGET_AT SRC_JPG DST_JPG
|
||||
PARENT="$(dirname "$EADIR_TMP")"
|
||||
TARGET_AT="$PARENT/@eaDir"
|
||||
[ -d "$EADIR_TMP" ] || return 0
|
||||
chmod -R 755 "$EADIR_TMP" 2>/dev/null || true
|
||||
find "$EADIR_TMP" -type f -exec chmod 644 '{}' \; 2>/dev/null || true
|
||||
echo " -> Flattening eaDir_tmp (indexer may have added .../<file>/@eaDir/) before copy: $EADIR_TMP"
|
||||
flatten_indexer_layout "$EADIR_TMP"
|
||||
echo " -> Replacing $EADIR_TMP -> $TARGET_AT"
|
||||
SRC_JPG=$(find "$EADIR_TMP" -type f -name 'SYNO*.jpg' 2>/dev/null | wc -l | tr -d '[:space:]')
|
||||
rm -rf "$TARGET_AT"
|
||||
mkdir -p "$TARGET_AT"
|
||||
if ! cp -R "$EADIR_TMP/." "$TARGET_AT/"; then
|
||||
echo "ERROR: eaDir_tmp -> @eaDir copy failed (see cp messages above)."
|
||||
exit 1
|
||||
fi
|
||||
echo " -> Flattening @eaDir after copy (same layout fix)"
|
||||
flatten_indexer_layout "$TARGET_AT"
|
||||
DST_JPG=$(find "$TARGET_AT" -type f -name 'SYNO*.jpg' 2>/dev/null | wc -l | tr -d '[:space:]')
|
||||
if [ "$SRC_JPG" -gt 0 ] && [ "$DST_JPG" -ne "$SRC_JPG" ]; then
|
||||
echo "ERROR: Promote incomplete: $DST_JPG SYNO*.jpg in @eaDir vs $SRC_JPG in eaDir_tmp (expected equal)."
|
||||
exit 1
|
||||
fi
|
||||
chmod -R 755 "$EADIR_TMP" 2>/dev/null || true
|
||||
find "$EADIR_TMP" -type f -exec chmod u+w '{}' \; 2>/dev/null || true
|
||||
find "$EADIR_TMP" -type d -exec chmod u+w '{}' \; 2>/dev/null || true
|
||||
rm -rf "$EADIR_TMP" 2>/dev/null || sudo -n rm -rf "$EADIR_TMP" 2>/dev/null || true
|
||||
}
|
||||
|
||||
echo "=== Enhanced Synology Thumbnail Cleanup ==="
|
||||
echo "Target directory: $TARGET_DIR"
|
||||
|
||||
@@ -77,33 +109,18 @@ find "$TARGET_DIR" -path '*/@eaDir/*' -type f -exec chmod 644 '{}' \; 2>/dev/nul
|
||||
# Now remove them (2>/dev/null: races when parents disappear mid-find)
|
||||
find "$TARGET_DIR" -type d -name '@eaDir' -exec rm -rf '{}' \; 2>/dev/null || true
|
||||
|
||||
# 5. Promote only TARGET_DIR/eaDir_tmp -> TARGET_DIR/@eaDir (thumbgen layout; not nested */eaDir_tmp)
|
||||
# 5. Promote every .../eaDir_tmp -> .../@eaDir (thumbgen writes eaDir_tmp next to each video folder, not only TARGET_DIR/eaDir_tmp)
|
||||
echo "=== Installing custom thumbnails ==="
|
||||
EADIR_TMP="$TARGET_DIR/eaDir_tmp"
|
||||
if [ -d "$EADIR_TMP" ]; then
|
||||
chmod -R 755 "$EADIR_TMP" 2>/dev/null || true
|
||||
find "$EADIR_TMP" -type f -exec chmod 644 '{}' \; 2>/dev/null || true
|
||||
echo " -> Flattening eaDir_tmp (indexer may have added .../<file>/@eaDir/) before copy"
|
||||
flatten_indexer_layout "$EADIR_TMP"
|
||||
TARGET_AT="$TARGET_DIR/@eaDir"
|
||||
echo " -> Replacing $EADIR_TMP -> $TARGET_AT"
|
||||
rm -rf "$TARGET_AT"
|
||||
mkdir -p "$TARGET_AT"
|
||||
cp -R "$EADIR_TMP/." "$TARGET_AT/"
|
||||
echo " -> Flattening @eaDir after copy (same layout fix)"
|
||||
flatten_indexer_layout "$TARGET_AT"
|
||||
chmod -R 755 "$EADIR_TMP" 2>/dev/null || true
|
||||
find "$EADIR_TMP" -type f -exec chmod u+w '{}' \; 2>/dev/null || true
|
||||
find "$EADIR_TMP" -type d -exec chmod u+w '{}' \; 2>/dev/null || true
|
||||
if ! rm -rf "$EADIR_TMP" 2>/dev/null; then
|
||||
echo " -> rm eaDir_tmp failed; retrying with sudo -n"
|
||||
sudo -n rm -rf "$EADIR_TMP" 2>/dev/null || echo " -> WARNING: remove stale eaDir_tmp manually: sudo rm -rf \"$EADIR_TMP\""
|
||||
fi
|
||||
else
|
||||
echo " -> No $EADIR_TMP; skipping install step"
|
||||
INSTALL_ANY=0
|
||||
while IFS= read -r -d '' EADIR_TMP; do
|
||||
INSTALL_ANY=1
|
||||
promote_one_eadir_tmp "$EADIR_TMP"
|
||||
done < <(find "$TARGET_DIR" -type d -name 'eaDir_tmp' -print0)
|
||||
if [ "$INSTALL_ANY" -eq 0 ]; then
|
||||
echo " -> No eaDir_tmp under $TARGET_DIR; skipping install step"
|
||||
fi
|
||||
echo "=== Removing stray nested eaDir_tmp directories ==="
|
||||
find "$TARGET_DIR" -mindepth 2 -type d -name 'eaDir_tmp' -exec rm -rf '{}' \; 2>/dev/null || true
|
||||
echo "=== Removing any leftover eaDir_tmp directories ==="
|
||||
find "$TARGET_DIR" -type d -name 'eaDir_tmp' -exec rm -rf '{}' \; 2>/dev/null || true
|
||||
|
||||
# 6. Protect custom thumbnails with read-only permissions
|
||||
echo "=== Adding indexing exclusion hints ==="
|
||||
@@ -124,5 +141,4 @@ elif command -v systemctl >/dev/null 2>&1; then
|
||||
sudo -n systemctl start "$u" 2>/dev/null || systemctl start "$u" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
echo "Note: If indexing was stopped and needs restarting, use DSM UI or: sudo systemctl restart synoindexd"
|
||||
echo "If rm failed with Permission denied: delete @eaDir and eaDir_tmp on your pasted Windows path (SMB), or: sudo rm -rf ${TARGET_DIR}/eaDir_tmp ${TARGET_DIR}/@eaDir — then re-run option 3."
|
||||
echo "Note: If indexing was stopped and needs restarting, use DSM UI or: sudo systemctl restart synoindexd"
|
||||
+61
-34
@@ -72,6 +72,29 @@ def _ffmpeg_enabled():
|
||||
return False
|
||||
|
||||
|
||||
_ANSI_BLUE = "\033[94m"
|
||||
_ANSI_GREEN = "\033[92m"
|
||||
_ANSI_RST = "\033[0m"
|
||||
|
||||
|
||||
def _print_icaros(msg):
|
||||
print(f"{_ANSI_BLUE}{msg}{_ANSI_RST}")
|
||||
|
||||
|
||||
def _print_ffmpeg(msg):
|
||||
print(f"{_ANSI_GREEN}{msg}{_ANSI_RST}")
|
||||
|
||||
|
||||
def _ffmpeg_first_for_gameplay_path(path):
|
||||
"""Under ...\\gameplay\\, Shell GetThumbnail often fails and Icaros cache misses; go straight to FFmpeg."""
|
||||
if not _ffmpeg_enabled():
|
||||
return False
|
||||
try:
|
||||
p = os.path.normpath(path).replace("/", "\\").lower()
|
||||
except Exception:
|
||||
return False
|
||||
return "\\gameplay\\" in p
|
||||
|
||||
|
||||
def find_files(dir):
|
||||
# Only process formats that Synology doesn't handle well
|
||||
@@ -133,16 +156,20 @@ def ensure_directory_exists(path):
|
||||
|
||||
def create_video_thumbnails(source_path, dest_dir):
|
||||
"""Generate video thumbnails: Windows Shell (Icaros-backed) first, FFmpeg fallback."""
|
||||
if _ffmpeg_first_for_gameplay_path(source_path):
|
||||
_print_ffmpeg(f"Using FFmpeg directly (gameplay subtree): {source_path}")
|
||||
create_video_thumbnails_ffmpeg(source_path, dest_dir)
|
||||
return
|
||||
# Try Windows thumbnail extraction first (leverages Icaros provider when present)
|
||||
windows_thumb = extract_windows_thumbnail(source_path)
|
||||
if windows_thumb:
|
||||
print(f" -> SUCCESS: Using Windows/Icaros provider")
|
||||
_print_icaros(f" -> SUCCESS: Using Windows/Icaros provider")
|
||||
generate_synology_thumbnails(windows_thumb, dest_dir, include_video_screenshot=True)
|
||||
return
|
||||
|
||||
# Optionally fall back to FFmpeg (disabled by default)
|
||||
if _ffmpeg_enabled():
|
||||
print(f"Windows/Icaros extraction failed for {source_path}, using FFmpeg...")
|
||||
_print_ffmpeg(f"Windows/Icaros extraction failed for {source_path}, using FFmpeg...")
|
||||
create_video_thumbnails_ffmpeg(source_path, dest_dir)
|
||||
else:
|
||||
print(f"Windows/Icaros extraction failed for {source_path}, FFmpeg disabled (THUMBGEN_ENABLE_FFMPEG=0). Skipping.")
|
||||
@@ -175,15 +202,15 @@ def create_video_thumbnails_ffmpeg(source_path, dest_dir):
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f"Warning: FFmpeg failed for {source_path}: {result.stderr}")
|
||||
_print_ffmpeg(f"Warning: FFmpeg failed for {source_path}: {result.stderr}")
|
||||
continue
|
||||
|
||||
except FileNotFoundError:
|
||||
print("Warning: FFmpeg not found. Video thumbnails will be skipped.")
|
||||
print("Install FFmpeg: https://ffmpeg.org/download.html")
|
||||
_print_ffmpeg("Warning: FFmpeg not found. Video thumbnails will be skipped.")
|
||||
_print_ffmpeg("Install FFmpeg: https://ffmpeg.org/download.html")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Error processing video {source_path}: {e}")
|
||||
_print_ffmpeg(f"Error processing video {source_path}: {e}")
|
||||
|
||||
|
||||
def create_psd_thumbnails(source_path, dest_dir):
|
||||
@@ -315,15 +342,15 @@ def extract_windows_thumbnail(file_path):
|
||||
|
||||
# Tier 3: Try Icaros cache extraction
|
||||
try:
|
||||
print(f" -> Trying Icaros cache extraction...")
|
||||
_print_icaros(f" -> Trying Icaros cache extraction...")
|
||||
icaros_thumb = extract_icaros_thumbnail(file_path)
|
||||
if icaros_thumb:
|
||||
print(f" -> Found thumbnail in Icaros cache for {filename}")
|
||||
_print_icaros(f" -> Found thumbnail in Icaros cache for {filename}")
|
||||
return icaros_thumb
|
||||
else:
|
||||
print(f" -> No thumbnail in Icaros cache for {filename}")
|
||||
_print_icaros(f" -> No thumbnail in Icaros cache for {filename}")
|
||||
except Exception as e:
|
||||
print(f" -> Icaros cache extraction failed: {e}")
|
||||
_print_icaros(f" -> Icaros cache extraction failed: {e}")
|
||||
|
||||
print(f" -> Windows thumbnail extraction failed for {filename}")
|
||||
return None
|
||||
@@ -679,28 +706,28 @@ def extract_icaros_thumbnail(file_path):
|
||||
# Locate Icaros cache directory
|
||||
icaros_cache_dir = _get_icaros_cache_dir()
|
||||
if not icaros_cache_dir:
|
||||
print(f" -> No Icaros cache directory found")
|
||||
_print_icaros(f" -> No Icaros cache directory found")
|
||||
return None
|
||||
|
||||
# Debug: Show cache directory contents (all files)
|
||||
try:
|
||||
cache_files = os.listdir(icaros_cache_dir)
|
||||
print(f" -> Icaros cache has {len(cache_files)} files")
|
||||
_print_icaros(f" -> Icaros cache has {len(cache_files)} files")
|
||||
if len(cache_files) > 0:
|
||||
print(f" -> All cache files: {cache_files}")
|
||||
_print_icaros(f" -> All cache files: {cache_files}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Discover .icdb databases by size preference
|
||||
icdb_paths = _list_icdb_databases(icaros_cache_dir)
|
||||
if not icdb_paths:
|
||||
print(f" -> No .icdb database files found")
|
||||
_print_icaros(f" -> No .icdb database files found")
|
||||
return None
|
||||
|
||||
# Lookup a precise index for this file from Icaros_idx.icdb (SQLite or binary)
|
||||
mapped_index, preferred_db = _lookup_icaros_index(icaros_cache_dir, file_path)
|
||||
if mapped_index is None:
|
||||
print(f" -> No exact Icaros index entry for this file; skipping Icaros")
|
||||
_print_icaros(f" -> No exact Icaros index entry for this file; skipping Icaros")
|
||||
return None
|
||||
|
||||
# Try preferred DB first if provided
|
||||
@@ -711,16 +738,16 @@ def extract_icaros_thumbnail(file_path):
|
||||
for icdb_path in ordered_dbs:
|
||||
img = extract_from_icdb_database(icdb_path, file_path, forced_index=mapped_index)
|
||||
if img:
|
||||
print(f" -> Found thumbnail in {os.path.basename(icdb_path)} via mapped index")
|
||||
_print_icaros(f" -> Found thumbnail in {os.path.basename(icdb_path)} via mapped index")
|
||||
return img
|
||||
print(f" -> Mapped index did not resolve a thumbnail in any DB")
|
||||
_print_icaros(f" -> Mapped index did not resolve a thumbnail in any DB")
|
||||
return None
|
||||
|
||||
except ImportError:
|
||||
print(f" -> Required modules not available for .icdb extraction")
|
||||
_print_icaros(f" -> Required modules not available for .icdb extraction")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f" -> Icaros cache extraction error: {e}")
|
||||
_print_icaros(f" -> Icaros cache extraction error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -814,12 +841,12 @@ def _build_icaros_index_map(cache_dir):
|
||||
conn.close()
|
||||
_ICAROS_INDEX_CACHE = mapping
|
||||
if mapping:
|
||||
print(f" -> Loaded Icaros index map for {len(mapping)} files")
|
||||
_print_icaros(f" -> Loaded Icaros index map for {len(mapping)} files")
|
||||
else:
|
||||
print(f" -> Icaros index database present but no usable mapping found")
|
||||
_print_icaros(f" -> Icaros index database present but no usable mapping found")
|
||||
return _ICAROS_INDEX_CACHE
|
||||
except Exception as e:
|
||||
print(f" -> Failed to read Icaros index: {e}")
|
||||
_print_icaros(f" -> Failed to read Icaros index: {e}")
|
||||
_ICAROS_INDEX_CACHE = {}
|
||||
return _ICAROS_INDEX_CACHE
|
||||
|
||||
@@ -988,7 +1015,7 @@ def extract_from_icdb_database(icdb_path, file_path, forced_index=None):
|
||||
|
||||
# Verify ICDB signature
|
||||
if not data.startswith(b'ICDB'):
|
||||
print(f" -> Invalid ICDB signature in {icdb_path}")
|
||||
_print_icaros(f" -> Invalid ICDB signature in {icdb_path}")
|
||||
return None
|
||||
|
||||
# Search for JPEG images in the file
|
||||
@@ -1002,10 +1029,10 @@ def extract_from_icdb_database(icdb_path, file_path, forced_index=None):
|
||||
pos += 1
|
||||
|
||||
if not jpeg_positions:
|
||||
print(f" -> No JPEG images found in {icdb_path}")
|
||||
_print_icaros(f" -> No JPEG images found in {icdb_path}")
|
||||
return None
|
||||
|
||||
print(f" -> Found {len(jpeg_positions)} JPEG images in {icdb_path}")
|
||||
_print_icaros(f" -> Found {len(jpeg_positions)} JPEG images in {icdb_path}")
|
||||
|
||||
# If we have a mapped index, try it directly first
|
||||
if isinstance(forced_index, int) and len(jpeg_positions) > 0:
|
||||
@@ -1021,11 +1048,11 @@ def extract_from_icdb_database(icdb_path, file_path, forced_index=None):
|
||||
jpeg_data = data[jpeg_start:jpeg_end]
|
||||
try:
|
||||
img = Image.open(io.BytesIO(jpeg_data))
|
||||
print(f" -> Used mapped index {pos_index} from {os.path.basename(icdb_path)}")
|
||||
_print_icaros(f" -> Used mapped index {pos_index} from {os.path.basename(icdb_path)}")
|
||||
return img.copy()
|
||||
except Exception:
|
||||
# If mapped position invalid, continue to heuristic
|
||||
print(f" -> Mapped index {pos_index} invalid; falling back to heuristic")
|
||||
_print_icaros(f" -> Mapped index {pos_index} invalid; falling back to heuristic")
|
||||
|
||||
# Heuristic alphabetical mapping (fallback)
|
||||
def get_alphabetical_position(target_file_path):
|
||||
@@ -1079,10 +1106,10 @@ def extract_from_icdb_database(icdb_path, file_path, forced_index=None):
|
||||
|
||||
try:
|
||||
position = all_files.index(target_rel_path)
|
||||
print(f" -> Alphabetical position estimate: {position} for {target_filename}")
|
||||
_print_icaros(f" -> Alphabetical position estimate: {position} for {target_filename}")
|
||||
return position
|
||||
except ValueError:
|
||||
print(f" -> File {target_filename} not found in current monitored directories")
|
||||
_print_icaros(f" -> File {target_filename} not found in current monitored directories")
|
||||
# Return a reasonable fallback position
|
||||
return len(all_files) // 2 # Middle position as fallback
|
||||
|
||||
@@ -1093,16 +1120,16 @@ def extract_from_icdb_database(icdb_path, file_path, forced_index=None):
|
||||
if cache_position >= len(jpeg_positions):
|
||||
# Use modulo to wrap around (cache might be from larger file set)
|
||||
cache_position = cache_position % len(jpeg_positions)
|
||||
print(f" -> Wrapped position to {cache_position} (cache has {len(jpeg_positions)} entries)")
|
||||
_print_icaros(f" -> Wrapped position to {cache_position} (cache has {len(jpeg_positions)} entries)")
|
||||
|
||||
jpeg_start = jpeg_positions[cache_position]
|
||||
|
||||
print(f" -> Alphabetical mapping -> JPEG {cache_position} for {os.path.basename(file_path)}")
|
||||
_print_icaros(f" -> Alphabetical mapping -> JPEG {cache_position} for {os.path.basename(file_path)}")
|
||||
|
||||
# Find the end of the JPEG (FF D9 marker)
|
||||
jpeg_end = data.find(b'\xff\xd9', jpeg_start)
|
||||
if jpeg_end == -1:
|
||||
print(f" -> Could not find JPEG end marker")
|
||||
_print_icaros(f" -> Could not find JPEG end marker")
|
||||
return None
|
||||
|
||||
jpeg_end += 2 # Include the end marker
|
||||
@@ -1110,11 +1137,11 @@ def extract_from_icdb_database(icdb_path, file_path, forced_index=None):
|
||||
|
||||
# Try to create PIL Image from the JPEG data
|
||||
img = Image.open(io.BytesIO(jpeg_data))
|
||||
print(f" -> Successfully extracted {img.size[0]}x{img.size[1]} JPEG from {icdb_path}")
|
||||
_print_icaros(f" -> Successfully extracted {img.size[0]}x{img.size[1]} JPEG from {icdb_path}")
|
||||
return img.copy()
|
||||
|
||||
except Exception as e:
|
||||
print(f" -> Error reading ICDB file {icdb_path}: {e}")
|
||||
_print_icaros(f" -> Error reading ICDB file {icdb_path}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user