Support for upgraded moviepy

This commit is contained in:
2024-03-02 15:19:30 +01:00
parent 6b6b18507a
commit 455514e475
13 changed files with 87 additions and 68 deletions

View File

@@ -4,7 +4,7 @@ google_auth_oauthlib==1.2.0
Google_Images_Search==1.4.6 Google_Images_Search==1.4.6
gradio==4.19.0 gradio==4.19.0
httplib2==0.22.0 httplib2==0.22.0
moviepy==1.0.3 git+https://github.com/OsaAjani/moviepy.git
openai==1.13.3 openai==1.13.3
orjson==3.9.15 orjson==3.9.15
protobuf==4.25.3 protobuf==4.25.3
@@ -14,8 +14,7 @@ PyYAML==6.0.1
Requests==2.31.0 Requests==2.31.0
SQLAlchemy==2.0.27 SQLAlchemy==2.0.27
git+https://github.com/Paillat-dev/tiktok-uploader.git git+https://github.com/Paillat-dev/tiktok-uploader.git
torch==2.2.0
TTS==0.22.0 TTS==0.22.0
TTS==0.22.0 TTS==0.22.0
whisper_timestamped==1.14.4 whisper_timestamped==1.14.4
Pillow==9.5.0

View File

@@ -3,7 +3,7 @@ import time
from datetime import datetime from datetime import datetime
import gradio import gradio
import moviepy.editor as mp import moviepy as mp
from .. import engines from .. import engines
from ..models import Video, SessionLocal from ..models import Video, SessionLocal
@@ -171,10 +171,10 @@ class GenerationContext:
*self.index_8, *self.index_8,
*self.index_9, *self.index_9,
] ]
clip = mp.CompositeVideoClip(clips, size=(self.width, self.height))
audio = mp.CompositeAudioClip(self.audio) audio = mp.CompositeAudioClip(self.audio)
clip.set_duration(self.duration) clip = mp.CompositeVideoClip(clips, size=(self.width, self.height)).with_duration(self.duration).with_audio(
clip: mp.CompositeVideoClip = clip.set_audio(audio) audio
)
clip.write_videofile(self.get_file_path("final.mp4"), fps=60, threads=4, codec="h264_nvenc") clip.write_videofile(self.get_file_path("final.mp4"), fps=60, threads=4, codec="h264_nvenc")
self.progress(0.8, "Generating metadata...") self.progress(0.8, "Generating metadata...")
@@ -192,7 +192,5 @@ class GenerationContext:
self.store_in_db() self.store_in_db()
self.progress(1, "Done!") self.progress(1, "Done!")
if os.name == 'nt': command = "start" if os.name == 'nt' else "open"
os.system(f"start {os.path.abspath(self.dir)}") os.system(f"{command} {os.path.abspath(self.dir)}")
else:
os.system(f"open {self.dir}")

View File

@@ -2,11 +2,11 @@ import os
from typing import Literal, TypedDict, List from typing import Literal, TypedDict, List
import gradio as gr import gradio as gr
import moviepy.editor as mp import moviepy as mp
import moviepy.video.fx as vfx
import openai import openai
from openai import OpenAI from openai import OpenAI
import requests import requests
from moviepy.video.fx.resize import resize
from . import BaseAssetsEngine from . import BaseAssetsEngine
@@ -79,15 +79,11 @@ class DallEAssetsEngine(BaseAssetsEngine):
img = mp.ImageClip("temp.png") img = mp.ImageClip("temp.png")
os.remove("temp.png") os.remove("temp.png")
img: mp.ImageClip = img.set_duration(end - start) img: mp.ImageClip = img.with_duration(end - start)
img: mp.ImageClip = img.set_start(start) img: mp.ImageClip = img.with_start(start)
img: mp.ImageClip = resize(img, width=max_width) img: mp.ImageClip = img.with_effects([vfx.Resize(width=max_width)])
if self.aspect_ratio == "portrait": position = img.with_position(("center", "top"))
img: mp.ImageClip = img.set_position(("center", "top")) img: mp.ImageClip = img.with_position(position)
elif self.aspect_ratio == "landscape":
img: mp.ImageClip = img.set_position(("center", "top"))
elif self.aspect_ratio == "square":
img: mp.ImageClip = img.set_position(("center", "top"))
clips.append(img) clips.append(img)
return clips return clips

