YT Audio Encoding

This commit is contained in:
2026-01-07 04:34:24 +00:00
parent abceb1be7b
commit d548bd6fde
6 changed files with 299 additions and 5 deletions

View File

@@ -102,16 +102,21 @@ def generate_art():
@app.route('/api/hide', methods=['POST'])
def hide_data():
if 'data' not in request.files or 'host' not in request.files:
return jsonify({"error": "Requires 'data' and 'host' files"}), 400
return jsonify({"error": "Missing files"}), 400
data_file = request.files['data']
host_file = request.files['host']
data_path = None
host_path = None
try:
data_path = save_upload(request.files['data'])
host_path = save_upload(request.files['host'])
data_path = save_upload(data_file)
host_path = save_upload(host_file)
output_path = processor.encode_stego(data_path, host_path)
return send_file(output_path, mimetype='image/png')
stego_path = processor.encode_stego(data_path, host_path)
return send_file(stego_path, mimetype='image/png')
except ValueError as e:
return jsonify({"error": str(e)}), 400
except Exception as e:
@@ -124,6 +129,41 @@ def hide_data():
try: os.remove(host_path)
except: pass
import youtube_utils
@app.route('/api/hide-yt', methods=['POST'])
def hide_yt_data():
if 'url' not in request.form or 'host' not in request.files:
return jsonify({"error": "Missing URL or Host Image"}), 400
youtube_url = request.form['url']
host_file = request.files['host']
audio_path = None
host_path = None
try:
# Download Audio
audio_path = youtube_utils.download_audio(youtube_url, app.config['UPLOAD_FOLDER'])
# Save Host
host_path = save_upload(host_file)
# Encode
output_path = processor.encode_stego(audio_path, host_path)
return send_file(output_path, mimetype='image/png')
except Exception as e:
return jsonify({"error": str(e)}), 500
finally:
# Cleanup
if audio_path and os.path.exists(audio_path):
try: os.remove(audio_path)
except: pass
if host_path and os.path.exists(host_path):
try: os.remove(host_path)
except: pass
@app.route('/api/decode', methods=['POST'])
def decode():
if 'image' not in request.files:

View File

@@ -7,3 +7,4 @@ librosa
matplotlib
torch
torchaudio
yt-dlp

52
server/youtube_utils.py Normal file
View File

@@ -0,0 +1,52 @@
import yt_dlp
import os
import time
def download_audio(url, output_folder, max_length_seconds=600):
"""
Downloads audio from a YouTube URL.
Returns the path to the downloaded file or raises an Exception.
Enforces max_length_seconds (default 10 mins).
"""
timestamp = int(time.time())
output_template = os.path.join(output_folder, f'yt_{timestamp}_%(id)s.%(ext)s')
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': output_template,
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
'quiet': True,
'no_warnings': True,
'noplaylist': True,
'match_filter': yt_dlp.utils.match_filter_func("duration <= " + str(max_length_seconds))
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=True)
# The file path might differ slightly because of the postprocessor (mp3 conversion)
# yt-dlp usually returns the final filename in 'requested_downloads' or similar,
# but constructing it from info is safer if we know the template.
# However, extract_info returns the info dict.
# Since we force mp3, the file will end in .mp3
# We used %(id)s in template, so we can reconstruct or find it.
# Let's find the file in the folder that matches the timestamp prefix
# This is safer than guessing what yt-dlp named it exactly
valid_files = [f for f in os.listdir(output_folder) if f.startswith(f'yt_{timestamp}_') and f.endswith('.mp3')]
if not valid_files:
raise Exception("Download failed: Audio file not found after processing.")
return os.path.join(output_folder, valid_files[0])
except yt_dlp.utils.DownloadError as e:
if "video is too long" in str(e).lower() or "duration" in str(e).lower():
raise Exception(f"Video is too long. Maximum allowed duration is {max_length_seconds} seconds.")
raise Exception(f"Failed to download video: {str(e)}")