Feat: import from https://github.com/rusconi/TFT-MoodeCoverArt/
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
backlight.py
|
||||||
|
backlight.sh
|
||||||
|
|
||||||
|
stst.py
|
146
README.md
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# TFT-MoodeCoverArt
|
||||||
|
*version = "0.0.5" : changes*
|
||||||
|
|
||||||
|
* radio icons location changed
|
||||||
|
|
||||||
|
*version = "0.0.4" : changes*
|
||||||
|
|
||||||
|
* added option to display cover art only without overlays (see config file for instructions)
|
||||||
|
|
||||||
|
*version = "0.0.3" : changes*
|
||||||
|
|
||||||
|
* added option to turn off backlight when mpd state = stop (see config file for instructions)
|
||||||
|
|
||||||
|
-------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Based on the look of the pirate audio plugin for mopidy.
|
||||||
|
|
||||||
|
Works with Pimoroni pirate audio boards with 240*240 TFT (ST7789), as well as standalone ST7789 boards.
|
||||||
|
|
||||||
|
![Sample Image](/pics/display.jpg)
|
||||||
|
|
||||||
|
### Features.
|
||||||
|
|
||||||
|
The script will display cover art (where available) for the moode library or radio stations.
|
||||||
|
|
||||||
|
* For the moode library, embedded art will be displayed first, then folder or cover images if there is no embedded art.
|
||||||
|
* For radio stations, the moode images are used.
|
||||||
|
* If no artwork is found a default image is displayed.
|
||||||
|
|
||||||
|
Metadata displayed:
|
||||||
|
* Artist
|
||||||
|
* Album/Radio Station
|
||||||
|
* Title
|
||||||
|
|
||||||
|
Overlays with a Time bar, Volume bar and Play/Pause, Next and Volume icons to match the Pirate Audio buttons are optional.
|
||||||
|
|
||||||
|
There is also an option in config.yml to not display metadata
|
||||||
|
|
||||||
|
The script has a built in test to see if the mpd service is running. This should allow enough delay when
|
||||||
|
used as a service. If a running mpd service is not found after around 30 seconds the script displays the following and stops.
|
||||||
|
|
||||||
|
```
|
||||||
|
MPD not Active!
|
||||||
|
Ensure MPD is running
|
||||||
|
Then restart script
|
||||||
|
```
|
||||||
|
|
||||||
|
**Limitations**
|
||||||
|
|
||||||
|
Metadata will only be displayed for Radio Stations and the Library.
|
||||||
|
|
||||||
|
For the `Airplay`, `Spotify`, `Bluetooth`, `Squeezelite` and `Dac Input` renderers, different backgrounds will display.
|
||||||
|
|
||||||
|
The overlay colours adjust for light and dark artwork, but can be hard to read with some artwork.
|
||||||
|
|
||||||
|
The script does not search online for artwork
|
||||||
|
|
||||||
|
### Assumptions.
|
||||||
|
|
||||||
|
**You can SSH into your RPI, enter commands at the shell prompt, and use the nano editor.**
|
||||||
|
|
||||||
|
**Your moode installation works and produces audio**
|
||||||
|
|
||||||
|
If your pirate audio board doesn't output anything
|
||||||
|
|
||||||
|
Choose "Pimoroni pHAT DAC" or "HiFiBerry DAC" in moode audio config
|
||||||
|
|
||||||
|
See the Installation section [**here**](https://github.com/pimoroni/pirate-audio) about gpio pin 25.
|
||||||
|
|
||||||
|
|
||||||
|
### Preparation.
|
||||||
|
|
||||||
|
**Enable SPI pn your RPI**
|
||||||
|
|
||||||
|
see [**Configuring SPI**](https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-spi)
|
||||||
|
|
||||||
|
Install these pre-requisites:
|
||||||
|
```
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install python3-rpi.gpio python3-spidev python3-pip python3-pil python3-numpy
|
||||||
|
sudo pip3 install mediafile
|
||||||
|
sudo pip3 install pyyaml
|
||||||
|
```
|
||||||
|
Install the TFT driver.
|
||||||
|
|
||||||
|
I have forked the Pimoroni driver and modified it to work with other ST7789 boards. Install it with the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo pip3 install RPI-ST7789
|
||||||
|
```
|
||||||
|
|
||||||
|
***Ensure 'Metadata file' is turned on in Moode System Configuration***
|
||||||
|
|
||||||
|
### Install the TFT-MoodeCoverArt script
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /home/pi
|
||||||
|
git clone https://github.com/rusconi/TFT-MoodeCoverArt.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Config File
|
||||||
|
|
||||||
|
The default config should work with Pirate Audio boards
|
||||||
|
|
||||||
|
The config.yml file can be edited to:
|
||||||
|
|
||||||
|
* suit different ST7789 boards
|
||||||
|
* set overlay display options
|
||||||
|
|
||||||
|
The comments in 'config.yml' should be self explanatory
|
||||||
|
|
||||||
|
|
||||||
|
**Make the shell scripts executable:**
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod 777 *.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Test the script:
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 /home/pi/TFT-MoodeCoverArt/tft_moode_coverart.py
|
||||||
|
|
||||||
|
|
||||||
|
Ctrl-c to quit
|
||||||
|
```
|
||||||
|
|
||||||
|
**If the script works, you may want to start the display at boot:**
|
||||||
|
|
||||||
|
### Install as a service.
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /home/pi/TFT-MoodeCoverArt
|
||||||
|
./install_service.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Follow the prompts.
|
||||||
|
|
||||||
|
If you wish to remove the script as a service:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /home/pi/TFT-MoodeCoverArt
|
||||||
|
./remove_service.sh
|
||||||
|
```
|
||||||
|
|
55
clear_display.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import ST7789
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
from os import path
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# set default config for pirate audio
|
||||||
|
|
||||||
|
MODE=0
|
||||||
|
|
||||||
|
OVERLAY=2
|
||||||
|
|
||||||
|
confile = 'config.yml'
|
||||||
|
|
||||||
|
# Read conf.json 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']
|
||||||
|
|
||||||
|
|
||||||
|
# Standard SPI connections for ST7789
|
||||||
|
# Create ST7789 LCD display class.
|
||||||
|
if MODE == 3:
|
||||||
|
disp = ST7789.ST7789(
|
||||||
|
port=0,
|
||||||
|
cs=ST7789.BG_SPI_CS_FRONT, # GPIO 8, Physical pin 24
|
||||||
|
dc=9,
|
||||||
|
rst=22,
|
||||||
|
backlight=13,
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
disp.begin()
|
||||||
|
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)
|
23
config.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# version = "0.0.4"
|
||||||
|
display:
|
||||||
|
# Display control overlays:
|
||||||
|
# 3=Artwork only
|
||||||
|
# 2=Full overlay displayed
|
||||||
|
# 1=Volume icon only.
|
||||||
|
# 0=no overlay displayed
|
||||||
|
overlay: 2
|
||||||
|
|
||||||
|
# Display Time Bar:
|
||||||
|
# 1=time bar displayed
|
||||||
|
# 0=no time bar displayed
|
||||||
|
timebar: 1
|
||||||
|
|
||||||
|
# mode=0 for pirate audio and display boards with cs pin
|
||||||
|
# mode=3 for display boards without cs pin
|
||||||
|
mode: 0
|
||||||
|
|
||||||
|
# Turn backlight off when mpd state=stop
|
||||||
|
# back on when play restarted
|
||||||
|
# blank=0 screen will not blank when mpd state=stop
|
||||||
|
# blank=n screen will blank after roughly n seconds when mpd state=stop
|
||||||
|
blank: 0
|
BIN
fonts/Roboto-Medium.ttf
Normal file
BIN
images/airplay.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
images/bta.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
images/controls-pause-dark.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
images/controls-pause.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
images/controls-play-dark.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
images/controls-play.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
images/controls-vol-dark.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
images/controls-vol.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
images/default-cover-v6.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
images/jack.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
images/spotify.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
images/squeeze.png
Normal file
After Width: | Height: | Size: 63 KiB |
36
install_service.sh
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo -e "Install TFT-MoodeCoverArt Service. \n"
|
||||||
|
cd /home/pi/TFT-MoodeCoverArt
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
read -p "Do you wish to install TFT-MoodeCoverArt as a service?" yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]* ) echo -e "Installing Service \n"
|
||||||
|
sudo cp tft-moodecoverart.service /lib/systemd/system
|
||||||
|
sudo chmod 644 /lib/systemd/system/tft-moodecoverart.service
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable tft-moodecoverart.service
|
||||||
|
echo -e "\nTFT-MoodeCoverArt installed as a service.\n"
|
||||||
|
echo -e "Please reboot the Raspberry Pi.\n"
|
||||||
|
break;;
|
||||||
|
[Nn]* ) echo -e "Service not installed \n"; break;;
|
||||||
|
* ) echo "Please answer yes or no.";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
read -p "Do you wish to reboot now?" yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]* ) echo -e "Rebooting \n"
|
||||||
|
sudo reboot
|
||||||
|
break;;
|
||||||
|
[Nn]* ) echo -e "Not rebooting \n"
|
||||||
|
break;;
|
||||||
|
* ) echo "Please answer yes or no.";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "TFT-MoodeCoverArt install complete"
|
BIN
pics/display.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
37
remove_service.sh
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo -e "Remove TFT-MoodeCoverArt Service\n"
|
||||||
|
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
read -p "Do you wish to Remove TFT-MoodeCoverArt as a service?" yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]* ) echo -e "Removing Service \n"
|
||||||
|
sudo systemctl stop tft-moodecoverart.service
|
||||||
|
sudo systemctl disable tft-moodecoverart.service
|
||||||
|
sudo rm /etc/systemd/system/tft-moodecoverart.service
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl reset-failed
|
||||||
|
echo -e "\nTFT-MoodeCoverArt removed as a service.\n"
|
||||||
|
echo -e "Please reboot the Raspberry Pi.\n"
|
||||||
|
break;;
|
||||||
|
[Nn]* ) echo -e "Service not removed \n"; break;;
|
||||||
|
* ) echo "Please answer yes or no.";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
read -p "Do you wish to reboot now?" yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]* ) echo -e "Rebooting \n"
|
||||||
|
sudo reboot
|
||||||
|
break;;
|
||||||
|
[Nn]* ) echo -e "Not rebooting \n"
|
||||||
|
break;;
|
||||||
|
* ) echo "Please answer yes or no.";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "TFT-MoodeCoverArt service removal complete"
|
14
tft-moodecoverart.service
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=TFT-MoodeCoverArt Display
|
||||||
|
Requires=mpd.socket mpd.service
|
||||||
|
After=mpd.socket mpd.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/python3 /home/pi/TFT-MoodeCoverArt/tft_moode_coverart.py &
|
||||||
|
#ExecStartPre=/bin/sleep 15
|
||||||
|
ExecStop=/home/pi/TFT-MoodeCoverArt/tft_moode_coverart.sh -q
|
||||||
|
Restart=on-abort
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
383
tft_moode_coverart.py
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
from PIL import Image, ImageDraw, ImageColor, ImageFont, ImageStat
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import musicpd
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from os import path
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
from mediafile import MediaFile
|
||||||
|
from io import BytesIO
|
||||||
|
from numpy import mean
|
||||||
|
import ST7789
|
||||||
|
from PIL import ImageFilter
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# set default config for pirate audio
|
||||||
|
|
||||||
|
__version__ = "0.0.5"
|
||||||
|
|
||||||
|
# get the path of the script
|
||||||
|
script_path = os.path.dirname(os.path.abspath( __file__ ))
|
||||||
|
# set script path as current directory -
|
||||||
|
os.chdir(script_path)
|
||||||
|
|
||||||
|
MODE=0
|
||||||
|
OVERLAY=2
|
||||||
|
TIMEBAR=1
|
||||||
|
BLANK=0
|
||||||
|
|
||||||
|
confile = 'config.yml'
|
||||||
|
|
||||||
|
# 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']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Standard SPI connections for ST7789
|
||||||
|
# Create ST7789 LCD display class.
|
||||||
|
if MODE == 3:
|
||||||
|
disp = ST7789.ST7789(
|
||||||
|
port=0,
|
||||||
|
cs=ST7789.BG_SPI_CS_FRONT, # GPIO 8, Physical pin 24
|
||||||
|
dc=9,
|
||||||
|
rst=22,
|
||||||
|
backlight=13,
|
||||||
|
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.
|
||||||
|
disp.begin()
|
||||||
|
|
||||||
|
|
||||||
|
WIDTH = 240
|
||||||
|
HEIGHT = 240
|
||||||
|
font_s = ImageFont.truetype(script_path + '/fonts/Roboto-Medium.ttf',20)
|
||||||
|
font_m = ImageFont.truetype(script_path + '/fonts/Roboto-Medium.ttf',24)
|
||||||
|
font_l = ImageFont.truetype(script_path + '/fonts/Roboto-Medium.ttf',30)
|
||||||
|
|
||||||
|
|
||||||
|
img = Image.new('RGB', (240, 240), color=(0, 0, 0, 25))
|
||||||
|
|
||||||
|
play_icons = Image.open(script_path + '/images/controls-play.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
|
||||||
|
play_icons_dark = Image.open(script_path + '/images/controls-play-dark.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
|
||||||
|
|
||||||
|
pause_icons = Image.open(script_path + '/images/controls-pause.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
|
||||||
|
pause_icons_dark = Image.open(script_path + '/images/controls-pause-dark.png').resize((240,240), resample=Image.LANCZOS).convert("RGBA")
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(img, 'RGBA')
|
||||||
|
|
||||||
|
|
||||||
|
def isServiceActive(service):
|
||||||
|
|
||||||
|
waiting = True
|
||||||
|
count = 0
|
||||||
|
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():
|
||||||
|
|
||||||
|
disp.set_backlight(True)
|
||||||
|
|
||||||
|
filename = '/var/local/www/currentsong.txt'
|
||||||
|
|
||||||
|
c = 0
|
||||||
|
p = 0
|
||||||
|
k=0
|
||||||
|
ol=0
|
||||||
|
ss = 0
|
||||||
|
x1 = 20
|
||||||
|
x2 = 20
|
||||||
|
x3 = 20
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
disp.reset()
|
||||||
|
disp.set_backlight(False)
|
||||||
|
pass
|
12
tft_moode_coverart.sh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
while getopts ":sq" opt; do
|
||||||
|
case ${opt} in
|
||||||
|
s ) sudo pkill -f tft_moode_coverart.py; sudo /usr/bin/python3 /home/pi/TFT-MoodeCoverArt/clear_display.py; sudo /usr/bin/python3 /home/pi/TFT-MoodeCoverArt/tft_moode_coverart.py &
|
||||||
|
;;
|
||||||
|
q ) sudo pkill -f tft_moode_coverart.py; sudo python3 /home/pi/TFT-MoodeCoverArt/clear_display.py
|
||||||
|
;;
|
||||||
|
\? ) echo "Usage: cmd [-s] [-q]"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|