View File

@@ -3,10 +3,10 @@ import shutil
from typing import TypedDict from typing import TypedDict
import gradio as gr import gradio as gr
import moviepy.editor as mp import moviepy as mp
from google_images_search import GoogleImagesSearch import moviepy.video.fx as vfx
from moviepy.video.fx.resize import resize
from google_images_search import GoogleImagesSearch
from . import BaseAssetsEngine from . import BaseAssetsEngine
@@ -38,7 +38,7 @@ class GoogleAssetsEngine(BaseAssetsEngine):
super().__init__() super().__init__()
def generate(self, options: list[Spec]) -> list[mp.ImageClip]: def generate(self, options: list[Spec]) -> list[mp.ImageClip]:
max_width = self.ctx.width / 3 * 2 max_width = int(self.ctx.width / 3 * 2)
clips = [] clips = []
for option in options: for option in options:
query = option["query"] query = option["query"]
@@ -61,13 +61,11 @@ class GoogleAssetsEngine(BaseAssetsEngine):
# delete the temp folder # delete the temp folder
except Exception as e: except Exception as e:
print(e) print(e)
continue
finally: finally:
shutil.rmtree("temp") shutil.rmtree("temp")
img: mp.ImageClip = img.set_duration(end - start) img = img.with_duration(end - start).with_start(start).with_effects([vfx.Resize(width=max_width)]).with_position(("center", "top"))
img: mp.ImageClip = img.set_start(start)
img: mp.ImageClip = resize(img, width=max_width)
img: mp.ImageClip = img.set_position(("center", "top"))
clips.append(img) clips.append(img)
return clips return clips

View File

@@ -4,9 +4,8 @@ import shutil
import time import time
import gradio as gr import gradio as gr
import moviepy.editor as mp import moviepy as mp
from moviepy.audio.fx.audio_fadein import audio_fadein import moviepy.audio.fx as afx
from moviepy.audio.fx.audio_fadeout import audio_fadeout
from . import BaseAudioBackgroundEngine from . import BaseAudioBackgroundEngine
@@ -43,18 +42,18 @@ class MusicAudioBackgroundEngine(BaseAudioBackgroundEngine):
background = mp.AudioFileClip(f"{self.background_audio.path}") background = mp.AudioFileClip(f"{self.background_audio.path}")
self.ctx.credits += f"\n{self.background_audio.data['credits']}" self.ctx.credits += f"\n{self.background_audio.data['credits']}"
# we add fade in and fade out to the audio # we add fade in and fade out to the audio
background: mp.AudioFileClip = background.fx(audio_fadein, 1).fx(audio_fadeout, 1) background = background.with_effects([afx.AudioFadeIn(1), afx.AudioFadeOut(1)])
# loop the audio to match the duration of the video # loop the audio to match the duration of the video
audio_clips = [] audio_clips = []
while sum([clip.duration for clip in audio_clips]) < self.ctx.duration: while sum([clip.duration for clip in audio_clips]) < self.ctx.duration:
audio_clips.append(background) audio_clips.append(background)
# now we cut the audio to match the duration of the video exactly # now we cut the audio to match the duration of the video exactly
audio: mp.AudioFileClip = mp.concatenate_audioclips(audio_clips) audio = mp.concatenate_audioclips(audio_clips)
audio: mp.AudioFileClip = audio.subclip(0, self.ctx.duration) audio = audio.with_subclip(0, self.ctx.duration)
# finally we add a new fade OUT only to the audio # finally we add a new fade OUT only to the audio
audio: mp.AudioFileClip = audio.fx(audio_fadeout, 1) audio = audio.with_effects([afx.AudioFadeOut(1)])
# change volume to 0.5 # change volume to 0.5
audio: mp.AudioFileClip = audio.volumex(0.5) audio: mp.AudioFileClip = audio.with_multiply_volume(0.5)
self.ctx.audio.append(audio) self.ctx.audio.append(audio)
@classmethod @classmethod

