Skip to content

Commit 039854b

Browse files
committed
fixed: bye function
1 parent a07eed2 commit 039854b

File tree

1 file changed

+80
-70
lines changed

1 file changed

+80
-70
lines changed

melody.py

Lines changed: 80 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ def __init__(self):
2020
self.currently_playing = None
2121
self.playback_thread = None
2222
self.is_paused = False
23-
self.manual_stop = False
2423
self.BASE_URL = 'https://www.youtube.com/watch?v='
2524
self.currSong = ""
2625
self.autoplay = True
2726
self.downloaded_tracks = []
28-
27+
self.max_cache_size = 10
28+
self.queue = []
29+
self.queue_index = 0
2930

3031
def print(self, text, color):
3132
builtins.print(colored(text, color))
@@ -39,54 +40,57 @@ def do_autoplay(self, arg):
3940
status = "ON" if self.autoplay else "OFF"
4041
self.print(f"Autoplay is now {status} 🔁", "cyan")
4142

42-
4343
def do_search(self, searchString):
4444
"Search for a song: search <song name>"
45+
if not searchString.strip():
46+
self.print("Please provide a search query!", "red")
47+
return
48+
4549
search_results = self.youtube_music.search(searchString)
4650
self.filtered_results = {}
4751
index = 1
4852
for result in search_results:
49-
if result["category"] in ["Songs", "Videos"]:
53+
if result.get("category") in ["Songs", "Videos"] and "videoId" in result:
5054
self.filtered_results[index] = [result['videoId'], result['title']]
51-
self.print(f"{index}. {result['title']} - {result['duration']}", "yellow")
55+
self.print(f"{index}. {result['title']} - {result.get('duration', 'Unknown Duration')}", "yellow")
5256
index += 1
5357

5458
def do_play(self, arg):
5559
try:
56-
song_id = self.filtered_results[int(arg)][0]
57-
self.currSong = self.filtered_results[int(arg)][1]
60+
index = int(arg)
61+
song_id = self.filtered_results[index][0]
62+
self.currSong = self.filtered_results[index][1]
5863
mp3_file = self.downloadSong(song_id)
5964
if mp3_file:
6065
self.playSong(mp3_file)
6166
self.generate_queue(song_id)
67+
except (KeyError, ValueError):
68+
self.print("Invalid index or no search results available.", "red")
6269
except Exception as e:
6370
self.print(f"Error: {e}", "red")
64-
71+
6572
def generate_queue(self, current_video_id):
6673
"Fetch related songs and build a queue"
6774
self.queue = []
68-
related_songs = self.youtube_music.get_watch_playlist(current_video_id)["tracks"]
69-
70-
if not related_songs:
71-
self.print("No related songs found!", "red")
72-
return
73-
74-
for track in related_songs:
75-
self.queue.append((track["videoId"], track["title"]))
76-
7775
self.queue_index = 0
78-
self.print(f"🎶 Queue Updated! {len(self.queue)} songs added.", "cyan")
76+
try:
77+
related_songs = self.youtube_music.get_watch_playlist(current_video_id).get("tracks", [])
78+
if not related_songs:
79+
self.print("No related songs found!", "red")
80+
return
81+
for track in related_songs:
82+
self.queue.append((track["videoId"], track["title"]))
83+
self.print(f"🎶 Queue Updated! {len(self.queue)} songs added.", "cyan")
84+
except Exception as e:
85+
self.print(f"Error fetching related songs: {e}", "red")
7986

8087
def do_queue(self, arg):
8188
"Show the queue"
82-
idx = 1
8389
self.print("\n🎶 Queue", color="cyan")
84-
for songTuple in self.queue:
90+
for idx, songTuple in enumerate(self.queue, start=1):
8591
self.print(f"{idx}. {songTuple[1]}", color="cyan")
86-
idx+=1
8792

8893
def play_next(self):
89-
"Play the next song in the queue"
9094
if self.queue_index < len(self.queue) - 1:
9195
self.queue_index += 1
9296
next_song = self.queue[self.queue_index]
@@ -99,16 +103,9 @@ def play_next(self):
99103
self.play_next()
100104

101105
def do_next(self, arg):
102-
"Skip to the next song in queue"
103-
if self.queue_index < len(self.queue) - 1:
104-
self.play_next()
105-
else:
106-
self.print("🎵 Queue empty! Fetching new songs...", "yellow")
107-
self.generate_queue(self.queue[self.queue_index][0]) # Get new queue
108-
self.play_next()
106+
self.play_next()
109107

110108
def do_prev(self, arg):
111-
"Play the previous song in queue"
112109
if self.queue_index > 0:
113110
self.queue_index -= 1
114111
prev_song = self.queue[self.queue_index]
@@ -119,47 +116,45 @@ def do_prev(self, arg):
119116
self.print("🚫 No previous songs!", "red")
120117

121118
def do_pause(self, arg):
122-
"Pause the currently playing song"
123119
if pygame.mixer.music.get_busy() and not self.is_paused:
124120
pygame.mixer.music.pause()
125121
self.is_paused = True
126122
self.print("Music paused ⏸️", "yellow")
127123

