Skip to content

Commit 3194c9f

Browse files
authored
Uploading updated versions of python script and exe file
1 parent 651210a commit 3194c9f

2 files changed

Lines changed: 333 additions & 0 deletions

File tree

OptiFetch_2026.exe

19.4 MB
Binary file not shown.

OptiFetch_2026.py

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
#!/usr/bin/env python3
2+
"""
3+
OptiFetch 2026 - Updated: separate audio and video download flows.
4+
Drop-in replacement for your script. Keeps settings persistence and adds
5+
distinct "Download Video" and "Download Audio" actions.
6+
"""
7+
import yt_dlp
8+
import os
9+
import json
10+
import shutil
11+
import sys
12+
13+
CONFIG_FILE = "youtube_downloader_config.json"
14+
DEFAULT_SAVE_PATH = os.path.expanduser("~/Downloads")
15+
16+
DEFAULT_CONFIG = {
17+
"save_path": DEFAULT_SAVE_PATH,
18+
"resolution": "best",
19+
"format": "mp4", # used for video downloads: mp4, mkv, webm
20+
"mp3_bitrate": "192" # used for audio downloads
21+
}
22+
23+
current_settings = {}
24+
25+
# -------------------- Utilities --------------------
26+
def find_ffmpeg():
27+
ffmpeg_path = shutil.which('ffmpeg')
28+
if ffmpeg_path:
29+
print(f"✓ ffmpeg found at: {ffmpeg_path}")
30+
else:
31+
print("⚠️ WARNING: ffmpeg not found in PATH!")
32+
print(" Please install ffmpeg or add it to your system PATH")
33+
return ffmpeg_path
34+
35+
def load_config():
36+
if os.path.exists(CONFIG_FILE):
37+
try:
38+
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
39+
cfg = json.load(f)
40+
merged = DEFAULT_CONFIG.copy()
41+
merged.update(cfg)
42+
return merged
43+
except Exception:
44+
return DEFAULT_CONFIG.copy()
45+
return DEFAULT_CONFIG.copy()
46+
47+
def save_config(config):
48+
try:
49+
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
50+
json.dump(config, f, indent=4)
51+
print("✓ Configuration saved permanently!")
52+
except Exception as e:
53+
print(f"Error saving config: {e}")
54+
55+
def get_current_settings():
56+
cfg = load_config()
57+
cfg.update(current_settings)
58+
return cfg
59+
60+
def ensure_save_path(path):
61+
try:
62+
os.makedirs(path, exist_ok=True)
63+
return True
64+
except Exception as e:
65+
print(f"Error creating save path '{path}': {e}")
66+
return False
67+
68+
# -------------------- UI --------------------
69+
def display_main_menu():
70+
print("\n" + "="*50)
71+
print(" OptiFetch 2026 - YouTube Downloader")
72+
print("="*50)
73+
print("1. Download Video")
74+
print("2. Download Audio (MP3)")
75+
print("3. View Current Settings")
76+
print("4. Change Settings (Temporary)")
77+
print("5. Change Settings (Permanent)")
78+
print("6. Reset to Default Settings")
79+
print("7. Exit")
80+
print("="*50)
81+
82+
def display_settings(settings):
83+
print("\n" + "-"*50)
84+
print(" CURRENT SETTINGS")
85+
print("-"*50)
86+
print(f"Save Path: {settings['save_path']}")
87+
print(f"Resolution: {settings['resolution']}")
88+
print(f"Video Format: {settings['format']}")
89+
print(f"MP3 Bitrate: {settings.get('mp3_bitrate')}")
90+
print("-"*50)
91+
92+
def change_resolution():
93+
print("\nResolution Options:")
94+
resolutions = {
95+
'1': ('best', 'Best Quality (auto-select)'),
96+
'2': ('1080', '1080p (Full HD)'),
97+
'3': ('720', '720p (HD)'),
98+
'4': ('480', '480p (SD)'),
99+
'5': ('360', '360p (Low)'),
100+
}
101+
for key, (_, name) in resolutions.items():
102+
print(f" {key}. {name}")
103+
choice = input("Select resolution (1-5): ").strip()
104+
return resolutions.get(choice, ('best',))[0]
105+
106+
def change_video_format():
107+
print("\nVideo Format Options:")
108+
formats = {
109+
'1': ('mp4', 'MP4 (Most Compatible)'),
110+
'2': ('mkv', 'MKV (Higher Quality)'),
111+
'3': ('webm', 'WEBM (Web Format)'),
112+
}
113+
for key, (_, name) in formats.items():
114+
print(f" {key}. {name}")
115+
choice = input("Select format (1-3): ").strip()
116+
return formats.get(choice, ('mp4',))[0]
117+
118+
def change_save_path():
119+
print("\nCurrent save path:", DEFAULT_SAVE_PATH)
120+
path = input("Enter new save path (or press Enter to keep current): ").strip()
121+
if not path:
122+
return None
123+
if not os.path.exists(path):
124+
create = input(f"Path doesn't exist. Create it? (y/n): ").strip().lower()
125+
if create == 'y':
126+
try:
127+
os.makedirs(path, exist_ok=True)
128+
return path
129+
except Exception as e:
130+
print(f"Error creating path: {e}")
131+
return None
132+
else:
133+
return None
134+
return path
135+
136+
def temporary_settings_menu():
137+
global current_settings
138+
print("\n" + "="*50)
139+
print(" TEMPORARY SETTINGS (This Session Only)")
140+
print("="*50)
141+
while True:
142+
settings = get_current_settings()
143+
print(f"\n1. Resolution: {settings['resolution']}")
144+
print(f"2. Video Format: {settings['format']}")
145+
print(f"3. Save Path: {settings['save_path']}")
146+
print(f"4. MP3 Bitrate: {settings.get('mp3_bitrate')}")
147+
print("5. Back to Main Menu")
148+
choice = input("\nSelect option to change (1-5): ").strip()
149+
if choice == '1':
150+
current_settings['resolution'] = change_resolution()
151+
elif choice == '2':
152+
current_settings['format'] = change_video_format()
153+
elif choice == '3':
154+
path = change_save_path()
155+
if path:
156+
current_settings['save_path'] = path
157+
elif choice == '4':
158+
br = input("Enter MP3 bitrate (e.g., 128, 192, 320) [192]: ").strip() or "192"
159+
current_settings['mp3_bitrate'] = br
160+
elif choice == '5':
161+
break
162+
else:
163+
print("Invalid choice!")
164+
165+
def permanent_settings_menu():
166+
cfg = load_config()
167+
print("\n" + "="*50)
168+
print(" PERMANENT SETTINGS (Saved for Future Use)")
169+
print("="*50)
170+
while True:
171+
print(f"\n1. Resolution: {cfg['resolution']}")
172+
print(f"2. Video Format: {cfg['format']}")
173+
print(f"3. Save Path: {cfg['save_path']}")
174+
print(f"4. MP3 Bitrate: {cfg.get('mp3_bitrate')}")
175+
print("5. Back to Main Menu")
176+
choice = input("\nSelect option to change (1-5): ").strip()
177+
if choice == '1':
178+
cfg['resolution'] = change_resolution()
179+
save_config(cfg)
180+
elif choice == '2':
181+
cfg['format'] = change_video_format()
182+
save_config(cfg)
183+
elif choice == '3':
184+
path = change_save_path()
185+
if path:
186+
cfg['save_path'] = path
187+
save_config(cfg)
188+
elif choice == '4':
189+
br = input("Enter MP3 bitrate (e.g., 128, 192, 320) [192]: ").strip() or "192"
190+
cfg['mp3_bitrate'] = br
191+
save_config(cfg)
192+
elif choice == '5':
193+
break
194+
else:
195+
print("Invalid choice!")
196+
197+
# -------------------- Download helpers --------------------
198+
def build_video_format_option(resolution, format_type):
199+
if resolution == 'best':
200+
return f'bestvideo[ext={format_type}]+bestaudio[ext=m4a]/best[ext={format_type}]/best'
201+
else:
202+
return f'bestvideo[height<={resolution}][ext={format_type}]+bestaudio/best[height<={resolution}]/best'
203+
204+
def progress_hook(d):
205+
if d.get('status') == 'finished':
206+
print('\n✓ Finished downloading, post-processing...')
207+
208+
# -------------------- Separate download flows --------------------
209+
def download_video(url):
210+
settings = get_current_settings()
211+
if not url:
212+
print("Invalid URL.")
213+
return
214+
215+
if not ensure_save_path(settings['save_path']):
216+
return
217+
218+
# Check ffmpeg presence when needed (merging or audio extraction)
219+
ffmpeg_needed = True # yt-dlp uses ffmpeg for merging and audio extraction
220+
ffmpeg_path = find_ffmpeg()
221+
if ffmpeg_needed and not ffmpeg_path:
222+
print("\n❌ Error: ffmpeg is required for merging/conversion.")
223+
print(" Please install ffmpeg or add it to your PATH.")
224+
print(" - winget install ffmpeg")
225+
print(" - or download from: https://ffmpeg.org/download.html")
226+
return
227+
228+
out_template = os.path.join(settings['save_path'], '%(title)s.%(ext)s')
229+
ydl_opts = {
230+
'format': build_video_format_option(settings['resolution'], settings['format']),
231+
'outtmpl': out_template,
232+
'merge_output_format': settings['format'],
233+
'ffmpeg_location': ffmpeg_path,
234+
'noplaylist': True,
235+
'progress_hooks': [progress_hook],
236+
'quiet': False,
237+
'no_warnings': True,
238+
}
239+
240+
print(f"\n📥 Downloading VIDEO:")
241+
print(f" Resolution: {settings['resolution']}")
242+
print(f" Container: {settings['format']}")
243+
print(f" Save Path: {settings['save_path']}\n")
244+
245+
try:
246+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
247+
ydl.download([url])
248+
print("✓ Video download complete.")
249+
except Exception as e:
250+
print(f"Error downloading video: {e}")
251+
252+
def download_audio_mp3(url):
253+
settings = get_current_settings()
254+
if not url:
255+
print("Invalid URL.")
256+
return
257+
258+
if not ensure_save_path(settings['save_path']):
259+
return
260+
261+
ffmpeg_path = find_ffmpeg()
262+
if not ffmpeg_path:
263+
print("\n❌ Error: ffmpeg is required to extract/convert audio.")
264+
print(" Please install ffmpeg or add it to your PATH.")
265+
return
266+
267+
out_template = os.path.join(settings['save_path'], '%(title)s.%(ext)s')
268+
bitrate = str(settings.get('mp3_bitrate', DEFAULT_CONFIG['mp3_bitrate']))
269+
270+
ydl_opts = {
271+
'format': 'bestaudio/best',
272+
'outtmpl': out_template,
273+
'ffmpeg_location': ffmpeg_path,
274+
'noplaylist': True,
275+
'progress_hooks': [progress_hook],
276+
'quiet': False,
277+
'no_warnings': True,
278+
'postprocessors': [{
279+
'key': 'FFmpegExtractAudio',
280+
'preferredcodec': 'mp3',
281+
'preferredquality': bitrate
282+
}],
283+
}
284+
285+
print(f"\n📥 Downloading AUDIO (MP3):")
286+
print(f" Bitrate: {bitrate} kbps")
287+
print(f" Save Path: {settings['save_path']}\n")
288+
289+
try:
290+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
291+
ydl.download([url])
292+
print("✓ Audio download complete.")
293+
except Exception as e:
294+
print(f"Error downloading audio: {e}")
295+
296+
# -------------------- Main --------------------
297+
def main():
298+
print(" (By Utilizing This Software, You Agree To The Terms And Conditions. \n https://github.com/yosuf-e/OptiFetch/blob/main/README.md)")
299+
print("\n🎥 Welcome to OptiFetch!")
300+
301+
while True:
302+
display_main_menu()
303+
choice = input("Select option (1-7): ").strip()
304+
if choice == '1':
305+
url = input("\nEnter video URL: ").strip()
306+
download_video(url)
307+
elif choice == '2':
308+
url = input("\nEnter video/audio URL (will produce MP3): ").strip()
309+
download_audio_mp3(url)
310+
elif choice == '3':
311+
display_settings(get_current_settings())
312+
elif choice == '4':
313+
temporary_settings_menu()
314+
elif choice == '5':
315+
permanent_settings_menu()
316+
elif choice == '6':
317+
confirm = input("\nReset to default settings? (y/n): ").strip().lower()
318+
if confirm == 'y':
319+
save_config(DEFAULT_CONFIG.copy())
320+
current_settings.clear()
321+
print("✓ Reset to defaults!")
322+
elif choice == '7':
323+
print("\nGoodbye!")
324+
break
325+
else:
326+
print("Invalid choice! Please try again.")
327+
328+
if __name__ == "__main__":
329+
try:
330+
main()
331+
except KeyboardInterrupt:
332+
print("\nInterrupted by user. Exiting.")
333+
sys.exit(0)

0 commit comments

Comments
 (0)