Skip to content

Commit 5f5cc85

Browse files
Add posibility to get instagram videos and use those videos as base
1 parent ecd4139 commit 5f5cc85

24 files changed

+604
-279
lines changed

Backend/classes/Shorts.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ def DownloadVideos(self, selectedVideoUrls):
236236
)
237237
try:
238238
saved_video_path = save_video(video_url)
239+
print(colored(f"[+] Saved video: {saved_video_path}", "green"))
239240
video_paths.append(saved_video_path)
240241
except Exception:
241242
print(colored(f"[-] Could not download video: {video_url}", "red"))
@@ -277,19 +278,19 @@ def GenerateVoice(self,voice):
277278
}
278279
)
279280
fileId = uuid4()
280-
current_tts_path = f"../static/assets/temp/{fileId}.mp3"
281+
current_tts_path = os.path.join("static/assets/temp", f"{fileId}.mp3")
281282
tts(sentence, self.voice, filename=current_tts_path)
282283

283284
# Add the audio clip to the list
284285
print(colored(f"[X] Save Audio ", "green"))
285-
audio_clip = AudioFileClip(f"../static/assets/temp/{fileId}.mp3")
286+
audio_clip = AudioFileClip(os.path.join("static/assets/temp", f"{fileId}.mp3"))
286287
paths.append(audio_clip)
287288

288289
# Combine all TTS files using moviepy
289290

290291
print(colored(f"[X] Start saving the audio ", "green"))
291292
final_audio = concatenate_audioclips(paths)
292-
self.tts_path = f"../static/assets/temp/{uuid4()}.mp3"
293+
self.tts_path = os.path.join("static/assets/temp", f"{uuid4()}.mp3")
293294
final_audio.write_audiofile(self.tts_path)
294295

295296
# Generate the subtitles
@@ -321,17 +322,17 @@ def WriteMetadataToFile(video_title, video_description, video_tags):
321322
# Remplace spaces with underscores
322323
fileName = video_title.replace(" ", "_")
323324

324-
with open(f"../../static/assets/temp/{fileName}.json", "w") as file:
325+
with open(os.path.join("static/generated_videos", f"{fileName}.json"), "w") as file:
325326
json.dump(metadata, file)
326327

327328
def AddMusic(self, use_music,custom_song_path=""):
328-
video_clip = VideoFileClip(f"../{self.final_video_path}")
329+
video_clip = VideoFileClip(f"{self.final_video_path}")
329330

330331
self.final_music_video_path = f"{uuid4()}-music.mp4"
331332
n_threads = 2
332333
if use_music:
333334
# if no song path choose random song
334-
song_path = f"../static/assets/music/{custom_song_path}"
335+
song_path = os.path.join("static/assets/music", custom_song_path)
335336
if not custom_song_path:
336337
song_path = choose_random_song()
337338

@@ -350,9 +351,9 @@ def AddMusic(self, use_music,custom_song_path=""):
350351
video_clip = video_clip.set_fps(30)
351352
video_clip = video_clip.set_duration(original_duration)
352353

353-
video_clip.write_videofile(f"../static/generated_videos/{self.final_music_video_path}", threads=n_threads or 1)
354+
video_clip.write_videofile(os.path.join("static/generated_videos", self.final_music_video_path), threads=n_threads or 1)
354355
else:
355-
video_clip.write_videofile(f"../static/generated_videos/{self.final_music_video_path}", threads=n_threads or 1)
356+
video_clip.write_videofile(os.path.join("static/generated_videos", self.final_music_video_path), threads=n_threads or 1)
356357

