2ch commited on
Commit
ab583e0
·
verified ·
1 Parent(s): bfd6562

Upload folder using huggingface_hub

Browse files
Files changed (8) hide show
  1. .gitattributes +3 -0
  2. Dockerfile +13 -0
  3. README.md +4 -4
  4. app.py +172 -0
  5. exhale +3 -0
  6. ffmpeg +3 -0
  7. opusenc +3 -0
  8. templates/index.html +171 -0
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ exhale filter=lfs diff=lfs merge=lfs -text
37
+ ffmpeg filter=lfs diff=lfs merge=lfs -text
38
+ opusenc filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12
2
+
3
+ # RUN useradd -m -u 1000 user
4
+ # USER user
5
+ # ENV PATH="/home/user/.local/bin:$PATH"
6
+
7
+ WORKDIR /app
8
+
9
+ RUN pip install flask httpx yt_dlp
10
+
11
+ COPY . /app
12
+ RUN chmod 777 /app && chmod 777 /app/*
13
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: Audiomini
3
- emoji: 👀
4
- colorFrom: blue
5
- colorTo: blue
6
  sdk: docker
7
  pinned: false
8
  ---
 
1
  ---
2
+ title: Audio
3
+ emoji: 🐢
4
+ colorFrom: red
5
+ colorTo: indigo
6
  sdk: docker
7
  pinned: false
8
  ---
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import subprocess
3
+ from re import compile, findall
4
+ from time import sleep
5
+ from typing import Literal
6
+ from urllib.parse import quote_plus
7
+
8
+ import uuid
9
+ import threading
10
+
11
+ from flask import Flask, render_template, request, jsonify, send_file
12
+ from httpx import Client
13
+
14
+ from yt_dlp import YoutubeDL
15
+
16
+
17
+ def extract_youtube_id(url: str) -> list[str] | str | None:
18
+ youtube_regex = r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/(watch\?v=|embed/|v/|.+\?v=|shorts/)?([^&=%\?]{11})'
19
+ youtube_url_format = compile(youtube_regex)
20
+ urls = findall(youtube_url_format, url)
21
+ if len(urls) > 0:
22
+ return urls[0][-1]
23
+ else:
24
+ return None
25
+
26
+
27
+ def download_yt_audio_ytdlp(youtube_id: str) -> Path:
28
+ ydl_opts = {
29
+ 'format': 'bestaudio/best',
30
+ 'outtmpl': '%(title)s.%(ext)s',
31
+ }
32
+ with YoutubeDL(ydl_opts) as ydl:
33
+ info_dict = ydl.extract_info(f'https://www.youtube.com/watch?v={youtube_id}', download=True)
34
+ file_name = Path(ydl.prepare_filename(info_dict))
35
+ return file_name
36
+
37
+
38
+ def download_yt_audio_api(youtube_id: str) -> Path:
39
+ encoded_youtube_url = quote_plus(f'https://www.youtube.com/watch?v={youtube_id}')
40
+ api_response_url = f'https://ab.cococococ.com/ajax/download.php?copyright=0&format=webm&url={encoded_youtube_url}&api=dfcb6d76f2f6a9894gjkege8a4ab232222'
41
+
42
+ with Client() as client:
43
+ response = client.get(api_response_url)
44
+ response.raise_for_status()
45
+ data = response.json()
46
+ file_name = data['info']['title'].replace(' ', '_')
47
+ video_id = data.get('id')
48
+ if not video_id:
49
+ raise ValueError("ошибка на стороне апи!")
50
+
51
+ progress_url = f'https://p.oceansaver.in/ajax/progress.php?id={video_id}'
52
+ while True:
53
+ response = client.get(progress_url)
54
+ response.raise_for_status()
55
+ data = response.json()
56
+ download_url = data.get('download_url')
57
+ if download_url:
58
+ break
59
+ sleep(1)
60
+
61
+ response = client.get(download_url)
62
+ response.raise_for_status()
63
+ path = Path('./')
64
+ file_name = path / f'{file_name}.webm'
65
+ with open(file_name, 'wb') as f:
66
+ f.write(response.content)
67
+ return file_name
68
+
69
+
70
+ def download_yt_audio(youtube_id: str) -> Path | None:
71
+ try:
72
+ return download_yt_audio_ytdlp(youtube_id)
73
+ except Exception as e:
74
+ print(e)
75
+ return download_yt_audio_api(youtube_id)
76
+
77
+
78
+ def audio_to_wav(input_file: Path) -> Path | None:
79
+ # if not input_file or not input_file.is_file():
80
+ # return None
81
+ wav_file = f'{input_file.stem}.wav'
82
+ ffmpeg_command = ["./ffmpeg", "-i", input_file, "-ac", "1", wav_file]
83
+ subprocess.run(ffmpeg_command, check=True)
84
+ input_file.unlink(missing_ok=True)
85
+ return Path(wav_file)
86
+
87
+
88
+ def wav_to_opus(wav_file: Path, del_input: bool = True) -> Path:
89
+ # if not wav_file or not wav_file.is_file():
90
+ # raise ValueError('входной файл для кодирования не был получен!')
91
+ opus_file = f'{wav_file.stem}.opus'
92
+ opusenc_command = [
93
+ "./opusenc",
94
+ "--bitrate", "18",
95
+ "--vbr",
96
+ "--speech",
97
+ "--comp", "10",
98
+ "--framesize", "60",
99
+ "--downmix-mono",
100
+ wav_file, opus_file
101
+ ]
102
+ subprocess.run(opusenc_command, check=True)
103
+ if del_input:
104
+ wav_file.unlink(missing_ok=True)
105
+ return Path(opus_file)
106
+
107
+
108
+ def wav_to_usac(wav_file: Path, del_input: bool = True) -> Path:
109
+ # if not wav_file or not wav_file.is_file():
110
+ # raise ValueError('входной файл для кодирования не был получен!')
111
+ exhale_file = f'{wav_file.stem}.m4a'
112
+ exhale_command = ["./exhale", "0", wav_file, exhale_file]
113
+ subprocess.run(exhale_command, check=True)
114
+ if del_input:
115
+ wav_file.unlink(missing_ok=True)
116
+ return Path(exhale_file)
117
+
118
+
119
+ def download_and_encode(youtube_url: str, codec: Literal['opus', 'usac', 'both'] = 'both') -> list[Path]:
120
+ temp_wav = audio_to_wav(download_yt_audio(extract_youtube_id(youtube_url)))
121
+ if codec == 'opus':
122
+ return [wav_to_opus(temp_wav)]
123
+ elif codec == 'usac':
124
+ return [wav_to_usac(temp_wav)]
125
+ else:
126
+ return [wav_to_opus(temp_wav, del_input=False), wav_to_usac(temp_wav)]
127
+
128
+
129
+ app = Flask(__name__)
130
+
131
+
132
+ tasks = {}
133
+
134
+ @app.route('/')
135
+ def index():
136
+ return render_template('index.html')
137
+
138
+ @app.route('/download', methods=['POST'])
139
+ def download():
140
+ youtube_url = request.form['youtube_url']
141
+ codec = request.form['codec']
142
+ task_id = str(uuid.uuid4())
143
+
144
+ def process_task():
145
+ tasks[task_id] = {'status': 'скачивание с ютуба...'}
146
+ audio_file = download_yt_audio(extract_youtube_id(youtube_url))
147
+ tasks[task_id]['status'] = 'предварительная конвертация...'
148
+ wav_file = audio_to_wav(audio_file)
149
+ tasks[task_id]['status'] = 'сжатие...'
150
+ encoded_files = download_and_encode(youtube_url, codec)
151
+ tasks[task_id]['status'] = 'готово'
152
+ tasks[task_id]['files'] = [str(f) for f in encoded_files]
153
+
154
+ threading.Thread(target=process_task).start()
155
+ return jsonify({'task_id': task_id})
156
+
157
+ @app.route('/download_status/<task_id>')
158
+ def download_status(task_id):
159
+ task = tasks.get(task_id)
160
+ if task:
161
+ return jsonify(task)
162
+ else:
163
+ return jsonify({'error': 'Task not found'}), 404
164
+
165
+
166
+ @app.route('/play/<path:filename>')
167
+ def play(filename):
168
+ return send_file(filename, as_attachment=True)
169
+
170
+
171
+ if __name__ == '__main__':
172
+ app.run(host='0.0.0.0', port=7860)
exhale ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bd563aed06aa623d35364f890b7ba194fafcd5c5e29fd36b2108b1d1f256410e
3
+ size 1097864
ffmpeg ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e5267ed734dc7b2f5955f93924a3d89f0fb43f8fbcc8f0da183bc6a2edde3ec5
3
+ size 125709160
opusenc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d52aef7d08218ae89c2cbc775f2d32f6f1276704c907c75a47f774ae120a8086
3
+ size 2892712
templates/index.html ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Audio Downloader</title>
6
+ <style>
7
+ body {
8
+ background-color: #121212;
9
+ color: #e0e0e0;
10
+ font-family: 'Segoe UI', Tahoma, Verdana, sans-serif;
11
+ margin: 0;
12
+ padding: 20px;
13
+ }
14
+
15
+ h1 {
16
+ color: #59a459;
17
+ font-size: 2em;
18
+ margin-bottom: 20px;
19
+ display: none;
20
+ }
21
+
22
+ #downloadForm {
23
+ display: flex;
24
+ flex-direction: column;
25
+ flex-wrap: wrap;
26
+ align-content: center;
27
+ justify-content: center;
28
+ align-items: center;
29
+ }
30
+
31
+ form {
32
+ background-color: #1f1f1f;
33
+ border-radius: 8px;
34
+ padding: 20px;
35
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
36
+ }
37
+
38
+ div.codec {
39
+ display: none;
40
+ }
41
+
42
+ label {
43
+ font-size: 1.1em;
44
+ margin-bottom: 8px;
45
+ }
46
+
47
+ label[for="youtube_url"] {
48
+ display: none
49
+ }
50
+
51
+ input[type="text"] {
52
+ width: 40%;
53
+ padding: 10px;
54
+ margin-bottom: 20px;
55
+ border: 1px solid #3c3c3c;
56
+ border-radius: 4px;
57
+ background-color: #2c2c2c;
58
+ color: #e0e0e0;
59
+ }
60
+
61
+ input[type="radio"] {
62
+ margin-right: 8px;
63
+ }
64
+
65
+ input[type="radio"] + label {
66
+ margin-right: 20px;
67
+ font-size: 1em;
68
+ }
69
+
70
+ button {
71
+ margin-top: 20px;
72
+ width: 40%;
73
+ background-color: #86e598;
74
+ color: #121212;
75
+ border: none;
76
+ padding: 10px 20px;
77
+ border-radius: 4px;
78
+ font-size: 1.1em;
79
+ cursor: pointer;
80
+ transition: background-color 0.3s;
81
+ }
82
+
83
+ button:hover {
84
+ background-color: #58cc4d;
85
+ }
86
+
87
+ #status {
88
+ margin-top: 20px;
89
+ font-size: 1.1em;
90
+ }
91
+
92
+ a {
93
+ color: #03da84;
94
+ text-decoration: none;
95
+ transition: color 0.3s;
96
+ }
97
+
98
+ a:hover {
99
+ color: #0ef758;
100
+ }
101
+
102
+ audio {
103
+ margin-top: 10px;
104
+ width: 100%;
105
+ }
106
+ </style>
107
+ <script>
108
+ function pollTaskStatus(task_id) {
109
+ fetch(`/download_status/${task_id}`)
110
+ .then(response => response.json())
111
+ .then(data => {
112
+ document.getElementById('status').innerText = data.status;
113
+ if (data.status === 'готово') {
114
+ data.files.forEach(file => {
115
+ const p = document.createElement('p');
116
+ const a = document.createElement('a');
117
+ a.href = `/play/${file}`;
118
+ a.innerText = file;
119
+ p.innerText = 'File: ';
120
+ p.appendChild(a);
121
+ document.body.appendChild(p);
122
+ const audio = document.createElement('audio');
123
+ audio.controls = true;
124
+ const source = document.createElement('source');
125
+ source.src = `/play/${file}`;
126
+ source.type = 'audio/mpeg';
127
+ audio.appendChild(source);
128
+ document.body.appendChild(audio);
129
+ });
130
+ } else {
131
+ setTimeout(() => pollTaskStatus(task_id), 1000);
132
+ }
133
+ });
134
+ }
135
+
136
+ function startDownload() {
137
+ const form = document.getElementById('downloadForm');
138
+ const formData = new FormData(form);
139
+ fetch('/download', {
140
+ method: 'POST',
141
+ body: formData
142
+ })
143
+ .then(response => response.json())
144
+ .then(data => {
145
+ document.getElementById('status').innerText = 'Starting download...';
146
+ pollTaskStatus(data.task_id);
147
+ });
148
+ }
149
+ </script>
150
+ </head>
151
+ <body>
152
+ <h1>получить минимальную звуковую дорожку с YouTube</h1>
153
+ <form id="downloadForm">
154
+ <label for="youtube_url">ссылка на видео:</label>
155
+ <input type="text" id="youtube_url" name="youtube_url" placeholder="ссылка на видео" required>
156
+ <br>
157
+ <div id="status"></div>
158
+ <div class="codec">
159
+ <label for="codec">кодек:</label>
160
+ <input type="radio" id="opus" name="codec" value="opus" checked>
161
+ <label for="opus">Opus</label>
162
+ <input type="radio" id="usac" name="codec" value="usac">
163
+ <label for="usac">USAC</label>
164
+ <input type="radio" id="both" name="codec" value="both">
165
+ <label for="both">Both</label>
166
+ </div>
167
+ <br>
168
+ <button type="button" onclick="startDownload()">Download</button>
169
+ </form>
170
+ </body>
171
+ </html>