diff --git a/app/core/_config.py b/app/core/_config.py index f3ea7de..4b36637 100644 --- a/app/core/_config.py +++ b/app/core/_config.py @@ -7,7 +7,12 @@ load_dotenv(dotenv_path='.env') PROJECT_HOST = os.getenv('PROJECT_HOST', 'http://127.0.0.1:8080') SANIC_PORT = int(os.getenv('SANIC_PORT', '8080')) +# Path inside the running backend container where content files are visible UPLOADS_DIR = os.getenv('UPLOADS_DIR', '/app/data') +# Host path where the same content directory is mounted (used for docker -v from within container) +BACKEND_DATA_DIR_HOST = os.getenv('BACKEND_DATA_DIR_HOST', '/Storage/storedContent') +# Host path for converter logs (used for docker -v). Optional. +BACKEND_LOGS_DIR_HOST = os.getenv('BACKEND_LOGS_DIR_HOST', '/Storage/logs/converter') if not os.path.exists(UPLOADS_DIR): os.makedirs(UPLOADS_DIR) diff --git a/app/core/background/convert_service.py b/app/core/background/convert_service.py index 0a3c783..4dd660c 100644 --- a/app/core/background/convert_service.py +++ b/app/core/background/convert_service.py @@ -14,7 +14,7 @@ from app.core.logger import make_log from app.core.models.user import User from app.core.models import WalletConnection from app.core.storage import db_session -from app.core._config import UPLOADS_DIR +from app.core._config import UPLOADS_DIR, BACKEND_DATA_DIR_HOST, BACKEND_LOGS_DIR_HOST from app.core.content.content_id import ContentId @@ -43,13 +43,16 @@ async def convert_loop(memory): return # Определяем путь и расширение входного файла - input_file_path = f"/Storage/storedContent/{decrypted_content.hash}" + # Путь внутри текущего контейнера (доступен Python процессу) + input_file_container = os.path.join(UPLOADS_DIR, decrypted_content.hash) + # Хостовый путь (нужен для docker -v маппинга при запуске конвертера) + input_file_host = os.path.join(BACKEND_DATA_DIR_HOST, decrypted_content.hash) input_ext = (unprocessed_encrypted_content.filename.split('.')[-1] if '.' in unprocessed_encrypted_content.filename else "mp4") # ==== Новая логика: определение MIME-тип через python-magic ==== try: - mime_type = magic.from_file(input_file_path.replace("/Storage/storedContent", "/app/data"), mime=True) + mime_type = magic.from_file(input_file_container, mime=True) except Exception as e: make_log("ConvertProcess", f"magic probe failed: {e}", level="warning") mime_type = "" @@ -100,7 +103,8 @@ async def convert_loop(memory): REQUIRED_CONVERT_OPTIONS = ['high', 'low'] # no preview for audio converted_content = {} - logs_dir = "/Storage/logs/converter" + # Директория логов на хосте для docker-контейнера конвертера + logs_dir_host = BACKEND_LOGS_DIR_HOST for option in REQUIRED_CONVERT_OPTIONS: # Set quality parameter and trim option (only for preview) @@ -113,14 +117,19 @@ async def convert_loop(memory): # Generate a unique output directory for docker container output_uuid = str(uuid.uuid4()) - output_dir = f"/Storage/storedContent/converter-output/{output_uuid}" + # Директория вывода в текущем контейнере (та же что и в UPLOADS_DIR, смонтирована с хоста) + output_dir_container = os.path.join(UPLOADS_DIR, "converter-output", output_uuid) + os.makedirs(output_dir_container, exist_ok=True) + # Соответствующая директория на хосте — нужна для docker -v + output_dir_host = os.path.join(BACKEND_DATA_DIR_HOST, "converter-output", output_uuid) # Build the docker command cmd = [ "docker", "run", "--rm", - "-v", f"{input_file_path}:/app/input", - "-v", f"{output_dir}:/app/output", - "-v", f"{logs_dir}:/app/logs", + # Важно: источники - это ХОСТОВЫЕ пути, так как docker демону они нужны на хосте + "-v", f"{input_file_host}:/app/input:ro", + "-v", f"{output_dir_host}:/app/output", + "-v", f"{logs_dir_host}:/app/logs", "media_converter", "--ext", input_ext, "--quality", quality @@ -142,7 +151,7 @@ async def convert_loop(memory): # List files in output dir try: - files = os.listdir(output_dir.replace("/Storage/storedContent", "/app/data")) + files = os.listdir(output_dir_container) except Exception as e: make_log("ConvertProcess", f"Error reading output directory {output_dir}: {e}", level="error") return @@ -152,10 +161,7 @@ async def convert_loop(memory): make_log("ConvertProcess", f"Expected one media file, found {len(media_files)} for option {option}", level="error") return - output_file = os.path.join( - output_dir.replace("/Storage/storedContent", "/app/data"), - media_files[0] - ) + output_file = os.path.join(output_dir_container, media_files[0]) # Compute SHA256 hash of the output file hash_process = await asyncio.create_subprocess_exec( @@ -198,10 +204,7 @@ async def convert_loop(memory): converted_content[option] = file_hash # Process output.json for ffprobe_meta - output_json_path = os.path.join( - output_dir.replace("/Storage/storedContent", "/app/data"), - "output.json" - ) + output_json_path = os.path.join(output_dir_container, "output.json") if os.path.exists(output_json_path) and unprocessed_encrypted_content.meta.get('ffprobe_meta') is None: try: with open(output_json_path, "r") as f: @@ -215,7 +218,7 @@ async def convert_loop(memory): # Cleanup output directory try: - shutil.rmtree(output_dir.replace("/Storage/storedContent", "/app/data")) + shutil.rmtree(output_dir_container) except Exception as e: make_log("ConvertProcess", f"Error removing output dir {output_dir}: {e}", level="warning")