my changes so far

This commit is contained in:
2025-06-27 09:01:51 -06:00
parent 7e9e4370a7
commit 3628729716
5 changed files with 214 additions and 10 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,6 @@
venv/
.idea/
specstory/
*.specstory
*.cursorindexingignore

26
autoThumbs.bat Normal file
View File

@@ -0,0 +1,26 @@
@echo off
setlocal
REM Always use 'nathan' as NAS username
set NASUSER=nathan
REM Prompt for Windows path
set /p WINPATH=Enter the full Windows path to your NAS directory (e.g., R:\YouTube\Streams\MixerTwitch):
if "%WINPATH%"=="" (
echo No path provided. Exiting.
exit /b
)
REM Run psthumbgen.py
python psthumbgen.py --directory "%WINPATH%"
REM Convert Windows path to NAS path
set "RELPATH=%WINPATH:~3%"
set "RELPATH=%RELPATH:\=/%"
set "NASPATH=/volume1/Hydra/%RELPATH%"
REM SSH cleanup commands (run separately)
ssh %NASUSER%@hydra "find '%NASPATH%' -type d -name '@eaDir' -exec rm -rf '{}' \;"
ssh %NASUSER%@hydra "find '%NASPATH%' -depth -type d -name 'eaDir_tmp' -exec bash -c 'mv \"\$1\" \"\$(dirname \"\$1\")/@eaDir\"' _ {} \;"
pause

8
nascleanup.sh Normal file
View File

@@ -0,0 +1,8 @@
# 1. Dry-run check (see what @eaDir directories exist)
find /volume1/Hydra/Creative/artsy -type d -name '@eaDir' -exec echo '{}' \;
# 2. Remove any existing @eaDir directories
find /volume1/Hydra/Creative/artsy -type d -name '@eaDir' -exec rm -rf '{}' \;
# 3. Rename eaDir_tmp to @eaDir (corrected syntax)
find /volume1/Hydra/Creative/artsy -type d -name 'eaDir_tmp' -exec bash -c 'mv "$0" "${0%/*}/@eaDir"' {} \;

View File

