Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added TIDAL Plugin + More #45

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/build_container.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.

name: Create and publish a Docker image

on:
push:
branches:
- 'master'

jobs:
push_to_registries:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: |
ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha

- name: Build and push Docker images
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
45 changes: 26 additions & 19 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ name: "CodeQL"

on:
push:
branches: [ master ]
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
branches: [ "master" ]
schedule:
- cron: '35 5 * * 3'
- cron: '22 1 * * 3'

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
Expand All @@ -33,38 +34,44 @@ jobs:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality


# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

#- run: |
# make bootstrap
# make release
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ dmypy.json
# VSCode
.vscode

# Pycharm
.idea

# Ultrasonics specifics
.vscode/*
!.vscode/launch.json
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
FROM python:3.8.5-alpine
FROM python:3.11-alpine

COPY . /
WORKDIR /

VOLUME [ "/config", "/plugins" ]

RUN apk --no-cache add gcc g++ musl-dev
RUN apk --no-cache add gcc g++ musl-dev cmake ninja
RUN pip install -r requirements.txt

EXPOSE 5000
Expand Down
12 changes: 12 additions & 0 deletions TIDAL_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Tidal Plugin
## Setup
The Tidal plugin requires [tidalapi](https://github.com/tamland/python-tidal/tree/0.7.x) version 0.7.x, which isn't on PyPI on the time of writing.
To install run:
```bash
git clone -b 0.7.x https://github.com/tamland/python-tidal.git
cd python-tidal/
python3 setup.py install
```
## Logging in
I couldn't work out a good way of logging in from ultrasonic's web UI, so for now run `python3 tidal_login.py` and go to the link.
When logged in, the session ids and keys will show in your terminal, copy them onto ultrasonic's tidal persistant settings page.
15 changes: 10 additions & 5 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@

import os

from ultrasonics import database, plugins, scheduler, webapp
from ultrasonics import updater, database, plugins, scheduler, webapp

_ultrasonics = {
"version": "1.0.0-rc.1",
"config_dir": os.path.join(os.path.dirname(__file__), "config")
}

database.Core().connect()
plugins.plugin_gather()
scheduler.scheduler_start()
webapp.server_start()
def start():
updater.start(_ultrasonics["version"])
database.Core().connect()
plugins.plugin_gather()
scheduler.scheduler_start()
webapp.server_start()

if __name__ == "__main__":
start()
21 changes: 11 additions & 10 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Main Program
Flask==2.0.1
Flask-SocketIO==5.1.1
eventlet==0.32.0
Flask~=2.3.2
Flask-SocketIO~=5.3.4
eventlet~=0.33.3

# Included Plugins
mutagen==1.45.1
requests==2.26.0
fuzzywuzzy==0.18.0
python-Levenshtein==0.12.2
spotipy==2.19.0
tqdm==4.62.3
PlexAPI==4.7.2
mutagen~=1.46.0
requests~=2.31.0
fuzzywuzzy~=0.18.0
python-Levenshtein~=0.21.0
spotipy~=2.23.0
tqdm~=4.65.0
PlexAPI~=4.14.0
tidalapi~=0.7.2
16 changes: 16 additions & 0 deletions tidal_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3

import tidalapi

session = tidalapi.Session()
session.login_oauth_simple()

print('-'*16)
print("Token Type:", session.token_type)
print('-'*16)
print("Access Token:", session.access_token)
print('-'*16)
print("Refresh Token:", session.refresh_token)
print('-'*16)
print("Expiry Time:", session.expiry_time.isoformat())
print('-'*16)
2 changes: 1 addition & 1 deletion ultrasonics.service
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ConditionPathIsDirectory=/opt/ultrasonics
[Service]
Restart=always
Type=simple
ExecStart=.venv/bin/activate && python3 app.py
ExecStart=/opt/ultrasonics/.venv/bin/python3 app.py
WorkingDirectory=/opt/ultrasonics

[Install]
Expand Down
6 changes: 4 additions & 2 deletions ultrasonics/official_plugins/up_log tracks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ def run(settings_dict, **kwargs):
songs_dict = kwargs["songs_dict"]

log.info(f"Below is the songs_dict passed to this plugin:")
log.info("\n\n" + json.dumps(songs_dict, indent=4) + "\n\n")

try:
log.info("\n\n" + json.dumps(songs_dict, indent=4) + "\n\n")
except TypeError:
log.info("\n\n" + str(songs_dict) + "\n\n")

def builder(**kwargs):
return []
6 changes: 3 additions & 3 deletions ultrasonics/official_plugins/up_spotify mixer.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ def request(self, sp_func, *args, **kwargs):
"""
errors = 0

# Try once again when error
while errors <= 1:
try:
return sp_func(*args, **kwargs)
Expand All @@ -194,9 +195,8 @@ def request(self, sp_func, *args, **kwargs):
errors += 1
continue

log.error(
"An error occurred while trying to contact the Spotify api.")
raise Exception(e)
# raise exception if no return
raise Exception("An error occurred while trying to contact the Spotify api.")

def search(self, track):
"""
Expand Down
35 changes: 12 additions & 23 deletions ultrasonics/official_plugins/up_spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ def request(self, sp_func, *args, **kwargs):
"""
errors = 0

# Try once again when error
while errors <= 1:
try:
return sp_func(*args, **kwargs)
Expand All @@ -195,8 +196,8 @@ def request(self, sp_func, *args, **kwargs):
errors += 1
continue

log.error("An error occurred while trying to contact the Spotify api.")
raise Exception(e)
# raise exception if no return
raise Exception("An error occurred while trying to contact the Spotify api.")

def search(self, track):
"""
Expand Down Expand Up @@ -414,37 +415,25 @@ def user_playlist_remove_all_occurrences_of_tracks(self, playlist_id, tracks):
def spotify_to_songs_dict(self, track):
"""
Convert dictionary received from Spotify API to ultrasonics songs_dict format.
Assumes title, artist(s), and id field are always present.
"""
artists = [artist["name"] for artist in track["artists"]]

try:
album = track["album"]["name"]
except KeyError:
album = None

try:
date = track["album"]["release_date"]
except KeyError:
date = None

try:
isrc = track["external_ids"]["isrc"]
except KeyError:
isrc = None
artists = [artist.get("name") for artist in track.get("artists", [])]
album = track.get("album", {}).get("name")
date = track.get("album", {}).get("release_date")
isrc = track.get("external_ids", {}).get("isrc")

item = {
"title": track["name"],
"title": track.get("name"),
"artists": artists,
"album": album,
"date": date,
"isrc": isrc,
}

if track["id"]:
item["id"] = {"spotify": str(track["id"])}
if track.get("id"):
item["id"] = {"spotify": str(track.get("id"))}
else:
log.debug(f"Invalid spotify id for song: {track['name']}")
item["id"] = {}
log.debug(f"Invalid spotify id for song: {track.get('name')}")

# Remove any empty fields
item = {k: v for k, v in item.items() if v}
Expand Down
Loading