View File

@@ -4,9 +4,8 @@ import shutil
import time import time
import gradio as gr import gradio as gr
import moviepy.editor as mp import moviepy as mp
from moviepy.video.fx.crop import crop import moviepy.video.fx as vfx
from moviepy.video.fx.resize import resize
from . import BaseBackgroundEngine from . import BaseBackgroundEngine
@@ -47,14 +46,16 @@ class VideoBackgroundEngine(BaseBackgroundEngine):
"The background video is shorter than the video to be generated." "The background video is shorter than the video to be generated."
) )
start = random.uniform(0, background_max_start) start = random.uniform(0, background_max_start)
clip = background.subclip(start, start + self.ctx.duration) clip = background.with_subclip(start, start + self.ctx.duration)
self.ctx.credits += f"\n{self.background_video.data['credits']}" self.ctx.credits += f"\n{self.background_video.data['credits']}"
clip = resize(clip, width=self.ctx.width, height=self.ctx.height)
w, h = clip.size w, h = clip.size
if w > h: resolution: float = w / h
clip = crop(clip, width=self.ctx.width, height=self.ctx.height, x_center=w / 2, y_center=h / 2) canvas_resolution: float = self.ctx.width / self.ctx.height
if resolution > canvas_resolution:
clip = clip.with_effects([vfx.Resize(height=self.ctx.height)])
else: else:
clip = crop(clip, width=self.ctx.width, height=self.ctx.height, x_center=w / 2, y_center=h / 2) clip = clip.with_effects([vfx.Resize(width=self.ctx.width)])
clip = clip.with_position(("center", "center"))
self.ctx.index_0.append(clip) self.ctx.index_0.append(clip)
@classmethod @classmethod

View File

@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import moviepy.editor as mp import moviepy as mp
from sqlalchemy.future import select from sqlalchemy.future import select
from ..chore import GenerationContext from ..chore import GenerationContext

View File

@@ -1,10 +1,37 @@
import gradio as gr import gradio as gr
import os import os
import shutil import platform
from moviepy.editor import TextClip import moviepy as mp
from . import BaseCaptioningEngine from . import BaseCaptioningEngine
def get_available_fonts():
#on windows, the fonts are in the C:\Windows\Fonts and C:\Users\Username\AppData\Local\Microsoft\Windows\Fonts
#on linux, the fonts are in the /usr/share/fonts directory
#on mac, the fonts are in the /Library/Fonts, /System/Library/Fonts, and ~/Library/Fonts directories
if platform.system() == "Windows":
font_dirs = [
"C:\\Windows\\Fonts",
"C:\\Users\\{}\\AppData\\Local\\Microsoft\\Windows\\Fonts".format(os.getlogin()),
]
elif platform.system() == "Linux":
font_dirs = ["/usr/share/fonts"]
elif platform.system() == "Darwin":
font_dirs = [
"/Library/Fonts",
"/System/Library/Fonts",
"{}/Library/Fonts".format(os.path.expanduser("~")),
]
else:
font_dirs = []
fonts = []
for font_dir in font_dirs:
for root, dirs, files in os.walk(font_dir):
for file in files:
if file.endswith(".ttf") or file.endswith(".otf"):
fonts.append(file)
return list(set(fonts))
class SimpleCaptioningEngine(BaseCaptioningEngine): class SimpleCaptioningEngine(BaseCaptioningEngine):
name = "SimpleCaptioningEngine" name = "SimpleCaptioningEngine"
@@ -20,21 +47,22 @@ class SimpleCaptioningEngine(BaseCaptioningEngine):
super().__init__() super().__init__()
def build_caption_object(self, text: str, start: float, end: float) -> TextClip: def build_caption_object(self, text: str, start: float, end: float) -> mp.TextClip:
return ( return (
TextClip( mp.TextClip(
text, text=text,
fontsize=self.font_size, font_size=self.font_size,
color=self.font_color, color=self.font_color,
font=self.font, font=self.font,
stroke_color=self.stroke_color, stroke_color=self.stroke_color,
stroke_width=self.stroke_width, stroke_width=self.stroke_width,
method="caption", method="caption",
size=(self.ctx.width / 3 * 2, None), size=(int(self.ctx.width / 3 * 2), None),
text_align="center",
) )
.set_position(("center", 0.65), relative=True) .with_position(("center", 0.65), relative=True)
.set_start(start) .with_start(start)
.set_duration(end - start) .with_duration(end - start)
) )
def ends_with_punctuation(self, text: str) -> bool: def ends_with_punctuation(self, text: str) -> bool:
@@ -95,7 +123,7 @@ class SimpleCaptioningEngine(BaseCaptioningEngine):
with gr.Group(): with gr.Group():
font = gr.Dropdown( font = gr.Dropdown(
label="Font", label="Font",
choices=TextClip.list("font"), choices=get_available_fonts(),
value="Comic-Sans-MS-Bold", value="Comic-Sans-MS-Bold",
allow_custom_value=True, # Allow custom font allow_custom_value=True, # Allow custom font
) )

