pythonvlcpysimpleguipafy

problem with window.read() and time.sleep() when playing an mp3 in python


I'm making a program with pafy, vlc, PySimpleGUI that takes a youtube url and plays it as mp3 the problem I got when I first tried the console mode is that the mp3 stops after a while and I fixed it with time.sleep(seconds) and now everything works fine in the console version. The problem gets when I tried to make it a GUI with PySimpleGUI when I used time.sleep(seconds) the GUI freezes until it the mp3 ends, I searched and found that window.read() may fixes the problem and it did but I couldn't then resume the mp3 after pausing it (like the console mode), when I press play it plays and when I press pause it pauses and when I press play again it starts from the beginning but I want it to start from when it paused is it because of the window.read() ? sorry if I couldn't explain it clearly. The console mode:

import pafy
import vlc
player = vlc.Instance()
media_player = player.media_player_new()    
def readurl():
    url=input("URL : ")
    vid=pafy.new(url)
    l=vid.length
    aud=vid.getbestaudio()
    media = player.media_new(aud.url)
    media.get_mrl()
    media_player.set_media(media)
def ans(a):
    if(a.upper()=="S"):
        media_player.pause()
    elif(a.upper()=="P"):
        media_player.play()
    elif(a.upper()=="R"):
        media_player.pause()
        readurl()
        ans("P")
    else:
        exit()
readurl()
while True:
    a=input("Choose from options P to play, S to stop(pause), Q to quit, R to get another video. ")
    ans(a)
    

The GUI mode:

import PySimpleGUI as sg
import vlc
import pafy
import time
player = vlc.Instance()
media_player = player.media_player_new()    
sg.theme('DarkBlue')
def btn(name):  
    return sg.Button(name, size=(6, 1), pad=(1, 1))
layout = [[sg.Button(button_text='Get video from youtube url : ', size=(20,1), key='geturl'),sg.Input(default_text='', size=(50, 10), key='-VIDEO_LOCATION-')],
          [btn('play'), btn('pause')]]
window = sg.Window('Youtube Radio Mode', layout, element_justification='center', finalize=True, resizable=True)              
#------------ Media Player Setup ---------#
"""
    """
def ans(a,q):
    if(a.upper()=="S"):
        media_player.pause()
        #window.read(q)
    elif(a.upper()=="P"):# or (a.upper()=="R")):
        media_player.play()
        window.read(q)
    else:
        exit()
#------------ The Event Loop ------------#
#def vi()
while True:
    event, values = window.read(timeout=1000)      
    url=values['-VIDEO_LOCATION-']
    z=''
    if event == sg.WIN_CLOSED:
        break
    if (url!='' or event == 'geturl'):
        vid=pafy.new(url)
        #l=vid.length
        aud=vid.getbestaudio()
        media = player.media_new(aud.url)
        media.get_mrl()
        media_player.set_media(media)
        z='l'
        q=vid.length*1000
    if event == 'play':
        if(z!='l'):
            sg.popup_error('PLEASE GET A URL FIRST')    
            continue
        ans("P",q)
        #window.refresh()
        z=''
    if event == 'pause':
        ans("S",q)
window.close()

Solution

  • Define your GUI, then go your event loop and make sure what event will happen in you GUI, then what's the next step to go for which event.

    Here, revised code for your reference, no test for the code.

    import PySimpleGUI as sg
    import vlc
    import pafy
    import time
    
    def vlc_player():
        player = vlc.Instance()
        media_player = player.media_player_new()
        return media_player
    
    def Button(button_text):
        return sg.Button(button_text, size=(6, 1), pad=(1, 1))
    
    media_player = vlc_player()
    
    sg.theme('DarkBlue')
    sg.set_options(font=("Courier New", 16))
    
    layout = [
        [sg.Button(button_text='Get video from youtube url : ', size=(20,1), key='-GET_URL-'),
         sg.Input(default_text='', size=(50, 10), key='URL')],
        [Button('PLAY'), BUTTON('PAUSE'), Button('QUIT')],
        [sg.Text("", size=(0, 1), key='-STATUS-')],
    ]
    
    window = sg.Window('Youtube Radio Mode', layout, element_justification='center',
        finalize=True, resizable=True)
    
    status = window['-STATUS-']
    mp3_load = False
    
    #------------ The Event Loop ------------#
    
    while True:
    
        event, values = window.read()
    
        if event in (sg.WIN_CLOSED, 'QUIT'):
            break
    
        status.update(value='')
    
        if event == '-GET_URL-':
            """
            if player.is_playing():
                player.stop()
            """
            url = values['-URL-'].strip()
    
            try:
                vid     = pafy.new(url)
                aud     = vid.getbestaudio()
                media   = player.media_new(aud.url)
                media.get_mrl()
                media_player.set_media(media)
                mp3_load = True
                continue
            except:
                pass
            status.update('URL load failed !')
            mp3_load = False
    
        elif event == 'PLAY' and mp3_load:
            media_player.play()
    
        elif event == 'PAUSE' and mp3_load:
            media_player.pause()
    
    if player.is_playing():
        media_player.stop()
    window.close()
    

    You may use multi-thread to load mp3 from Youtube if it will take long time, and set some flags to confirm if in downloading or download complete, or disable button '-GET_URL-' or else buttons.

    Don't update GUI directly in another thread, you can call window.write_event_value to generate new event, then update GUI in event loop.

    Refer https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Media_Player_VLC_Based.py