hivecorp commited on
Commit
95d33e2
·
verified ·
1 Parent(s): 091d27c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -34
app.py CHANGED
@@ -3,17 +3,17 @@ import edge_tts
3
  import asyncio
4
  import tempfile
5
  import os
6
- from moviepy.editor import concatenate_videoclips, AudioFileClip, ImageClip
7
  from wand.image import Image
8
  from wand.drawing import Drawing
9
  from wand.color import Color
10
 
11
- # Function to get available voices
12
  async def get_voices():
13
  voices = await edge_tts.list_voices()
14
  return {f"{v['ShortName']} - {v['Locale']} ({v['Gender']})": v['ShortName'] for v in voices}
15
 
16
- # Text-to-Speech function
17
  async def text_to_speech(text, voice, rate, pitch):
18
  if not text.strip():
19
  return None, gr.Warning("Please enter the text to convert.")
@@ -29,64 +29,84 @@ async def text_to_speech(text, voice, rate, pitch):
29
  await communicate.save(tmp_path)
30
  return tmp_path, None
31
 
32
- # Text-to-Video function
33
  def text_to_video(text, voice, rate, pitch, video_width, video_height, bg_color, text_color, text_font, text_size):
34
- # Ensure the font file exists, else use a default font
35
- font_path = os.path.abspath(text_font) if os.path.exists(text_font) else "Arial"
36
 
37
- # Split text into lines
 
 
 
 
38
  words = text.split()
39
  lines = []
40
  current_line = ""
 
41
 
42
  for word in words:
