-
Notifications
You must be signed in to change notification settings - Fork 0
/
lyricstamp.py
225 lines (206 loc) · 9.06 KB
/
lyricstamp.py
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
import pygame as pg
import pygame.scrap as scrap
import os
# import argparse
import player_control
import time
# clean up get_texts
from get_lyricstexts import get_texts, cleanup, musicsmatch
from phonetics import add_phonetics
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GRAY = (200, 200, 200)
space = (18, 44)
def stamp_internal(pos):
m = str(int(pos / 60))
s = str(round(pos % 60, 3))
return "[" + m + ":" + s + "]"
def save_lyrics(lines, stamps, out_name):
home = os.path.expanduser("~")
path = home + '/Music/LyricsX'
out_name += '.lrcx'
with open(os.path.join(path, out_name), "w") as f:
[f.write(i + j + '\n') for (i, j) in zip(stamps, lines)]
print('Saved ' + out_name + ' in ' + path)
def welcome():
out_name = 'Settings'
lines = ['']
secs = ['']
stamps = ['']
width, height = (1200, 900)
screen = pg.display.set_mode((width, height))
pg.display.set_caption('LyricStamp: ' + out_name)
return out_name, lines, secs, stamps, screen
def get_song_info(lines):
title, artist = player_control.now_playing()
out_name = title + ' - ' + artist
if not lines:
lines = get_texts(title, artist)
lines.insert(0, out_name)
secs = [0] * len(lines)
stamps = [''] * len(lines)
num_chars = (max([len(i) for i in lines]) + 10)
(width, height) = (max(1200, num_chars * space[0]), max(16 * space[1], 900))
screen = pg.display.set_mode((width, height))
pg.display.set_caption('LyricStamp: ' + out_name)
return out_name, lines, secs, stamps, screen
def print_info(screen, font, counter, lines, stamps, out_name):
def text_to_screen(text, y, color):
try:
text = font.render(text, True, color)
# everything starts at x=20
screen.blit(text, (20, y))
except Exception as e:
raise e
def screen_banner(*vargs):
[text_to_screen(j, 10 + k * space[1], RED) for (k, j) in enumerate(vargs)]
h = space[1]
cursor = None
if counter == -2:
# TODO, streamline below
welcome_msg = ["Let's get the lyrics. A few ways to do that:",
"",
"",
"- Press Right Arrow ➡ and we'll try fetch it from Genius.com.",
"- Press M and we'll try fetch from Musixmatch.com",
"- Press Enter and we'll get the lyrics from your clipboard.",
"- Drop a text file into this window and we'll read it."]
screen_banner(*welcome_msg)
elif counter == -1:
welcome_msg = ["Type in the language code if wanna phonetics added.",
"Press Enter to skip.",
"",
"Supported phonetics:",
"-Romaji for Japanese (type in 'J')",
"-Jyutping for Cantonese (type in 'Y')"]
n = len(welcome_msg)
screen_banner(*welcome_msg)
text_to_screen('Lyrics Preview: ', h*(n+2), BLACK)
for i, l in enumerate(lines[:5]):
yy = h * (i + n+3)
text_to_screen(l, yy, BLACK)
elif 0 <= counter <= len(lines) - 1:
screen_banner("Press:",
"- Down Arrow ⬇ to go to the next line,",
"- Left arrow ⬅ to erase the stamp of the current line,",
"- Up Arrow ⬆ to go back to the stamp of the previous line",
"- Space bar to pause Apple Music")
topleft = (20 + 3.5 * space[0], h * (counter - max(counter, 2) + 8.3))
if counter <= len(lines) - 2 and lines[counter + 1].startswith('[tr]'):
h = 2 * h
cursor = pg.Rect(topleft, (3, h * .9))
for i, l in enumerate(lines):
if counter - 3 < i < max(counter, 2) + 9:
yy = space[1] * (i - max(counter, 2) + 8)
text_to_screen(str(i).zfill(3) + ': ' + stamps[i] + l, yy, BLACK)
elif counter >= len(lines):
screen_banner("Press Enter to end stamping and confirm that", out_name + " will be saved")
return cursor
def main():
pg.init()
font = pg.font.Font('fonts/NotoSansCJK-Light.ttc', 30)
# seems that CJK fonts have a pretty good coverage of western chars. Hard-code for now.
# TODO: check e.g. Korean and Spanish
# font = pg.font.Font('fonts/NotoSerifDisplay-Light.ttf', 30)
# counter:
# -2: Choose the source of text lyrics
# -1: Choose if to add phonetics
# ≥0: Adding stamps
counter = -2
out_name, lines, secs, stamps, screen = welcome()
screen.fill(GRAY)
pg.display.flip()
running = True
clock = pg.time.Clock()
while running:
for e in pg.event.get():
if e.type == pg.QUIT or (e.type == pg.KEYDOWN and e.key == pg.K_ESCAPE):
running = False
# source selection page
if counter == -2:
if e.type == pg.KEYDOWN and (e.key == pg.K_RETURN or e.key == pg.K_m):
# Initialize the scrap module and use the clipboard mode.
scrap.init()
scrap.set_mode(pg.SCRAP_CLIPBOARD)
for t in scrap.get_types():
r = scrap.get(t)
clipboard = (r.decode('UTF-8'))
if e.key == pg.K_RETURN:
lines = cleanup(clipboard)
else:
lines = musicsmatch(clipboard)
out_name, lines, secs, stamps, screen = get_song_info(lines)
counter = -1
if e.type == pg.DROPFILE:
in_name = e.file
with open(in_name) as f:
lines = [line.replace('\n', '') for line in f.readlines() if line.strip()]
out_name, lines, secs, stamps, screen = get_song_info(lines)
counter = -1
if e.type == pg.KEYDOWN and e.key == pg.K_RIGHT:
lines = None
out_name, lines, secs, stamps, screen = get_song_info(lines)
counter = -1
# phonetics page
elif counter == -1:
keys = [pg.K_j, pg.K_y, pg.K_RETURN]
if e.type == pg.KEYDOWN and e.key in keys:
p = None
if e.key == pg.K_j:
p = 'J'
if e.key == pg.K_y:
p = 'Y'
lines = add_phonetics(lines, p)
counter = 0
elif counter >= 0:
if e.type == pg.KEYDOWN:
if e.key == pg.K_RIGHT:
player_control.play_next()
counter = -2
pg.display.set_caption('LyricStamp')
if e.key == pg.K_SPACE and counter > 0:
player_control.play_pause()
if e.key == pg.K_DOWN:
if counter == 0:
stamps[counter] = "[00:00.00]"
# player_control.play()
elif counter <= len(lines) - 1:
# insert new stamp into line; relies on iTunes/Music's internal player's position
pos = player_control.player_position()
secs[counter] = pos
stamps[counter] = stamp_internal(pos)
if counter <= len(lines) - 2 and lines[counter + 1].startswith('[tr]'):
counter += 1
secs[counter] = pos
stamps[counter] = stamp_internal(pos)
counter += 1
if e.key == pg.K_UP or e.key == pg.K_LEFT:
counter -= 1
secs[counter] = 0
stamps[counter] = ''
if counter >= 0 and lines[counter].startswith('[tr]'):
counter -= 1
secs[counter] = 0
stamps[counter] = ''
if e.key == pg.K_UP:
player_control.set_player_position(secs[counter - 1])
if counter >= len(lines) and e.key == pg.K_RETURN:
save_lyrics(lines, stamps, out_name)
screen.fill(GRAY)
screen.fill(GRAY)
cursor = print_info(screen, font, counter, lines, stamps, out_name)
if cursor and time.time() % 1 > 0.5:
pg.draw.rect(screen, RED, cursor)
pg.display.update()
clock.tick(10)
pg.quit()
if __name__ == "__main__":
# parser = argparse.ArgumentParser(description=""".""")
# parser.add_argument("-i", "--in_name",
# help="local file to read static lyrics from; if none supplied, fetch from e.g. genius.com")
# parser.add_argument("--lang",
# help="if the song/book is in Chinese (use C), Japanese (J), or Korean (K).")
# args = parser.parse_args()
# in_name = args.in_name
# lang = args.lang
main()