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/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# 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.
|
||||
#.idea/
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.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/
|
||||
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
|
||||
from datetime import datetime
|
||||
|
||||
import gradio
|
||||
import moviepy.editor as mp
|
||||
|
||||
from .. import engines
|
||||
@@ -35,8 +36,16 @@ class GenerationContext:
|
||||
backgroundengine,
|
||||
metadataengine,
|
||||
uploadengine,
|
||||
audiobackgroundengine,
|
||||
progress,
|
||||
) -> 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.powerfulllmengine: engines.LLMEngine.BaseLLMEngine = powerfulllmengine[0]
|
||||
@@ -79,17 +88,13 @@ class GenerationContext:
|
||||
for eng in self.uploadengine:
|
||||
eng.ctx = self
|
||||
|
||||
def setup_dir(self):
|
||||
self.dir = f"output/{time.time()}"
|
||||
os.makedirs(self.dir)
|
||||
self.audiobackgroundengine: engines.AudioBackgroundEngine.BaseAudioBackgroundEngine = (
|
||||
audiobackgroundengine[0]
|
||||
)
|
||||
self.audiobackgroundengine.ctx = self
|
||||
|
||||
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.
|
||||
|
||||
# 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.
|
||||
# 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_1 = []
|
||||
self.index_2 = []
|
||||
@@ -100,23 +105,45 @@ class GenerationContext:
|
||||
self.index_7 = []
|
||||
self.index_8 = []
|
||||
self.index_9 = []
|
||||
|
||||
self.audio = []
|
||||
|
||||
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.setup_dir()
|
||||
self.settingsengine.load()
|
||||
if not isinstance(self.settingsengine, engines.NoneEngine):
|
||||
self.settingsengine.load()
|
||||
|
||||
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.ttsengine.synthesize(self.script, self.get_file_path("tts.wav"))
|
||||
self.duration: float # for type hinting
|
||||
self.progress(0.3, "Synthesizing voice...")
|
||||
if not isinstance(self.ttsengine, engines.NoneEngine):
|
||||
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):
|
||||
self.progress(0.4, "Generating 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 = [
|
||||
engine
|
||||
for engine in self.assetsengine
|
||||
@@ -129,10 +156,6 @@ class GenerationContext:
|
||||
if not isinstance(self.captioningengine, engines.NoneEngine):
|
||||
self.progress(0.6, "Generating captions...")
|
||||
self.captioningengine.get_captions()
|
||||
else:
|
||||
self.captions = []
|
||||
|
||||
# add any other processing steps here
|
||||
|
||||
# we render to a file called final.mp4
|
||||
self.progress(0.7, "Rendering video...")
|
||||
@@ -149,18 +172,22 @@ class GenerationContext:
|
||||
*self.index_9,
|
||||
]
|
||||
clip = mp.CompositeVideoClip(clips, size=(self.width, self.height))
|
||||
audio = mp.CompositeAudioClip(self.audio)
|
||||
clip.set_duration(self.duration)
|
||||
audio = mp.AudioFileClip(self.get_file_path("tts.wav"))
|
||||
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.metadataengine.get_metadata()
|
||||
|
||||
self.description = self.description + "\n" + self.credits
|
||||
self.progress(0.9, "Uploading video...")
|
||||
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.store_in_db()
|
||||
self.progress(1, "Done!")
|
||||
|
||||
@@ -49,16 +49,18 @@ class GoogleAssetsEngine(BaseAssetsEngine):
|
||||
"num": 1,
|
||||
}
|
||||
os.makedirs("temp", exist_ok=True)
|
||||
self.google.search(
|
||||
search_params=_search_params,
|
||||
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]
|
||||
img = mp.ImageClip(f"./temp/{filename}")
|
||||
# delete the temp folder
|
||||
shutil.rmtree("temp")
|
||||
try:
|
||||
self.google.search(
|
||||
search_params=_search_params,
|
||||
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]
|
||||
img = mp.ImageClip(f"./temp/{filename}")
|
||||
# delete the temp folder
|
||||
finally:
|
||||
shutil.rmtree("temp")
|
||||
|
||||
img: mp.ImageClip = img.set_duration(end - 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 ..BaseEngine import BaseEngine
|
||||
from src.engines.BaseEngine import BaseEngine
|
||||
|
||||
|
||||
class BaseBackgroundEngine(BaseEngine):
|
||||
|
||||
@@ -7,7 +7,6 @@ from ..chore import GenerationContext
|
||||
from ..models import SessionLocal, File, Setting
|
||||
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
class BaseEngine(ABC):
|
||||
num_options: int
|
||||
name: str
|
||||
@@ -15,7 +14,6 @@ class BaseEngine(ABC):
|
||||
|
||||
def __init__(self):
|
||||
self.ctx: GenerationContext # This is for type hinting only
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import gradio as gr
|
||||
import os
|
||||
import shutil
|
||||
from moviepy.editor import TextClip
|
||||
|
||||
from . import BaseCaptioningEngine
|
||||
@@ -80,6 +82,13 @@ class SimpleCaptioningEngine(BaseCaptioningEngine):
|
||||
|
||||
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
|
||||
def get_options(cls) -> list:
|
||||
with gr.Column() as font_options:
|
||||
@@ -87,7 +96,8 @@ class SimpleCaptioningEngine(BaseCaptioningEngine):
|
||||
font = gr.Dropdown(
|
||||
label="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(
|
||||
label="Font Size",
|
||||
|
||||
@@ -2,13 +2,28 @@ from . import BaseEngine
|
||||
|
||||
|
||||
class NoneEngine(BaseEngine):
|
||||
num_options = 0
|
||||
name = "None"
|
||||
description = "No engine selected"
|
||||
"""
|
||||
This class represents a NoneEngine which is a subclass of BaseEngine.
|
||||
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__()
|
||||
|
||||
@classmethod
|
||||
def get_options(cls):
|
||||
return []
|
||||
"""
|
||||
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 []
|
||||
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"
|
||||
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)
|
||||
results = [word for chunk in result["segments"] for word in chunk["words"]]
|
||||
|
||||
@@ -36,11 +36,11 @@ class TikTokUploadEngine(BaseUploadEngine):
|
||||
description = description.replace(word, "")
|
||||
|
||||
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 ""
|
||||
failed = upload_video(
|
||||
filename=self.ctx.get_file_path("final.mp4"),
|
||||
description=f"{title} {description} {hashtags_str}",
|
||||
description=f"{title}\n{description} {hashtags_str}",
|
||||
cookies_str=cookies,
|
||||
browser="chrome",
|
||||
comment=True, stitch=False, duet=False
|
||||
|
||||
@@ -7,6 +7,7 @@ from . import ScriptEngine
|
||||
from . import SettingsEngine
|
||||
from . import TTSEngine
|
||||
from . import UploadEngine
|
||||
from . import AudioBackgroundEngine
|
||||
from .BaseEngine import BaseEngine
|
||||
from .NoneEngine import NoneEngine
|
||||
|
||||
@@ -49,7 +50,11 @@ ENGINES: dict[str, dict[str, bool | list[BaseEngine]]] = {
|
||||
"multiple": True,
|
||||
},
|
||||
"BackgroundEngine": {
|
||||
"classes": [BackgroundEngine.VideoBackgroundEngine, NoneEngine],
|
||||
"classes": [NoneEngine, BackgroundEngine.VideoBackgroundEngine],
|
||||
"multiple": False,
|
||||
},
|
||||
"AudioBackgroundEngine": {
|
||||
"classes": [NoneEngine, AudioBackgroundEngine.MusicAudioBackgroundEngine],
|
||||
"multiple": False,
|
||||
},
|
||||
"MetadataEngine": {
|
||||
|
||||
@@ -186,13 +186,13 @@ class GenerateUI:
|
||||
output_title.render()
|
||||
output_description.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():
|
||||
output_video.render()
|
||||
|
||||
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... 🚀")
|
||||
options = self.repack_options(*args)
|
||||
arguments = {name.lower(): options[name] for name in ENGINES.keys()}
|
||||
|
||||
Reference in New Issue
Block a user