357358
def Stop(self):
358359
global GENERATING
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import yt_dlp
2+
import os
3+
from typing import Optional, Dict, Any
4+
from datetime import datetime
5+
6+
class InstagramDownloader:
7+
def __init__(self, output_path: str = "downloads"):
8+
"""
9+
Initialize the Instagram video downloader
10+
11+
Args:
12+
output_path (str): Directory where videos will be saved
13+
"""
14+
self.output_path = output_path
15+
self._create_output_directory()
16+
17+
# Configure yt-dlp options
18+
self.ydl_opts = {
19+
'format': 'best', # Download best quality
20+
'outtmpl': os.path.join(self.output_path, '%(id)s.%(ext)s'),
21+
'quiet': False,
22+
'no_warnings': False,
23+
'extract_flat': False,
24+
}
25+
26+
def _create_output_directory(self) -> None:
27+
"""Create the output directory if it doesn't exist"""
28+
os.makedirs(self.output_path, exist_ok=True)
29+
30+
def download_video(self, url: str) -> Dict[str, Any]:
31+
"""
32+
Download a video from Instagram
33+
34+
Args:
35+
url (str): Instagram video URL
36+
37+
Returns:
38+
Dict[str, Any]: Information about the downloaded video
39+
40+
Raises:
41+
Exception: If download fails
42+
"""
43+
try:
44+
with yt_dlp.YoutubeDL(self.ydl_opts) as ydl:
45+
# Extract video information
46+
info = ydl.extract_info(url, download=True)
47+
48+
return {
49+
'title': info.get('title', ''),
50+
'filename': ydl.prepare_filename(info),
51+
'duration': info.get('duration'),
52+
'thumbnail': info.get('thumbnail'),
53+
'download_time': datetime.now().isoformat(),
54+
'status': 'success'
55+
}
56+
57+
except Exception as e:
58+
error_info = {
59+
'status': 'error',
60+
'error_message': str(e),
61+
'url': url,
62+
'time': datetime.now().isoformat()
63+
}
64+
raise Exception(f"Failed to download video: {str(e)}") from e
65+
66+
def update_options(self, new_options: Dict[str, Any]) -> None:
67+
"""
68+
Update yt-dlp options
69+
70+
Args:
71+
new_options (Dict[str, Any]): New options to update
72+
"""
73+
self.ydl_opts.update(new_options)
74+
75+
def set_output_template(self, template: str) -> None:
76+
"""
77+
Set custom output template for downloaded files
78+
79+
Args:
80+
template (str): Output template string
81+
"""
82+
self.ydl_opts['outtmpl'] = os.path.join(self.output_path, template)

Backend/gpt.py

+23-14
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
import re
22
import json
33
import g4f
4-
import openai
4+
# from openai import OpenAI
55
from typing import Tuple, List
66
from termcolor import colored
77
from dotenv import load_dotenv
88
import os
99
import google.generativeai as genai
1010

1111
# Load environment variables
12-
load_dotenv("../.env")
12+
if os.path.exists(".env"):
13+
load_dotenv(".env")
14+
else:
15+
load_dotenv("../.env")
1316

1417
# Set environment variables
1518
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
16-
openai.api_key = OPENAI_API_KEY
19+
# openai.api_key = OPENAI_API_KEY
1720
GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
1821
genai.configure(api_key=GOOGLE_API_KEY)
1922

