commit 7b6ae717ab21ff4cd77c980a5fe3df92baef6dc8 Author: Fusselkater Date: Fri Nov 11 06:22:19 2022 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b58e201 --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..ea118bd --- /dev/null +++ b/Pipfile @@ -0,0 +1,14 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +requests = "*" +miniaudio = "*" +pillow = "*" + +[dev-packages] + +[requires] +python_version = "3.10" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f414879 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# Jamendo OBS Player +This is a tiny little player for jamendo.com music, with display on OBS + +## Installation +As this project uses pipenv, you should install pipenv + +### Fedora +```bash +sudo dnf install pipenv +``` + +### Ubuntu +```bash +sudo apt install pipenv +``` + +Now inside the project directory install the pip environment: +```bash +pipenv install +``` + +## Usage +To run the player, just use pipenv run command to run jamendo_obs.py: +```bash +pipenv run ./jamendo_obs.py chillhop +``` + +You can use the ```--offset``` option to start the player with an initial offset + +To skip a title, send USR1 signal to the player process (you can use this command for a hotkey): +```bash +pkill -USR1 -f jamendo_obs.py +``` + +### OBS Studio +Just add a Picture object and choose the OutputPath from the ini file \ No newline at end of file diff --git a/jamendo_obs.ini b/jamendo_obs.ini new file mode 100644 index 0000000..7227e3e --- /dev/null +++ b/jamendo_obs.ini @@ -0,0 +1,37 @@ +[renderer.png_renderer] +# Output path of the rendered picture +OutputPath = /tmp/obs_music.png + +# Choose if you want to see the album cover (0: no, 1: yes) +Cover = 1 + +# Background color (RGBA) +BackgroundColor = FFFFFF00 + +# Title font +TitleFont = NotoSans-Bold.ttf +TitleFontSize = 44 +TitleFontColor = FFFFFF + +# Artist font +ArtistFont = NotoSans-SemiBold.ttf +ArtistFontSize = 24 +ArtistFontColor = FFFFFF + +# Album font +AlbumFont = NotoSans-Regular.ttf +AlbumFontSize = 24 +AlbumFontColor = FFFFFF + +[jamendo] +# Set the format of the downloaded audio: mp31, mp32, ogg, flac +AudioFormat = ogg + +# Set the order of the requested tracks: relevance, buzzrate, downloads_week, downloads_month, downloads_total, listens_week, listens_month, listens_total, popularity_week, popularity_month, popularity_total, name, album_name, artist_name, releasedate, duration, id +Order = relevance + +# Choose if you want to play vocal or instrumental music: vocal, instrumental (remove setting if not relevant) +VocalInstrumental = instrumental + +# Set the speed of music you like: verylow, low, medium, high, veryhigh (remove setting if not relevant) +;Speed = low \ No newline at end of file diff --git a/jamendo_obs.py b/jamendo_obs.py new file mode 100755 index 0000000..e4992a2 --- /dev/null +++ b/jamendo_obs.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +import argparse +import tempfile +import logging +import signal +import sys +import configparser +import os +from time import sleep +from lib import jamendo +from lib.player import Player +from lib.png_renderer import PNG_Renderer + +# Jamendo Client ID for this project +CLIENT_ID = '32361b03' + +def main(): + parser = argparse.ArgumentParser( + prog = 'Jamendo OBS Player', + description='Plays jamendo music and shows a nice window to include in OBS' + ) + parser.add_argument('tags', help='Search for tags (Example: chillhop+lofi') + parser.add_argument('--offset', type=int, default=0, help='Set lookup offset') + parser.add_argument('--config', type=str, default='jamendo_obs.ini', help='Path to config file') + args = parser.parse_args() + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger('Jamendo OBS') + + config = configparser.ConfigParser() + config.read(args.config) + + track_offset = args.offset + + jamendo_client = jamendo.JamendoClient(CLIENT_ID) + renderer = PNG_Renderer(config['renderer.png_renderer']) + + # remove files if playback is finished + def play_finished_handler(audio_file, cover_file): + logger.debug(f'Removing {audio_file} and {cover_file}...') + os.remove(audio_file) + os.remove(cover_file) + player = Player(renderer, play_finished_handler) + + # we want to cleanup files if CTRL+C is hit + def sigint_handler(sig, frame): + print('Cleaning up...') + player.quit() + sys.exit(0) + signal.signal(signal.SIGINT, sigint_handler) + + # we want to register USR1 signal to skip track + def sigusr1_handler(sig, frame): + player.next() + signal.signal(signal.SIGUSR1, sigusr1_handler) + + while True: + tracks = jamendo_client.tracks( + args.tags, + offset=track_offset, + order=config['jamendo']['Order'] if 'Order' in config['jamendo'] else 'relevance', + audioformat=config['jamendo']['AudioFormat'] if 'AudioFormat' in config['jamendo'] else 'ogg', + vocalinstrumental=config['jamendo']['VocalInstrumental'] if 'VocalInstrumental' in config['jamendo'] else None, + acousticelectric=config['jamendo']['AcousticElectric'] if 'AcousticElectric' in config['jamendo'] else None, + speed=config['jamendo']['Speed'] if 'Speed' in config['jamendo'] else None + ) + + # Break loop if we cannot get more results + if len(tracks) == 0: + logger.error('Search requests had no more results. Please check your settings.') + break + + # Download files and put them on the playlist + for track in tracks: + with tempfile.NamedTemporaryFile('wb', delete=False) as f: + audio_url = track['audio'] + audio_file = f.name + logger.debug(f'Downloading audio {audio_url} to {audio_file}...') + jamendo_client.download(audio_url, f) + + with tempfile.NamedTemporaryFile('wb', delete=False) as f: + cover_url = track['album_image'] + cover_file = f.name + logger.debug(f'Downloading cover {cover_url} to {cover_file}...') + jamendo_client.download(cover_url, f) + + player.add_file(audio_file, track['artist_name'], track['album_name'], track['name'], cover_file) + + # Wait while we have enough files in queue + while player.queue_size > 2: + sleep(1) + logger.debug('Remaining tracks in queue: ' + str(player.queue_size)) + + track_offset += 5 + + # Wait for last track to finish + while True: + sleep(1) + +if __name__ == '__main__': + main() \ No newline at end of file