-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
730 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
name: Publish Regular multi-arch Docker images | ||
|
||
on: | ||
push: | ||
tags: | ||
- "release/*" | ||
- "feature/*" | ||
- "daily/*" | ||
- "v*" # release | ||
- "f*" # feature | ||
- "d*" # daily | ||
|
||
jobs: | ||
release: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
base: ["stable"] | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
|
||
- name: Prepare for docker build | ||
id: prepare | ||
run: | | ||
image_name=${{secrets.DOCKER_USERNAME}}/upnp-scrobbler | ||
ref_type=${{ github.ref_type }} | ||
echo "REF_TYPE: ["$ref_type"]" | ||
ref_name=${{ github.ref_name }} | ||
echo "REF_NAME: ["$ref_name"]" | ||
ref=${{ github.ref }} | ||
echo "REF: ["$ref"]" | ||
declare -A base_image_from_matrix | ||
base_image_from_matrix[stable]=library/python:3.9-slim | ||
select_base_image=${base_image_from_matrix[${{ matrix.base }}]} | ||
if [ -z "${select_base_image}" ]; then | ||
select_base_image=library/python:3.9-slim | ||
fi | ||
echo "Select Base Image [" $select_base_image "]" | ||
declare -A special_tags | ||
special_tags[stable]="${image_name}:latest,${image_name}:stable" | ||
declare -A distro_friendly_name_dict | ||
distro_friendly_name_dict[stable]=stable | ||
distro_friendly_name=${{ matrix.base }} | ||
lookup_distro_name=${distro_friendly_name_dict[${{ matrix.base }}]} | ||
if [ -n "${lookup_distro_name}" ]; then | ||
distro_friendly_name=$lookup_distro_name | ||
fi | ||
tags="" | ||
if [ "${ref_type}" = "tag" ]; then | ||
echo "tag mode" | ||
echo "tag is ["${ref_name}"]" | ||
if [[ "${ref_name}" = *"/"* ]]; then | ||
tag_type=$(echo ${ref_name} | cut -d '/' -f 1) | ||
tag_name=$(echo ${ref_name} | cut -d '/' -f 2) | ||
else | ||
if [[ "${ref_name}" = "v"* || "${ref_name}" = "f"* || "${ref_name}" = "d"* ]]; then | ||
tag_type=${ref_name:0:1} | ||
tag_name=${ref_name:1} | ||
fi | ||
fi | ||
echo "tag_type=[$tag_type]" | ||
echo "tag_name=[$tag_name]" | ||
if [[ "${tag_type}" == "release" || "${tag_type}" == "v" ]]; then | ||
echo "release tag" | ||
tags="$image_name:${distro_friendly_name}" | ||
tags="$tags,$image_name:${distro_friendly_name}-${tag_name}" | ||
special_tag_lookup="${{ matrix.base }}" | ||
select_special_tags=${special_tags["${special_tag_lookup}"]} | ||
building_now="${distro_friendly_name}" | ||
echo "Building now: ["$building_now"]" | ||
if [[ -n "${select_special_tags}" ]]; then | ||
echo "Found special tags for ["${building_now}"]=["${select_special_tags}"]" | ||
tags="$tags,${select_special_tags}" | ||
else | ||
echo "No special tags found for ["${building_now}"]" | ||
fi | ||
elif [[ "${tag_type}" == "feature" || "${tag_type}" == "f" ]]; then | ||
echo "feature tag" | ||
tags="${image_name}:feature-${tag_name}-${distro_friendly_name}" | ||
elif [[ "${tag_type}" = "daily" || "${tag_type}" = "d" ]]; then | ||
echo "daily build" | ||
tags="${image_name}:daily-${distro_friendly_name}" | ||
fi | ||
fi | ||
echo "Building tags: ["${tags}"]" | ||
echo "RELEASE_TAGS=${tags}" >> $GITHUB_OUTPUT | ||
echo "BASE_IMAGE=${select_base_image}" >> $GITHUB_OUTPUT | ||
- name: Set up QEMU | ||
uses: docker/setup-qemu-action@v3 | ||
with: | ||
platforms: all | ||
|
||
- name: Set up Docker Buildx | ||
id: buildx | ||
uses: docker/setup-buildx-action@v3 | ||
|
||
- name: Login to DockerHub | ||
uses: docker/login-action@v3 | ||
with: | ||
username: ${{ secrets.DOCKER_USERNAME }} | ||
password: ${{ secrets.DOCKER_TOKEN }} | ||
|
||
- name: Build and push | ||
uses: docker/build-push-action@v5 | ||
with: | ||
context: . | ||
build-args: | | ||
BASE_IMAGE=${{ steps.prepare.outputs.BASE_IMAGE }} | ||
platforms: linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v5 | ||
push: true | ||
tags: ${{ steps.prepare.outputs.RELEASE_TAGS }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name: Sync README.md to Docker Hub | ||
|
||
on: | ||
push: | ||
branches: | ||
- "main" | ||
|
||
jobs: | ||
sync-readme: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
- name: Docker Hub README & description sync | ||
uses: meeDamian/sync-readme@v1.0.6 | ||
with: | ||
user: ${{ secrets.DOCKER_USERNAME }} | ||
pass: ${{ secrets.DOCKER_TOKEN }} | ||
slug: ${{ secrets.DOCKER_USERNAME }}/upnp-scrobbler | ||
readme: ./README.md | ||
description: Scrobble to last.fm from your WiiM device |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Python Debugger: scrobbler", | ||
"type": "debugpy", | ||
"request": "launch", | ||
"program": "${workspaceFolder}/upnp_scrobbler/scrobbler.py", | ||
"console": "integratedTerminal", | ||
"envFile": "${workspaceFolder}/.env" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"flake8.args": [ | ||
"--max-line-length=120", | ||
"--ignore=E701,W503,W504" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
FROM python:3.9-slim AS BASE | ||
|
||
RUN apt-get update | ||
RUN apt-get install -y build-essential | ||
|
||
COPY requirements.txt requirements.txt | ||
RUN pip3 install --no-cache-dir -r requirements.txt | ||
RUN rm requirements.txt | ||
|
||
RUN apt-get remove -y build-essential | ||
RUN apt-get autoremove -y | ||
|
||
RUN rm -rf /var/lib/apt/lists/* | ||
|
||
FROM scratch | ||
COPY --from=BASE / / | ||
|
||
LABEL maintainer="GioF71" | ||
LABEL source="https://github.com/GioF71/upnp-scrobbler" | ||
|
||
ENV DEVICE_URL "" | ||
ENV LAST_FM_API_KEY "" | ||
ENV LAST_FM_SHARED_SECRET "" | ||
ENV LAST_FM_USERNAME "" | ||
ENV LAST_FM_PASSWORD_HASH "" | ||
ENV LAST_FM_PASSWORD "" | ||
|
||
ENV DURATION_THRESHOLD "" | ||
|
||
ENV ENABLE_NOW_PLAYING "" | ||
|
||
ENV PYTHONUNBUFFERED=1 | ||
|
||
RUN mkdir /code | ||
COPY upnp_scrobbler/*.py /code/ | ||
|
||
VOLUME /config | ||
|
||
WORKDIR /code | ||
|
||
ENTRYPOINT [ "python3", "scrobbler.py" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# UPnP Scrobbler | ||
|
||
A simple LAST.fm scrobbler for WiiM devices. | ||
|
||
## References | ||
|
||
I started taking the code published in [this post](https://forum.wiimhome.com/threads/last-fm.3144/post-44653) as the starting point. | ||
It seems that I cannot get a valid link to the user profile on that board, but anyway the nickname is `cc_rider`. | ||
A big thank you goes to him/her for the code he/she shared. | ||
I then used [pylast](https://github.com/pylast/pylast), as suggested in that thread, for the actual operations on [last.fm](https://www.last.fm/). | ||
|
||
## Links | ||
|
||
REPOSITORY TYPE|LINK | ||
:---|:--- | ||
Git Repository|[GitHub](https://github.com/GioF71/upnp-scrobbler) | ||
Docker Images|[Docker Hub](https://hub.docker.com/repository/docker/giof71/upnp-scrobbler) | ||
|
||
## Task List | ||
|
||
- [x] Scrobbling to last.fm from a WiiM device, using Tidal Connect | ||
- [x] Scrobbling to last.fm from a WiiM device, using it as a generic UPnP Renderer | ||
- [ ] Scrobbling to last.fm from gmrenderer-resurrect ([Source](https://github.com/hzeller/gmrender-resurrect) and [Docker image](https://github.com/gioF71/gmrender-resurrect-docker)), using it as a generic UPnP Renderer | ||
- [ ] Scrobbling to libre.fm | ||
|
||
## Build | ||
|
||
You can build the docker image by typing: | ||
|
||
```text | ||
docker build . -t giof71/upnp-scrobbler | ||
``` | ||
|
||
## Configuration | ||
|
||
### Create your API key and secret | ||
|
||
Open your browser at [this](https://www.last.fm/api/account/create), follow the instruction and accurately store the generated values. | ||
Those will be needed for the configuration. | ||
|
||
### Environment Variables | ||
|
||
NAME|DESCRIPTION | ||
:---|:--- | ||
DEVICE_URL|Device URL of your UPnP Device (example: `http://192.168.1.7:49152/description.xml`) | ||
LAST_FM_API_KEY|Your LAST.fm api key | ||
LAST_FM_SHARED_SECRET|Your LAST.fm api key | ||
LAST_FM_USERNAME|Your LAST.fm account username | ||
LAST_FM_PASSWORD_HASH|Your LAST.fm account password encoded using md5 | ||
LAST_FM_PASSWORD|Your LAST.fm account password in clear text, used when LAST_FM_PASSWORD_HASH is not provided | ||
ENABLE_NOW_PLAYING|Update `now playing` information if set to `yes` (default) | ||
DURATION_THRESHOLD|Minimum duration required from scrobbling (unless at least half of the duration has elapsed), defaults to `240` | ||
|
||
## Running | ||
|
||
The preferred way of running this application is through Docker. | ||
|
||
### Sample compose file | ||
|
||
Here is a simple compose file, valid for a WiiM device: | ||
|
||
```text | ||
--- | ||
version: "3" | ||
services: | ||
scrobbler: | ||
image: giof71/upnp-scrobbler:latest | ||
container_name: wiim-scrobbler | ||
network_mode: host | ||
environment: | ||
- DEVICE_URL=http://192.168.1.7:49152/description.xml | ||
- LAST_FM_API_KEY=your-lastfm-api-key | ||
- LAST_FM_SHARED_SECRET=your-lastfm-api-secret | ||
- LAST_FM_USERNAME=your-lastfm-username | ||
- LAST_FM_PASSWORD_HASH=your-lastfm-md5-hashed-password | ||
restart: unless-stopped | ||
``` | ||
|
||
It is advisable to put the variable values in a .env file in the same directory with this `docker-compose.yaml` file. | ||
Start the container with the following: | ||
|
||
`docker-compose up -d` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[tool.poetry] | ||
name = "upnp-scrobbler" | ||
version = "0.1.0" | ||
description = "A LAST.fm scrobbler for UPnP players" | ||
authors = ["GioF_71 <giovanni.fulco@gmail.com>"] | ||
license = "MIT" | ||
|
||
[tool.poetry.dependencies] | ||
python = "3.9" | ||
xmltodict | ||
requests | ||
python-didl-lite | ||
async-upnp-client | ||
pylast | ||
|
||
[tool.poetry.dev-dependencies] | ||
|
||
[build-system] | ||
requires = ["poetry-core>=1.0.0"] | ||
build-backend = "poetry.core.masonry.api" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
xmltodict | ||
requests | ||
python-didl-lite | ||
async-upnp-client | ||
pylast |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# customize DEVICE_URL to your device ip/port | ||
DEVICE_URL=http://192.168.1.200:49152/description.xml | ||
|
||
# Last.FM KEY and SECRET | ||
LAST_FM_API_KEY=your-api-key | ||
LAST_FM_SHARED_SECRET=your-shared-secret | ||
|
||
LAST_FM_USERNAME=your-account-username | ||
|
||
# hashed password has priority | ||
LAST_FM_PASSWORD_HASH=your-account-password-encoded-in-md5 | ||
|
||
# if there is not hashed password, the app tries with cleartext (not recommended) | ||
LAST_FM_PASSWORD=your-account-password-in-clear-text | ||
|
||
# Optional duration threshold, defaults to 240 (4 minutes) | ||
# DURATION_THRESHOLD=240 | ||
|
||
# Also update the now playing information if set to yes | ||
# default is yes | ||
# ENABLE_NOW_PLAYING=yes |
Oops, something went wrong.