mirror of
https://github.com/Paillat-dev/viralfactory.git
synced 2026-01-02 01:06:19 +00:00
I have no time to review my commit
This commit is contained in:
82
.gitignore
vendored
82
.gitignore
vendored
@@ -145,11 +145,83 @@ dmypy.json
|
|||||||
cython_debug/
|
cython_debug/
|
||||||
|
|
||||||
# PyCharm
|
# PyCharm
|
||||||
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# User-specific stuff
|
||||||
#.idea/
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
output/
|
output/
|
||||||
local/
|
local/
|
||||||
|
|||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="db" uuid="18885715-8b98-4b45-8dd2-b39a20d145e1">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:C:\Users\jerem\D\09._AI_projects\viralautomator\local\database\db.db</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.10 (viralautomator)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (viralautomator)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/viralautomator.iml" filepath="$PROJECT_DIR$/.idea/viralautomator.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
16
.idea/viralautomator.iml
generated
Normal file
16
.idea/viralautomator.iml
generated
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/local/database" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/output" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.10 (viralautomator)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -2,6 +2,7 @@ import os
|
|||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import gradio
|
||||||
import moviepy.editor as mp
|
import moviepy.editor as mp
|
||||||
|
|
||||||
from .. import engines
|
from .. import engines
|
||||||
@@ -35,8 +36,16 @@ class GenerationContext:
|
|||||||
backgroundengine,
|
backgroundengine,
|
||||||
metadataengine,
|
metadataengine,
|
||||||
uploadengine,
|
uploadengine,
|
||||||
|
audiobackgroundengine,
|
||||||
progress,
|
progress,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
self.captions = []
|
||||||
|
self.dir = None
|
||||||
|
self.script = None
|
||||||
|
self.description = None
|
||||||
|
self.title = None
|
||||||
|
self.credits = None
|
||||||
|
self.duration = None
|
||||||
self.progress = progress
|
self.progress = progress
|
||||||
|
|
||||||
self.powerfulllmengine: engines.LLMEngine.BaseLLMEngine = powerfulllmengine[0]
|
self.powerfulllmengine: engines.LLMEngine.BaseLLMEngine = powerfulllmengine[0]
|
||||||
@@ -79,17 +88,13 @@ class GenerationContext:
|
|||||||
for eng in self.uploadengine:
|
for eng in self.uploadengine:
|
||||||
eng.ctx = self
|
eng.ctx = self
|
||||||
|
|
||||||
def setup_dir(self):
|
self.audiobackgroundengine: engines.AudioBackgroundEngine.BaseAudioBackgroundEngine = (
|
||||||
self.dir = f"output/{time.time()}"
|
audiobackgroundengine[0]
|
||||||
os.makedirs(self.dir)
|
)
|
||||||
|
self.audiobackgroundengine.ctx = self
|
||||||
|
|
||||||
def get_file_path(self, name: str) -> str:
|
# Kinda like in css, we have a z-index of moviepy clips (any). Then the engines append some clips to this,
|
||||||
return os.path.join(self.dir, name)
|
# and we render it all with index 0 below, and index 9 at the top.
|
||||||
|
|
||||||
def process(self):
|
|
||||||
# ⚠️ IMPORTANT NOTE: All methods called here are expected to be defined as abstract methods in the base classes, if not there is an issue with the engine implementation.
|
|
||||||
|
|
||||||
# Kinda like in css, we have a z-index of moviepy clips (any). Then the engines append some clips to this, and we render it all with index 0 below, and index 9 at the top.
|
|
||||||
self.index_0 = []
|
self.index_0 = []
|
||||||
self.index_1 = []
|
self.index_1 = []
|
||||||
self.index_2 = []
|
self.index_2 = []
|
||||||
@@ -100,23 +105,45 @@ class GenerationContext:
|
|||||||
self.index_7 = []
|
self.index_7 = []
|
||||||
self.index_8 = []
|
self.index_8 = []
|
||||||
self.index_9 = []
|
self.index_9 = []
|
||||||
|
|
||||||
|
self.audio = []
|
||||||
|
|
||||||
self.credits = "Generated by AI"
|
self.credits = "Generated by AI"
|
||||||
|
|
||||||
|
def setup_dir(self):
|
||||||
|
self.dir = f"output/{time.time()}"
|
||||||
|
os.makedirs(self.dir)
|
||||||
|
|
||||||
|
def get_file_path(self, name: str) -> str:
|
||||||
|
return os.path.join(self.dir, name)
|
||||||
|
|
||||||
|
def process(self):
|
||||||
|
# ⚠️ IMPORTANT NOTE: All methods called here are expected to be defined as abstract methods in the base
|
||||||
|
# classes, if not there is an issue with the engine implementation.
|
||||||
|
|
||||||
self.progress(0.1, "Loading settings...")
|
self.progress(0.1, "Loading settings...")
|
||||||
self.setup_dir()
|
self.setup_dir()
|
||||||
self.settingsengine.load()
|
if not isinstance(self.settingsengine, engines.NoneEngine):
|
||||||
|
self.settingsengine.load()
|
||||||
|
|
||||||
self.progress(0.2, "Generating script...")
|
self.progress(0.2, "Generating script...")
|
||||||
self.scriptengine.generate()
|
if not isinstance(self.powerfulllmengine, engines.NoneEngine):
|
||||||
|
self.scriptengine.generate()
|
||||||
|
|
||||||
self.progress(0.3, "Generating synthtetizing voice...")
|
self.progress(0.3, "Synthesizing voice...")
|
||||||
self.ttsengine.synthesize(self.script, self.get_file_path("tts.wav"))
|
if not isinstance(self.ttsengine, engines.NoneEngine):
|
||||||
self.duration: float # for type hinting
|
self.ttsengine.synthesize(self.script, self.get_file_path("tts.wav"))
|
||||||
|
self.duration: float # for type hinting
|
||||||
|
self.audio.append(mp.AudioFileClip(self.get_file_path("tts.wav")))
|
||||||
|
|
||||||
if not isinstance(self.backgroundengine, engines.NoneEngine):
|
if not isinstance(self.backgroundengine, engines.NoneEngine):
|
||||||
self.progress(0.4, "Generating background...")
|
self.progress(0.4, "Generating background...")
|
||||||
self.backgroundengine.get_background()
|
self.backgroundengine.get_background()
|
||||||
|
|
||||||
|
if not isinstance(self.audiobackgroundengine, engines.NoneEngine):
|
||||||
|
self.progress(0.45, "Generating audio background...")
|
||||||
|
self.audiobackgroundengine.get_background()
|
||||||
|
|
||||||
self.assetsengine = [
|
self.assetsengine = [
|
||||||
engine
|
engine
|
||||||
for engine in self.assetsengine
|
for engine in self.assetsengine
|
||||||
@@ -129,10 +156,6 @@ class GenerationContext:
|
|||||||
if not isinstance(self.captioningengine, engines.NoneEngine):
|
if not isinstance(self.captioningengine, engines.NoneEngine):
|
||||||
self.progress(0.6, "Generating captions...")
|
self.progress(0.6, "Generating captions...")
|
||||||
self.captioningengine.get_captions()
|
self.captioningengine.get_captions()
|
||||||
else:
|
|
||||||
self.captions = []
|
|
||||||
|
|
||||||
# add any other processing steps here
|
|
||||||
|
|
||||||
# we render to a file called final.mp4
|
# we render to a file called final.mp4
|
||||||
self.progress(0.7, "Rendering video...")
|
self.progress(0.7, "Rendering video...")
|
||||||
@@ -149,18 +172,22 @@ class GenerationContext:
|
|||||||
*self.index_9,
|
*self.index_9,
|
||||||
]
|
]
|
||||||
clip = mp.CompositeVideoClip(clips, size=(self.width, self.height))
|
clip = mp.CompositeVideoClip(clips, size=(self.width, self.height))
|
||||||
|
audio = mp.CompositeAudioClip(self.audio)
|
||||||
clip.set_duration(self.duration)
|
clip.set_duration(self.duration)
|
||||||
audio = mp.AudioFileClip(self.get_file_path("tts.wav"))
|
|
||||||
clip: mp.CompositeVideoClip = clip.set_audio(audio)
|
clip: mp.CompositeVideoClip = clip.set_audio(audio)
|
||||||
clip.write_videofile(self.get_file_path("final.mp4"), fps=60, threads=4)
|
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...")
|
||||||
self.metadataengine.get_metadata()
|
self.metadataengine.get_metadata()
|
||||||
|
|
||||||
|
self.description = self.description + "\n" + self.credits
|
||||||
self.progress(0.9, "Uploading video...")
|
self.progress(0.9, "Uploading video...")
|
||||||
for engine in self.uploadengine:
|
for engine in self.uploadengine:
|
||||||
engine.upload()
|
try:
|
||||||
|
engine.upload()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
gradio.Warning(f"{engine.name} failed to upload the video.")
|
||||||
self.progress(0.99, "Storing in database...")
|
self.progress(0.99, "Storing in database...")
|
||||||
self.store_in_db()
|
self.store_in_db()
|
||||||
self.progress(1, "Done!")
|
self.progress(1, "Done!")
|
||||||
|
|||||||
@@ -49,16 +49,18 @@ class GoogleAssetsEngine(BaseAssetsEngine):
|
|||||||
"num": 1,
|
"num": 1,
|
||||||
}
|
}
|
||||||
os.makedirs("temp", exist_ok=True)
|
os.makedirs("temp", exist_ok=True)
|
||||||
self.google.search(
|
try:
|
||||||
search_params=_search_params,
|
self.google.search(
|
||||||
path_to_dir="./temp/",
|
search_params=_search_params,
|
||||||
custom_image_name="temp",
|
path_to_dir="./temp/",
|
||||||
)
|
custom_image_name="temp",
|
||||||
# we find the file called temp. extension
|
)
|
||||||
filename = [f for f in os.listdir("./temp/") if f.startswith("temp.")][0]
|
# we find the file called temp. extension
|
||||||
img = mp.ImageClip(f"./temp/{filename}")
|
filename = [f for f in os.listdir("./temp/") if f.startswith("temp.")][0]
|
||||||
# delete the temp folder
|
img = mp.ImageClip(f"./temp/{filename}")
|
||||||
shutil.rmtree("temp")
|
# delete the temp folder
|
||||||
|
finally:
|
||||||
|
shutil.rmtree("temp")
|
||||||
|
|
||||||
img: mp.ImageClip = img.set_duration(end - start)
|
img: mp.ImageClip = img.set_duration(end - start)
|
||||||
img: mp.ImageClip = img.set_start(start)
|
img: mp.ImageClip = img.set_start(start)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from ..BaseEngine import BaseEngine
|
||||||
|
|
||||||
|
|
||||||
|
class BaseAudioBackgroundEngine(BaseEngine):
|
||||||
|
@abstractmethod
|
||||||
|
def get_background(self) -> None:
|
||||||
|
...
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
import shutil
|
||||||
|
import time
|
||||||
|
|
||||||
|
import gradio as gr
|
||||||
|
import moviepy.editor as mp
|
||||||
|
from moviepy.audio.fx.audio_fadein import audio_fadein
|
||||||
|
from moviepy.audio.fx.audio_fadeout import audio_fadeout
|
||||||
|
|
||||||
|
from . import BaseAudioBackgroundEngine
|
||||||
|
|
||||||
|
|
||||||
|
class MusicAudioBackgroundEngine(BaseAudioBackgroundEngine):
|
||||||
|
name = "Music Audio Background Engine"
|
||||||
|
description = "A basic background engine to set the background audio to a music track."
|
||||||
|
num_options = 1
|
||||||
|
|
||||||
|
def __init__(self, options: list[str]):
|
||||||
|
assets = self.get_assets(type="bcg_music")
|
||||||
|
self.background_audio = [asset for asset in assets if asset.data["name"] == options[0]][0]
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls) -> list:
|
||||||
|
assets = cls.get_assets(type="bcg_music")
|
||||||
|
choices = (
|
||||||
|
[asset.data["name"] for asset in assets]
|
||||||
|
if len(assets) > 0
|
||||||
|
else ["No audios available"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
gr.Dropdown(
|
||||||
|
choices=choices,
|
||||||
|
label="Background Music",
|
||||||
|
value=choices[0] if len(assets) > 0 else "No audios available",
|
||||||
|
type="value",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_background(self):
|
||||||
|
background = mp.AudioFileClip(f"{self.background_audio.path}")
|
||||||
|
self.ctx.credits += f"\n{self.background_audio.data['credits']}"
|
||||||
|
# we add fade in and fade out to the audio
|
||||||
|
background: mp.AudioFileClip = background.fx(audio_fadein, 1).fx(audio_fadeout, 1)
|
||||||
|
# loop the audio to match the duration of the video
|
||||||
|
audio_clips = []
|
||||||
|
while sum([clip.duration for clip in audio_clips]) < self.ctx.duration:
|
||||||
|
audio_clips.append(background)
|
||||||
|
# now we cut the audio to match the duration of the video exactly
|
||||||
|
audio: mp.AudioFileClip = mp.concatenate_audioclips(audio_clips)
|
||||||
|
audio: mp.AudioFileClip = audio.subclip(0, self.ctx.duration)
|
||||||
|
# finally we add a new fade OUT only to the audio
|
||||||
|
audio: mp.AudioFileClip = audio.fx(audio_fadeout, 1)
|
||||||
|
# change volume to 0.5
|
||||||
|
audio: mp.AudioFileClip = audio.volumex(0.5)
|
||||||
|
self.ctx.audio.append(audio)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_settings(cls):
|
||||||
|
def add_file(fp: str, name: str, credits: str):
|
||||||
|
if name == "":
|
||||||
|
raise ValueError("Name cannot be empty.")
|
||||||
|
new_fp = f"local/assets/audios/{time.time()}{os.path.splitext(fp)[1]}"
|
||||||
|
shutil.move(fp, new_fp)
|
||||||
|
cls.add_asset(
|
||||||
|
path=new_fp,
|
||||||
|
metadata={"name": name, "credits": credits},
|
||||||
|
type="bcg_music",
|
||||||
|
)
|
||||||
|
gr.Info("Video added successfully.")
|
||||||
|
|
||||||
|
with gr.Column() as add_asset_inputs:
|
||||||
|
add_asset_name = gr.Textbox(label="Name of the audio", value="")
|
||||||
|
add_asset_credits = gr.Textbox(label="Credits", value="")
|
||||||
|
add_asset_input = gr.File(
|
||||||
|
file_count="single",
|
||||||
|
file_types=["audio"],
|
||||||
|
type="filepath",
|
||||||
|
)
|
||||||
|
with gr.Column() as add_asset_button:
|
||||||
|
add_asset_button = gr.Button(value="Add Audio")
|
||||||
|
add_asset_button.click(
|
||||||
|
add_file,
|
||||||
|
inputs=[add_asset_input, add_asset_name, add_asset_credits],
|
||||||
|
outputs=[],
|
||||||
|
)
|
||||||
2
src/engines/AudioBackgroundEngine/__init__.py
Normal file
2
src/engines/AudioBackgroundEngine/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .BaseAudioBackgroundEngine import BaseAudioBackgroundEngine
|
||||||
|
from .MusicAudioBackgroundEngine import MusicAudioBackgroundEngine
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from ..BaseEngine import BaseEngine
|
from src.engines.BaseEngine import BaseEngine
|
||||||
|
|
||||||
|
|
||||||
class BaseBackgroundEngine(BaseEngine):
|
class BaseBackgroundEngine(BaseEngine):
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from ..chore import GenerationContext
|
|||||||
from ..models import SessionLocal, File, Setting
|
from ..models import SessionLocal, File, Setting
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyTypeChecker
|
|
||||||
class BaseEngine(ABC):
|
class BaseEngine(ABC):
|
||||||
num_options: int
|
num_options: int
|
||||||
name: str
|
name: str
|
||||||
@@ -15,7 +14,6 @@ class BaseEngine(ABC):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ctx: GenerationContext # This is for type hinting only
|
self.ctx: GenerationContext # This is for type hinting only
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import gradio as gr
|
import gradio as gr
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
from moviepy.editor import TextClip
|
from moviepy.editor import TextClip
|
||||||
|
|
||||||
from . import BaseCaptioningEngine
|
from . import BaseCaptioningEngine
|
||||||
@@ -80,6 +82,13 @@ class SimpleCaptioningEngine(BaseCaptioningEngine):
|
|||||||
|
|
||||||
self.ctx.index_7.extend(clips)
|
self.ctx.index_7.extend(clips)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_settings(cls):
|
||||||
|
gr.Markdown(
|
||||||
|
"To add a custom font, simply install the font on your system, restart the server, and input the exact "
|
||||||
|
"file name (without the path) in the dropdown."
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_options(cls) -> list:
|
def get_options(cls) -> list:
|
||||||
with gr.Column() as font_options:
|
with gr.Column() as font_options:
|
||||||
@@ -87,7 +96,8 @@ class SimpleCaptioningEngine(BaseCaptioningEngine):
|
|||||||
font = gr.Dropdown(
|
font = gr.Dropdown(
|
||||||
label="Font",
|
label="Font",
|
||||||
choices=TextClip.list("font"),
|
choices=TextClip.list("font"),
|
||||||
value="Comic-Sans-MS",
|
value="Comic-Sans-MS-Bold",
|
||||||
|
allow_custom_value=True, # Allow custom font
|
||||||
)
|
)
|
||||||
font_size = gr.Number(
|
font_size = gr.Number(
|
||||||
label="Font Size",
|
label="Font Size",
|
||||||
|
|||||||
@@ -2,13 +2,28 @@ from . import BaseEngine
|
|||||||
|
|
||||||
|
|
||||||
class NoneEngine(BaseEngine):
|
class NoneEngine(BaseEngine):
|
||||||
num_options = 0
|
"""
|
||||||
name = "None"
|
This class represents a NoneEngine which is a subclass of BaseEngine.
|
||||||
description = "No engine selected"
|
It is used when no engine is selected. It does not have any options.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
num_options = 0 # The number of options available for this engine.
|
||||||
|
name = "None" # The name of the engine.
|
||||||
|
description = "No engine selected" # A brief description of the engine.
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Constructor method for the NoneEngine class. It calls the constructor of the BaseEngine class.
|
||||||
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_options(cls):
|
def get_options(cls):
|
||||||
|
"""
|
||||||
|
This class method returns the options available for the NoneEngine.
|
||||||
|
Since NoneEngine does not have any options, it returns an empty list.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: An empty list as there are no options for NoneEngine.
|
||||||
|
"""
|
||||||
return []
|
return []
|
||||||
37
src/engines/ScriptEngine/ScientificFactsScriptEngine.py
Normal file
37
src/engines/ScriptEngine/ScientificFactsScriptEngine.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import gradio as gr
|
||||||
|
|
||||||
|
from .BaseScriptEngine import BaseScriptEngine
|
||||||
|
from ...utils.prompting import get_prompt
|
||||||
|
|
||||||
|
|
||||||
|
class ScientificFactsScriptEngine(BaseScriptEngine):
|
||||||
|
name = "Scientific facts Thoughts"
|
||||||
|
description = "Generate a scientific facts script."
|
||||||
|
num_options = 1
|
||||||
|
|
||||||
|
def __init__(self, options: list[list | tuple | str | int | float | bool | None]):
|
||||||
|
self.n_sentences = options[0]
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
sys_prompt, chat_prompt = get_prompt(
|
||||||
|
"scientific_facts",
|
||||||
|
location=os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), "prompts"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sys_prompt = sys_prompt.format(n_sentences=self.n_sentences)
|
||||||
|
chat_prompt = chat_prompt.format(n_sentences=self.n_sentences)
|
||||||
|
self.ctx.script = self.ctx.powerfulllmengine.generate(
|
||||||
|
system_prompt=sys_prompt,
|
||||||
|
chat_prompt=chat_prompt,
|
||||||
|
max_tokens=20 * self.n_sentences,
|
||||||
|
temperature=1.3,
|
||||||
|
json_mode=False,
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls) -> list:
|
||||||
|
return [gr.Number(label="Number of sentences", value=5, minimum=1)]
|
||||||
19
src/engines/ScriptEngine/prompts/scientific_facts.yaml
Normal file
19
src/engines/ScriptEngine/prompts/scientific_facts.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
system: |-
|
||||||
|
You are an expert content writer of a YouTube shorts channel. You specialize in scientific facts shorts.
|
||||||
|
Your shorts are {n_sentences} sentences long. This is VERY IMPORTANT, MAKE SURE TO RESPECT THIS LENGTH.
|
||||||
|
They are extremely captivating, and original.
|
||||||
|
You need to follow the following guidelines:
|
||||||
|
- **Hook the Viewer:** Start with a compelling question, fact, or scenario to grab attention immediately. Your fact can also be a bit wierd or shocking (not really shocking, but you get the point), so that the viewer wants to know the actual truth.
|
||||||
|
Specifically, you can start with something that isn't completely correct, then when you continue the actual explanation unfolds, in order to make the first few words more attractive.
|
||||||
|
- **Keep it Short and Sweet:** Deliver your content concisely and rapidly to match the platform's fast-paced nature.
|
||||||
|
- **Tap into Relatability or Curiosity:** Make your content relatable or introduce surprising elements to spark curiosity.
|
||||||
|
- **Maintain a Conversational Tone:** Use conversational language to make your content more accessible and engaging.
|
||||||
|
- **Use Visual Imagery:** Describe concepts in a way that invokes visual imagery, enhancing engagement.
|
||||||
|
- **Include a Call to Action:** End with a direct call to action to encourage viewer interaction if applicable.
|
||||||
|
You are now tasked to produce the greatest short script for the user.
|
||||||
|
Start with a compelling information, fact, or scenario to grab attention IMMEDIATELY.
|
||||||
|
Keep it short, EXTREMELY interesting and original.
|
||||||
|
If it is appropriate, at the end, ask a question to the user, and end point blank.
|
||||||
|
YOU never respond with anything else that the video script, not even a hello.
|
||||||
|
chat: |
|
||||||
|
Please give me a script. Make sure to keep it {n_sentences} sentences long, including any questions or calls to action.
|
||||||
@@ -49,7 +49,7 @@ class BaseTTSEngine(BaseEngine):
|
|||||||
"""
|
"""
|
||||||
device = "cuda" if is_available() else "cpu"
|
device = "cuda" if is_available() else "cpu"
|
||||||
audio = wt.load_audio(path)
|
audio = wt.load_audio(path)
|
||||||
model = wt.load_model("small", device=device)
|
model = wt.load_model("large-v3", device=device)
|
||||||
|
|
||||||
result = wt.transcribe(model=model, audio=audio)
|
result = wt.transcribe(model=model, audio=audio)
|
||||||
results = [word for chunk in result["segments"] for word in chunk["words"]]
|
results = [word for chunk in result["segments"] for word in chunk["words"]]
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ class TikTokUploadEngine(BaseUploadEngine):
|
|||||||
description = description.replace(word, "")
|
description = description.replace(word, "")
|
||||||
|
|
||||||
title = title.strip()
|
title = title.strip()
|
||||||
description = description.strip().replace("\n", " ") # Newlines are not supported by this uploader
|
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} {description} {hashtags_str}",
|
description=f"{title}\n{description} {hashtags_str}",
|
||||||
cookies_str=cookies,
|
cookies_str=cookies,
|
||||||
browser="chrome",
|
browser="chrome",
|
||||||
comment=True, stitch=False, duet=False
|
comment=True, stitch=False, duet=False
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from . import ScriptEngine
|
|||||||
from . import SettingsEngine
|
from . import SettingsEngine
|
||||||
from . import TTSEngine
|
from . import TTSEngine
|
||||||
from . import UploadEngine
|
from . import UploadEngine
|
||||||
|
from . import AudioBackgroundEngine
|
||||||
from .BaseEngine import BaseEngine
|
from .BaseEngine import BaseEngine
|
||||||
from .NoneEngine import NoneEngine
|
from .NoneEngine import NoneEngine
|
||||||
|
|
||||||
@@ -49,7 +50,11 @@ ENGINES: dict[str, dict[str, bool | list[BaseEngine]]] = {
|
|||||||
"multiple": True,
|
"multiple": True,
|
||||||
},
|
},
|
||||||
"BackgroundEngine": {
|
"BackgroundEngine": {
|
||||||
"classes": [BackgroundEngine.VideoBackgroundEngine, NoneEngine],
|
"classes": [NoneEngine, BackgroundEngine.VideoBackgroundEngine],
|
||||||
|
"multiple": False,
|
||||||
|
},
|
||||||
|
"AudioBackgroundEngine": {
|
||||||
|
"classes": [NoneEngine, AudioBackgroundEngine.MusicAudioBackgroundEngine],
|
||||||
"multiple": False,
|
"multiple": False,
|
||||||
},
|
},
|
||||||
"MetadataEngine": {
|
"MetadataEngine": {
|
||||||
|
|||||||
@@ -186,13 +186,13 @@ class GenerateUI:
|
|||||||
output_title.render()
|
output_title.render()
|
||||||
output_description.render()
|
output_description.render()
|
||||||
open_folder.render()
|
open_folder.render()
|
||||||
open_folder.click(lambda x: os.system(f"open {os.path.abspath(x)}") if os.name == "posix" else os.system(f"explorer {os.abspath(x)}"), inputs=output_path)
|
open_folder.click(lambda x: os.system(f"open {os.path.abspath(x)}") if os.name == "posix" else os.system(f"explorer {os.path.abspath(x)}"), inputs=output_path)
|
||||||
with gr.Column():
|
with gr.Column():
|
||||||
output_video.render()
|
output_video.render()
|
||||||
|
|
||||||
return interface
|
return interface
|
||||||
|
|
||||||
def run_generate_interface(self, progress=gr.Progress(), *args) -> list[gr.update]:
|
def run_generate_interface(self, progress=gr.Progress(track_tqdm=True), *args) -> list[gr.update]:
|
||||||
progress(0, desc="Loading engines... 🚀")
|
progress(0, desc="Loading engines... 🚀")
|
||||||
options = self.repack_options(*args)
|
options = self.repack_options(*args)
|
||||||
arguments = {name.lower(): options[name] for name in ENGINES.keys()}
|
arguments = {name.lower(): options[name] for name in ENGINES.keys()}
|
||||||
|
|||||||
Reference in New Issue
Block a user