# inc/mpc.py
# https://python-mpd2.readthedocs.io/en/latest/topics/commands.html
import cfg
import inc.infos as infos
def sub_proc(cmd, retour = 'matiere_utile'):
""" Lance une commande shell en synchrone,
càd qu'elle ne rend la main qu'à la fin de la commande
gère aussi les commandes avec pipe """
# ex : find |grep pro
# appeler avec : sub_proc('find |grep pro')
# https://stackoverflow.com/questions/13332268/how-to-use-subprocess-command-with-pipes
import subprocess
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = ps.communicate()[0]
if retour == 'matiere_utile':
return output.decode('utf-8').strip()
else:
return output
def sub_proc_bg(cmd):
""" Lance une commande shell en arrière-plan """
import subprocess
ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def add(url):
""" ajoute une url ou un dossier local dans la playliste"""
mlogger('ADD : mpc -q add "' + url + '"')
sub_proc('mpc -q add "' + url + '"')
def auteur_titre():
""" renvoie une chaîne lisible du type titre de auteur """
# ex : Miss Maggie de Renaud
# variables définies depuis les tags mp3
auteur = sub_proc("mpc -f %artist% | head -1")
titre = sub_proc("mpc -f %title% | head -1")
# variables définies depuis nom fichier et nom dossier
if auteur == "":
auteur = "auteur inconnu"
if titre == "":
titre = "titre inconnu"
return titre + ", de " + auteur
def bip(numero):
""" vide la liste de lecture """
sounds = [ cfg.SOUND_1, cfg.SOUND_2, cfg.SOUND_3 ]
un_son = sounds[numero-1]
infos.m_g("un_son = " + un_son)
sub_proc("mpg321 " + un_son )
def clear():
""" vide la liste de lecture """
sub_proc("mpc -q clear")
def ferme_ton_clapet():
""" coupe brutalement un message vocal en cours
et supprime tous ceux en attente
"""
infos.g_m("Ferme ton clapet")
sub_proc("killall aplay")
sub_proc("rm {}/*".format(cfg.DIRE_FILE_ATTENTE))
# f-strings
# https://realpython.com/python-f-strings/#f-strings-a-new-and-improved-way-to-format-strings-in-python
# ~ sub_proc(f"rm {cfg.DIRE_FILE_ATTENTE}/*")
def fwd_10s(info = 'None'):
""" avance de 10 secondes dans un fichier audio """
infos.m_g("mpc fwd_10s")
# bash = [ "x$(mpc playlist)" = "x" ] && return 1
# bash = m_g "+10s, $(mpc seek +00:00:10 | tail -2 | head -1)"; }
# vérifier la nature du fichier (si url, on ne peut pas avancer)
if is_playing() or is_paused():
r = sub_proc("mpc seek +00:00:10 | tail -2 | head -1")
infos.m_g("fwd_10s, " + r)
def fwd_30s(info = 'None'):
""" avance de 30 secondes dans un fichier audio """
infos.m_g("mpc fwd_30s")
# TODO : vérifier la nature du fichier (si url, on ne peut pas avancer)
if is_playing() or is_paused():
r = sub_proc("mpc seek +00:00:30 | tail -2 | head -1")
infos.m_g("fwd_30s, " + r)
def fwd_60s(info = 'None'): # avancer de 1 minute
infos.m_g("mpc fwd_60s")
def fwd_3mn(info = 'None'): # avancer de 3 minutes
infos.m_g("mpc fwd_3mn")
def is_playing():
"""
teste si lecture en cours
"""
r = sub_proc("mpc | grep '\[playing\]'")
if r.startswith("[playing]"):
return True
return False
def is_paused():
"""
teste si pause en cours
"""
r = sub_proc("mpc | grep '\[paused\]'")
if r.startswith("[paused]"):
return True
return False
def killer_pico():
""" interrompt la synthèse vocale en cours """
# TODO : émettre un bip spécial
# soit avec mpg123
# ou bien avec python3 si plus rapide
if not is_playing():
toggle()
sub_proc("killall aplay")
infos.g_m("interrompt synthèse vocale (-> killall aplay et mplayer)")
def load(playlist):
""" charge une playlist présentes dans /m/playlists """
# TODO : vérifier que le fichier /m/playlists/{}.m3u existe
return sub_proc("mpc -q load " + playlist + " > /dev/null")
def mlogger(s):
""" fichier log pour débugger """
pass
def mpc_file():
""" fichier/url en cours de lecture """
# type1/S/Joe Satriani/Surfing with the alien/02 - Ice nine.mp3
#
# TODO : vérifier d'abord qu'on est en lecture ou en pause
if is_playing() or is_paused():
return sub_proc("mpc -f %file% | head -1")
return None
def next_trk(info = 'None'):
""" passe à la plage suivante,
sauf si on est déjà à la dernière plage """
position = playlist_position()
if position == playlist_longueur():
infos.m_g("Vous êtes déjà à la dernière position")
msg = "on est déjà à la dernière piste, la numéro " + \
position + ", " + \
"appuyez sur 5 pour mettre en pause, ou 55 pour stopper la lecture, " + \
"ou encore sur 11 pour écouter le menu spécial"
infos.pdrNew(msg)
else:
ferme_ton_clapet()
sub_proc("mpc -q next")
def options_lecture():
""" volume, repeat, random, ... """
return sub_proc('mpc status | grep "^volume"')
def pause():
""" met en pause """
r = sub_proc("mpc -q pause")
def pause_if_playing(info = 'None'):
""" met en pause si playing """
if is_playing():
pause()
def play(piste = None):
""" lance la lecture à la position donnée dans la playliste
si fournie """
if piste is None:
piste = ""
r = sub_proc("mpc -q play {}".format(piste))
def play_if_paused(info = 'None'):
""" reprend la lecture si mis en pause """
if is_paused():
toggle()
def playlist_longueur():
return sub_proc("mpc playlist | wc -l")
def playlist_position():
""" position dans playlist actuelle """
# renvoie un nombre si lecture en cours
return sub_proc("mpc -f %position% | head -1")
def prev_trk(info = 'None'):
""" passe à la plage précédente, sauf si on est déjà à la première plage """
position = playlist_position()
if position == 1:
infos.m_g("Vous êtes déjà à la position 1")
msg = "on est déjà à la première piste, " + \
"appuyez sur 5 pour mettre en pause, " + \
"ou 55 pour stopper la lecture"
infos.pdrNew(msg)
else:
ferme_ton_clapet()
sub_proc("mpc -q prev")
def rm(playliste):
""" supprime une playliste """
return sub_proc("mpc -q rm " + playliste)
def save(playliste):
""" enregistre une playliste """
return sub_proc("mpc -q save " + playliste)
def seek(where):
""" avance à la position demandée """
return sub_proc("mpc -q seek " + where)
def status():
""" informations sur lecture en cours """
return sub_proc("mpc status")
def status_progression(info = 'None'):
""" progression, en minutes:secondes """
# https://unix.stackexchange.com/questions/19014/how-to-strip-multiple-spaces-to-one-using-sed
# sed 's/ */|/g' remplace une série d'espaces par un pipe
cmd = "mpc status | head -2 | tail -1 | sed 's/ */|/g'"
r = sub_proc(cmd)
progress = ""
prog_sur_total = ""
if r.startswith("[playing]") or r.startswith("[paused]") :
# [playing]|#1/1|0:38/37:28|(1%)
progress = r.split("|")[2].split("/")[0]
prog_sur_total = r.split("|")[2].replace("/", " sur ")
# 0:56 0:56 sur 18:23
return [ progress, prog_sur_total ]
def status_progression_secondes():
""" progression, en secondes """
r = status_progression()[0]
# ~ infos.m_g("status_progression()[0] = -" + str(status_progression()[0]) + "-")
if r == "":
secondes_ecoulees = -1
else:
s = r.split(":")
if len(s) == 2:
infos.m_g("s[0]= " + str(s[0]))
infos.m_g("s[1]= " + str(s[1]))
secondes_ecoulees = int(s[0]) * 60 + int(s[1])
else:
infos.m_g("s[0]= " + str(s[0]))
infos.m_g("s[1]= " + str(s[1]))
infos.m_g("s[2]= " + str(s[2]))
secondes_ecoulees = int(s[0]) * 3600 + int(s[1]) * 60 + int(s[2])
return secondes_ecoulees
def rand_toggle(info = 'None'): # bascule mode random on/off
infos.m_g("mpc rand_toggle")
def random_off():
""" désactive mode random """
sub_proc("mpc -q random off")
def random_on():
""" active mode random """
sub_proc("mpc -q random on")
def rew_10s(info = 'None'): # reculer de 10 secondes
secondes_ecoulees = status_progression_secondes()
if secondes_ecoulees > 10:
secondes_ecoulees -= 10
infos.m_g("mpc rew_10s")
if is_playing() or is_paused():
r = sub_proc("mpc seek " + str(secondes_ecoulees) + " | tail -2 | head -1")
infos.m_g("rew_10s, " + r)
def rew_1mn(info = 'None'): # reculer de 1 minute
infos.m_g("mpc rew_1mn")
def show_duree(info = 'None'): # durée, au format minutes:secondes
infos.m_g("mpc show_duree")
def show_duree_sec(info = 'None'): # durée, au format secondes
infos.m_g("mpc show_duree_sec")
def stop(info = 'None'): # stoppe la lecture
sub_proc("mpc -q stop")
def trk_first(info = 'None'):
""" aller à la première piste de la playlist actuelle """
piste_actu = playlist_position()
if piste_actu == "1":
infos.m_g("--> on est déjà au début de la playlist (piste " + piste_actu + ")")
return 0
ferme_ton_clapet()
play("1")
infos.log("--> début de la playlist (piste 1)")
return 1
def trk_last(info = 'None'):
""" aller à la dernière piste de la playlist actuelle """
derPiste = playlist_longueur()
if playlist_position() == derPiste:
infos.m_g("--> on est déjà à la fin de la playlist (piste " + derPiste + ")")
return 0
ferme_ton_clapet()
play(derPiste)
infos.log("--> fin de la playlist (piste " + derPiste + ")")
return 1
def time0pct(info = 'None'): # aller à 0% du morceau
infos.m_g("mpc time0pct")
def time33pct(info = 'None'): # aller à 33% du morceau
infos.m_g("mpc time33pct")
def time66pct(info = 'None'): # aller à 66% du morceau
infos.m_g("mpc time66pct")
def toggle(info = 'None'):
""" reprendre après une pause de lecture """
cmd = "mpc -q toggle"
r = sub_proc(cmd)
def update(dossier_mpd = ""):
""" màj base de données mpd """
sub_proc("mpc -q update {}".format(dossier_mpd))
def update_wait(dossier_mpd = ""):
""" màj base de données mpd """
sub_proc("mpc -q update --wait {}".format(dossier_mpd))
def volume(texte: str, sens: str, info: str = None):
""" affiche le volume en pourcentage """
r = sub_proc("amixer -c 0 set PCM " + sens + " | grep '%'")
# r est du type :
# "Mono: Playback 0 [96%] [0.00dB] [on] "
# on isole >> 96% <<
if not info is None:
pourcentage = r.split("[")[1]
pourcentage = pourcentage.split("]")[0]
infos.m_g(pourcentage + ' | ' + texte)
def vol_up(info = 'None'): # augmenter le son
volume("up", "2dB+", info)
def vol_down(info = 'None'): # réduire le son
volume("down", "2dB-", info)
def vol_up2(info = 'None'): # augmenter le son 2x
volume("up2", "4dB+", info)
def vol_down2(info = 'None'): # réduire le son 2x
volume("down2", "4dB-", info)
def vol_down4(info = 'None'): # réduire le son 4x
volume("down4", "8dB-", info)