vlc player freezes GUI (python thread?) - python

I have this piece of code that works with no issues :
Media_list = instance.media_list_new(song_list)
list_player = instance.media_list_player_new()
list_player.set_media_list(Media_list)
list_player.play()
how ever i would like to itterate through a list, and use normal vlc player to play it.
playing = set([1,2,3,4])
for i in song_list:
player.set_mrl(i)
player.play()
play=True
while play == True:
time.sleep(1)
play_state = player.get_state()
if play_state in playing:
continue
else:
play = False
This does almost the same thing, and it suits my needs better, however it freezes my GUi,(qml/pyside2). So now i am cofused, am i supposed to make a new thread for this, or is there some other way to do this in vlc.
Well i did try creating new thread and running the function above in it, however same issue, the moment player goes in to for loop and start play method, the gui freezes.(the vlc works normaly, and plays the playlist, but gui is unresponsive for duration)
so just to expand a bit , this is the part that i have, and it works ok, but i cant get data from my songs during their play time, since all i have is url, and not the metadata .
song_list=[]
r = requests.get('https://www.youtube.com/playlist?list=PLD6s0l-FZhjkc-TYwXO5GbwyxFqTd5Y9J')
page = r.text
soup=bs(page,'html.parser')
res=soup.find_all('a',{'class':'pl-video-title-link'})
for l in res:
#print (l.get("href"))
#print("https://www.youtube.com"+l.get("href"))
yt ='https://www.youtube.com'
temp =l.get("href")
url =yt+temp
video = pafy.new(url)
bestaudio = video.getbestaudio()
song = bestaudio.url
#print(video.getbestaudio().url)
song_list.append(song)
Media_list = instance.media_list_new(song_list)
list_player = instance.media_list_player_new()
list_player.set_media_list(Media_list)
list_player.play()
what i would want is:
#Slot()
def print_yt_playlist(self):
song_list=[]
r = requests.get('https://www.youtube.com/playlist?list=PLD6s0l-FZhjkc-TYwXO5GbwyxFqTd5Y9J')
page = r.text
soup=bs(page,'html.parser')
res=soup.find_all('a',{'class':'pl-video-title-link'})
for l in res:
#print (l.get("href"))
#print("https://www.youtube.com"+l.get("href"))
yt ='https://www.youtube.com'
temp =l.get("href")
url =yt+temp
video = pafy.new(url)
bestaudio = video.getbestaudio()
song = bestaudio.url
#print(video.getbestaudio().url)
song_list.append(video)
playing = set([1,2,3,4])
for i in song_list:
media = instance.media_new(i.getbestaudio().url)
print(i.Artist) #THIS is what i want, i want to be able to acces that data for the song that is playing
print(i.Duration) #and this and so on, that is why i want to loop through list, since i dont think i can do it with media_list
player.set_media(media)
player.play()
play=True
while play == True:
time.sleep(1)
play_state = player.get_state()
if play_state in playing:
continue
else:
play = False
Or more simple, is there a way that i paste "video" in to the media_list and then from there i could access data about current song, as well playing the song ?
I dont know what could help you from qml side, the only thing i do is trigger this function on button click.

