generated from Code-Institute-Org/python-essentials-template
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrun.py
579 lines (446 loc) · 14 KB
/
run.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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
"""
Main app functions.
"""
import time
import sys
import os
from simple_term_menu import TerminalMenu
from utils import (
JUKEBOX,
LIBRARY,
GENRE_LIST,
SEARCH_MENU
)
from validations import (
validate_menu_choice,
validate_length,
validate_genre,
validate_removal,
validate_year,
link_validation,
validate_empty_input
)
def get_menu_option():
"""
Main menu function enables user to select between
Add, Remove, or Search song.
params:
none
return:
<str> one of the dictionary keys
"""
print("------------------------------------------------------------\n")
print("Welcome to...")
print("__ ___ _ _ _ _ __ ___")
print("\ \ / (_) | | | | | | | | \ \ / | |")
print(" \ \ / / _ __| | | |_ _| | _____| |__ ___ \ V /| |")
print(" \ \/ / | |/ _` | _ | | | | | |/ / _ | '_ \ / _ \ > < | |")
print(" \ / | | (_| || |__| | |_| | | __| |_) | (_) / . \|_|")
print(" \/ |_|\__,_| \____/ \__,_|_|\_\___|_.__/ \___/_/ \_(_)\n")
print("------------------------------------------------------------")
print("Please begin by selecting from the menu below:\n")
for option, description in MENU_HANDLERS.items():
print(option + ')', description.__name__.replace('_', ' ').title())
print("")
while True:
main_menu_choice = input(
"""Please select a menu choice type from A, B, C, D:\n"""
).upper()
if validate_menu_choice(main_menu_choice, MENU_HANDLERS):
break
return main_menu_choice
def handle_menu_selection(option):
"""
Takes users main menu selection and moves to the correct function.
param:
option: function name from MENU_HANDLERS
return:
None
"""
print("------------------------------------------------------------\n")
print(
f"You chose to {MENU_HANDLERS[option].__name__.replace('_', ' ')}.\n"
)
MENU_HANDLERS[option]()
main()
def show_library():
"""
Shows scrollable version of entire library and displays link
when selected. Shows restart as an option also.
params:
none
return:
<lst> all_songs populated if song is in library.
"""
os.system('clear')
all_songs = []
for info in LIBRARY:
all_songs.append(info)
all_songs.sort()
all_songs.append(['Restart'])
show_all_menu = TerminalMenu(
[" - ".join(song[:2]).title() for song in all_songs]
)
menu = show_all_menu.show()
library_choice = all_songs[menu]
if library_choice == all_songs[-1]:
print('Restart Jukebox...\n')
time.sleep(1.5)
reboot()
else:
os.system('clear')
print("You selected:\n")
print("\n".join(library_choice[:4]).title() + "\n")
url = library_choice[-1]
print("Video link (copy and paste url):\n")
print(f"{url}\n")
return_buttons = ['Back to list', 'Home']
back_menu = TerminalMenu(return_buttons)
button = back_menu.show()
back_choice = return_buttons[button]
if back_choice == 'Home':
reboot()
else:
show_library()
return all_songs
def add_song():
"""
Enables user to add songs to the library via these inputs:
artist name, song title, genre, year, url.
Creates a new list (new_song) which represents the new song data
params:
none
return:
<list> new_song a list of the added data
"""
print(
"To add a song to JukeboX please follow the steps below. \n"
)
new_song = []
while True:
add_artist = input("Please enter artist name:\n").lower()
print("")
if (
validate_length(len(add_artist)) and
validate_empty_input(add_artist)
):
new_song.append(add_artist)
break
while True:
add_title = input("Please enter song title:\n")
print("")
if (
validate_length(len(add_title)) and
validate_empty_input(add_title)
):
new_song.append(add_title)
break
while True:
add_genre = input("Please enter genre:\n").lower()
if validate_empty_input(add_genre):
new_song.append(add_genre.lower())
print("")
break
if add_genre not in GENRE_LIST:
GENRE_LIST.append(add_genre.lower())
while True:
try:
add_year = input("Please enter year of release:\n")
if validate_year(int(add_year)):
new_song.append(add_year)
break
except ValueError:
print(
"""\nNot a number! Please input a number.
(example 1989)\n"""
)
search = (
f'https://www.youtube.com/results?search_query='
f'{add_artist.replace(" ", "")}+{add_title.replace(" ", "")}'
)
print("Use the search link to find a video of your choice.")
print("Then copy and paste the link below.\n")
time.sleep(1)
print(f"Link to search for video (copy/paste):\n{search}\n")
time.sleep(2)
while True:
add_link = input("Paste url here:\n")
if link_validation(add_link):
new_song.append(add_link)
break
search_for_duplicates(new_song, add_year)
return new_song
def remove_song():
"""
Enables user to remove song from library by displaying a menu
to select from.
First validates that the song is in library and if not displays
another input attempt.
Ability to cancel remove song and return to main menu using 'c'
as an input.
param:
none
return:
<str> input by user in order to search library
"""
print(
"To remove a song from JukeboX please follow the steps below. \n"
)
while True:
delete_song_input = input(
"Enter some song information to remove ((c) cancel):\n"
).lower()
print("")
if delete_song_input == 'c':
print('Cancelled restarting Jukebox...\n')
time.sleep(2)
reboot()
if validate_empty_input(delete_song_input):
library_values = []
for song in LIBRARY:
if delete_song_input in song:
library_values += song
if validate_removal(delete_song_input, library_values):
break
get_remove_options(delete_song_input)
return delete_song_input
def get_remove_options(search):
"""
If song is found in library this function will display a menu
from which the user can choose which song to delete.
param:
search: <str> input by user to delete it.
return:
delete_menu: <lst> list of options to delete
"""
search_list = JUKEBOX.findall(search)
delete_list = []
for item in search_list:
row_num = item.row
row_info = JUKEBOX.row_values(row_num)
delete_list.append(row_info[:2])
delete_list.sort()
delete_list.append(['Cancel'])
options = TerminalMenu(
[" ".join(i[:4]).title() for i in delete_list]
)
print("------------------------------------------------------------\n")
print("Choose from the list below and press Enter to delete song:\n")
delete_menu = options.show()
delete = delete_list[delete_menu]
deleted_song = " - ".join(delete[:2]).title()
if delete == delete_list[-1]:
print('Restarting Jukebox...\n')
time.sleep(2)
reboot()
else:
JUKEBOX.delete_rows(row_num)
print("Deleting...")
print(f"\n{deleted_song} from JukeboX...\n")
time.sleep(1)
print('Song deleted. Restarting JukeboX...')
time.sleep(3.5)
reboot()
return delete_menu
def search_for_duplicates(entry, year):
"""
Validates if entry is already in library
If it is it is displayed as an option.
If it isn't it is added
params:
entry: <lst> data of the new song
year: <str> the year input as string to concat with list,
reverted to integer.
return:
none
"""
print("------------------------------------------------------------\n")
if entry in JUKEBOX.get_all_values()[1:]:
print("Song already in JukeBox!\n")
get_songs_from_library(entry[4])
else:
print("Adding:\n")
print(' - '.join(entry[:2]).title())
print("")
entry[3] = int(year)
add_song_to_library(entry)
def add_song_to_library(data):
"""
Updates google sheet by adding new song
param:
data: <lst> list of new song data
return:
none
"""
print("Updating library...\n")
JUKEBOX.append_row(data)
time.sleep(3)
print("Library updated. Restarting JukeboX...\n")
time.sleep(2)
reboot()
def search_library():
"""
Search menu options.
Provides search options for the user to select from.
param:
none
return:
search_choice: <str> value of SEARCH_MENU
"""
print("Please select a search method from the list below:\n")
for option, description in SEARCH_MENU.items():
print(option + ')', description.title())
print("")
while True:
search_choice = input(
"""Please select a search type from A, B, C, D:\n"""
).upper()
if validate_menu_choice(search_choice, SEARCH_MENU):
get_search_type(search_choice)
break
return search_choice
def get_search_type(option):
"""
Takes search type option selected by user and provides input method.
Depending on search type!
param:
option: <str> choice from SEARCH_MENU keys.
return:
user_Search: <str> validated by length
genre_input: <str>
year: <int> to search library for an integer as numbers are not
found.
"""
search_name = SEARCH_MENU[option]
print("------------------------------------------------------------\n")
print(f"You selected to search via {option}) {search_name}.\n")
if option in ('A', 'B'):
while True:
user_search = input(f"Enter {search_name}:\n")
print("")
if validate_length(len(user_search)):
break
print("Searching library...\n")
time.sleep(1)
get_songs_from_library(user_search)
elif option == 'C':
print("Please select from the list of genres below.\n")
get_genre_list()
while True:
genre_input = input("Enter Genre:\n")
print("")
if validate_genre(genre_input):
print(f"Searching library for {genre_input.title()}...\n")
break
get_songs_from_library(genre_input)
else:
while True:
try:
year = int(input("Enter year between 1900 and now:\n"))
print("")
if validate_year(year):
print(f"Searching library for {year}...\n")
time.sleep(1)
get_songs_from_library(str(year))
break
except ValueError:
print(
"""\nNot a number! Please input a number.
(example 1989)\n"""
)
return user_search, genre_input, year
def get_genre_list():
"""
Populates genre list with just one instance of each genre in the
library.
param:
none
return:
short_genre_list: <lst> list of one instance of each genre.
"""
short_genre_list = []
for genre in GENRE_LIST:
if genre not in short_genre_list:
short_genre_list.append(genre)
short_genre_list.sort()
for item in short_genre_list:
print(item.title())
print("")
return short_genre_list
def get_songs_from_library(search_input):
"""
Takes input information from chosen search after validation
and searches through the library.
Adds available songs to playlist list.
param:
search_input: <str> defined by user inputs
return:
is_song_available: <bool> True if song in library
"""
is_song_available = False
playlist = []
for tracks in LIBRARY:
for data in tracks:
if search_input in data:
is_song_available = True
playlist.append(tracks)
if not is_song_available:
print(
f"Sorry we cannot find {search_input} in our library. "
f"Please try another search.\n"
)
time.sleep(2)
search_library()
display_user_playlist(playlist)
return is_song_available
def display_user_playlist(songs):
"""
Displays all songs generated in the
playlist for the user to select from. Also displays a restart method
to return to the original main menu if needed.
param:
songs: <lst> A list of songs found in library.
return:
none
"""
songs.sort()
songs.append(['Restart'])
playlist_menu = TerminalMenu(
[" ".join(song[:4]).title() for song in songs]
)
restart = False
while restart is False:
menu = playlist_menu.show()
chosen_song = songs[menu]
if chosen_song == songs[-1]:
restart = True
print('Restarting Jukebox...\n')
time.sleep(1.5)
reboot()
break
print("---------------------------------------------------------")
print("\n".join(chosen_song[:4]).title() + "\n")
url = chosen_song[-1]
print("Video link (copy and paste url):\n")
print(f"{url}\n")
def reboot():
"""
Restarts program and clears terminal
"""
os.system('clear')
python = sys.executable
os.execl(python, python, * sys.argv)
MENU_HANDLERS = {
'A': add_song,
'B': remove_song,
'C': search_library,
'D': show_library
}
def main():
"""
Run all program functions.
"""
main_choice = get_menu_option()
handle_menu_selection(main_choice)
if __name__ == '__main__':
main()