-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwishes_code.py
More file actions
185 lines (162 loc) · 5.82 KB
/
wishes_code.py
File metadata and controls
185 lines (162 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import os
import sys
import time
import threading
import pygame
import yt_dlp
import imageio_ffmpeg
"""Download the song locally as song.mp3 and then play it."""
# ------------ DOWNLOAD SONG (yt-dlp + ffmpeg) ------------
url = "https://www.youtube.com/watch?v=Gz38Yj09k3A"
target_mp3 = os.path.join(os.getcwd(), "song.mp3")
if os.path.exists(target_mp3):
print("Audio already present:", target_mp3)
else:
print("Downloading audio with yt-dlp...")
ffmpeg_exe = imageio_ffmpeg.get_ffmpeg_exe()
ydl_opts = {
"format": "bestaudio/best",
"outtmpl": os.path.join(os.getcwd(), "song.%(ext)s"),
"prefer_ffmpeg": True,
"ffmpeg_location": ffmpeg_exe,
"postprocessors": [
{
"key": "FFmpegExtractAudio",
"preferredcodec": "mp3",
"preferredquality": "192",
}
],
# ensure stable sample rate for pygame
"postprocessor_args": ["-ar", "44100"],
"quiet": False,
"noprogress": False,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
# After postprocessing, song.mp3 should exist
if not os.path.exists(target_mp3):
# Fallback: find any song.* and rename if needed
for fname in os.listdir(os.getcwd()):
if fname.startswith("song.") and fname.endswith(".mp3"):
target_mp3 = os.path.join(os.getcwd(), fname)
break
print("Downloaded:", target_mp3)
# ------------ SETUP PYGAME ------------
pygame.mixer.init(frequency=44100)
pygame.mixer.music.load(target_mp3)
# Start playing from 1:55 → 115 seconds
start_time = 115
pygame.mixer.music.play(start=start_time)
# ------------ CONTROLS (pause/resume/stop) ------------
pause_flag = threading.Event()
stop_flag = threading.Event()
def input_controls():
global lyric_offset
#print("Controls: p=pause, r=resume, s/q=stop, [ / ] nudge lyrics, o=offset")
while not stop_flag.is_set():
try:
cmd = input().strip().lower()
except EOFError:
break
if cmd == "p":
if not pause_flag.is_set():
pygame.mixer.music.pause()
pause_flag.set()
print("Paused")
elif cmd == "r":
if pause_flag.is_set():
pygame.mixer.music.unpause()
pause_flag.clear()
print("Resumed")
elif cmd == "[":
lyric_offset -= 0.25
print(f"Lyric offset: {lyric_offset:+.2f}s (earlier)")
elif cmd == "]":
lyric_offset += 0.25
print(f"Lyric offset: {lyric_offset:+.2f}s (later)")
elif cmd == "o":
print(f"Lyric offset: {lyric_offset:+.2f}s")
elif cmd in ("s", "q"):
stop_flag.set()
pygame.mixer.music.stop()
print("Stopped")
break
ctrl_thread = threading.Thread(target=input_controls, daemon=True)
ctrl_thread.start()
# ------------ LYRICS WITH TIMING (absolute seconds from playback start) ------------
# The first element of each tuple is an ABSOLUTE timestamp (in seconds)
# measured from when playback starts, not a relative delay.
lyrics = [
(0, "Hoke tetho duur mein"),
(2, "Khoya apne aap nu......"),
(5, "Adda hissa mere dil da"),
(7, "Hoeya tere khilaaf kyun..."),
(10, "Adda dil chaunda ae tenu"),
(12, "Karni chaunda baat kyun..."),
(15, "Tareyaan di roshni de wangu"),
(17, "Gal nal laaja raat nu"),
(19, "Dil mere nu samjhaja"),
(21, "Hathan wich hath tu paaja"),
(24, "Pehlan jo dekhi nahi mein"),
(27, "Aaisi duniya tu dikhaa ja")
]
lyric_offset = 0.0 # allow fine-tuning in real time
def show_lyrics():
# Typewriter effect synced to the audio clock
idx = 0
total = len(lyrics)
line_started = False
typed_idx = 0
char_delay = 0.01 # default; recomputed per line based on available window
while idx < total and not stop_flag.is_set():
if pause_flag.is_set():
time.sleep(0.02)
continue
pos_ms = pygame.mixer.music.get_pos()
if pos_ms < 0:
time.sleep(0.02)
continue
pos = pos_ms / 1000.0 # seconds since play() (doesn't advance while paused)
cue_time, text = lyrics[idx]
cue_time = float(cue_time)
# Wait for cue
if not line_started:
if pos + lyric_offset >= cue_time:
# Type so the last character lands exactly at the next cue.
if idx + 1 < total:
char_delay = 0.12
else:
# Last line: use a reasonable default
char_delay = 0.12
line_started = True
typed_idx = 0
# Immediately render first character at cue
# (the loop below will compute target based on elapsed)
else:
time.sleep(0.01)
continue
# How many characters should be visible by now?
elapsed = (pos + lyric_offset) - cue_time
target = int(elapsed / char_delay) + 1 # start typing at cue
target = max(0, min(target, len(text)))
# Print any missing characters
while typed_idx < target and not stop_flag.is_set():
sys.stdout.write(text[typed_idx])
sys.stdout.flush()
typed_idx += 1
# If the line is complete, newline and advance
if typed_idx >= len(text):
sys.stdout.write("\n")
sys.stdout.flush()
idx += 1
line_started = False
else:
time.sleep(0.01)
lyrics_thread = threading.Thread(target=show_lyrics, daemon=True)
lyrics_thread.start()
# Keep script running until song ends
try:
while pygame.mixer.music.get_busy() and not stop_flag.is_set():
time.sleep(0.2)
finally:
stop_flag.set()