6
6
import tkinter
7
7
import time
8
8
import datetime
9
- # from PIL import Image
9
+ from PIL import Image
10
10
11
11
# thumbnail image functions
12
- '''
12
+
13
13
def makeThumbnail (imagepath ):
14
- IM=Image.open(imagepath)
15
- print(IM.size)
16
- '''
14
+ app .debug (f'making thumbnail for { imagepath } ' )
15
+ try :
16
+ IM = Image .open (imagepath )
17
+ IM = IM .resize ((150 ,150 ))
18
+ thumbnailpath = f'{ imagepath [:- 4 ]} _thumbnail.gif'
19
+ IM .save (thumbnailpath )
20
+ del IM
21
+ return thumbnailpath
22
+ except Exception as e :
23
+ app .error (f'error making thumbnail: { e } ' )
24
+ return 'no_image.gif'
25
+
17
26
# misc functions
18
27
19
28
def reorderLibrary ():
@@ -60,8 +69,13 @@ def milliFormat(milliseconds,subtractH=True):
60
69
# player functions
61
70
62
71
def closePlayerWindow ():
63
- vlcPlayer .set_pause (1 )
64
- updateLeftoffTime ()
72
+ try :
73
+ state = str (vlcPlayer .get_state ()).lower ().replace ('state.' ,'' )
74
+ if state == 'playing' or state == 'paused' :
75
+ vlcPlayer .set_pause (1 )
76
+ updateLeftoffTime ()
77
+ except :
78
+ pass
65
79
return True
66
80
67
81
def updateLeftoffTime ():
@@ -143,7 +157,7 @@ def queueClick(name):
143
157
fullfilename = f'{ directory } /{ filename } '
144
158
playFile (fullfilename )
145
159
library [currentlyPlaying ]['leftoff' ]['file' ] = filename
146
- app .setLabel ('player_current_playing_title' ,' ' .join (app .getListBox ("list_queue" )))
160
+ app .setLabel ('player_current_playing_title' ,cropText ( ' ' .join (app .getListBox ("list_queue" )), cropnum = 17 ))
147
161
app .setLabel ('player_author' ,library [currentlyPlaying ]['author' ])
148
162
app .setLabel ('player_name' ,library [currentlyPlaying ]['name' ])
149
163
@@ -167,17 +181,34 @@ def preparePlayer(entry):
167
181
app .showSubWindow ('window_player' )
168
182
169
183
184
+ def rateScaleHandler ():
185
+ settings = {1 :0.3 , 2 :0.50 , 3 :0.8 , 4 :0.9 , 5 :1.0 , 6 :1.1 , 7 :1.20 , 8 :1.50 , 9 :1.80 , 10 :2.0 }
186
+ currate = app .getScale ('scale_player_rate' )
187
+ setting = int (round (currate ))
188
+ newrate = settings [setting ]
189
+ app .setScale ('scale_player_rate' ,setting ,callFunction = False )
190
+ try :
191
+ vlcPlayer .set_rate (newrate )
192
+ except BaseException as e :
193
+ app .warn (f'cannot change playback rate: { e } ' )
194
+
195
+ def volumeScaleHandler ():
196
+ vlc .audio_set_volume (app .getScale ('scale_player_volume' ))
197
+
170
198
171
199
def playerButtons (name ):
200
+ state = str (vlcPlayer .get_state ()).lower ().replace ('state.' ,'' )
172
201
if name == 'btn_player_play' :
173
- vlcPlayer .play ()
174
- app .hideButton ('btn_player_play' )
175
- app .showButton ('btn_player_pause' )
202
+ if state == 'paused' :
203
+ vlcPlayer .set_pause (0 )
204
+ app .hideButton ('btn_player_play' )
205
+ app .showButton ('btn_player_pause' )
176
206
elif name == 'btn_player_pause' :
177
- updateLeftoffTime ()
178
- vlcPlayer .set_pause (1 )
179
- app .showButton ('btn_player_play' )
180
- app .hideButton ('btn_player_pause' )
207
+ if state == 'playing' :
208
+ updateLeftoffTime ()
209
+ vlcPlayer .set_pause (1 )
210
+ app .showButton ('btn_player_play' )
211
+ app .hideButton ('btn_player_pause' )
181
212
elif name == 'btn_player_fastforward' :
182
213
vlcPlayer .set_time (vlcPlayer .get_time ()+ 30000 )
183
214
elif name == 'btn_player_fastbackbackward' :
@@ -186,6 +217,10 @@ def playerButtons(name):
186
217
nextFile ()
187
218
elif name == 'btn_player_previous' :
188
219
previousFile ()
220
+ elif name == 'btn_player_resetvolume' :
221
+ app .setScale ('scale_player_volume' ,100 ,callFunction = True )
222
+ elif name == 'btn_player_resetrate' :
223
+ app .setScale ('scale_player_rate' ,5 ,callFunction = True )
189
224
190
225
# external file loading stuff
191
226
@@ -299,8 +334,20 @@ def toolbarButtons(name):
299
334
sortLibraryEntries ()
300
335
elif name == 'radio_color' :
301
336
changeColorScheme (app .getMenuRadioButton ('Color scheme' ,'radio_color' ))
337
+ elif name == 'Clear library' :
338
+ if app .yesNoBox ('Confirm library deletion' ,'Are you sure you want to clear the library? This will remove all your entries & forget their timestamps.\n Note: this will NOT remove the files themselves, you can always re-add the entries.' ):
339
+ try :
340
+ global library
341
+ app .info ('clearing library' )
342
+ os .remove ('library.json' )
343
+ library = {}
344
+ sortLibraryEntries ()
345
+ app .infoBox ('Clear completed' ,'Library has been cleared.' )
346
+ except Exception as e :
347
+ app .error (f'error deleting library.json: { e } ' )
348
+ app .warningBox (f'Error' ,'Error clearing library: {e}' )
302
349
elif name == 'About' :
303
- app .infoBox ('About' ,f'Pycasts version { version } ( build { buildDate } ) \n Made by Anne mocha (@mocchapi) for Lilli snaog :>\n Follow development at https://github.com/mocchapi/PyCasts' )
350
+ app .infoBox ('About' ,f'Version { version } - build { buildDate } \n \n PyCasts is an podcast/audiobook player made by Anne mocha (@mocchapi) for Lilli snaog :>\n \n Follow development at https://github.com/mocchapi/PyCasts' )
304
351
305
352
def newLibButtons (name ):
306
353
if name == 'btn_newLib_cancel' :
@@ -323,10 +370,11 @@ def newLibButtons(name):
323
370
thumbnail = 'no_image.gif'
324
371
else :
325
372
thumbnail = entries ['entry_newLib_thumbnail' ]
326
- if os .path .isfile (thumbnail ) and thumbnail .endswith ('.gif' ):
327
- tempdict ['thumbnail' ] = thumbnail
373
+ if os .path .isfile (thumbnail ):
374
+ generatedThumb = makeThumbnail (thumbnail )
375
+ tempdict ['thumbnail' ] = generatedThumb
328
376
else :
329
- app .warningBox ('Invalid file' ,'The entered file is invalid. \n Must be an image of type .gif @ 150x150 .\n This entry can be left blank.' )
377
+ app .warningBox ('Invalid thumbnail file' ,'The entered thumbnail file is invalid.\n This entry can be left blank.' )
330
378
return
331
379
tempdict ['leftoff' ] = {'file' :None ,'time' :0 }
332
380
global library
@@ -397,11 +445,11 @@ def editEntry(entry):
397
445
thumbnail = 'no_image.gif'
398
446
else :
399
447
thumbnail = entries ['entry_editLib_thumbnail' ]
400
- if os .path .isfile (thumbnail ) and thumbnail .endswith ('.gif' ):
401
- tempdict ['thumbnail' ] = thumbnail
448
+ if os .path .isfile (thumbnail ):
449
+ generatedThumb = makeThumbnail (thumbnail )
450
+ tempdict ['thumbnail' ] = generatedThumb
402
451
else :
403
- app .warningBox ('Invalid file' ,'The entered thumbnail is invalid.\n Must be an image of type .gif @ 150x150.\n This entry can be left blank.' )
404
- return
452
+ app .warningBox ('Invalid thumbnail file' ,'The entered thumbnail file is invalid.\n This entry can be left blank.' )
405
453
tempdict ['leftoff' ] = {'file' :None ,'time' :0 }
406
454
global library
407
455
tempdict ['leftoff' ] = library [entry ]['leftoff' ]
@@ -422,42 +470,46 @@ def removeEntry(name):
422
470
app .error (f'couldnt remove entry { name } : { e } ' )
423
471
424
472
def buildEntry (entrydict ):
425
- app .debug (f'building entry { entrydict ["name" ]} ' )
426
- global libraryX
427
- global libraryY
428
- name = entrydict ['name' ]
429
- author = entrydict ['author' ]
430
- thumbnail = entrydict ['thumbnail' ]
431
- directory = entrydict ['directory' ]
432
-
433
- app .openScrollPane ('frame_libraryView' )
434
- app .startLabelFrame (f'libraryEntry_frame_{ name } ' ,libraryY ,libraryX ,label = '' )
435
- app .setSticky ('n' )
436
- app .setStretch ('column' )
437
- app .addImage (f'libraryEntry_thumbnail_{ name } ' ,thumbnail )
438
- try :
439
- app .setImageSize (f'libraryEntry_thumbnail_{ name } ' ,150 ,150 )
440
- except :
441
- pass
442
- #app.setImageMouseOver(f'libraryEntry_thumbnail_{name}','play_button.gif')
443
473
try :
444
- app .createRightClickMenu (f'libraryEntry_rClick_{ name } ' )
445
- app .addMenuItem (f'libraryEntry_rClick_{ name } ' ,f'Edit { name } ' ,editEntryButton )
446
- app .addMenuItem (f'libraryEntry_rClick_{ name } ' ,f'Remove { name } ' ,removeEntryButton )
447
- except :
448
- pass
449
- app .setImageRightClick (f'libraryEntry_thumbnail_{ name } ' ,f'libraryEntry_rClick_{ name } ' )
450
- app .setImageSubmitFunction (f'libraryEntry_thumbnail_{ name } ' ,libraryButton )
451
- app .setImagePadding (f'libraryEntry_thumbnail_{ name } ' ,[20 ,20 ])
452
- app .addLabel (f'libraryEntry_name_{ name } ' ,cropText (name ,cropnum = 30 ))
453
- app .addLabel (f'libraryEntry_author_{ name } ' ,cropText (author ,cropnum = 30 ))
454
- app .stopLabelFrame ()
455
- app .stopScrollPane ()
456
- if libraryX < 4 :
457
- libraryX += 1
458
- else :
459
- libraryX = 0
460
- libraryY += 1
474
+ app .debug (f'building entry { entrydict ["name" ]} ' )
475
+ global libraryX
476
+ global libraryY
477
+ name = entrydict ['name' ]
478
+ author = entrydict ['author' ]
479
+ thumbnail = entrydict ['thumbnail' ]
480
+ directory = entrydict ['directory' ]
481
+
482
+ app .openScrollPane ('frame_libraryView' )
483
+ app .startLabelFrame (f'libraryEntry_frame_{ name } ' ,libraryY ,libraryX ,label = '' )
484
+ app .setSticky ('n' )
485
+ app .setStretch ('column' )
486
+ app .addImage (f'libraryEntry_thumbnail_{ name } ' ,thumbnail )
487
+ try :
488
+ app .setImageSize (f'libraryEntry_thumbnail_{ name } ' ,150 ,150 )
489
+ except :
490
+ pass
491
+ #app.setImageMouseOver(f'libraryEntry_thumbnail_{name}','play_button.gif')
492
+ try :
493
+ app .createRightClickMenu (f'libraryEntry_rClick_{ name } ' )
494
+ app .addMenuItem (f'libraryEntry_rClick_{ name } ' ,f'Edit { name } ' ,editEntryButton )
495
+ app .addMenuItem (f'libraryEntry_rClick_{ name } ' ,f'Remove { name } ' ,removeEntryButton )
496
+ except :
497
+ pass
498
+ app .setImageRightClick (f'libraryEntry_thumbnail_{ name } ' ,f'libraryEntry_rClick_{ name } ' )
499
+ app .setImageSubmitFunction (f'libraryEntry_thumbnail_{ name } ' ,libraryButton )
500
+ app .setImagePadding (f'libraryEntry_thumbnail_{ name } ' ,[20 ,20 ])
501
+ app .addLabel (f'libraryEntry_name_{ name } ' ,cropText (name ,cropnum = 30 ))
502
+ app .addLabel (f'libraryEntry_author_{ name } ' ,cropText (author ,cropnum = 30 ))
503
+ app .stopLabelFrame ()
504
+ app .stopScrollPane ()
505
+ if libraryX < 4 :
506
+ libraryX += 1
507
+ else :
508
+ libraryX = 0
509
+ libraryY += 1
510
+ except Exception as e :
511
+ app .error (f'could not build entry { entrydict ["name" ]} : { e } ' )
512
+ app .warningBox ('Library error' ,f'could not build entry { entrydict ["name" ]} : { e } ' )
461
513
462
514
def mainUI ():
463
515
app .setFont (size = 15 , family = "Open Sans" )
@@ -471,14 +523,14 @@ def mainUI():
471
523
app .stopScrollPane ()
472
524
app .stopLabelFrame ()
473
525
474
- app .addMenuList ('Library' ,['Add new entry' ,'Sort library' ],toolbarButtons )
526
+ app .addMenuList ('Library' ,['Add new entry' ,'Sort library' , 'Clear library' ],toolbarButtons )
475
527
app .addMenuList ('View' ,['About' ,'Open player' ],toolbarButtons )
476
528
app .addSubMenu ('View' ,'Ttk theme' )
477
529
478
530
479
531
480
532
def playerUI ():
481
- app .startSubWindow ('window_player' ,'Player' ,transient = False ,modal = False )
533
+ app .startSubWindow ('window_player' ,'PyCasts Player' ,transient = False ,modal = False )
482
534
app .setStopFunction (closePlayerWindow )
483
535
app .setFont (size = 12 , family = "Open Sans" )
484
536
app .setSize (600 , 350 )
@@ -533,7 +585,7 @@ def playerUI():
533
585
app .setStretch ('column' )
534
586
app .setSticky ('nesw' )
535
587
app .addScale ('scale_player_timeline' ,0 ,0 )
536
- app .setScaleRange ('scale_player_timeline' ,0 ,10000 )
588
+ app .setScaleRange ('scale_player_timeline' ,0 ,10000 )
537
589
app .setScaleChangeFunction ('scale_player_timeline' ,timelineScrub )
538
590
app .setScaleIncrement ('scale_player_timeline' ,0 )
539
591
app .startFrame ('frame_player_buttons' ,1 ,0 ,2 )
@@ -546,7 +598,22 @@ def playerUI():
546
598
app .hideButton ('btn_player_pause' )
547
599
app .addIconButton ('btn_player_fastforward' ,playerButtons ,'md-fast-forward' ,1 ,3 )
548
600
app .addIconButton ('btn_player_next' ,playerButtons ,'md-next' ,1 ,4 )
601
+
602
+ app .startFrame ('frame_player_buttons2' ,2 ,0 ,4 )
603
+ app .setStretch ('column' )
604
+ app .setSticky ('nsw' )
605
+ app .addIconButton ('btn_player_resetvolume' ,playerButtons ,'md-volume-3' ,0 ,0 )
606
+ app .addScale ('scale_player_volume' ,0 ,1 )
607
+ app .setSticky ('nse' )
608
+ app .addIconButton ('btn_player_resetrate' ,playerButtons ,'time' ,0 ,3 )
609
+ app .addScale ('scale_player_rate' ,0 ,4 )
610
+ app .setScaleChangeFunction ('scale_player_rate' ,rateScaleHandler )
611
+ app .setScaleRange ('scale_player_volume' ,1 ,200 )
612
+ app .setScaleRange ('scale_player_rate' ,1 ,10 )
613
+ app .setScale ('scale_player_volume' ,100 ,callFunction = False )
614
+ app .setScale ('scale_player_rate' ,5 ,callFunction = False )
549
615
app .stopFrame ()
616
+ app .stopFrame ()
550
617
app .stopFrame ()
551
618
app .stopSubWindow ()
552
619
@@ -567,7 +634,7 @@ def newLibUI():
567
634
app .setStretch ('none' )
568
635
app .addLabel ('lbl_newLib_name' ,'Name' ,3 ,1 )
569
636
app .addLabel ('lbl_newLib_author' ,'Author(s)' ,6 ,1 )
570
- app .addLabel ('lbl_newLib_directory' ,'Podcast folder ' ,9 ,1 )
637
+ app .addLabel ('lbl_newLib_directory' ,'Audio folder ' ,9 ,1 )
571
638
app .addLabel ('lbl_newLib_thumbnail' ,'Thumbnail (optional) ' ,12 ,1 )
572
639
app .stopLabelFrame ()
573
640
app .setSticky ('esw' )
@@ -593,7 +660,7 @@ def editLibUI():
593
660
app .setStretch ('none' )
594
661
app .addLabel ('lbl_editLib_name' ,'Name' ,3 ,1 )
595
662
app .addLabel ('lbl_editLib_author' ,'Author(s)' ,6 ,1 )
596
- app .addLabel ('lbl_editLib_directory' ,'Podcast folder ' ,9 ,1 )
663
+ app .addLabel ('lbl_editLib_directory' ,'Audio folder ' ,9 ,1 )
597
664
app .addLabel ('lbl_editLib_thumbnail' ,'Thumbnail (optional) ' ,12 ,1 )
598
665
app .stopLabelFrame ()
599
666
app .setSticky ('esw' )
@@ -615,8 +682,8 @@ def setup():
615
682
app .setStopFunction (stopFunction )
616
683
617
684
if __name__ == '__main__' :
618
- version = '1.0 .0'
619
- buildDate = '24/5 /2020'
685
+ version = '1.1 .0'
686
+ buildDate = '15/6 /2020'
620
687
# very first things, init of appjar & basic global settings, stage 0
621
688
starttime = time .time ()
622
689
app = gui ('PyCasts library' ,'10x10' ,useTtk = True ,startWindow = None )
@@ -647,7 +714,7 @@ def setup():
647
714
except BaseException as e :
648
715
app .critical (f'error creating vlc instance: { e } ' )
649
716
app .critical (f'cannot continue' )
650
- app .warningBox ('critical error' ,' ' )
717
+ app .warningBox ('critical error' ,f'error creating vlc instance: " { e } " \n Is VLC media player installed? ' )
651
718
exit ()
652
719
app .thread (saveTimeAndFile )
653
720
app .registerEvent (updatePlayerInfo )
0 commit comments