@@ -4,9 +4,13 @@ import re
import argparse
import errno
import time
import subprocess
from PIL import Image
from multiprocessing import Pool
from multiprocessing import Value
from PIL import Image, ImageDraw, ImageFont
import io
import struct
class State(object):
@@ -55,14 +59,18 @@ def parse_args():
def find_files(dir):
valid_exts = ('jpeg', 'jpg', 'bmp', 'gif')
# Only process formats that Synology doesn't handle well
# Exclude common images (jpg, png, gif, etc.) since NAS handles them fine
valid_exts = ('mp4', 'webm', 'avi', 'mkv', 'mov', 'wmv', 'flv', 'm4v', 'ts',
'psd', 'blend')
valid_exts_re = "|".join(
map((lambda ext: ".*\\.{0}$".format(ext)), valid_exts))
for root, dirs, files in os.walk(dir):
for name in files:
if re.match(valid_exts_re, name, re.IGNORECASE) \
and not name.startswith('SYNOPHOTO_THUMB'):
and not name.startswith('SYNOPHOTO_THUMB') \
and not name.startswith('SYNOVIDEO_VIDEO_SCREENSHOT'):
yield os.path.join(root, name)
@@ -71,9 +79,10 @@ def print_progress():
state.increment(1)
processed = state.value
if processed % 10 == 0:
elapsed = float(time.process_time() - state.start)
rate = float(processed) / elapsed if elapsed > 0 else float(processed)
print("{0} files processed so far, averaging {1:.2f} files per second."
.format(processed, float(processed) /
(float(time.process_time() - state.start))))
.format(processed, rate))
def process_file(file_path):
@@ -83,7 +92,18 @@ def process_file(file_path):
thumb_dir = os.path.join(dir, 'eaDir_tmp', filename)
ensure_directory_exists(thumb_dir)
create_thumbnails(file_path, thumb_dir)
# Determine file type and process accordingly
file_ext = os.path.splitext(filename)[1].lower()
if file_ext in ['.mp4', '.webm', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.m4v', '.ts']:
create_video_thumbnails(file_path, thumb_dir)
elif file_ext in ['.psd']:
create_psd_thumbnails(file_path, thumb_dir)
elif file_ext in ['.blend']:
create_blend_thumbnails(file_path, thumb_dir)
else:
# Standard image processing
create_thumbnails(file_path, thumb_dir)
print_progress()
@@ -96,8 +116,118 @@ def ensure_directory_exists(path):
raise
def create_thumbnails(source_path, dest_dir):
im = Image.open(source_path)
def create_video_thumbnails(source_path, dest_dir):
"""Generate video thumbnails using FFmpeg"""
to_generate = (('SYNOVIDEO_VIDEO_SCREENSHOT.jpg', 1280),
('SYNOPHOTO_THUMB_XL.jpg', 1280),
('SYNOPHOTO_THUMB_B.jpg', 640),
('SYNOPHOTO_THUMB_M.jpg', 320),
('SYNOPHOTO_THUMB_PREVIEW.jpg', 160),
('SYNOPHOTO_THUMB_S.jpg', 120))
for thumb in to_generate:
thumb_path = os.path.join(dest_dir, thumb[0])
if os.path.exists(thumb_path):
continue
try:
# Use FFmpeg to extract a frame from the video at 10% duration
cmd = [
'ffmpeg', '-i', source_path,
'-ss', '00:00:05', # Seek to 5 seconds
'-vframes', '1',
'-vf', f'scale={thumb[1]}:{thumb[1]}:force_original_aspect_ratio=decrease',
'-y', # Overwrite output file
thumb_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(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")
break
except Exception as e:
print(f"Error processing video {source_path}: {e}")
def create_psd_thumbnails(source_path, dest_dir):
"""Generate PSD thumbnails using PIL with PSD support"""
try:
# Try to open PSD file - requires pillow-psd plugin
from psd_tools import PSDImage
psd = PSDImage.open(source_path)
pil_image = psd.composite()
to_generate = (('SYNOPHOTO_THUMB_XL.jpg', 1280),
('SYNOPHOTO_THUMB_B.jpg', 640),
('SYNOPHOTO_THUMB_M.jpg', 320),
('SYNOPHOTO_THUMB_PREVIEW.jpg', 160),
('SYNOPHOTO_THUMB_S.jpg', 120))
for thumb in to_generate:
thumb_path = os.path.join(dest_dir, thumb[0])
if os.path.exists(thumb_path):
continue
pil_image.thumbnail((thumb[1], thumb[1]), Image.LANCZOS)
# Convert to RGB if needed for JPEG
if pil_image.mode == 'RGBA':
pil_image = pil_image.convert('RGB')
pil_image.save(thumb_path, 'JPEG')
except ImportError:
print("Warning: psd-tools not installed. Install with: pip install psd-tools")
except Exception as e:
print(f"Error processing PSD {source_path}: {e}")
def extract_blend_thumbnail(blend_path):
"""Extracts the largest PNG thumbnail from all PREV blocks in a .blend file."""
import struct, re
with open(blend_path, 'rb') as f:
data = f.read()
if not data.startswith(b'BLENDER'):
return None
pointer_size = 8 if data[7:8] == b'-' else 4
endian = '<' if data[8:9] == b'v' else '>'
offset = 12
candidates = []
while offset + 16 < len(data):
code = data[offset:offset+4]
size = struct.unpack(endian + 'I', data[offset+4:offset+8])[0]
block_data_start = offset + 16 + pointer_size
block_data_end = block_data_start + size
if code == b'PREV':
block = data[block_data_start:block_data_end]
# Find all PNGs in this block
for m in re.finditer(b'\x89PNG\r\n\x1a\n', block):
start = m.start()
end = block.find(b'IEND', start)
if end != -1:
end += 8
png_data = block[start:end]
try:
img = Image.open(io.BytesIO(png_data))
candidates.append((img.size[0] * img.size[1], img))
except Exception:
continue
offset = block_data_end
if not candidates:
return None
return max(candidates, key=lambda x: x[0])[1]
def create_blend_thumbnails(source_path, dest_dir):
"""Extract embedded Blender thumbnail and generate Synology thumbnails."""
img = extract_blend_thumbnail(source_path)
if img is None:
print(f"No embedded thumbnail in {source_path}")
return
to_generate = (('SYNOPHOTO_THUMB_XL.jpg', 1280),
('SYNOPHOTO_THUMB_B.jpg', 640),
@@ -106,11 +236,39 @@ def create_thumbnails(source_path, dest_dir):
('SYNOPHOTO_THUMB_S.jpg', 120))
for thumb in to_generate:
if os.path.exists(os.path.join(dest_dir, thumb[0])):
thumb_path = os.path.join(dest_dir, thumb[0])
if os.path.exists(thumb_path):
continue
im_copy = img.copy()
im_copy.thumbnail((thumb[1], thumb[1]), Image.LANCZOS)
if im_copy.mode == 'RGBA':
im_copy = im_copy.convert('RGB')
im_copy.save(thumb_path, 'JPEG', quality=85)
im.thumbnail((thumb[1], thumb[1]), Image.ANTIALIAS)
im.save(os.path.join(dest_dir, thumb[0]))
def create_thumbnails(source_path, dest_dir):
"""Original image thumbnail creation"""
try:
im = Image.open(source_path)
to_generate = (('SYNOPHOTO_THUMB_XL.jpg', 1280),
('SYNOPHOTO_THUMB_B.jpg', 640),
('SYNOPHOTO_THUMB_M.jpg', 320),
('SYNOPHOTO_THUMB_PREVIEW.jpg', 160),
('SYNOPHOTO_THUMB_S.jpg', 120))
for thumb in to_generate:
thumb_path = os.path.join(dest_dir, thumb[0])
if os.path.exists(thumb_path):
continue
# Create a copy for thumbnailing
im_copy = im.copy()
im_copy.thumbnail((thumb[1], thumb[1]), Image.LANCZOS)
im_copy.save(thumb_path)
except Exception as e:
print(f"Error processing image {source_path}: {e}")
if __name__ == "__main__":

8
run_thumbgen_prompt.bat Normal file
View File

@@ -0,0 +1,8 @@
@echo off
set /p NASDIR=Enter the full path to your NAS directory:
if "%NASDIR%"=="" (
echo No directory provided. Exiting.
exit /b
)
python psthumbgen.py --directory "%NASDIR%"
pause