From 74b01c4942e38afce9efdf60fa46ab573f4f6f86 Mon Sep 17 00:00:00 2001 From: HombreLaser Date: Sat, 14 May 2022 14:39:45 -0500 Subject: Commit inicial --- src/ampache/__init__.py | 0 src/ampache/ampache.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++ src/ampache/exceptions.py | 20 +++++++++ src/ampache/models.py | 25 +++++++++++ src/ampache/test.py | 4 ++ 5 files changed, 153 insertions(+) create mode 100644 src/ampache/__init__.py create mode 100644 src/ampache/ampache.py create mode 100644 src/ampache/exceptions.py create mode 100644 src/ampache/models.py create mode 100644 src/ampache/test.py (limited to 'src') diff --git a/src/ampache/__init__.py b/src/ampache/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/ampache/ampache.py b/src/ampache/ampache.py new file mode 100644 index 0000000..9282040 --- /dev/null +++ b/src/ampache/ampache.py @@ -0,0 +1,104 @@ +from http import client +from urllib import parse +from datetime import datetime +import exceptions +import models +import json +import requests + + +def get_utc_offset(time): + """ + All the datetimes the API yields are in the form yyyy-mm-ddthh:mm:ss-00:00, + where the last 5 characters are the UTC offset. Python datetime objects can + parse this data, but in the form 0000 (sans the :) so we have to remove it. + """ + prefix = time[:len(time) - 5] + suffix = time[len(time) - 5:].replace(':', '') + + return prefix + suffix + + +class AmpacheClient: + """ + The ampache client class. Responsible for making API calls to your ampache + instance. + """ + def __init__(self, host, api_key, version): + """ + Host: url of your apache instance. + api_key: your api key (you can get it under settings->account). It's + preferable that you provide an admin's api key. + """ + self._endpoint = '/server/json.server.php?' + self._version = version + self._host = host + self._api_key = api_key + self.authenticate() + + def raise_by_status(self, error_code): + """ + The ampache API has a bunch of error codes that can help us diagnose + the problem. Depending of the error code, we'll raise an appropiate exception. + """ + pass + + def request(self, params, headers): + """ + All in one function to pass JSON requests to the API. + """ + self.renew_token() + + response = requests.get(self._host + self._endpoint, params=params, + headers=headers) + if not response.ok: + response.raise_for_status() + + data = response.json() + + if 'error' in data: + raise possible_exception(data['error']['errorMessage']) + + return data + + + def authenticate(self): + """ + Authenticate with the API, setting the token. + """ + params = { + 'action': 'handshake', + 'auth': self._api_key, + 'version': self._version + } + headers = { + 'Content-type': 'application/json' + } + + data = self.request(params, headers) + self._auth = models.AuthToken(datetime.strptime(get_utc_offset(data['session_expire']), '%Y-%m-%dT%H:%M:%S%z'), + data['auth']) + + def get_song(self, song_id): + """ + Get the detail of a song given its primary key. + """ + self.renew_token() + + params = { + 'action': 'song', + 'auth': self._auth, + 'filter': song_id, + 'version': self._version + } + headers = { + 'Content-type': 'application/json' + } + data = self.request(params, headers) + + def renew_token(self): + if datetime.now(self._auth.expires.tzinfo) > self._auth.expires: + self.authenticate() + + def get_token(self): + return self._auth diff --git a/src/ampache/exceptions.py b/src/ampache/exceptions.py new file mode 100644 index 0000000..345d8e2 --- /dev/null +++ b/src/ampache/exceptions.py @@ -0,0 +1,20 @@ +class AccessException(Exception): + """ + Happens when the API is not enabled. Error code: 4700- + """ + pass + + +class AuthenticationException(Exception): + """ + Happens in faulty authentication or when the session expires. + Error code: 4701. + """ + pass + + +class AccessDeniedException(Exception): + """ + Happens when the request method is not enabled. Error code: 4703 + """ + pass diff --git a/src/ampache/models.py b/src/ampache/models.py new file mode 100644 index 0000000..2e0ed1b --- /dev/null +++ b/src/ampache/models.py @@ -0,0 +1,25 @@ +""" +The data structures of the ampache API (songs, artists, albums, etc). +""" +from dataclasses import dataclass +import datetime + + +@dataclass +class AuthToken: + expires: datetime.datetime + auth: str + + def __str__(self): + return f"Expires: {self.expires}\nToken: {self.auth}" + + +@dataclass +class Song: + song_id: str + title: str + album: str + artist: str + + def __str__(self): + return f"{self.title} by {self.artist}" diff --git a/src/ampache/test.py b/src/ampache/test.py new file mode 100644 index 0000000..8ca38df --- /dev/null +++ b/src/ampache/test.py @@ -0,0 +1,4 @@ +from ampache import AmpacheClient + +my_ampache = AmpacheClient('https://music.silosneeded.com', '22efaa9fecfd8f074ef4bc95e85a1b84', '532000') +print(my_ampache.get_token()) -- cgit v1.2.3