File size: 5,146 Bytes
517cc63
8075337
517cc63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8075337
517cc63
 
 
 
8075337
517cc63
 
 
8075337
 
 
 
517cc63
8075337
99c06c9
8075337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517cc63
 
 
 
 
 
 
 
 
 
 
8075337
 
 
517cc63
 
 
8075337
 
517cc63
8075337
 
 
517cc63
 
8075337
 
 
517cc63
 
 
 
8075337
517cc63
 
8075337
 
517cc63
 
aff7142
 
 
 
 
 
 
8075337
aff7142
 
517cc63
 
 
 
aff7142
517cc63
 
 
 
aff7142
 
 
517cc63
 
8075337
517cc63
8075337
 
 
 
517cc63
8075337
 
 
aff7142
517cc63
 
 
 
aff7142
517cc63
 
 
 
 
8075337
517cc63
 
aff7142
517cc63
 
8075337
517cc63
 
 
 
 
 
 
 
 
8075337
 
 
 
 
 
 
 
 
 
517cc63
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
from gradio_client import Client
import yt_dlp  # pip install yt-dlp

import subprocess

import os
from pathlib import Path
import gradio as gr
import shutil
import random
from theme import theme

VIDEO_DIRECTORY = "videos"


client = Client("abidlabs/music-separation")


def acapellify(audio_path):
    result = client.predict(audio_path, api_name="/predict")
    return result[0]


# based on https://github.com/gradio-app/gradio/blob/bebfb72b353a4280155cf7070441fc476ac10172/guides/06_client-libraries/fastapi-app-with-the-gradio-client.md
def process_video(video_path):
    old_audio = os.path.basename(video_path).split(".")[0] + ".m4a"
    subprocess.run(
        ["ffmpeg", "-y", "-i", video_path, "-vn", "-acodec", "copy", old_audio]
    )

    new_audio = acapellify(old_audio)

    new_video_path = f"{VIDEO_DIRECTORY}/{Path(video_path).name}"
    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,
        ]
    )

    # remove old audio and video
    os.remove(old_audio)
    os.remove(video_path)

    new_audio_path = f"{VIDEO_DIRECTORY}/{old_audio}"
    shutil.move(new_audio, new_audio_path)

    return new_video_path, new_audio_path


# filename default value will return name of video on youtube
def download_yt_url(url: str, filename: str = "%(title)s", format="mp4"):
    output_path = f"{filename}.{format}"

    # restrict video length so one user doesn't take up all our bandwidth
    def video_filter(info):
        MAX_DURATION = 10 * 60
        duration = info.get("duration")
        if duration and duration > MAX_DURATION:
            raise gr.Error(
                f"The video is too long at {duration}s, choose a video less than {MAX_DURATION}s"
            )

    ydl_opts = {
        "match_filter": video_filter,
        "format": f"{format}/bestaudio",
        "outtmpl": output_path,
    }

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        error_code = ydl.download([url])
        # info = ydl.extract_info(url, extra_info={"output_path": output_path})

    if error_code:
        raise gr.Error(f"Failed to download video, error code: {error_code}")

    return output_path


def wrap_html(filename):
    yt_url = f"https://www.youtube.com/watch?v={filename}"

    return f"""<h1><center>Original Video</center></h1> 
        <center><a href='{yt_url}'>{yt_url}</a></center>
        <br>
        <center><iframe width="100%" height="315" src="https://www.youtube.com/embed/{filename}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe></center>
    """


# ideally yt_url should be validated
def acapellify_url(yt_url, gr_request: gr.Request):
    # example filename: https://www.youtube.com/watch?v=TasKo5HHWb4 -> TasKo5HHWb4
    filename = yt_url.split("=", 1)[-1].split("=", 1)[0]

    video_path = download_yt_url(yt_url, filename)

    new_video_path, new_audio_path = process_video(video_path)
    return new_video_path, new_audio_path, wrap_html(filename)


def load_mystery_video(gr_request: gr.Request):
    video_paths = list(Path(VIDEO_DIRECTORY).glob("*.mp4"))
    n_videos = len(video_paths)

    if not n_videos:
        raise gr.Error(
            "No videos archived yet. Enter a Youtube URL to add the first video!"
        )

    selected_video = video_paths[random.randrange(n_videos)]

    filename = selected_video.name.split(".", 1)[0]
    selected_audio = Path(VIDEO_DIRECTORY) / f"{filename}.m4a"

    selected_video = str(selected_video)
    selected_audio = str(selected_audio)

    return selected_video, selected_audio, wrap_html(filename)


with open("header.md", "r") as markdown_file:
    markdown_text = markdown_file.read()

with gr.Blocks(theme=theme, css="footer {visibility: hidden}") as demo:
    with gr.Row():
        header = gr.Markdown(markdown_text)
    with gr.Row().style(equal_height=True):
        with gr.Column(scale=0.4, variant="panel"):
            input_url = gr.Textbox(label="Youtube URL")

            process_video_btn = gr.Button("Acapellify", variant="primary")
            mystery_btn = gr.Button("Mysterious Video")

            static_path_display = gr.HTML(label="Output File Paths", visible=True)

        with gr.Column(scale=0.6, variant="panel"):
            output_video = gr.Video(label="Acapellified Video")
            output_audio = gr.Audio(label="Acapellified Audio")

    process_video_btn.click(
        fn=acapellify_url,
        inputs=[input_url],
        outputs=[output_video, output_audio, static_path_display],
    )
    mystery_btn.click(
        fn=load_mystery_video,
        inputs=[],
        outputs=[output_video, output_audio, static_path_display],
    )

if __name__ == "__main__":
    os.makedirs(VIDEO_DIRECTORY, exist_ok=True)
    demo.launch()