diff --git a/app.py b/app.py index ca979d4..ed57499 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,8 @@ from flask import Flask, render_template, request, jsonify import os import json +import ffmpeg +import subprocess app = Flask(__name__) @@ -19,6 +21,98 @@ config = load_config() + +def get_media_info_with_ffprobe(file_path): + """ + Извлекает информацию о видео- и аудиопотоках, а также контейнере. + :param file_path: Путь к файлу + :return: Словарь с информацией о видео, аудио и контейнере + """ + try: + if not os.path.exists(file_path): + print(f"File not found: {file_path}") + return None + + # Команда для потоков + streams_command = [ + "ffprobe", "-v", "error", + "-show_entries", "stream=index,codec_type,codec_name,width,height,bit_rate,channels,channel_layout", + "-of", "json", file_path + ] + + # Команда для контейнера + format_command = [ + "ffprobe", "-v", "error", + "-show_entries", "format=duration,bit_rate,size", + "-of", "json", file_path + ] + + # Выполняем команды + streams_result = subprocess.run(streams_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + format_result = subprocess.run(format_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Проверяем ошибки + if streams_result.returncode != 0: + print(f"ffprobe stream error: {streams_result.stderr}") + return None + if format_result.returncode != 0: + print(f"ffprobe format error: {format_result.stderr}") + return None + + # Парсим выводы + streams_data = json.loads(streams_result.stdout) + format_data = json.loads(format_result.stdout) + + # Извлекаем данные контейнера + format_info = format_data.get("format", {}) + container_duration = float(format_info.get("duration", 0)) + container_bitrate = int(format_info.get("bit_rate", 0)) // 1000 # Перевод в Кбит/с + file_size = int(format_info.get("size", 0)) + + # Обрабатываем потоки + media_info = { + "video": [], + "audio": [], + "container": { + "duration": f"{container_duration:.2f} seconds" if container_duration else "Unknown", + "bitrate": f"{container_bitrate} Kbit/s" if container_bitrate else "Unknown", + "size": f"{file_size / (1024**2):.2f} MB" if file_size else "Unknown" + } + } + + for stream in streams_data.get("streams", []): + codec_type = stream.get("codec_type") + codec_name = stream.get("codec_name") + stream_bitrate = int(stream.get("bit_rate", 0)) // 1000 if stream.get("bit_rate") else None + width = stream.get("width") + height = stream.get("height") + channels = stream.get("channels", "Unknown") + channel_layout = stream.get("channel_layout", "Unknown") + + # Если битрейт потока отсутствует, используем битрейт контейнера + bitrate = stream_bitrate if stream_bitrate else container_bitrate + + if codec_type == "video": + media_info["video"].append({ + "resolution": f"{width}x{height}" if width and height else "Unknown", + "bitrate": f"{bitrate} Kbit/s" if bitrate else "Unknown", + "codec": codec_name + }) + elif codec_type == "audio": + media_info["audio"].append({ + "bitrate": f"{bitrate} Kbit/s" if bitrate else "Unknown", + "channels": channels, + "layout": channel_layout, + "codec": codec_name + }) + + return media_info + + except Exception as e: + print(f"Error processing file {file_path}: {e}") + return None + + # Function to list media files def list_media_files(directories): media_files = [] @@ -27,18 +121,55 @@ for root, _, files in os.walk(directory): for file in files: file_path = os.path.join(root, file) - file_info = { - "name": file, - "path": file_path, - "size": os.path.getsize(file_path) - } - media_files.append(file_info) + try: + file_info = { + "name": file, + "path": file_path, + "size": os.path.getsize(file_path) + } + media_files.append(file_info) + except OSError as e: + print(f"Error accessing file {file_path}: {e}") + else: + print(f"Directory does not exist: {directory}") return media_files +def filter_media_files(media_files, allowed_formats): + """ + Фильтрует список файлов по допустимым форматам. + + :param media_files: Список файлов, полученный из list_media_files. + :param allowed_formats: Список допустимых форматов, например [".mp3", ".jpg"]. + :return: Отфильтрованный список файлов. + """ + filtered_files = [ + file for file in media_files + if any(file['name'].lower().endswith(fmt.lower()) for fmt in allowed_formats) + ] + return filtered_files + +def human_readable_size(size_bytes): + if size_bytes < 1024: + return f"{size_bytes} B" + elif size_bytes < 1024**2: + return f"{size_bytes / 1024:.2f} KB" + elif size_bytes < 1024**3: + return f"{size_bytes / 1024**2:.2f} MB" + else: + return f"{size_bytes / 1024**3:.2f} GB" + @app.route('/') def index(): + # Получаем список медиафайлов media_files = list_media_files(config.get("directories", [])) - return render_template('index.html', media_files=media_files) + # Фильтруем медиафайлы по формату + allowed_formats = config.get("allowed_formats", []) + filtered_files = filter_media_files(media_files, allowed_formats) + for file in filtered_files: + file["size"] = human_readable_size(file["size"]) + # file["info"] = get_media_info(file["path"]) + file["info"] = get_media_info_with_ffprobe(file["path"]) + return render_template('index.html', media_files=filtered_files) @app.route('/configure', methods=['GET', 'POST']) def configure(): diff --git a/config.json b/config.json index d246ffe..d8fde95 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,6 @@ { "directories": [ - "/home/gbook/media-storage/secraid5-storage" - ] + "/home/gbook/media-storage/secraid5_storage/films" + ], + "allowed_formats": [".mp4", ".m4p", ".m4v", ".mkv", ".webm", ".flv", ".vob", ".ogv", ".amv", ".mng", ".avi", ".mov", ".viv"] } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index fe43de8..0ea9fdd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,8 @@ blinker==1.9.0 click==8.1.8 +ffmpeg-python==0.2.0 Flask==3.1.0 +future==1.0.0 itsdangerous==2.2.0 Jinja2==3.1.5 MarkupSafe==3.0.2 diff --git a/templates/index.html b/templates/index.html index b3143e0..b87643e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -13,22 +13,61 @@
# | File Name | -Path | -Size (Bytes) | +Size |
---|---|---|---|---|
{{ file.name }} | -{{ file.path }} | -{{ file.size }} | -||
{{ loop.index }} | +
+ {{ file.name }}
+ {{ file.path }}
+ {% if file.info.video %}
+ Container:
+
|
+ {{ file.size }} | +||
No files found in the configured directories. | +