128124
def do_resume(self, arg):
129-
"Resume a paused song"
130125
if self.is_paused:
131126
pygame.mixer.music.unpause()
132127
self.is_paused = False
133128
self.print("Music resumed ▶️", "green")
134129

135-
136-
def do_stop(self, arg):
137-
"Stop the currently playing song and prevent autoplay"
138-
if self.currently_playing:
139-
self.manual_stop = True
140-
pygame.mixer.music.stop()
141-
self.currently_playing = None
142-
self.is_paused = False
143-
self.clear_now_playing()
144-
self.print("Music stopped ⏹️", "yellow")
145-
146-
147130
def do_bye(self, arg):
148-
"Exit the application"
149-
self.print("Goodbye! 👋", "yellow")
131+
self.print("Shutting down Melody CLI... 👋", "yellow")
132+
try:
133+
if pygame.mixer.get_init():
134+
pygame.mixer.music.stop()
135+
pygame.mixer.quit()
136+
pygame.quit()
137+
self.print("Audio system closed 🔇", "magenta")
138+
except Exception as e:
139+
self.print(f"Error stopping audio: {e}", "red")
140+
141+
if self.playback_thread and self.playback_thread.is_alive():
142+
self.print("Waiting for playback thread to close... ⏳", "yellow")
143+
self.playback_thread.join(timeout=2)
144+
145+
self.print("Goodbye! 👋", "green")
150146
sys.exit(0)
151147

152148
def downloadSong(self, videoID):
153-
"Check if song exists in cache, else download it."
154149
file_path = f"temp_audio/{videoID}.mp3"
155-
150+
156151
if os.path.exists(file_path):
157152
self.print(f"🎵 Using cached song: {file_path}", "green")
158153
return file_path
159154

160155
url = f"{self.BASE_URL}{videoID}"
161156
os.makedirs("temp_audio", exist_ok=True)
162-
157+
163158
ydl_opts = {
164159
'format': 'bestaudio/best',
165160
'outtmpl': file_path.replace(".mp3", ".%(ext)s"),
@@ -171,51 +166,66 @@ def downloadSong(self, videoID):
171166
'preferredquality': '192',
172167
}],
173168
}
174-
169+
175170
try:
176171
with YoutubeDL(ydl_opts) as ydl:
177172
ydl.extract_info(url, download=True)
173+
self.cleanup_cache()
178174
return file_path
179175
except Exception as e:
180176
self.print(f"❌ Error downloading: {e}", "red")
181177
return None
182178

179+
def cleanup_cache(self):
180+
"Keep only the latest N files in the temp_audio folder"
181+
try:
182+
files = [os.path.join("temp_audio", f) for f in os.listdir("temp_audio") if f.endswith(".mp3")]
183+
files.sort(key=os.path.getmtime, reverse=True)
184+
for f in files[self.max_cache_size:]:
185+
os.remove(f)
186+
self.print(f"Deleted old cached file: {f}", "magenta")
187+
except Exception as e:
188+
self.print(f"Error cleaning up cache: {e}", "red")
189+
183190
def playSong(self, mp3_file):
184191
def _play():
185-
pygame.init()
186-
pygame.mixer.init()
187-
pygame.mixer.music.load(mp3_file)
188-
pygame.mixer.music.play()
189-
self.is_paused = False
190-
self.manual_stop = False
191-
self.display_now_playing()
192-
193-
while pygame.mixer.music.get_busy() or self.is_paused:
194-
time.sleep(1)
195-
196-
was_manual = self.manual_stop
197-
198-
self.manual_stop = False
199-
200-
if not was_manual and self.autoplay and self.queue_index < len(self.queue) - 1:
201-
self.play_next()
192+
try:
193+
pygame.init()
194+
pygame.mixer.init()
195+
pygame.mixer.music.load(mp3_file)
196+
pygame.mixer.music.play()
197+
self.is_paused = False
198+
self.display_now_playing()
199+
200+
while pygame.mixer.music.get_busy() or self.is_paused:
201+
time.sleep(1)
202+
203+
if self.autoplay and self.queue_index < len(self.queue) - 1:
204+
self.play_next()
205+
except Exception as e:
206+
self.print(f"Playback error: {e}", "red")
202207

203208
self.currently_playing = mp3_file
204209
self.playback_thread = threading.Thread(target=_play)
205210
self.playback_thread.start()
206211

207-
208212
def display_now_playing(self):
209213
print("\n" + "="*40)
210214
print(f"🎶 NOW PLAYING: {colored(self.currSong, 'cyan')}")
211215
print("="*40 + "\n")
212216

213217
def clear_now_playing(self):
214-
print("\n" + " "*40, end="\r")
215218
print("\n" + "="*40)
216219
print("🎵 No song is currently playing.")
217220
print("="*40 + "\n")
218221

219222

220223
if __name__ == "__main__":
221-
MelodyCLI().cmdloop()
224+
try:
225+
MelodyCLI().cmdloop()
226+
except KeyboardInterrupt:
227+
print("\n💥 Keyboard Interrupt! Shutting down Melody CLI...")
228+
pygame.mixer.music.stop()
229+
pygame.mixer.quit()
230+
pygame.quit()
231+
sys.exit(0)

0 commit comments

Comments
 (0)