43
- if len(current_line) + len(word) + 1 > (video_width // (text_size // 2)):
44
  lines.append(current_line)
45
  current_line = word
 
 
 
46
  else:
47
  current_line = f"{current_line} {word}".strip()
48
 
49
- if current_line:
50
- lines.append(current_line)
 
51
 
 
52
  audio_clips = []
53
  video_clips = []
54
-
55
- for line in lines:
56
- audio, warning = asyncio.run(text_to_speech(line, voice, rate, pitch))
 
57
  if warning:
58
  return None, warning
59
  audio_clip = AudioFileClip(audio)
60
  audio_clips.append(audio_clip)
61
 
62
- # Create an image for each line of text
63
- with Drawing() as draw:
64
- draw.font = font_path
65
- draw.font_size = text_size
66
- draw.fill_color = Color(text_color)
67
- with Image(width=video_width, height=video_height, background=Color(bg_color)) as img:
68
- # Draw the text in the center
69
- text_width = draw.get_text_dimensions(line)[0]
70
- y_position = (video_height - text_size) // 2 # Center vertically
71
- draw.text((video_width - text_width) // 2, y_position, line) # Center horizontally
72
- draw(img)
73
- img_path = os.path.join(tempfile.gettempdir(), f"{line}.png")
74
- img.save(filename=img_path)
75
- text_clip = ImageClip(img_path).set_duration(audio_clip.duration).set_audio(audio_clip)
76
- video_clips.append(text_clip)
 
 
 
77
 
78
- # Combine all video clips
 
 
 
 
 
 
79
  final_video = concatenate_videoclips(video_clips)
80
  final_video_path = os.path.join(tempfile.gettempdir(), "output_video.mp4")
81
  final_video.write_videofile(final_video_path, fps=24, codec="libx264")
82
  return final_video_path, None
83
 
84
- # Gradio interface function
85
  def tts_interface(text, voice, rate, pitch, video_width, video_height, bg_color, text_color, text_font, text_size):
86
  video, warning = text_to_video(text, voice, rate, pitch, video_width, video_height, bg_color, text_color, text_font, text_size)
87
  return None, video, warning
88
 
89
- # Create Gradio app
90
  async def create_demo():
91
  voices = await get_voices()
92
 
@@ -101,7 +121,7 @@ async def create_demo():
101
  gr.Slider(minimum=480, maximum=1080, value=720, label="Video Height", step=10),
102
  gr.ColorPicker(value="#000000", label="Background Color"),
103
  gr.ColorPicker(value="#FFFFFF", label="Text Color"),
104
- gr.Textbox(label="Text Font", value="Arial"), # Default to Arial for testing
105
  gr.Slider(minimum=10, maximum=100, value=24, label="Text Size", step=1)
106
  ],
107
  outputs=[
@@ -117,7 +137,7 @@ async def create_demo():
117
 
118
  return demo
119
 
120
- # Run the app
121
  if __name__ == "__main__":
122
  demo = asyncio.run(create_demo())
123
- demo.launch(share=True)
 
3
  import asyncio
4
  import tempfile
5
  import os
6
+ from moviepy.editor import TextClip, concatenate_videoclips, CompositeVideoClip, AudioFileClip, ImageClip
7
  from wand.image import Image
8
  from wand.drawing import Drawing
9
  from wand.color import Color
10
 
11
+ # 获取所有可用的语音
12
  async def get_voices():
13
  voices = await edge_tts.list_voices()
14
  return {f"{v['ShortName']} - {v['Locale']} ({v['Gender']})": v['ShortName'] for v in voices}
15
 
16
+ # 文字转语音功能
17
  async def text_to_speech(text, voice, rate, pitch):
18
  if not text.strip():
19
  return None, gr.Warning("Please enter the text to convert.")
 
29
  await communicate.save(tmp_path)
30
  return tmp_path, None
31
 
32
+ # 文字转视频功能
33
  def text_to_video(text, voice, rate, pitch, video_width, video_height, bg_color, text_color, text_font, text_size):
34
+ # 字体文件路径
35
+ font_path = os.path.abspath(text_font)
36
 
37
+ # 计算每页可以容纳的行数和每行可以容纳的字符数
38
+ max_chars_per_line = video_width // (text_size // 2) # 字体宽度假设为字体大小的一半
39
+ max_lines_per_page = video_height // (text_size + 15) # 10是行间距
40
+
41
+ # 按页拆分文本
42
  words = text.split()
43
  lines = []
44
  current_line = ""
45
+ pages = []
46
 
47
  for word in words:
48
+ if len(current_line) + len(word) + 1 > max_chars_per_line:
49
  lines.append(current_line)
50
  current_line = word
51
+ if len(lines) == max_lines_per_page:
52
+ pages.append("\n".join(lines))
53
+ lines = []
54
  else:
55
  current_line = f"{current_line} {word}".strip()
56
 
57
+ lines.append(current_line)
58
+ if lines:
59
+ pages.append("\n".join(lines))
60
 
61
+ # 为每页生成独立音频
62
  audio_clips = []
63
  video_clips = []
64
+ for i, page in enumerate(pages):
65
+ # 将每页的文本连贯朗读生成一个音频文件
66
+ audio_text = page.replace("\n", " ") # 移除换行符以防止 TTS 停顿
67
+ audio, warning = asyncio.run(text_to_speech(audio_text, voice, rate, pitch))
68
  if warning:
69
  return None, warning
70
  audio_clip = AudioFileClip(audio)
71
  audio_clips.append(audio_clip)
72
 
73
+ # 使用 wand 生成视频片段
74
+ with Image(width=video_width, height=video_height, background=Color(bg_color)) as img:
75
+ with Drawing() as draw:
76
+ draw.font = font_path
77
+ draw.font_size = text_size
78
+ draw.fill_color = Color(text_color)
79
+ draw.text_alignment = 'center'
80
+ draw.text_interline_spacing = 10
81
+
82
+ # Calculate the y position for each line of text
83
+ lines = page.split("\n")
84
+ y_position = (video_height - (len(lines) * (text_size + 10))) / 2 # Center vertically
85
+
86
+ for line in lines:
87
+ draw.text(int(video_width / 2), y_position, line)
88
+ y_position += text_size + 10 # Move down for the next line
89
+
90
+ draw(img) # Draw the text on the image
91
 
92
+ img.format = 'png'
93
+ img_path = os.path.join(tempfile.gettempdir(), f"page_{i}.png")
94
+ img.save(filename=img_path)
95
+ text_clip = ImageClip(img_path).set_duration(audio_clip.duration).set_audio(audio_clip)
96
+ video_clips.append(text_clip)
97
+
98
+ # 合并所有视频片段
99
  final_video = concatenate_videoclips(video_clips)
100
  final_video_path = os.path.join(tempfile.gettempdir(), "output_video.mp4")
101
  final_video.write_videofile(final_video_path, fps=24, codec="libx264")
102
  return final_video_path, None
103
 
104
+ # Gradio接口函数
105
  def tts_interface(text, voice, rate, pitch, video_width, video_height, bg_color, text_color, text_font, text_size):
106
  video, warning = text_to_video(text, voice, rate, pitch, video_width, video_height, bg_color, text_color, text_font, text_size)
107
  return None, video, warning
108
 
109
+ # 创建Gradio应用
110
  async def create_demo():
111
  voices = await get_voices()
112
 
 
121
  gr.Slider(minimum=480, maximum=1080, value=720, label="Video Height", step=10),
122
  gr.ColorPicker(value="#000000", label="Background Color"),
123
  gr.ColorPicker(value="#FFFFFF", label="Text Color"),
124
+ gr.Textbox(label="Text Font", value="msyh.ttf"), # 请确保字体文件路径正确
125
  gr.Slider(minimum=10, maximum=100, value=24, label="Text Size", step=1)
126
  ],
127
  outputs=[
 
137
 
138
  return demo
139
 
140
+ # 运行应用
141
  if __name__ == "__main__":
142
  demo = asyncio.run(create_demo())
143
+ demo.launch()