Feat: fix encoding

This commit is contained in:
Bertrand Benjamin 2020-09-20 21:00:46 +02:00
parent c0ab490a34
commit 251744a632
1 changed files with 231 additions and 350 deletions

View File

@ -1,383 +1,264 @@
from PIL import Image, ImageDraw, ImageColor, ImageFont, ImageStat #!/usr/bin/env python
import subprocess
import time import time
import musicpd from colorsys import hsv_to_rgb
from PIL import ImageFont, Image, ImageDraw, ImageStat
import os import os
import os.path import os.path
from os import path from os import path
import RPi.GPIO as GPIO import ST7789 as ST7789
from mediafile import MediaFile from socketIO_client import SocketIO, LoggingNamespace
import requests
from io import BytesIO from io import BytesIO
from numpy import mean from numpy import mean
import ST7789 import sys
from PIL import ImageFilter import logging
import yaml
# set default config for pirate audio # logging.getLogger('socketIO-client').setLevel(logging.DEBUG)
# logging.basicConfig()
__version__ = "0.0.5" import signal
import RPi.GPIO as GPIO
# get the path of the script # get the path of the script
script_path = os.path.dirname(os.path.abspath( __file__ )) # script_path = os.path.dirname(os.path.abspath( __file__ ))
# set script path as current directory - script_path = os.path.dirname(os.path.abspath(__file__))
# set script path as current directory
os.chdir(script_path) os.chdir(script_path)
MODE=0
OVERLAY=2
TIMEBAR=1
BLANK=0
confile = 'config.yml' def on_connect():
print("connect")
# Read config.yml for user config
if path.exists(confile):
with open(confile) as config_file:
data = yaml.load(config_file, Loader=yaml.FullLoader)
displayConf = data['display']
OVERLAY = displayConf['overlay']
MODE = displayConf['mode']
TIMEBAR = displayConf['timebar']
BLANK = displayConf['blank']
def on_disconnect():
print("disconnect")
img = Image.new("RGBA", (240, 240), color=(0, 0, 0, 25))
img = Image.open("/volumio/app/plugins/miscellanea/albumart/default.jpg")
img = img.resize((WIDTH, HEIGHT))
draw = ImageDraw.Draw(img, "RGBA")
draw.text((10, 200), "shuting down ...", font=font_m, fill=(255, 255, 255))
disp.display(img)
def on_push_state(*args):
img = Image.new("RGBA", (240, 240), color=(0, 0, 0, 25))
global status
global service
status = args[0]["status"]
service = args[0]["service"]
# Load albumcover or radio cover
albumart = args[0]["albumart"]
if len(albumart) == 0: # to catch a empty field on start
albumart = "http://localhost:3000/albumart"
if "http:" not in albumart:
albumart = "http://localhost:3000" + args[0]["albumart"]
print(albumart)
response = requests.get(albumart)
img = Image.open(BytesIO(response.content))
img = img.resize((WIDTH, HEIGHT))
# Light / Dark Symbols an bars, depending on background
im_stat = ImageStat.Stat(img)
im_mean = im_stat.mean
mn = mean(im_mean)
txt_col = (255, 255, 255)
# bar_col = (255, 255, 255, 255)
bar_bgcol = (200, 200, 200)
bar_col = (255, 255, 255)
dark = False
if mn > 175:
txt_col = (55, 55, 55)
dark = True
# bar_col = (100, 100, 100, 225)
bar_bgcol = (255, 255, 255)
bar_col = (100, 100, 100)
if mn < 80:
txt_col = (200, 200, 200)
# paste button symbol overlay in light/dark mode
if status == "play":
if dark is False:
img.paste(pause_icons, (0, 0), pause_icons)
else:
img.paste(pause_icons_dark, (0, 0), pause_icons_dark)
else:
if dark is False:
img.paste(play_icons, (0, 0), play_icons)
else:
img.paste(play_icons_dark, (0, 0), play_icons_dark)
draw = ImageDraw.Draw(img, "RGBA")
top = 7
if "artist" in args[0]:
x1 = 20
w1, y1 = draw.textsize(args[0]["artist"], font_m)
x1 = x1 - 20
if x1 < (WIDTH - w1 - 20):
x1 = 0
if w1 <= WIDTH:
x1 = (WIDTH - w1) // 2
draw.text((x1, top), args[0]["artist"], font=font_m, fill=txt_col)
top = 35
if "album" in args[0]:
if args[0]["album"] is not None:
x2 = 20
w2, y2 = draw.textsize(args[0]["album"], font_s)
x2 = x2 - 20
if x2 < (WIDTH - w2 - 20):
x2 = 0
if w2 <= WIDTH:
x2 = (WIDTH - w2) // 2
draw.text((x2, top), args[0]["album"], font=font_s, fill=txt_col)
if "title" in args[0]:
x3 = 20
w3, y3 = draw.textsize(args[0]["title"], font_l)
x3 = x3 - 20
if x3 < (WIDTH - w3 - 20):
x3 = 0
if w3 <= WIDTH:
x3 = (WIDTH - w3) // 2
# thin border
# shadowcolor = "black"
# draw.text((x3-1, 105), args[0]['title'], font=font_l,
# fill=shadowcolor)
# draw.text((x3+1, 105), args[0]['title'], font=font_l,
# fill=shadowcolor)
# draw.text((x3, 105-1), args[0]['title'], font=font_l,
# fill=shadowcolor)
# draw.text((x3, 105+1), args[0]['title'], font=font_l,
# fill=shadowcolor)
draw.text(
(x3, 105), args[0]["title"], font=font_l, fill=txt_col
) # fill by mean
# volume bar
vol_x = int((float(args[0]["volume"]) / 100) * (WIDTH - 33))
# draw.rectangle((5, 184, WIDTH-34, 184+8), (255, 255, 255, 145))
draw.rectangle((5, 184, WIDTH - 34, 184 + 8), bar_bgcol) # background
draw.rectangle((5, 184, vol_x, 184 + 8), bar_col)
# time bar
if "duration" in args[0]:
duration = args[0]["duration"] # seconds
if duration != 0:
if "seek" in args[0]:
seek = args[0]["seek"] # time elapsed seconds
print("seek: ", seek)
if seek != 0 and seek is not None:
el_time = int(float(seek) / 1000)
du_time = int(float(args[0]["duration"]))
dur_x = int((float(el_time) / float(du_time)) * (WIDTH - 10))
# draw.rectangle((5, 222, WIDTH-5, 222 + 12), (255, 255, 255, 145))
draw.rectangle(
(5, 222, WIDTH - 5, 222 + 12), bar_bgcol
) # background
draw.rectangle((5, 222, dur_x, 222 + 12), bar_col)
# Draw the image on the display hardware
disp.display(img)
socketIO = SocketIO("localhost", 3000)
socketIO.on("connect", on_connect)
# Standard SPI connections for ST7789
# Create ST7789 LCD display class. # Create ST7789 LCD display class.
if MODE == 3: disp = ST7789.ST7789(
disp = ST7789.ST7789( rotation=90, # Needed to display the right way up on Pirate Audio
port=0, port=0, # SPI port
cs=ST7789.BG_SPI_CS_FRONT, # GPIO 8, Physical pin 24 cs=1, # SPI port Chip-select channel
dc=9, dc=9, # BCM pin used for data/command
rst=22, backlight=13,
backlight=13, spi_speed_hz=80 * 1000 * 1000,
mode=3, )
rotation=0,
spi_speed_hz=80 * 1000 * 1000
)
else:
disp = ST7789.ST7789(
port=0,
cs=ST7789.BG_SPI_CS_FRONT, # GPIO 8, Physical pin 24
dc=9,
backlight=13,
spi_speed_hz=80 * 1000 * 1000
)
# Initialize display. # Initialize display.
disp.begin() disp.begin()
WIDTH = 240 WIDTH = 240
HEIGHT = 240 HEIGHT = 240
font_s = ImageFont.truetype(script_path + '/fonts/Roboto-Medium.ttf',20) font_s = ImageFont.truetype(script_path + "/fonts/Roboto-Medium.ttf", 20)
font_m = ImageFont.truetype(script_path + '/fonts/Roboto-Medium.ttf',24) font_m = ImageFont.truetype(script_path + "/fonts/Roboto-Medium.ttf", 24)
font_l = ImageFont.truetype(script_path + '/fonts/Roboto-Medium.ttf',30) font_l = ImageFont.truetype(script_path + "/fonts/Roboto-Medium.ttf", 30)
play_icons = Image.open("images/controls-play.png").resize((240, 240))
play_icons_dark = Image.open("images/controls-play-dark.png").resize((240, 240))
pause_icons = Image.open("images/controls-pause.png").resize((240, 240))
pause_icons_dark = Image.open("images/controls-pause-dark.png").resize((240, 240))
# vol_icons = Image.open(script_path + '/images/controls-vol.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
# vol_icons_dark = Image.open(script_path + '/images/controls-vol-dark.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
# bt_back = Image.open(script_path + '/images/bta.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
# ap_back = Image.open(script_path + '/images/airplay.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
# jp_back = Image.open(script_path + '/images/jack.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
# sp_back = Image.open(script_path + '/images/spotify.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
# sq_back = Image.open(script_path + '/images/squeeze.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
BUTTONS = [5, 6, 16, 20]
# BUTTONS = [16]
LABELS = ["A", "B", "X", "Y"]
# Set up RPi.GPIO with the "BCM" numbering scheme
GPIO.setmode(GPIO.BCM)
# Buttons connect to ground when pressed, so we should set them up
# with a "PULL UP", which weakly pulls the input signal to 3.3V.
GPIO.setup(BUTTONS, GPIO.IN, pull_up_down=GPIO.PUD_UP)
img = Image.new('RGB', (240, 240), color=(0, 0, 0, 25)) def handle_button(pin):
label = LABELS[BUTTONS.index(pin)]
play_icons = Image.open(script_path + '/images/controls-play.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") print("Button press detected on pin: {} label: {}".format(pin, label))
play_icons_dark = Image.open(script_path + '/images/controls-play-dark.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") if pin == 5:
# print('Play/pause')
pause_icons = Image.open(script_path + '/images/controls-pause.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") print(status, service)
pause_icons_dark = Image.open(script_path + '/images/controls-pause-dark.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") if (status == "play") and (service == "webradio"):
print("stop - webradio")
vol_icons = Image.open(script_path + '/images/controls-vol.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") socketIO.emit("stop")
vol_icons_dark = Image.open(script_path + '/images/controls-vol-dark.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") elif status == "play":
print("pause")
bt_back = Image.open(script_path + '/images/bta.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") socketIO.emit("pause")
ap_back = Image.open(script_path + '/images/airplay.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") else:
jp_back = Image.open(script_path + '/images/jack.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") print("play")
sp_back = Image.open(script_path + '/images/spotify.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") socketIO.emit("play")
sq_back = Image.open(script_path + '/images/squeeze.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA") if pin == 6:
print("volDown")
draw = ImageDraw.Draw(img, 'RGBA') socketIO.emit("volume", "-")
if pin == 16:
print("Next")
def isServiceActive(service): socketIO.emit("next")
if pin == 20:
waiting = True print("volUp")
count = 0 socketIO.emit("volume", "+")
active = False
while (waiting == True):
process = subprocess.run(['systemctl','is-active',service], check=False, stdout=subprocess.PIPE, universal_newlines=True)
output = process.stdout
stat = output[:6]
if stat == 'active':
waiting = False
active = True
if count > 29:
waiting = False
count += 1
time.sleep(1)
return active
def getMoodeMetadata(filename):
# Initalise dictionary
metaDict = {}
if path.exists(filename):
# add each line fo a list removing newline
nowplayingmeta = [line.rstrip('\n') for line in open(filename)]
i = 0
while i < len(nowplayingmeta):
# traverse list converting to a dictionary
(key, value) = nowplayingmeta[i].split('=')
metaDict[key] = value
i += 1
metaDict['source'] = 'library'
if 'file' in metaDict:
if (metaDict['file'].find('http://', 0) > -1) or (metaDict['file'].find('https://', 0) > -1):
# set radio stream to true
metaDict['source'] = 'radio'
# if radio station has arist and title in one line separated by a hyphen, split into correct keys
if metaDict['title'].find(' - ', 0) > -1:
(art,tit) = metaDict['title'].split(' - ', 1)
metaDict['artist'] = art
metaDict['title'] = tit
elif metaDict['file'].find('Bluetooth Active', 0) > -1:
metaDict['source'] = 'bluetooth'
elif metaDict['file'].find('Airplay Active', 0) > -1:
metaDict['source'] = 'airplay'
elif metaDict['file'].find('Spotify Active', 0) > -1:
metaDict['source'] = 'spotify'
elif metaDict['file'].find('Squeezelite Active', 0) > -1:
metaDict['source'] = 'squeeze'
elif metaDict['file'].find('Input Active', 0) > -1:
metaDict['source'] = 'input'
# return metadata
return metaDict
def get_cover(metaDict):
cover = None
cover = Image.open(script_path + '/images/default-cover-v6.jpg')
covers = ['Cover.jpg', 'cover.jpg', 'Cover.jpeg', 'cover.jpeg', 'Cover.png', 'cover.png', 'Cover.tif', 'cover.tif', 'Cover.tiff', 'cover.tiff',
'Folder.jpg', 'folder.jpg', 'Folder.jpeg', 'folder.jpeg', 'Folder.png', 'folder.png', 'Folder.tif', 'folder.tif', 'Folder.tiff', 'folder.tiff']
if metaDict['source'] == 'radio':
if 'coverurl' in metaDict:
rc = '/var/local/www/' + metaDict['coverurl']
if path.exists(rc):
if rc != '/var/local/www/images/default-cover-v6.svg':
cover = Image.open(rc)
elif metaDict['source'] == 'airplay':
cover = ap_back
elif metaDict['source'] == 'bluetooth':
cover = bt_back
elif metaDict['source'] == 'input':
cover = jp_back
elif metaDict['source'] == 'spotify':
cover = sp_back
elif metaDict['source'] == 'squeeze':
cover = sq_back
else:
if 'file' in metaDict:
if len(metaDict['file']) > 0:
fp = '/var/lib/mpd/music/' + metaDict['file']
mf = MediaFile(fp)
if mf.art:
cover = Image.open(BytesIO(mf.art))
return cover
else:
for it in covers:
cp = os.path.dirname(fp) + '/' + it
if path.exists(cp):
cover = Image.open(cp)
return cover
return cover
def main(): def main():
while True:
disp.set_backlight(True) print("In main")
for pin in BUTTONS:
filename = '/var/local/www/currentsong.txt' GPIO.add_event_detect(pin, GPIO.FALLING, handle_button, bouncetime=100)
disp.set_backlight(True)
c = 0 # mit socket verbinden
p = 0 socketIO.on("pushState", on_push_state)
k=0 # get initial state
ol=0 socketIO.emit("getState", "", on_push_state)
ss = 0 socketIO.wait()
x1 = 20 # socketIO.wait_for_callbacks(seconds=1)
x2 = 20 # time.sleep(1)
x3 = 20 time.sleep(0.01)
title_top = 105
volume_top = 184
time_top = 222
act_mpd = isServiceActive('mpd')
if act_mpd == True:
while True:
client = musicpd.MPDClient() # create client object
try:
client.connect() # use MPD_HOST/MPD_PORT
except:
pass
else:
moode_meta = getMoodeMetadata(filename)
mpd_current = client.currentsong()
mpd_status = client.status()
cover = get_cover(moode_meta)
if __name__ == "__main__":
mn = 50
if OVERLAY == 3:
img.paste(cover.resize((WIDTH,HEIGHT), Image.LANCZOS).convert('RGB'))
else:
img.paste(cover.resize((WIDTH,HEIGHT), Image.LANCZOS).filter(ImageFilter.GaussianBlur).convert('RGB'))
if 'state' in mpd_status:
if (mpd_status['state'] == 'stop') and (BLANK != 0):
if ss < BLANK:
ss = ss + 1
else:
disp.set_backlight(False)
else:
ss = 0
disp.set_backlight(True)
im_stat = ImageStat.Stat(cover)
im_mean = im_stat.mean
mn = mean(im_mean)
#txt_col = (255-int(im_mean[0]), 255-int(im_mean[1]), 255-int(im_mean[2]))
txt_col = (255,255,255)
bar_col = (255, 255, 255, 255)
dark = False
if mn > 175:
txt_col = (55,55,55)
dark=True
bar_col = (100,100,100,225)
if mn < 80:
txt_col = (200,200,200)
if (moode_meta['source'] == 'library') or (moode_meta['source'] == 'radio'):
if (OVERLAY > 0) and (OVERLAY < 3):
if 'state' in mpd_status:
if OVERLAY == 2:
if mpd_status['state'] != 'play':
if dark is False:
img.paste(pause_icons, (0,0), pause_icons)
else:
img.paste(pause_icons_dark, (0,0), pause_icons_dark)
else:
if dark is False:
img.paste(play_icons, (0,0), play_icons)
else:
img.paste(play_icons_dark, (0,0), play_icons_dark)
elif OVERLAY == 1:
if dark is False:
img.paste(vol_icons, (0,0), vol_icons)
else:
img.paste(vol_icons_dark, (0,0), vol_icons_dark)
else:
img.paste(play_icons, (0,0), play_icons)
if 'volume' in mpd_status:
vol = int(mpd_status['volume'])
vol_x = int((vol/100)*(WIDTH - 33))
draw.rectangle((5, volume_top, WIDTH-34, volume_top+8), (255,255,255,145))
draw.rectangle((5, volume_top, vol_x, volume_top+8), bar_col)
if OVERLAY < 3:
if TIMEBAR == 1:
if 'elapsed' in mpd_status:
el_time = int(float(mpd_status['elapsed']))
if 'duration' in mpd_status:
du_time = int(float(mpd_status['duration']))
dur_x = int((el_time/du_time)*(WIDTH-10))
draw.rectangle((5, time_top, WIDTH-5, time_top + 12), (255,255,255,145))
draw.rectangle((5, time_top, dur_x, time_top + 12), bar_col)
top = 7
if 'artist' in moode_meta:
w1, y1 = draw.textsize(moode_meta['artist'], font_m)
x1 = x1-20
if x1 < (WIDTH - w1 - 20):
x1 = 0
if w1 <= WIDTH:
x1 = (WIDTH - w1)//2
draw.text((x1, top), moode_meta['artist'], font=font_m, fill=txt_col)
top = 35
if 'album' in moode_meta:
w2, y2 = draw.textsize(moode_meta['album'], font_s)
x2 = x2-20
if x2 < (WIDTH - w2 - 20):
x2 = 0
if w2 <= WIDTH:
x2 = (WIDTH - w2)//2
draw.text((x2, top), moode_meta['album'], font=font_s, fill=txt_col)
if 'title' in moode_meta:
w3, y3 = draw.textsize(moode_meta['title'], font_l)
x3 = x3-20
if x3 < (WIDTH - w3 - 20):
x3 = 0
if w3 <= WIDTH:
x3 = (WIDTH - w3)//2
draw.text((x3, title_top), moode_meta['title'], font=font_l, fill=txt_col)
else:
if 'file' in moode_meta:
txt = moode_meta['file'].replace(' ', '\n')
w3, h3 = draw.multiline_textsize(txt, font_l, spacing=6)
x3 = (WIDTH - w3)//2
y3 = (HEIGHT - h3)//2
draw.text((x3, y3), txt, font=font_l, fill=txt_col, spacing=6, align="center")
disp.display(img)
if c == 0:
im7 = img.save(script_path+'/dump.jpg')
c += 1
time.sleep(1)
ol += 1
client.disconnect()
else:
draw.rectangle((0,0,240,240), fill=(0,0,0))
txt = 'MPD not Active!\nEnsure MPD is running\nThen restart script'
mlw, mlh = draw.multiline_textsize(txt, font=font_m, spacing=4)
draw.multiline_text(((WIDTH-mlw)//2, 20), txt, fill=(255,255,255), font=font_m, spacing=4, align="center")
disp.display(img)
if __name__ == '__main__':
try: try:
main() main()
except KeyboardInterrupt: except KeyboardInterrupt:
disp.reset() img = Image.new("RGB", (240, 240), color=(0, 0, 0))
draw = ImageDraw.Draw(img)
draw.rectangle((0, 0, 240, 240), (0, 0, 0))
disp.display(img)
disp.set_backlight(False) disp.set_backlight(False)
pass pass