23+
# openaiClient = OpenAI(
24+
# api_key=OPENAI_API_KEY , # This is the default and can be omitted
25+
# )
26+
2027
# Configure g4f
2128
g4f.debug.logging = True # Enable debug logging
2229
g4f.debug.version_check = False # Disable automatic version checking
@@ -34,20 +41,22 @@ def generate_response(prompt: str, ai_model: str) -> str:
3441
"""
3542

3643
if ai_model == 'g4f':
37-
response = g4f.ChatCompletion.create(
38-
model="gpt-3.5-turbo",
44+
client = g4f.Client()
45+
response = client.chat.completions.create(
46+
model="gpt-4o-mini",
3947
messages=[{"role": "user", "content": prompt}],
4048
stream=False
49+
# Add any other necessary parameters
4150
)
42-
# Ensure we return a string
43-
return response if isinstance(response, str) else str(response)
44-
45-
elif ai_model in ["gpt3.5-turbo", "gpt4"]:
46-
model_name = "gpt-3.5-turbo" if ai_model == "gpt3.5-turbo" else "gpt-4-1106-preview"
47-
response = openai.chat.completions.create(
48-
model=model_name,
49-
messages=[{"role": "user", "content": prompt}],
50-
).choices[0].message.content
51+
return response if isinstance(response, str) else str(response.choices[0].message.content)
52+
53+
# elif ai_model in ["gpt3.5-turbo", "gpt4"]:
54+
55+
# model_name = "gpt-3.5-turbo" if ai_model == "gpt3.5-turbo" else "gpt-4-1106-preview"
56+
# response = openaiClient.chat.completions.create(
57+
# model=model_name,
58+
# messages=[{"role": "user", "content": prompt}],
59+
# ).choices[0].message.content
5160

5261
elif ai_model == 'gemmini':
5362
model = genai.GenerativeModel('gemini-pro')

Backend/main.py

+68-24
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
from dotenv import load_dotenv
44

55
# Load environment variables
6-
load_dotenv("../.env")
6+
# check if .env is in the folder or look one more level up
7+
if os.path.exists(".env"):
8+
load_dotenv(".env")
9+
else:
10+
load_dotenv("../.env")
711
# Check if all required environment variables are set
812
# This must happen before importing video which uses API keys without checking
913
check_env_vars()
@@ -20,8 +24,7 @@
2024
from apiclient.errors import HttpError
2125
from flask import Flask, request, jsonify
2226
from moviepy.config import change_settings
23-
24-
27+
from classes.instagram_downloader import InstagramDownloader
2528

2629
# Set environment variables
2730
SESSION_ID = os.getenv("TIKTOK_SESSION_ID")
@@ -30,7 +33,7 @@
3033

3134

3235
# Initialize Flask
33-
app = Flask(__name__, static_folder="../static", static_url_path="/static")
36+
app = Flask(__name__, static_folder="static", static_url_path="/static")
3437
CORS(app)
3538

3639
# Constants
@@ -41,19 +44,55 @@
4144

4245
# Create a method to create all the required folders
4346
def create_folders():
44-
# Create static folder if it doesn't exist
45-
if not os.path.exists("../static"):
46-
os.makedirs("../static")
47-
# Create static/Songs and static/generated_videos folder if it doesn't exist
48-
if not os.path.exists("../static/assets"):
49-
os.makedirs("../static/assets")
50-
if not os.path.exists("../static/generated_videos"):
51-
os.makedirs("../static/generated_videos")
52-
47+
"""Create all required folders for the application"""
48+
folders = [
49+
"static",
50+
"static/assets",
51+
"static/assets/temp",
52+
"static/assets/subtitles",
53+
"static/generated_videos",
54+
"static/generated_videos/instagram",
55+
]
56+
57+
for folder in folders:
58+
folder_path = os.path.join(os.path.dirname(__file__), folder)
59+
os.makedirs(folder_path, exist_ok=True)
60+
print(f"Created/verified folder: {folder_path}")
5361

5462
# Create folders
5563
create_folders()
5664

65+
# Instagram video download endpoint
66+
@app.route("/api/instagram/download", methods=["POST"])
67+
def download_instagram_video():
68+
try:
69+
data = request.get_json()
70+
video_url = data.get('url')
71+
72+
if not video_url:
73+
return jsonify({
74+
"status": "error",
75+
"message": "No Instagram URL provided",
76+
}), 400
77+
78+
# Initialize downloader with output path in static/assets
79+
downloader = InstagramDownloader(output_path=os.path.join(os.path.dirname(__file__), "static/generated_videos/instagram"))
80+
81+
# Download the video
82+
result = downloader.download_video(video_url)
83+
84+
return jsonify({
85+
"status": "success",
86+
"message": "Video downloaded successfully",
87+
"data": result
88+
})
89+
90+
except Exception as e:
91+
return jsonify({
92+
"status": "error",
93+
"message": str(e),
94+
}), 500
95+
5796

5897
# Generation Endpoint
5998
@app.route("/api/generate", methods=["POST"])
@@ -64,8 +103,8 @@ def generate():
64103
GENERATING = True
65104

66105
# Clean
67-
clean_dir("../static/assets/temp/")
68-
clean_dir("../static/assets/subtitles/")
106+
clean_dir("static/assets/temp/")
107+
clean_dir("static/assets/subtitles/")
69108

70109

71110
# Parse JSON
@@ -209,7 +248,7 @@ def generate_script_only():
209248
# Set generating to true
210249
GENERATING = True
211250

212-
clean_dir("../static/assets/subtitles/")
251+
clean_dir("static/assets/subtitles/")
213252
print(colored("[+] Received script request...", "green"))
214253

215254
data = request.get_json()
@@ -245,8 +284,8 @@ def search_and_download():
245284
global GENERATING
246285
GENERATING = True
247286
# Clean
248-
clean_dir("../static/assets/temp")
249-
clean_dir("../static/assets/subtitles")
287+
clean_dir("static/assets/temp")
288+
clean_dir("static/assets/subtitles")
250289

251290

252291
print(colored("[+] Received search and download request...", "green"))
@@ -280,6 +319,7 @@ def search_and_download():
280319

281320
videoClass.CombineVideos()
282321

322+
# videoClass.GenerateMetadata()
283323
videoClass.Stop()
284324

285325

@@ -337,7 +377,7 @@ def addAudio():
337377
# Get all available songs
338378
@app.route("/api/getSongs", methods=["GET"])
339379
def get_songs():
340-
songs = os.listdir("../static/assets/music")
380+
songs = os.listdir(os.path.join(os.path.dirname(__file__), "static/assets/music"))
341381
return jsonify({
342382
"status": "success",
343383
"message": "Songs retrieved successfully!",
@@ -350,22 +390,25 @@ def get_songs():
350390
@app.route("/api/getVideos", methods=["GET"])
351391
def get_videos():
352392
# Get all videos mp4 only
353-
videos = os.listdir("../static/generated_videos")
393+
videos = os.listdir(os.path.join(os.path.dirname(__file__), "static/generated_videos"))
354394
videos = [video for video in videos if video.endswith(".mp4")]
395+
instagramVideos = os.listdir(os.path.join(os.path.dirname(__file__), "static/generated_videos/instagram"))
396+
instagramVideos = [video for video in instagramVideos if video.endswith(".mp4")]
355397
return jsonify(
356398
{
357399
"status": "success",
358400
"message": "Videos retrieved successfully!",
359401
"data": {
360-
"videos": videos
402+
"videos": videos,
403+
"instagram": instagramVideos
361404
}
362405
}
363406
)
364407

365408
# Get all available subtitles
366409
@app.route("/api/getSubtitles", methods=["GET"])
367410
def get_subtitles():
368-
subtitles = os.listdir("../static/assets/subtitles")
411+
subtitles = os.listdir(os.path.join(os.path.dirname(__file__), "static/assets/subtitles"))
369412
return jsonify(
370413
{
371414
"status": "success",
@@ -393,8 +436,9 @@ def get_models():
393436

394437
@app.route("/api/assets", methods=["GET"])
395438
def get_assets():
396-
video_assets = os.listdir("../static/assets/temp")
397-
videos = [video for video in videos if video.endswith(".mp4")]
439+
assets_path = os.path.join(os.path.dirname(__file__), "static/assets/temp")
440+
video_assets = os.listdir(assets_path)
441+
videos = [video for video in video_assets if video.endswith(".mp4")]
398442
return jsonify(
399443
{
400444
"status": "success",

0 commit comments

Comments
 (0)