mirror of
https://github.com/Paillat-dev/viralfactory.git
synced 2026-01-02 01:06:19 +00:00
Support for upgraded moviepy
This commit is contained in:
@@ -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
|
|
||||||
|
|
||||||
|
|||||||
@@ -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}")
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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="",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user