1lint commited on
Commit
517cc63
β€’
1 Parent(s): 1948c1d

init commit

Browse files
Files changed (7) hide show
  1. .gitignore +4 -0
  2. Dockerfile +13 -0
  3. README.md +7 -5
  4. app.py +126 -0
  5. header.md +4 -0
  6. requirements.txt +2 -0
  7. theme.py +91 -0
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ test*
2
+ test/
3
+ __pycache__/
4
+ videos/
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10.9
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+
7
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
8
+
9
+ RUN sudo apt install ffmpeg
10
+
11
+ COPY . .
12
+
13
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
  title: Acapellify
3
- emoji: 😻
4
- colorFrom: indigo
5
- colorTo: red
6
- sdk: docker
 
 
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Acapellify
3
+ emoji: πŸ“š
4
+ colorFrom: red
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 3.28.3
8
+ app_file: app.py
9
  pinned: false
10
  ---
11
 
12
+ ### Run with `python app.py`
app.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from gradio_client import Client
2
+ import yt_dlp # pip install yt-dlp
3
+
4
+ import subprocess
5
+
6
+ import os
7
+ from pathlib import Path
8
+ import gradio as gr
9
+ import shutil
10
+ import random
11
+ from theme import theme
12
+
13
+ VIDEO_DIRECTORY = "videos"
14
+
15
+
16
+ client = Client("abidlabs/music-separation")
17
+
18
+ def acapellify(audio_path):
19
+ result = client.predict(audio_path, api_name="/predict")
20
+ return result[0]
21
+
22
+ # based on https://github.com/gradio-app/gradio/blob/bebfb72b353a4280155cf7070441fc476ac10172/guides/06_client-libraries/fastapi-app-with-the-gradio-client.md
23
+ def process_video(video_path):
24
+ old_audio = os.path.basename(video_path).split(".")[0] + ".m4a"
25
+ subprocess.run(['ffmpeg', '-y', '-i', video_path, '-vn', '-acodec', 'copy', old_audio])
26
+
27
+ new_audio = acapellify(old_audio)
28
+
29
+ new_video = f"acap_{Path(video_path).name}"
30
+ new_video_path = f"{VIDEO_DIRECTORY}/{new_video}"
31
+ subprocess.call(['ffmpeg', '-y', '-i', video_path, '-i', new_audio, '-map', '0:v', '-map', '1:a', '-c:v', 'copy', '-c:a', 'aac', '-strict', 'experimental', new_video_path])
32
+
33
+ # remove old audio and video
34
+ os.remove(old_audio)
35
+ os.remove(video_path)
36
+
37
+ new_audio_path = f"{VIDEO_DIRECTORY}/{old_audio}"
38
+ shutil.move(new_audio, new_audio_path)
39
+
40
+ return new_video_path, new_audio_path
41
+
42
+ # filename default value will return name of video on youtube
43
+ def download_yt_url(url: str, filename: str = "%(title)s", format = 'mp4'):
44
+
45
+ output_path = f"{VIDEO_DIRECTORY}/{filename}.{format}"
46
+
47
+ # restrict video length so one user doesn't take up all our bandwidth
48
+ def video_filter(info):
49
+ MAX_DURATION = 5*60
50
+ duration = info.get('duration')
51
+ if duration and duration > MAX_DURATION:
52
+ return f'The video is too long at {duration}s, choose a video less than {MAX_DURATION}s'
53
+
54
+ ydl_opts = {
55
+ 'match_filter': video_filter,
56
+ 'format': f'{format}/bestaudio',
57
+ 'outtmpl': output_path,
58
+ }
59
+
60
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
61
+ error_code = ydl.download([url])
62
+ #info = ydl.extract_info(url, extra_info={"output_path": output_path})
63
+
64
+ if error_code:
65
+ raise Exception(f"Failed to download video, error code: {error_code}")
66
+
67
+ return output_path
68
+
69
+ def wrap_html(yt_url):
70
+ return f"""<h1><center>Original Video: <a href='{yt_url}'>{yt_url}</a></center><h1>"""
71
+
72
+ # ideally yt_url should be validated
73
+ def acapellify_url(yt_url, gr_request: gr.Request):
74
+
75
+ # example filename: https://www.youtube.com/watch?v=TasKo5HHWb4 -> TasKo5HHWb4
76
+ filename = yt_url.rsplit("=", 1)[-1]
77
+
78
+ video_path = download_yt_url(yt_url, filename)
79
+
80
+ new_video_path, new_audio_path = process_video(video_path)
81
+ return new_video_path, new_audio_path, wrap_html(yt_url)
82
+
83
+
84
+ def load_mystery_video(gr_request: gr.Request):
85
+
86
+ video_paths = list(Path(VIDEO_DIRECTORY).glob("*.mp4"))
87
+
88
+ selected_video = video_paths[random.randrange(len(video_paths))]
89
+
90
+ file_name = selected_video.name.replace("acap_", "").split(".", 1)[0]
91
+ selected_audio = Path(VIDEO_DIRECTORY) / f"{file_name}.m4a"
92
+ yt_url = f"https://www.youtube.com/watch?v={file_name}"
93
+
94
+ selected_video = str(selected_video)
95
+ selected_audio = str(selected_audio)
96
+
97
+ return selected_video, selected_audio, wrap_html(yt_url)
98
+
99
+
100
+ with open("header.md", "r") as markdown_file:
101
+ markdown_text = markdown_file.read()
102
+
103
+ with gr.Blocks(theme=theme) as demo:
104
+
105
+ with gr.Row():
106
+ header = gr.Markdown(markdown_text)
107
+ with gr.Row().style():
108
+
109
+ with gr.Column(scale=0.4, variant="panel"):
110
+ input_url = gr.Textbox(label="Youtube URL")
111
+
112
+ process_video_btn = gr.Button("Acapellify", variant="primary")
113
+ mystery_btn = gr.Button("Mysterious Video")
114
+
115
+ static_path_display = gr.HTML(label="Output File Paths", visible=True)
116
+
117
+ with gr.Column(scale=0.6, variant="panel"):
118
+ output_video = gr.Video(label="Acapellified Video")
119
+ output_audio = gr.Audio(label="Acapellified Audio")
120
+
121
+ process_video_btn.click(fn=acapellify_url, inputs=[input_url], outputs=[output_video, output_audio, static_path_display])
122
+ mystery_btn.click(fn=load_mystery_video, inputs=[], outputs=[output_video, output_audio, static_path_display])
123
+
124
+ if __name__ == "__main__":
125
+ os.makedirs(VIDEO_DIRECTORY, exist_ok=True)
126
+ demo.launch()
header.md ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # <p style="text-align: center;">Acapellify: Extract vocals from Youtube Video URL</p>
2
+ ### <p style="text-align: center;">Based on https://gradio.app/fastapi-app-with-the-gradio-client/</p>
3
+ ### <p style="text-align: center;">Backend uses gradio client linked to abidlabs/music-separation</p>
4
+ ### <p style="text-align: center;">Free to Use (courtesy of HF Spaces)</p>
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio==3.28.3
2
+ yt-dlp
theme.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ from gradio.themes.utils import colors, fonts, sizes
4
+
5
+ from gradio.themes import Soft, Base
6
+
7
+
8
+ yfu_red = colors.Color(
9
+ name="yfu_red",
10
+ c50="#ff2c65",
11
+ c100="#fee2e2",
12
+ c200="#fecaca",
13
+ c300="#fca5a5",
14
+ c400="#fca5a5",
15
+ c500="#ff2c65",
16
+ c600="#ff2c65",
17
+ c700="#ff2c65",
18
+ c800="#ff2c65",
19
+ c900="#ff2c65",
20
+ c950="#fee2e2",
21
+ )
22
+
23
+ white = colors.Color(
24
+ name="white",
25
+ c50="#ff2c65",
26
+ c100="#ff2c65", # text color
27
+ c200="#ff2c65",
28
+ c300="#ff2c65",
29
+ c400="#ff2c65",
30
+ c500="#ff2c65", # secondary text color
31
+ c600="#ffffff",
32
+ c700="#ffffff",
33
+ c800="#ffffff",
34
+ c900="#ffffff",
35
+ c950="#ffffff",
36
+ )
37
+
38
+ theme = gr.themes.Soft(
39
+ primary_hue=yfu_red,
40
+ secondary_hue=yfu_red,
41
+ neutral_hue=white,
42
+ ).set(
43
+ body_background_fill="white",
44
+ panel_background_fill="white",
45
+
46
+ background_fill_primary="white",
47
+ background_fill_primary_dark="white",
48
+
49
+ panel_background_fill_dark="white",
50
+ panel_border_color="*primary_50",
51
+ panel_border_color_dark="*primary_50",
52
+
53
+ body_text_color="*primary_50",
54
+ body_text_color_dark="*primary_50",
55
+ body_text_color_subdued="*primary_50",
56
+ body_text_color_subdued_dark="*primary_50",
57
+
58
+ table_even_background_fill="white",
59
+ table_even_background_fill_dark="white",
60
+ table_odd_background_fill="*primary_100",
61
+ table_odd_background_fill_dark="*primary_100",
62
+ table_row_focus="*primary_200",
63
+ table_row_focus_dark="*primary_200",
64
+
65
+ input_background_fill_focus="*primary_200",
66
+ input_background_fill_focus_dark="*primary_200",
67
+
68
+ checkbox_background_color_focus="*primary_200",
69
+ checkbox_background_color_focus_dark="*primary_200",
70
+
71
+ border_color_primary_dark="*primary_50",
72
+ checkbox_border_color_dark="*primary_50",
73
+ table_border_color_dark="*primary_50",
74
+ button_secondary_text_color="*primary_50",
75
+ block_info_text_color="*primary_50",
76
+ block_label_text_color="*primary_50",
77
+ block_title_text_color="*primary_50",
78
+ checkbox_label_text_color="*primary_50",
79
+ checkbox_label_text_color_selected="*primary_50",
80
+ button_cancel_text_color="white",
81
+ block_border_color="*primary_50",
82
+
83
+ #button_secondary_background_fill=None,
84
+ #button_secondary_background_fill_dark=None,
85
+ button_secondary_background_fill_hover="*primary_200",
86
+ button_secondary_background_fill_hover_dark="*primary_200",
87
+
88
+ stat_background_fill="*primary_200",
89
+ stat_background_fill_dark="*primary_200",
90
+
91
+ )