Spaces:
No application file
No application file
File size: 5,934 Bytes
6755a2d |
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
import json
from typing import Tuple
import logging
from librosa.core.audio import get_duration
from moviepy.editor import CompositeVideoClip, concatenate_videoclips, TextClip
import moviepy as mvp
ignored_log = logging.getLogger("PIL")
ignored_log.setLevel(level=logging.INFO)
import logging
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
def generate_lyric_video_from_music_map(
music_map: dict,
size=None,
duration: float = None,
fontsize: float = 50,
padding: int = 0,
gap_th: float = 2,
font: str = "STXinwei",
):
"""从音乐谱面生成歌词 videoclip
Args:
music_map (dict): 音乐谱面,meta_info中必须含有歌词clip信息
size (_type_, optional): _description_. Defaults to None.
duration (float, optional): 歌词总时长. Defaults to None.
fontsize (float, optional): 歌词字体大小. Defaults to 50.
padding (int, optional): _description_. Defaults to 0.
gap_th (float, optional): 补全歌词clip中的间隙部分. Defaults to 2.
font (str, optional): 字体. Defaults to "STXinwei",需要安装.
Returns:
moviepy.VideoClip: 生成的歌词视频
"""
if isinstance(music_map, str):
music_map = MusicInfo(music_map)
lyric_clipseq = complete_clipseq(
clipseq=music_map.meta_info.lyric, duration=duration, gap_th=gap_th
)
videoclips = []
if music_map.meta_info.media_name is not None:
media_name = music_map.meta_info.media_name
else:
media_name = ""
if music_map.meta_info.singer is not None:
singer = music_map.meta_info.singer
else:
singer = ""
if music_map.meta_info.album is not None:
album = music_map.meta_info.album
else:
album = ""
title = "{} {} {}".format(album, media_name, singer)
# if size is not None
title_clip = TextClip(
title,
fontsize=int(fontsize * 1.1),
color="white",
font=font,
stroke_width=2,
)
title_clip = title_clip.set_duration(3)
for i, clip in enumerate(lyric_clipseq):
time_start = clip.time_start
duration = clip.duration
if clip.text is not None:
txt = clip.text
else:
txt = " "
logger.debug(
"render lyric, lyric={}, time_start={}, duration={}".format(
txt, time_start, duration
)
)
txt_clip = TextClip(
txt, fontsize=fontsize, color="white", font=font, stroke_width=2
)
txt_clip = txt_clip.set_duration(duration)
videoclips.append(txt_clip)
videoclips = concatenate_videoclips(videoclips, method="compose")
videoclips = CompositeVideoClip([videoclips, title_clip])
videoclips.audio = None
if duration is None:
duration = lyric_clipseq[-1].time_start + lyric_clipseq[-1].duration
# videoclips.set_duration(duration)
return videoclips
def generate_lyric_video_from_lyric(
path: str,
audio_path: str = None,
duration: float = None,
size: Tuple = None,
fontsize: int = None,
padding: int = 0,
font: str = "Courier",
):
"""从歌词文件中生成歌词视频
Args:
path (str): 歌词文件
audio_path (str, optional): 对应的音频文件,主要用于提取音频总时长. Defaults to None.
duration (float, optional): 歌曲总时长. Defaults to None.
size (Tuple, optional): _description_. Defaults to None.
fontsize (int, optional): 渲染的歌词字体大小. Defaults to None.
padding (int, optional): _description_. Defaults to 0.
Returns:
moviepy. VideoClip: 渲染好的歌词视频
"""
if audio_path is not None:
duration = get_duration(audio_path)
music_map = generate_lyric_map(path=path, duration=duration)
clip = generate_lyric_video_from_music_map(
music_map,
size=size,
duration=duration,
padding=padding,
fontsize=fontsize,
font=font,
)
return clip
def render_lyric2video(
videoclip,
lyric: dict,
lyric_info_type: str = "music_map",
fontsize: int = 25,
font: str = "Courier",
audio_path: str = None,
duration: float = None,
padding: int = 0,
):
"""对视频进行歌词渲染
Args:
videoclip (moviepy.VideoClip): 待渲染的视频
lyric (dict): 歌词信息,也可以是歌词路径
lyric_info_type (str, optional): 歌词类型,可以是 qrc, 也可以是谱面. Defaults to "music_map".
fontsize (int, optional): 渲染的歌词大小. Defaults to 25.
audio_path (str, optional): 音频路径,主要提供一些必要信息. Defaults to None.
duration (float, optional): 音频总时长. Defaults to None.
padding (int, optional): _description_. Defaults to 0.
Raises:
ValueError: _description_
Returns:
moviepy.VideoClip: 渲染好歌词的视频文件
"""
size = (videoclip.w, videoclip.h)
if fontsize is None:
fontsize = int(videoclip.w / 1280 * fontsize)
if lyric_info_type == "lyric":
lyric_clip = generate_lyric_video_from_lyric(
lyric,
size=size,
fontsize=fontsize,
font=font,
)
elif lyric_info_type == "music_map":
lyric_clip = generate_lyric_video_from_music_map(
lyric,
size=size,
fontsize=fontsize,
font=font,
)
else:
raise ValueError("not support {}".format(lyric_info_type))
lyric_clip = lyric_clip.set_position(("center", "bottom"))
lyric_video_clip = CompositeVideoClip([videoclip, lyric_clip], size=size)
lyric_video_clip.audio = videoclip.audio
logger.debug("lyric_clip: duration={}".format(lyric_clip.duration))
return lyric_video_clip
|