Save local changes (GSC/Bing integrations) before merging PR #354
This commit is contained in:
@@ -114,8 +114,19 @@ def save_asset_to_library(
|
||||
try:
|
||||
source_module_enum = AssetSource(source_module.lower())
|
||||
except ValueError:
|
||||
logger.warning(f"Invalid source module: {source_module}, defaulting to 'story_writer'")
|
||||
source_module_enum = AssetSource.STORY_WRITER
|
||||
logger.warning(f"Invalid source module: {source_module}, attempting fallback based on asset type")
|
||||
|
||||
# Smart fallback based on asset type
|
||||
if asset_type_enum == AssetType.IMAGE:
|
||||
source_module_enum = AssetSource.MAIN_IMAGE_GENERATION
|
||||
elif asset_type_enum == AssetType.AUDIO:
|
||||
source_module_enum = AssetSource.MAIN_AUDIO_GENERATION
|
||||
elif asset_type_enum == AssetType.VIDEO:
|
||||
source_module_enum = AssetSource.MAIN_VIDEO_GENERATION
|
||||
else:
|
||||
source_module_enum = AssetSource.MAIN_TEXT_GENERATION
|
||||
|
||||
logger.info(f"Fallback source module: {source_module_enum.value}")
|
||||
|
||||
# Sanitize filename (remove path traversal attempts)
|
||||
filename = re.sub(r'[^\w\s\-_\.]', '', filename.split('/')[-1])
|
||||
@@ -151,6 +162,25 @@ def save_asset_to_library(
|
||||
)
|
||||
|
||||
logger.info(f"✅ Asset saved to library: {asset.id} ({asset_type} from {source_module})")
|
||||
|
||||
# Trigger SIF Indexing for all new assets (Text, Image, etc.)
|
||||
try:
|
||||
from models.website_analysis_monitoring_models import SIFIndexingTask
|
||||
from datetime import datetime
|
||||
|
||||
# Check if a SIF Indexing task exists for this user
|
||||
existing_task = db.query(SIFIndexingTask).filter(SIFIndexingTask.user_id == user_id).first()
|
||||
if existing_task:
|
||||
logger.info(f"Triggering SIF Indexing task for user {user_id} due to new {asset_type} asset")
|
||||
existing_task.next_execution = datetime.utcnow() # Run immediately
|
||||
existing_task.status = "pending" # Ensure it gets picked up
|
||||
db.add(existing_task)
|
||||
# Note: Commit depends on the caller's transaction management
|
||||
else:
|
||||
logger.debug(f"No SIF Indexing task found for user {user_id} - skipping trigger")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to trigger SIF Indexing task in asset_tracker: {e}")
|
||||
|
||||
return asset.id
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -88,6 +88,14 @@ def save_file_safely(
|
||||
Tuple of (file_path, error_message). file_path is None on error.
|
||||
"""
|
||||
try:
|
||||
# Handle max_file_size if it comes as string (e.g. from env vars)
|
||||
if isinstance(max_file_size, str):
|
||||
try:
|
||||
max_file_size = int(max_file_size)
|
||||
except ValueError:
|
||||
# Fallback to default if conversion fails
|
||||
max_file_size = 100 * 1024 * 1024
|
||||
|
||||
# Validate file size
|
||||
if len(content) > max_file_size:
|
||||
return None, f"File size {len(content)} exceeds maximum {max_file_size}"
|
||||
|
||||
116
backend/utils/media_utils.py
Normal file
116
backend/utils/media_utils.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
Media Utility Functions
|
||||
|
||||
Centralized helper functions for loading and managing media assets across modules.
|
||||
Promotes reuse between Podcast, YouTube, and other media-heavy modules.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional, List
|
||||
from urllib.parse import urlparse
|
||||
|
||||
# Configure logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Base Directories
|
||||
# backend/utils/media_utils.py -> parents[2] = backend/.. = root
|
||||
ROOT_DIR = Path(__file__).resolve().parents[2]
|
||||
DATA_MEDIA_DIR = ROOT_DIR / "data" / "media"
|
||||
|
||||
# Module-specific directories
|
||||
YOUTUBE_AVATARS_DIR = DATA_MEDIA_DIR / "youtube_avatars"
|
||||
YOUTUBE_IMAGES_DIR = DATA_MEDIA_DIR / "youtube_images"
|
||||
PODCAST_IMAGES_DIR = DATA_MEDIA_DIR / "podcast_images"
|
||||
PODCAST_AVATARS_DIR = PODCAST_IMAGES_DIR / "avatars"
|
||||
|
||||
# Ensure directories exist
|
||||
for directory in [YOUTUBE_AVATARS_DIR, YOUTUBE_IMAGES_DIR, PODCAST_IMAGES_DIR, PODCAST_AVATARS_DIR]:
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def resolve_media_path(media_url_or_path: str) -> Optional[Path]:
|
||||
"""
|
||||
Resolve a media URL or filename to a concrete file path on disk.
|
||||
|
||||
Handles cross-module lookups (e.g. checking podcast avatars if not found in youtube).
|
||||
|
||||
Args:
|
||||
media_url_or_path: URL path (e.g. /api/youtube/avatars/foo.png) or filename
|
||||
|
||||
Returns:
|
||||
Path object if found, None otherwise
|
||||
"""
|
||||
if not media_url_or_path:
|
||||
return None
|
||||
|
||||
try:
|
||||
# Extract filename from URL/path
|
||||
if "/" in media_url_or_path or "\\" in media_url_or_path:
|
||||
# It's a URL or path
|
||||
parsed = urlparse(media_url_or_path)
|
||||
path = parsed.path if parsed.scheme else media_url_or_path
|
||||
filename = path.split("/")[-1].split("?")[0]
|
||||
else:
|
||||
# It's just a filename
|
||||
filename = media_url_or_path.split("?")[0]
|
||||
|
||||
if not filename:
|
||||
return None
|
||||
|
||||
# Define search paths in order of likelihood
|
||||
# We search all avatar/image directories
|
||||
search_paths: List[Path] = [
|
||||
YOUTUBE_AVATARS_DIR / filename,
|
||||
PODCAST_AVATARS_DIR / filename,
|
||||
YOUTUBE_IMAGES_DIR / filename,
|
||||
PODCAST_IMAGES_DIR / filename,
|
||||
# Fallback for nested podcast images (if they exist directly in podcast_images)
|
||||
PODCAST_IMAGES_DIR / "avatars" / filename
|
||||
]
|
||||
|
||||
# Check specific module based on URL prefix if present
|
||||
if "/api/youtube/" in media_url_or_path:
|
||||
# Prioritize YouTube paths
|
||||
pass # Already first in list
|
||||
elif "/api/podcast/" in media_url_or_path:
|
||||
# Prioritize Podcast paths
|
||||
search_paths = [
|
||||
PODCAST_AVATARS_DIR / filename,
|
||||
PODCAST_IMAGES_DIR / filename,
|
||||
YOUTUBE_AVATARS_DIR / filename,
|
||||
YOUTUBE_IMAGES_DIR / filename
|
||||
]
|
||||
|
||||
# Iterate and find first existing file
|
||||
for path in search_paths:
|
||||
if path.exists() and path.is_file():
|
||||
logger.debug(f"[MediaUtils] Resolved {media_url_or_path} to {path}")
|
||||
return path
|
||||
|
||||
logger.warning(f"[MediaUtils] Could not resolve media path for: {media_url_or_path}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[MediaUtils] Error resolving media path: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def load_media_bytes(media_url_or_path: str) -> Optional[bytes]:
|
||||
"""
|
||||
Load media bytes from a URL or path with cross-module fallback.
|
||||
|
||||
Args:
|
||||
media_url_or_path: URL path or filename
|
||||
|
||||
Returns:
|
||||
File bytes if found, None otherwise
|
||||
"""
|
||||
path = resolve_media_path(media_url_or_path)
|
||||
if path:
|
||||
try:
|
||||
return path.read_bytes()
|
||||
except Exception as e:
|
||||
logger.error(f"[MediaUtils] Error reading file {path}: {e}")
|
||||
return None
|
||||
return None
|
||||
@@ -150,6 +150,29 @@ def save_and_track_text_content(
|
||||
|
||||
if asset_id:
|
||||
logger.info(f"✅ Text asset saved to library: ID={asset_id}, filename={filename}")
|
||||
|
||||
# Trigger SIF Content Guardian Indexing
|
||||
try:
|
||||
from models.website_analysis_monitoring_models import SIFIndexingTask
|
||||
from datetime import datetime
|
||||
|
||||
# Use the existing DB session
|
||||
# Check if a SIF Indexing task exists for this user
|
||||
existing_task = db.query(SIFIndexingTask).filter(SIFIndexingTask.user_id == user_id).first()
|
||||
if existing_task:
|
||||
logger.info(f"Triggering SIF Indexing task for user {user_id} due to new content")
|
||||
existing_task.next_execution = datetime.utcnow() # Run immediately
|
||||
existing_task.status = "pending" # Ensure it gets picked up
|
||||
db.add(existing_task)
|
||||
# We don't force commit here as the session might be managed by the caller
|
||||
# But if the caller commits, this change will be included.
|
||||
# If the caller uses autocommit=False and commits later, this is fine.
|
||||
# Most API endpoints commit at the end.
|
||||
else:
|
||||
logger.debug(f"No SIF Indexing task found for user {user_id} - skipping trigger")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to trigger SIF Indexing task: {e}")
|
||||
|
||||
else:
|
||||
logger.warning(f"Asset tracking returned None for {filename}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user