audiomini / app.py
2ch's picture
Update app.py
941eff1 verified
from pathlib import Path
import subprocess
from re import compile, findall, sub
from time import sleep
from typing import Literal
from urllib.parse import quote_plus
import uuid
import threading
from flask import Flask, render_template, request, jsonify, send_file
from httpx import Client
from yt_dlp import YoutubeDL
def extract_youtube_id(url: str) -> list[str] | str | None:
youtube_regex = r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/(watch\?v=|embed/|v/|.+\?v=|shorts/)?([^&=%\?]{11})'
youtube_url_format = compile(youtube_regex)
urls = findall(youtube_url_format, url)
if len(urls) > 0:
return urls[0][-1]
else:
return None
def download_yt_audio_ytdlp(youtube_id: str) -> Path:
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': '%(title)s.%(ext)s',
}
with YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(f'https://www.youtube.com/watch?v={youtube_id}', download=True)
file_name = Path(ydl.prepare_filename(info_dict))
print('yt_dlp ok')
return file_name
def download_yt_audio_api(youtube_id: str) -> Path:
encoded_youtube_url = quote_plus(f'https://www.youtube.com/watch?v={youtube_id}')
api_response_url = f'https://ab.cococococ.com/ajax/download.php?copyright=0&format=webm&url={encoded_youtube_url}&api=dfcb6d76f2f6a9894gjkege8a4ab232222'
with Client(verify=False, timeout=90, follow_redirects=True) as client:
response = client.get(api_response_url)
response.raise_for_status()
data = response.json()
file_name = sub(r'[^A-Za-zА-Яа-я_]', '', data['info']['title'].replace(' ', '_'))
video_id = data.get('id')
if not video_id:
raise ValueError("ошибка на стороне апи!")
progress_url = f'https://p.oceansaver.in/ajax/progress.php?id={video_id}'
while True:
response = client.get(progress_url)
response.raise_for_status()
data = response.json()
download_url = data.get('download_url')
if download_url:
break
sleep(1)
response = client.get(download_url)
response.raise_for_status()
path = Path('./')
file_name = path / f'{file_name}.webm'
with open(file_name, 'wb') as f:
f.write(response.content)
print('===========================================')
print(' API API APIAPI API API API API API API')
print('===========================================')
return file_name
def download_yt_audio(youtube_id: str) -> Path | None:
try:
return download_yt_audio_ytdlp(youtube_id)
except Exception as e:
print(e)
return download_yt_audio_api(youtube_id)
def audio_to_wav(input_file: Path) -> Path | None:
# if not input_file or not input_file.is_file():
# return None
wav_file = f'{input_file.stem}.wav'
ffmpeg_command = ["./ffmpeg", "-i", input_file, "-ac", "1", wav_file]
subprocess.run(ffmpeg_command, check=True)
input_file.unlink(missing_ok=True)
return Path(wav_file)
def wav_to_opus(wav_file: Path, del_input: bool = True) -> Path:
# if not wav_file or not wav_file.is_file():
# raise ValueError('входной файл для кодирования не был получен!')
opus_file = f'{wav_file.stem}.opus'
opusenc_command = [
"./opusenc",
"--bitrate", "18",
"--vbr",
"--speech",
"--comp", "10",
"--framesize", "60",
"--downmix-mono",
wav_file, opus_file
]
subprocess.run(opusenc_command, check=True)
if del_input:
wav_file.unlink(missing_ok=True)
return Path(opus_file)
def wav_to_usac(wav_file: Path, del_input: bool = True) -> Path:
# if not wav_file or not wav_file.is_file():
# raise ValueError('входной файл для кодирования не был получен!')
exhale_file = f'{wav_file.stem}.m4a'
exhale_command = ["./exhale", "0", wav_file, exhale_file]
subprocess.run(exhale_command, check=True)
if del_input:
wav_file.unlink(missing_ok=True)
return Path(exhale_file)
def download_and_encode(youtube_url: str, codec: Literal['opus', 'usac', 'both'] = 'both') -> list[Path]:
temp_wav = audio_to_wav(download_yt_audio(extract_youtube_id(youtube_url)))
if codec == 'opus':
return [wav_to_opus(temp_wav)]
elif codec == 'usac':
return [wav_to_usac(temp_wav)]
else:
return [wav_to_opus(temp_wav, del_input=False), wav_to_usac(temp_wav)]
app = Flask(__name__)
tasks = {}
@app.route('/')
def index():
return render_template('index.html')
@app.route('/download', methods=['POST'])
def download():
youtube_url = request.form['youtube_url']
codec = request.form['codec']
task_id = str(uuid.uuid4())
def process_task():
tasks[task_id] = {'status': 'скачивание с ютуба...'}
audio_file = download_yt_audio(extract_youtube_id(youtube_url))
tasks[task_id]['status'] = 'предварительная конвертация...'
wav_file = audio_to_wav(audio_file)
tasks[task_id]['status'] = 'сжатие...'
encoded_files = download_and_encode(youtube_url, codec)
tasks[task_id]['status'] = 'готово'
tasks[task_id]['files'] = [str(f) for f in encoded_files]
threading.Thread(target=process_task).start()
return jsonify({'task_id': task_id})
@app.route('/download_status/<task_id>')
def download_status(task_id):
task = tasks.get(task_id)
if task:
return jsonify(task)
else:
return jsonify({'error': 'Task not found'}), 404
@app.route('/play/<path:filename>')
def play(filename):
return send_file(filename, as_attachment=True)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860)