View File

@@ -19,8 +19,8 @@ class CustomScriptEngine(BaseScriptEngine):
def get_options(cls) -> list: def get_options(cls) -> list:
return [ return [
gr.Textbox( gr.Textbox(
label="Prompt", label="Script",
placeholder="Enter your prompt here", placeholder="Enter your script here",
value="", value="",
) )
] ]

View File

@@ -7,7 +7,7 @@ from ...utils.prompting import get_prompt
class ScientificFactsScriptEngine(BaseScriptEngine): class ScientificFactsScriptEngine(BaseScriptEngine):
name = "Scientific facts Thoughts" name = "Scientific facts"
description = "Generate a scientific facts script." description = "Generate a scientific facts script."
num_options = 1 num_options = 1

View File

@@ -1,7 +1,7 @@
from abc import abstractmethod from abc import abstractmethod
from typing import TypedDict from typing import TypedDict
import moviepy.editor as mp import moviepy as mp
import whisper_timestamped as wt import whisper_timestamped as wt
from torch.cuda import is_available from torch.cuda import is_available

View File

@@ -89,7 +89,7 @@ class CoquiTTSEngine(BaseTTSEngine):
"ko", # Korean "ko", # Korean
"hi", # Hindi "hi", # Hindi
] ]
num_options = 4 num_options = 5
def __init__(self, options: list): def __init__(self, options: list):
super().__init__() super().__init__()
@@ -99,7 +99,7 @@ class CoquiTTSEngine(BaseTTSEngine):
self.to_force_duration = options[2] self.to_force_duration = options[2]
self.duration = options[3] self.duration = options[3]
os.environ["COQUI_TOS_AGREED"] = options[4] os.environ["COQUI_TOS_AGREED"] = str(options[4])
try: try:
self.tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2") self.tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2")
except: except:

View File

@@ -25,7 +25,9 @@ class TikTokUploadEngine(BaseUploadEngine):
description: str = self.ctx.description description: str = self.ctx.description
hashtags = self.hashtags.strip().split(" ") hashtags = self.hashtags.strip().split(" ")
# extract any hashtags from the description / title and remove them from the description title = title.strip()
description = description.strip()
#we get all the hastags from title and description and add them to the list of hashtags, while removing them from the title and description
for word in title.split(): for word in title.split():
if word.startswith("#"): if word.startswith("#"):
hashtags.append(word) hashtags.append(word)
@@ -35,12 +37,10 @@ class TikTokUploadEngine(BaseUploadEngine):
hashtags.append(word) hashtags.append(word)
description = description.replace(word, "") description = description.replace(word, "")
title = title.strip()
description = description.strip()
hashtags_str = " ".join(hashtags) + " " if hashtags else "" hashtags_str = " ".join(hashtags) + " " if hashtags else ""
failed = upload_video( failed = upload_video(
filename=self.ctx.get_file_path("final.mp4"), filename=self.ctx.get_file_path("final.mp4"),
description=f"{title}\n{description} {hashtags_str}", description=f"{title} {hashtags_str}\n{description}",
cookies_str=cookies, cookies_str=cookies,
browser="chrome", browser="chrome",
comment=True, stitch=False, duet=False comment=True, stitch=False, duet=False