Merge pull request #17 from bartkl/main
Album art fetching from HTTP server
This commit is contained in:
11
README.md
11
README.md
@ -24,19 +24,22 @@ pip install miniplayer
|
|||||||
The config file is located at `~/.config/miniplayer/config`. The example configuration file, [`config.example`](config.example), has all the default values. You will need to create the file yourself.
|
The config file is located at `~/.config/miniplayer/config`. The example configuration file, [`config.example`](config.example), has all the default values. You will need to create the file yourself.
|
||||||
|
|
||||||
#### player
|
#### player
|
||||||
* ***music_directory*:** The path to your music directory for extracting album art.
|
|
||||||
* ***font_width*:** The width of your font in pixels in the actual terminal.
|
* ***font_width*:** The width of your font in pixels in the actual terminal.
|
||||||
* ***font_height*:** The height of your font in pixels in the actual terminal.
|
* ***font_height*:** The height of your font in pixels in the actual terminal.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
* ***image_method*:** The method to use for drawing album art. Available values are `pixcat` and `ueberzug`
|
|
||||||
If you are not using Kitty, try `ueberzug`.
|
|
||||||
* ***volume_step*:** The ammount (in percents) the volume will be adjusted on pressing the volume up and volume down keys.
|
* ***volume_step*:** The ammount (in percents) the volume will be adjusted on pressing the volume up and volume down keys.
|
||||||
* ***album_art_only*:** Whether or not to only draw the album art and no other track info (`true/false`).
|
* ***album_art_only*:** Whether or not to only draw the album art and no other track info (`true/false`).
|
||||||
* ***auto_close*:** Whether or not to automatically close the player once the mpd playlist has concluded (`true/false`).
|
* ***auto_close*:** Whether or not to automatically close the player once the mpd playlist has concluded (`true/false`).
|
||||||
* ***show_playlist*:** Whether or not to show the playlist view.
|
* ***show_playlist*:** Whether or not to show the playlist view.
|
||||||
|
|
||||||
|
#### art
|
||||||
|
* ***music_directory*:** The path to your music directory for extracting album art from the files.
|
||||||
|
* ***http_base_url*:** Base URL of webserver which serves the album art for your albums (takes precedence over `music_directory`). Useful for users of Android MPD clients _MAFA_ or _MPDroid_. For more information see [the MPDroid wiki](https://github.com/abarisain/dmix/wiki/Album-Art-on-your-LAN).
|
||||||
|
* ***http_cover_filenames*:** Space separated list of filenames to use in the call to the webserver to fetch the album art.
|
||||||
|
* ***image_method*:** The method to use for drawing album art. Available values are `pixcat` and `ueberzug`
|
||||||
|
If you are not using Kitty, try `ueberzug`.
|
||||||
|
|
||||||
#### mpd
|
#### mpd
|
||||||
* ***host*:** The mpd host
|
* ***host*:** The mpd host
|
||||||
@ -80,7 +83,7 @@ These keybinds can be changed by editing the config file. See the [`config.examp
|
|||||||
|
|
||||||
## F.A.Q.
|
## F.A.Q.
|
||||||
- **Q:** Album art is not showing up.
|
- **Q:** Album art is not showing up.
|
||||||
**A:** Make sure your `music_directory` is not quoted i.e. if your music directory is `~/My Music` then your config should look like `music_directory = ~/My Music`.
|
**A:** If you're using `music_directory` for fetching your album art, make sure your it is not quoted i.e. if your music directory is `~/My Music` then your config should look like `music_directory = ~/My Music`.
|
||||||
If this does not work, try changing `image_method` from `pixcat` to `ueberzug` or vice versa.
|
If this does not work, try changing `image_method` from `pixcat` to `ueberzug` or vice versa.
|
||||||
|
|
||||||
- **Q:** Album art is too big/too small.
|
- **Q:** Album art is too big/too small.
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/bin/python
|
#!/bin/python
|
||||||
import curses
|
import curses
|
||||||
import os
|
import os
|
||||||
|
import posixpath
|
||||||
|
import requests
|
||||||
from mpd import MPDClient
|
from mpd import MPDClient
|
||||||
import ffmpeg
|
import ffmpeg
|
||||||
import pixcat
|
import pixcat
|
||||||
@ -9,21 +11,25 @@ import configparser
|
|||||||
import ueberzug.lib.v0 as ueberzug
|
import ueberzug.lib.v0 as ueberzug
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
|
||||||
# Get config
|
# Get config
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read(os.path.expanduser("~/.config/miniplayer/config"))
|
config.read(os.path.expanduser("~/.config/miniplayer/config"))
|
||||||
|
|
||||||
if "player" not in config.sections():
|
if "player" not in config.sections():
|
||||||
config["player"] = {"music_directory": "~/Music",
|
config["player"] = {"font_width": 11,
|
||||||
"font_width": 11,
|
|
||||||
"font_height": 24,
|
"font_height": 24,
|
||||||
"image_method": "pixcat",
|
|
||||||
"album_art_only": False,
|
"album_art_only": False,
|
||||||
"volume_step": 5,
|
"volume_step": 5,
|
||||||
"auto_close": False,
|
"auto_close": False,
|
||||||
"show_playlist": True,
|
"show_playlist": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if "art" not in config.sections():
|
||||||
|
config["art"] = {"music_directory": "~/Music",
|
||||||
|
"image_method": "pixcat",
|
||||||
|
}
|
||||||
|
|
||||||
if "mpd" not in config.sections():
|
if "mpd" not in config.sections():
|
||||||
config["mpd"] = {"host": "localhost",
|
config["mpd"] = {"host": "localhost",
|
||||||
"port": "6600",
|
"port": "6600",
|
||||||
@ -46,6 +52,23 @@ default_bindings = {">": "next_track",
|
|||||||
if "keybindings" not in config.sections():
|
if "keybindings" not in config.sections():
|
||||||
config["keybindings"] = default_bindings
|
config["keybindings"] = default_bindings
|
||||||
|
|
||||||
|
# Ensure compatibility with older configs
|
||||||
|
deprecated_field_notice = ("The config option `{field}` under the `player` "
|
||||||
|
"section is deprecated and will be removed in the "
|
||||||
|
"future. Use the config option `{field}` under the "
|
||||||
|
"`art` section instead.")
|
||||||
|
deprecated_fields_present = False
|
||||||
|
for field in ["music_directory", "image_method"]:
|
||||||
|
if config.has_option("player", field):
|
||||||
|
deprecated_fields_present = True
|
||||||
|
print(deprecated_field_notice.format(field=field))
|
||||||
|
|
||||||
|
if not config.has_option("art", field):
|
||||||
|
config["art"][field] = config["player"][field]
|
||||||
|
|
||||||
|
if deprecated_fields_present:
|
||||||
|
input("(Press <Enter> to continue) ...")
|
||||||
|
|
||||||
# Load configured keybindings
|
# Load configured keybindings
|
||||||
keybindings = config["keybindings"]
|
keybindings = config["keybindings"]
|
||||||
|
|
||||||
@ -59,6 +82,7 @@ for key, action in default_bindings.items():
|
|||||||
keybindings[key] = action
|
keybindings[key] = action
|
||||||
|
|
||||||
player_config = config["player"]
|
player_config = config["player"]
|
||||||
|
art_config = config["art"]
|
||||||
mpd_config = config["mpd"]
|
mpd_config = config["mpd"]
|
||||||
|
|
||||||
|
|
||||||
@ -71,17 +95,13 @@ IMAGERATIO = (player_config.getint("font_width", 11),
|
|||||||
player_config.getint("font_height", 24)
|
player_config.getint("font_height", 24)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Music directory
|
|
||||||
MUSICDIR = player_config.get("music_directory", "~/Music")
|
|
||||||
MUSICDIR = os.path.expanduser(MUSICDIR)
|
|
||||||
|
|
||||||
# MPD config
|
# MPD config
|
||||||
MPDHOST = mpd_config.get("host", "localhost")
|
MPDHOST = mpd_config.get("host", "localhost")
|
||||||
MPDPORT = mpd_config.getint("port", 6600)
|
MPDPORT = mpd_config.getint("port", 6600)
|
||||||
MPDPASS = mpd_config.get("pass", False)
|
MPDPASS = mpd_config.get("pass", False)
|
||||||
|
|
||||||
# What to use to draw images
|
# What to use to draw images
|
||||||
IMAGEMETHOD = player_config.get("image_method", "pixcat")
|
IMAGEMETHOD = art_config.get("image_method", "pixcat")
|
||||||
|
|
||||||
# Volume step
|
# Volume step
|
||||||
VOLUMESTEP = player_config.getint("volume_step", 5)
|
VOLUMESTEP = player_config.getint("volume_step", 5)
|
||||||
@ -141,6 +161,11 @@ class Player:
|
|||||||
|
|
||||||
self.last_song = None
|
self.last_song = None
|
||||||
|
|
||||||
|
# Album art HTTP server
|
||||||
|
|
||||||
|
if art_config.get("http_base_url"):
|
||||||
|
self.art_http_session = requests.Session()
|
||||||
|
|
||||||
# Album art only flag
|
# Album art only flag
|
||||||
self.album_art_only = player_config.getboolean("album_art_only", False)
|
self.album_art_only = player_config.getboolean("album_art_only", False)
|
||||||
|
|
||||||
@ -314,14 +339,57 @@ class Player:
|
|||||||
|
|
||||||
self.screen_height, self.screen_width = window_height, window_width
|
self.screen_height, self.screen_width = window_height, window_width
|
||||||
|
|
||||||
|
|
||||||
def getAlbumArt(self, song_file):
|
def getAlbumArt(self, song_file):
|
||||||
|
"""
|
||||||
|
A function that fetches the album art and saves
|
||||||
|
it to self.album_art_loc
|
||||||
|
"""
|
||||||
|
http_base_url = art_config.get("http_base_url")
|
||||||
|
|
||||||
|
if http_base_url:
|
||||||
|
self._getAlbumArtFromHttpServer(http_base_url, song_file)
|
||||||
|
else:
|
||||||
|
self._getAlbumArtFromFile(song_file)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _getAlbumArtFromHttpServer(self, base_url, song_file):
|
||||||
|
"""
|
||||||
|
A function that fetches the album art from the configured
|
||||||
|
HTTP server, and saves it to self.album_art_loc
|
||||||
|
"""
|
||||||
|
|
||||||
|
album = os.path.dirname(song_file)
|
||||||
|
|
||||||
|
for cover_filename in art_config.get("http_cover_filenames", "cover.jpg").split():
|
||||||
|
album_art_url = posixpath.join(base_url, album, cover_filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
album_art_resp = self.art_http_session.get(album_art_url)
|
||||||
|
except requests.RequestException:
|
||||||
|
# If any exception occurs, simply give up and show default art.
|
||||||
|
self.drawDefaultAlbumArt()
|
||||||
|
break
|
||||||
|
|
||||||
|
if album_art_resp.ok:
|
||||||
|
with open(self.album_art_loc, "wb") as f:
|
||||||
|
f.write(album_art_resp.content)
|
||||||
|
break
|
||||||
|
elif album_art_resp.status_code == 404:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
self.drawDefaultAlbumArt()
|
||||||
|
|
||||||
|
|
||||||
|
def _getAlbumArtFromFile(self, song_file):
|
||||||
"""
|
"""
|
||||||
A function that extracts the album art from song_file and
|
A function that extracts the album art from song_file and
|
||||||
saves it to self.album_art_loc
|
saves it to self.album_art_loc
|
||||||
"""
|
"""
|
||||||
|
music_dir = os.path.expanduser(
|
||||||
|
art_config.get("music_directory", "~/Music"))
|
||||||
|
|
||||||
song_file_abs = os.path.join(MUSICDIR, song_file)
|
song_file_abs = os.path.join(music_dir, song_file)
|
||||||
|
|
||||||
process = (
|
process = (
|
||||||
ffmpeg
|
ffmpeg
|
||||||
@ -332,6 +400,10 @@ class Player:
|
|||||||
try:
|
try:
|
||||||
process.run(quiet=True, overwrite_output=True)
|
process.run(quiet=True, overwrite_output=True)
|
||||||
except ffmpeg._run.Error:
|
except ffmpeg._run.Error:
|
||||||
|
self.drawDefaultAlbumArt()
|
||||||
|
|
||||||
|
|
||||||
|
def drawDefaultAlbumArt(self):
|
||||||
foregroundCol = "#D8DEE9"
|
foregroundCol = "#D8DEE9"
|
||||||
backgroundCol = "#262A33"
|
backgroundCol = "#262A33"
|
||||||
|
|
||||||
@ -413,8 +485,6 @@ class Player:
|
|||||||
self.selected_song = int(song["pos"])
|
self.selected_song = int(song["pos"])
|
||||||
|
|
||||||
self.album, self.artist, self.title = self.getSongInfo(song)
|
self.album, self.artist, self.title = self.getSongInfo(song)
|
||||||
self.last_song = song
|
|
||||||
|
|
||||||
self.getAlbumArt(song["file"])
|
self.getAlbumArt(song["file"])
|
||||||
self.last_song = song
|
self.last_song = song
|
||||||
|
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
[player]
|
[player]
|
||||||
music_directory = ~/Music
|
|
||||||
font_width = 11
|
font_width = 11
|
||||||
font_height = 24
|
font_height = 24
|
||||||
image_method = pixcat
|
|
||||||
volume_step = 5
|
volume_step = 5
|
||||||
auto_close = false
|
auto_close = false
|
||||||
album_art_only = false
|
album_art_only = false
|
||||||
show_playlist = true
|
show_playlist = true
|
||||||
|
|
||||||
|
[art]
|
||||||
|
image_method = pixcat
|
||||||
|
music_directory = ~/Music
|
||||||
|
# http_base_url = http://localhost:6667/cover-art
|
||||||
|
# http_cover_filenames = cover.jpg cover.png folder.jpg folder.png art.jpg art.png artwork.jpg artwork.png
|
||||||
|
|
||||||
[mpd]
|
[mpd]
|
||||||
host = localhost
|
host = localhost
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = miniplayer
|
name = miniplayer
|
||||||
version = 1.3.2
|
version = 1.4.0
|
||||||
description = An mpd client with album art and basic functionality.
|
description = An mpd client with album art and basic functionality.
|
||||||
long_description = file: README.md
|
long_description = file: README.md
|
||||||
long_description_content_type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
@ -22,5 +22,6 @@ install_requires =
|
|||||||
ffmpeg-python
|
ffmpeg-python
|
||||||
pixcat
|
pixcat
|
||||||
pillow
|
pillow
|
||||||
|
requests
|
||||||
ueberzug
|
ueberzug
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user