-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7bdec6d
Showing
12 changed files
with
820 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
CLIENT_SECRETS_FILE=src/client_secrets.json | ||
SCOPES=https://www.googleapis.com/auth/youtube.upload | ||
API_SERVICE_NAME=youtube | ||
API_VERSION=v3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
venv/* | ||
temp/* | ||
output/* | ||
**/client_secrets.json | ||
.pytest_cache/* | ||
**/__pycache__/* | ||
.DS_Store | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 FujiwaraChoki | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Youtube-short creater with BERT-VITS2 & DALLE-3 & ChatGPT 🚀 | ||
|
||
Automate the creation of YouTube Shorts locally, simply by providing a video topic to talk about. | ||
|
||
[![ビデオの例](docs/thumbnail.png)](https://www.youtube.com/shorts/Eah3Pb9s2Kk "例を見る") | ||
|
||
|
||
## Installation 📥 | ||
|
||
`YoutubeShortCreator` requires Python 3.11 to run effectively. If you don't have Python installed, you can download it from [here](https://www.python.org/downloads/). | ||
|
||
After you finished installing Python, you can install `YoutubeShortCreator` by following the steps below: | ||
|
||
```bash | ||
git clone https://github.com/avonx/YoutubeShortCreator.git | ||
cd YoutubeShortCreator | ||
|
||
# Install requirements | ||
pip install -r requirements.txt | ||
|
||
# Copy .env.example and fill out values | ||
cp .env.example .env | ||
|
||
# Run the python program | ||
python generate_video.py | ||
|
||
# Alternatively, run this to upload videos to youtube automatically. | ||
python upload_video.py | ||
``` | ||
|
||
See [`.env.example`](.env.example) for the required environment variables. | ||
|
||
## Usage 🛠️ | ||
|
||
1. Copy the `.env.example` file to `.env` and fill in the required values | ||
2. Run this command `python generate_video.py` | ||
3. Enter a topic to talk about | ||
4. Wait for the video to be generated | ||
5. The video's location is `output/<タイトルは自動的に生成されます>.mp4` | ||
|
||
|
||
## Automatic YouTube Uploading 🎥 | ||
|
||
To use this feature, you need to: | ||
|
||
1. Create a project inside your Google Cloud Platform -> [GCP](https://console.cloud.google.com/). | ||
2. Obtain `client_secret.json` from the project and add it to the Backend/ directory. | ||
3. Enable the YouTube v3 API in your project -> [GCP-API-Library](https://console.cloud.google.com/apis/library/youtube.googleapis.com) | ||
4. Create an `OAuth consent screen` and add yourself (the account of your YouTube channel) to the testers. | ||
5. Enable the following scopes in the `OAuth consent screen` for your project: | ||
|
||
``` | ||
'https://www.googleapis.com/auth/youtube.upload' | ||
``` | ||
|
||
## License 📝 | ||
|
||
See [`LICENSE`](LICENSE) file for more information. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
termcolor | ||
openai | ||
pytest | ||
black | ||
pydub | ||
requests | ||
google-api-python-client | ||
google-auth | ||
google-auth-httplib2 | ||
google-auth-oauthlib | ||
httplib2 | ||
oauthlib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from openai import OpenAI | ||
import json | ||
from termcolor import colored | ||
|
||
client = OpenAI() | ||
|
||
|
||
def generate_ideas(meta_topic: str, num_ideas: int) -> dict: | ||
""" | ||
メタトピックについて、アイデアを生成する。 | ||
""" | ||
# プロンプトの構築 | ||
prompt_valuables = f""" | ||
下記のメタトピックに応じて、youtubeのショート動画のアイデアを考えてください。 | ||
主題: {meta_topic} | ||
出力するアイデアの個数: {num_ideas} | ||
""" | ||
|
||
prompt_format = """ | ||
次の形式のjsonで出力するようにしてください。 | ||
"ideas": [ | ||
"アイデア1", "アイデア2", ... | ||
] | ||
例) | ||
"ideas": [ | ||
"実は宇宙人はすでに紛れ込んでいる", "ドラえもんが怒った時に何が起こるか", ... | ||
] | ||
""" | ||
response = client.chat.completions.create( | ||
model="gpt-4-1106-preview", | ||
# model="gpt-3.5-turbo-1106", | ||
messages=[ | ||
{"role": "system", "content": "You are a helpful assistant."}, | ||
{ | ||
"role": "user", | ||
"content": f"次の文章からJSONを生成してください。\n{prompt_valuables}{prompt_format}", | ||
}, | ||
], | ||
response_format={"type": "json_object"}, | ||
) | ||
|
||
script = response.choices[0].message.content.strip() | ||
# scriptをjsonに変換 | ||
try: | ||
script = json.loads(script) | ||
return script | ||
except json.JSONDecodeError as e: | ||
print(colored("スクリプトの生成に失敗しました。", "red")) | ||
print(colored("エラー内容: ", "red"), e) | ||
return None | ||
|
||
|
||
if __name__ == "__main__": | ||
meta_topic = "SFチックな、ちょっとオカルトな話を具体的に。" | ||
num_ideas = 2 | ||
ideas = generate_ideas(meta_topic, num_ideas) | ||
print(ideas) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from pathlib import Path | ||
from termcolor import colored | ||
|
||
from generate_ideas import generate_ideas | ||
from topic2text import generate_script | ||
from text2voice import generate_audio_for_clips | ||
from voice2video import process_json_data, create_combined_video_from_clips | ||
|
||
|
||
def topic2video(video_subject, num_clips, output_file_path): | ||
# Create or clear the temp folder | ||
temp_folder = Path("./temp") | ||
temp_folder.mkdir(exist_ok=True) | ||
for file in temp_folder.glob("*"): | ||
file.unlink() | ||
|
||
clips = generate_script(video_subject, num_clips) | ||
print(colored("[+] Generating script...", "yellow")) | ||
print(clips) | ||
|
||
updated_clips = generate_audio_for_clips(clips) | ||
print(colored("[+] Generating audio for clips...", "yellow")) | ||
print(updated_clips) | ||
|
||
updated_clips = process_json_data(updated_clips) | ||
print(colored("[+] Processing JSON data...", "yellow")) | ||
print(updated_clips) | ||
|
||
video_path = create_combined_video_from_clips( | ||
updated_clips, output_file_path) | ||
print(colored("[+] Creating combined video...", "yellow")) | ||
|
||
# Delete temporary files | ||
for file in temp_folder.glob("*"): | ||
file.unlink() | ||
|
||
return video_path, updated_clips | ||
|
||
|
||
if __name__ == "__main__": | ||
meta_topic = "科学に関する話題を具体的に。" | ||
num_ideas = 3 | ||
ideas = generate_ideas(meta_topic, num_ideas) | ||
print(colored("[+] Generating ideas...", "yellow")) | ||
print(ideas) | ||
|
||
for idea in ideas["ideas"]: | ||
video_subject = idea | ||
num_clips = 5 | ||
output_file_path = f"./output/{video_subject}.mp4" | ||
video_path, updated_clips = topic2video( | ||
video_subject, num_clips, output_file_path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import requests | ||
from pydub import AudioSegment | ||
import io | ||
import uuid | ||
from pathlib import Path | ||
from termcolor import colored | ||
|
||
# tempフォルダのパスを定義(存在しない場合は作成) | ||
temp_folder = Path("./temp") | ||
temp_folder.mkdir(exist_ok=True) | ||
|
||
|
||
def generate_audio_for_clips(clips): | ||
""" | ||
Generate audio files for each clip in the given list. | ||
Args: | ||
clips (list): A list of clips, where each clip is a dictionary containing the "text" key. | ||
Returns: | ||
None | ||
""" | ||
for clip in clips["clips"]: | ||
filename = f"{str(uuid.uuid4())}.mp3" | ||
filepath = temp_folder / filename # 保存するファイルパス | ||
text = clip["text"] # 音声に変換するテキスト | ||
|
||
combined_audio = process_and_combine_audio(text) | ||
if combined_audio: | ||
combined_audio.export(filepath, format="mp3") | ||
clip["audio_path"] = str(filepath) # 生成した音声ファイルのパスをJSONに追加 | ||
else: | ||
clip["audio_path"] = None # 音声生成に失敗した場合 | ||
return clips | ||
|
||
|
||
def process_and_combine_audio(text: str) -> AudioSegment: | ||
""" | ||
Process and combine audio segments for each paragraph in the given text. | ||
Args: | ||
text (str): The input text. | ||
Returns: | ||
AudioSegment: The combined audio segment. | ||
""" | ||
text_no_newlines = text.replace("\n\n", "") | ||
paragraphs = [f"{p}。" for p in text_no_newlines.split("。") if p] | ||
|
||
combined_audio = None | ||
for i, paragraph in enumerate(paragraphs): | ||
print( | ||
colored(f"[+] Processing paragraph {i+1}/{len(paragraphs)}...", "yellow")) | ||
audio_content = get_tts_audio(paragraph) | ||
if audio_content: | ||
current_audio = AudioSegment.from_file( | ||
io.BytesIO(audio_content), format="wav") | ||
silence = AudioSegment.silent(duration=1000) # 1秒の無音を追加 | ||
combined_audio = combined_audio + silence + \ | ||
current_audio if combined_audio else current_audio | ||
return combined_audio | ||
|
||
|
||
def get_tts_audio(text: str) -> bytes: | ||
""" | ||
Get text-to-speech audio for the given text. | ||
Args: | ||
text (str): The text to convert to audio. | ||
Returns: | ||
bytes: The audio content in bytes if successful, None otherwise. | ||
""" | ||
url = "https://igeb37tgif2guh-5000.proxy.runpod.net/voice" | ||
params = { | ||
"text": text, | ||
"encoding": "utf-8", | ||
"model_id": 4, | ||
"speaker_id": 0, | ||
"sdp_ratio": 0.6, | ||
"noise": 0.7, | ||
"noisew": 1.1, | ||
"length": 0.75, | ||
"language": "JP", | ||
"auto_split": True, | ||
"split_interval": 0.5, | ||
"assist_text_weight": 1, | ||
"style": "Neutral", | ||
"style_weight": 5, | ||
} | ||
response = requests.get(url, params=params) | ||
if response.status_code == 200 and "audio/wav" in response.headers.get("Content-Type"): | ||
return response.content | ||
return None | ||
|
||
|
||
if __name__ == "__main__": | ||
# JSONデータを処理するための例 | ||
clips = { | ||
"clips": [ | ||
{ | ||
"num": 0, | ||
"title": "サンプルタイトル", | ||
"text": "これはサンプルのテキストです。宇宙人はいます。会ったことがあります!", | ||
"video_prompt": "静かな森の風景を描写する文章。", | ||
}, | ||
# 他のクリップ... | ||
] | ||
} | ||
|
||
updated_clips = generate_audio_for_clips(clips) | ||
|
||
# 結果を確認 | ||
print(updated_clips) |
Oops, something went wrong.