Well i took a bit time, and i have a solution, its still in "rough" condition, but it works and it does not block the gui when i use it.
I put that logic in a new thread, and i call it from there, there needs to done a lot tweaking.
I dont know if this is the most "elegant" approach, so if anyone else have better idea, please dont hesitate to say.
class Threaddy(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
song_list=[]
r = requests.get('https://www.youtube.com/playlist?list=PLD6s0l-FZhjkc-TYwXO5GbwyxFqTd5Y9J')
page = r.text
soup=bs(page,'html.parser')
res=soup.find_all('a',{'class':'pl-video-title-link'})
for l in res:
#print (l.get("href"))
#print("https://www.youtube.com"+l.get("href"))
yt ='https://www.youtube.com'
temp =l.get("href")
url =yt+temp
video = pafy.new(url)
bestaudio = video.getbestaudio()
song = bestaudio.url
#print(video.getbestaudio().url)
song_list.append(video)
for song in song_list:
media=instance.media_new(song.getbestaudio().url) #THIS WORKS NOW
media.get_mrl()
player.set_media(media)
player.play()
print(song.title) #SO DOES THIS
playing = set([1,2,3,4])
time.sleep(1)
duration = player.get_length() / 1000
mm, ss = divmod(duration, 60)
while True:
state = player.get_state()
if state not in playing:
break
continue

Related

python gpiozero when_motion callback modify global variable

i'm using the methode when_motion from the gpiozero library to listen on 3 motion sensor.
for what i understand, this methode permit to callback a fonction when motion is detected while the rest of the programme is running
i'm trying to modify the global variabled i've named state and pickedup to trigger different phase of the programme, to get more control on the overall behaviour.
sensors are triggering sound as expected but the main programme is stuck, never seem to pass the WaitUntil function at the beginning of the main loop
i suspect the state variable not really being global and/or not changing value (but it could be something else
how can i really affect global variable from my method in order for my main programme to go on?
every design suggestion is welcome
here's my code so far:
# a fonction that wait until a condition is fulfill to continue the code
def WaitUntil(condition, output): #defines function
wU = True
while True:
if condition: #checks the condition
output
wU = False
def State(val): #return a global variable without interrupting motion methode
global state
state = val
print("state = {}".format(val))
def Pickedup(val): #return a global variable without interrupting motion methode
global pickedup
pickedup = val
print("pickedup = {}".format(val))
class PIR: # motion sensor object
global state #variables to trigger different phase of the programme
state = 0
global pickedup
pickedup = False
def __init__(self, pin, element):
self.pir = MotionSensor(pin=pin)
self.pir.when_motion = self.motion
self.element = element
self.state = state
self.pickedup = pickedup
def motion(self):
global state
global pickedup
if state == 3: #this is for not playing a sound when you juste put back the object in its jar
self.pir.wait_for_no_motion()
sleep(2)
print("object is back")
State(0)
elif state == 0:
sa.stop_all() #stop alredy playing sound
wave_obj = sa.WaveObject.from_wave_file("{}.wav".format(self.element)) #play the sound associated with this sensor when you pick an object from the associated jar
wave_obj.play()
sleep(5)
print ("objet {} is picked".format(self.element))
Pickedup(True) #wait and consider that the object is picked
WaitUntil(state == 2,FadeOut(0.5)) #wait for programme to reach state 2 and fading out the current playing sound
pir1 = PIR(17,"feu")
pir2 = PIR(27,"eau")
pir3 = PIR(22,"vent")
while True: #from here is the start of the continuous programme
WaitUntil(pickedup == True, print("pick an object")) #here is the problem, cant seem to pass to the next line from here
print("put object in the box")
while cardcount == 0 and pickedup == True:
cards = PN5180().inventory()
cardcount = len(cards)
print(f"{len(cards)} card(s) detected: {' - '.join(cards)}")
#code continue

Pyglet Audio Queue - Python

import pyglet, pafy
from pyglet.window import key
import urllib.request
from urllib.parse import *
from googleapiclient.discovery import build
api_key = ''
yt = build('youtube', 'v3', developerKey=api_key)
def download_file(search):
try:
os.remove("song.m4a")
except:
pass
request = yt.search().list(
part="snippet",
maxResults=1,
q=search
)
response = request.execute()
items = response['items']
item = items[0]
snippet = item['snippet']
title = snippet['title']
id_ = item["id"]
videoid = id_["videoId"]
url = "https://www.youtube.com/watch?v=" + videoid
info = pafy.new(url)
audio = info.getbestaudio(preftype="m4a")
audio.download('song.m4a', quiet=True)
file = 'song.m4a'
#if len(sys.argv)>1:
# file = sys.argv[1]
#
window = pyglet.window.Window()
#music = pyglet.resource.media(file)
player = pyglet.media.Player()
#player.queue(music)
#player.play()
paused = False
def help():
print("""\nCommands:
\tEsc or x \t Exit program
\tp \t Pause/unpause music
\th \t See this list again""")
print("""Welcome to this music player!
You can give a file as an argument or use the commands below.""")
help()
#window.event
def on_key_press(symbol, modifiers):
global paused
global player
global window
file = 'song.m4a'
if symbol == key.P:
if paused:
print("Resume")
player.play()
paused = False
else:
print("Pause")
player.pause()
paused = True
elif symbol == key.R:
pass
#Doesn't work :P
#player.seek(0)
elif symbol == key.H:
help()
elif symbol == key.ESCAPE:
try:
os.remove("song.m4a")
except:
pass
window.close()
elif symbol == key.Q:
srch = input("Queue Song: ")
download_file(srch)
music = pyglet.media.load(file)
player.queue(music)
#music.play()
player.next_source()
print(f'Added to queue.')
elif symbol == key.S:
srch = input("Play Song: ")
download_file(srch)
music = pyglet.media.load(file)
#music.play()
player.queue(music)
player.next_source()
pyglet.app.run()
pyglet.app.exit()
So I have been running into some issues with the Pyglet lib. I can't seem to get this part to work. Am I calling player.next_source() correctly? Because I just ran symbol == key.S put in a value for srch and nothing played. My goal is to make it so the input I enter when I press q will queue the song and automatically play after the current one, while pressing s and inputting a value there will overwrite the queue and prioritize that. Some help would be appreciated.
First, you are using player.next_source(). That forces the player to go to the next in queue. So your Q key function is incorrect, you aren't queuing it, you are queuing it and then forcing it to play. Which is what you should be doing with your S key. I think you have those confused or are mixed up on what the next_source actually does.
Secondly, when loading music, it's streamed from the file itself. It looks like you are overwriting the file each time you press a key (which isn't really ideal, and prone to errors doing it this way). So this will not work with streaming.
Your best bet is to load the file in it's entirety so that if the file gets overwritten, it's not going to cause weird issues with queued buffers. Try changing your
music = pyglet.media.load(file)
to
music = pyglet.media.load(file, streaming=False)
You also may have better luck downloading the file to a separate file or a temp file (see tempfile library) instead of trying to re-use the same filename.
Also you most likely have to store music into a global dictionary or global list to prevent it from being garbage collected. As music is a local variable to the function and will disappear as soon as the event is over. (This also means you will have to delete them from the list when you no longer need them to prevent a memory leak)

How to play next song in the playlist automatically if no key is pressed by the user?

I want to give the option to the user to play, pause, play next or previous song. If the user does not press anything, the next song in the list should be played automatically. Currently the program halts for the input from the user.
I have tried playing the song in another thread and wait for the user to enter the choice in main thread. But the problem is that main thread waits for the input from the user before playing the next song.
def music(file):
pygame.mixer.music.load(file)
pygame.mixer.music.play()
try:
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(0)
except:
pass
def pause():
print(file + " paused")
pygame.mixer.music.pause()
global set
set = 1
def playsong():
global set
global t1
print ("Playing song named : " + file)
if file != "" and set==0:
t1 = threading.Thread(target = music ,args=(file,))
t1.setName("audio bajao")
t1.start()
ch = 1
song_list = ["song1.mp3","song2.mp3"]
while((i < len(song_list)) and (ch != 5) ):
ch = input("Enter choice ")
file = song_list[i]
if(ch == 1):
playsong()
elif(ch == 2):
ch = 1
pause()
elif(ch == 3):
#play previous song
ch = 1
i -= 2
elif (ch == 4):
#play next song
ch = 1
elif (ch == 5):
break
i += 1
I want the program should play the next song from the list when the song ends if no key is pressed by the user. It should not ask the user again to press the play key to play the next song.
You can try pygame.mixer.get_busy()
To implement this, add some kind of timer variable, I personally prefer tick counters, So...
inactive_ticks = 0
After that, in your main event loop, use pygame.mixer.get_busy() to check if there is music being played or not, then...
if not pygame.mixer.get_busy():
inactive_ticks += 1
if inactive_ticks == 100:
# Play the next song
inactive_ticks = 0
That should work.

How to create parallel tasks in Python

I am creating a battle game for telegram groups as my beginner's project, one player challenges the other, and then a battle happens in the chat. Now I want to implement a "timeout" function for it.
I've managed to make it work using _thread:
import _thread
class Battle:
def __init__(self, chat_id):
self.chat = chat
print('Instance of battle started on chat %s' % self.chat)
self.pcount = 0
self.p1score = 0
self.p2score = 0
def reset(self):
self.pcount = 0
self.p1score = 0
self.p2score = 0
def timeout(chat_id):
time.sleep(90)
battle = battledic[chat_id]
if battledic[chat_id].timer == 1:
sendmessage(chat_id, 'no one accepted') # first parameter is the chat address and the second one is the message
battle.reset()
else:
return
battle = Battle(chat_id)
if message == "challenge":
if battle.pcount == 0:
_thread.start_new_thread(timeout, (chat_id,))
...
So basically what I'm doing is calling the timeout function, that is going to sleep in a different thread for 90 seconds, and then it checks if the battle still with only one player (pcount == 1), if it is the case, it sends the chat a message telling them the fight did not start, and then resets the battle instance.
The problem here is that I'm raising exceptions very often, even though it works fine most of the time, also sometimes it doesn't have the desired effect, closing ongoing battles. Researching I've found out that this is not the desired way of doing this butI could not find better solution, and maybe I want to run other battles from other chats in parallel too. What should be used in this case? cooperative multitasking? multiprocessing? the trheading module? thanks for any tips on this task!
I've got you started. I'm not sure how you are going to chat or keep record of who's playing.
My first thoughts were to run threads, but the while loop is easier.
# from threading import Thread
from time import sleep
no_one = True
battledic = {} # your dict here. It may be better to use a list of objects (classes) for your opponents
class Battle(object):
def __init__(self, chat_id):
self.chat_id = chat_id
self.pcount = 0
self.p1score = 0
self.p2score = 0
print('Instance of battle started on chat %s' % self.chat_id)
def reset(self):
self.pcount = 0
self.p1score = 0
self.p2score = 0
def wheres_waldo():
x = 0; no_one = True
while x < 90: # waits 90 seconds unless found
for player in battledic.keys():
if player.is_requesting: # found someone
no_one = False
break # quit looking
else:
print('No one yet')
sleep(1); x+= 1
if no_one:
sendmessage(chat_id, 'no one accepted') # first parameter is the chat address and the second one is the message
battle.reset()
else:
sendmessage(chat_id, 'Found someone')
def sendmessage(chat_id,message):
print('%s on chat %s' % (message,chat_id))
battle = Battle(chat_id)
while True:
message = raw_input('Command? ')
if message == "challenge":
if battle.pcount == 0:
wheres_waldo()
# first thoughts were to run threads but the while loop is easier
# wait = Thread(target=timeout, args=(chat_id))
# where = Thread(target=wheres_waldo)
# wait.start();where.start()
# wait.join();where.join()

Playing a sound once pygame

I'm trying to play background music and the music keeps on starting. I tried to use music.busy, but I can't figure it out. I want it to check if sound/music is playing, then I want it to print "music is playing". If a sound/music isn't playing I want it to start up a new song and loop it.
def musica():
if pygame.mixer.music.get_busy() == True:
print("music is playing")
if pygame.mixer.music.get_busy() == False:
music.play(loops=-1)
An easy way to play looped sounds is winsound. See the python documentation (https://docs.python.org/3.4/library/winsound.html) and set the parameter 'flags' in PlaySound to winsound.SND_LOOP.
I ended up doing this it is more of a workaround but it works:
m = 0
def musica():
global m
if m == 0:
music.play(loops=-1)
m = 1
if you are looking to play a sound during a specific event in a loop (e.g. opening a menu), it is possible to play sound only once by using timers.
example:
sound = pygame.mixer.Sound('sound.wav')
playSound = True
playSound_counter = 0
while:
playSound_counter += 1
if playSound_counter >= 11:
playSound = True
playSound_counter = 0
if i.rect.collidepoint(mouse_position):
if playSound == True :
sound.play()
playSound = False
elif playSound_counter == 10:
playSound_counter = 0
This is essentially two timers - one sets sound back to True every 11 secs, another negates it while mouse is over a rect.
by "playing once" i meant to be able to play it again in a controlled way when a certain event is triggered

Categories

Resources