Odtwarzacz mp3 na Raspberry



Jakiś czas temu zapragnąłem odtwarzać muzykę z Raspberry Pi, urządzenia bardzo fajnego, tym, bardziej, że wyposażonego w port jack. Problem polegał jednak na tym, że nie mogłem znaleźć odpowiednio wygodnego programu do odtwarzania muzyki. Mpalyer i Omxplayer są niby jakimś rozwiązaniem, ale żeby z nich korzystać trzeba shella, tzn. trzeba się zalogować do Raspberry przez ssh. Napisałem sobie więc takie cudeńko.

Graj.py

Udostępniam to na licencji GPL3, więc NIE BIORĘ ABSOLUTNIE ŻADNEJ ODPOWIEDZIALNOŚCI ZA ZNISZCZENIE CI... Dobra, a tak na poważnie, to w mało precyzyjnym skrócie licencja ta daje prawo do robienia wszystkiego z danym oprogramowaniem, poza tylko podszywaniem się pod autora. Ale wracając do programu.

Jest to wielowątkowy odtwarzacz muzyki ze (skromnym) interfejsem http. Znaczy to, że można sterować nim przez stronę www. Całość napisana jest w Pythonie 3. Odtwarzanie muzyki realizuję przez pygame a żądania http obsługuję prostym socket serwerem. Wątki tworzę za pomocą threading. Dodatkowo w ramach ciekawostki dodałem scrobblowanie utworów do serwisu last.fm - tym zajmuje się biblioteka pylast. Poniżej wszystkie importy:

 1 import os, sys
2 import re
3 from pygame import mixer
4 import threading, queue
5 import socket
6 from random import randint
7 import time
8 import pylast
9 import stagger
10 from stagger.id3 import *

A oto i webinterfejs:

Mogło być gorzej. Przyciski są odpowiednio duże, żeby dało radę obsługiwać wszystko z telefonu.

Sam kod jest po polsku, ponieważ pisałem to na własne potrzeby. Przykładowo, to jest klasa odpowiedzialna za socket serwer:

 1 class Serwer(threading.Thread):
2
3 def __init__(self, lista):
4 threading.Thread.__init__(self)
5 self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
6 self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
7 self.s.bind(("192.168.1.1", 8080)) # tu ustawiam na jakim interfejsie slucha serwer
8 self.s.listen(5)
9 self.lista = lista
10
11 def jedziesz(self, nazwa, kolejka, kolejka_odp, stop):
12 while True: # petla przetwarzania zapytan od klienta
13 if stop.is_set():
14 sys.exit()
15 print("Koniec serwera.")
16 time.sleep(0.1)
17 klient, caddr = self.s.accept()
18 req = klient.recv(1024).decode()
19 print("dzialam")
20 kolejka.put(req)
21 try:
22 dane = kolejka_odp.get(True, 0.2)
23
24 except:
25 print("Kolejka pusta!")
26 dane = ('', True)
27
28 self.odpowiadam(klient, dane)
29
30 def odpowiadam(self, klient, dane): # funkcja do wysylania requestow zwrotnych
31
32 #(...duzo htmlek do odpowiedzi... )
33
34 while wyslane < len(msg): #jesli sa jeszcze jakies dane do wyslania, krec dalej
35 sle = klient.send(msg[wyslane:])
36 wyslane = wyslane + sle

Dane pomiędzy wątkami aplikacji przekazuję za pomocą kolejek, o na przykład takich:

self.q_sluch = queue.Queue()

Requesty GET z interfejsu webowego przetwarzam po stronie serwera w sposób może mało wyrafinowany, ale jak na moje potrzeby - wystarczająco skuteczny:

 1 if "GET /losuj" in zapytanie:
2 self.stop = False
3 if losuj:
4 utwor = self.g.losuj()
5 else:
6 utwor = self.g.nastepny(utwor)
7 self.q_lastfm.put(utwor)
8 self.tm = threading.Thread(target=self.g.odtwarzacz, args=(True, self.t_stop)).start()
9 self.tlastfm = threading.Thread(target=self.g.skrobluj, args=(self.t_stop, self.q_lastfm)).start()
10 msg = "Zaczynam grac: " + utwor
11 print(msg)
12 self.t_stop.clear()

Nie chciałem się wikłać w biblioteki do parsowania requestów, bo niepotrzebnie dociążyłoby to aplikację, a wiadomo - Raspbbery potężnym serwerem nie jest.

Zachęcam do zapoznania się z kodem źródłowym i przetestowania programu.

Graj.py