From e07e82b9dd93c97e53521f9fcdc967febf8d0eeb Mon Sep 17 00:00:00 2001 From: HombreLaser Date: Wed, 15 Jun 2022 12:05:51 -0500 Subject: Added bot's code --- src/ampache/ampache.py | 170 ------------------------------------------------- src/ampache/client.py | 170 +++++++++++++++++++++++++++++++++++++++++++++++++ src/bot.py | 62 ++++++++++++++++++ src/config.py | 23 +++++-- src/main.py | 24 ++++--- 5 files changed, 264 insertions(+), 185 deletions(-) delete mode 100644 src/ampache/ampache.py create mode 100644 src/ampache/client.py create mode 100644 src/bot.py (limited to 'src') diff --git a/src/ampache/ampache.py b/src/ampache/ampache.py deleted file mode 100644 index b58ba36..0000000 --- a/src/ampache/ampache.py +++ /dev/null @@ -1,170 +0,0 @@ -""" -The main file. The AmpacheClient class allows us to make requests to the -API. -""" -from datetime import datetime -from ampache import models, exceptions -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): - """ - 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. - """ - if error.code == exceptions.ACCESS_ERROR_CODE: - raise exceptions.AccessException(error.message) - elif error.code == exceptions.AUTHENTICATION_ERROR_CODE: - raise exceptions.AuthenticationException(error.message) - elif error.code == exceptions.ACCESS_DENIED_ERROR_CODE: - raise exceptions.AccessDeniedException(error.message) - elif error.code == exceptions.NOT_FOUND_ERROR_CODE: - raise exceptions.NotFoundException(error.message) - elif error.code == exceptions.MISSING_ERROR_CODE: - raise exceptions.MissingMethodException(error.message) - elif error.code == exceptions.DEPRECIATED_ERROR_CODE: - raise exceptions.DepreciatedMethodException(error.message) - elif error.code == exceptions.BAD_REQUEST_ERROR_CODE: - raise exceptions.BadRequestException(error.message) - elif error.code == exceptions.FAILED_ACCESS_ERROR_CODE: - raise exceptions.FailedAccessException(error.message) - - def request(self, params, headers): - """ - All in one function to pass requests to the API. - """ - response = requests.get(self._host + self._endpoint, params=params, - headers=headers) - if not response.ok: - response.raise_for_status() - - if headers['Content-type'] == 'application/json': - response = response.json() - - if 'error' in response: - self.raise_by_status(models.Error(int(response['error']['errorCode']), - response['error']['errorMessage'])) - - return response - - 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.auth, - 'filter': song_id, - 'version': self._version - } - headers = { - 'Content-type': 'application/json' - } - data = self.request(params, headers) - song = models.Song(data['id'], data['title'], data['album']['name'], - data['albumartist']['name']) - - return song - - def get_album(self, album_id): - params = { - 'action': 'album_songs', - 'auth': self._auth.auth, - 'filter': album_id, - 'version': self._version - } - headers = { - 'Content-type': 'application/json' - } - data = self.request(params, headers) - album = self.process_album(data) - - return album - - def process_album(self, album_data): - album_id = album_data['song'][0]['album']['id'] - album_title = album_data['song'][0]['album']['name'] - album_artist = album_data['song'][0]['artist']['name'] - songs = [] - - for song in album_data['song']: - new_song = models.Song(song['id'], song['title'], album_title, - album_artist) - songs.append(new_song) - - return models.Album(album_id, album_title, album_artist, songs) - - def download(self, song_id, destination): - """ - Download a song to destination. - """ - params = { - 'action': 'download', - 'auth': self._auth.auth, - 'type': 'song', - 'id': song_id, - 'version': self._version, - 'format': 'mp3' - } - headers = { - 'Content-type': 'audio/mpeg' - } - data = self.request(params, headers) - - with open(destination, 'wb') as song: - song.write(data.content) - - 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/client.py b/src/ampache/client.py new file mode 100644 index 0000000..b58ba36 --- /dev/null +++ b/src/ampache/client.py @@ -0,0 +1,170 @@ +""" +The main file. The AmpacheClient class allows us to make requests to the +API. +""" +from datetime import datetime +from ampache import models, exceptions +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): + """ + 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. + """ + if error.code == exceptions.ACCESS_ERROR_CODE: + raise exceptions.AccessException(error.message) + elif error.code == exceptions.AUTHENTICATION_ERROR_CODE: + raise exceptions.AuthenticationException(error.message) + elif error.code == exceptions.ACCESS_DENIED_ERROR_CODE: + raise exceptions.AccessDeniedException(error.message) + elif error.code == exceptions.NOT_FOUND_ERROR_CODE: + raise exceptions.NotFoundException(error.message) + elif error.code == exceptions.MISSING_ERROR_CODE: + raise exceptions.MissingMethodException(error.message) + elif error.code == exceptions.DEPRECIATED_ERROR_CODE: + raise exceptions.DepreciatedMethodException(error.message) + elif error.code == exceptions.BAD_REQUEST_ERROR_CODE: + raise exceptions.BadRequestException(error.message) + elif error.code == exceptions.FAILED_ACCESS_ERROR_CODE: + raise exceptions.FailedAccessException(error.message) + + def request(self, params, headers): + """ + All in one function to pass requests to the API. + """ + response = requests.get(self._host + self._endpoint, params=params, + headers=headers) + if not response.ok: + response.raise_for_status() + + if headers['Content-type'] == 'application/json': + response = response.json() + + if 'error' in response: + self.raise_by_status(models.Error(int(response['error']['errorCode']), + response['error']['errorMessage'])) + + return response + + 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.auth, + 'filter': song_id, + 'version': self._version + } + headers = { + 'Content-type': 'application/json' + } + data = self.request(params, headers) + song = models.Song(data['id'], data['title'], data['album']['name'], + data['albumartist']['name']) + + return song + + def get_album(self, album_id): + params = { + 'action': 'album_songs', + 'auth': self._auth.auth, + 'filter': album_id, + 'version': self._version + } + headers = { + 'Content-type': 'application/json' + } + data = self.request(params, headers) + album = self.process_album(data) + + return album + + def process_album(self, album_data): + album_id = album_data['song'][0]['album']['id'] + album_title = album_data['song'][0]['album']['name'] + album_artist = album_data['song'][0]['artist']['name'] + songs = [] + + for song in album_data['song']: + new_song = models.Song(song['id'], song['title'], album_title, + album_artist) + songs.append(new_song) + + return models.Album(album_id, album_title, album_artist, songs) + + def download(self, song_id, destination): + """ + Download a song to destination. + """ + params = { + 'action': 'download', + 'auth': self._auth.auth, + 'type': 'song', + 'id': song_id, + 'version': self._version, + 'format': 'mp3' + } + headers = { + 'Content-type': 'audio/mpeg' + } + data = self.request(params, headers) + + with open(destination, 'wb') as song: + song.write(data.content) + + 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/bot.py b/src/bot.py new file mode 100644 index 0000000..9b10736 --- /dev/null +++ b/src/bot.py @@ -0,0 +1,62 @@ +from config import Config +from ampache.client import AmpacheClient +from collections import deque +import pymumble_py3 as pymumble + +class Bot: + """ + The bot class. It reads a config file (given in the constructor) + and with it populates its own and the ampache client's variables. + """ + def __init__(self, config_file): + # The file may not exist. If it doesn't, the configparser will + # throw a key error exception. TODO: first open the file, if + # it doesn't exist it will throw a more ad hoc exception. + self._configuration = Config(config_file) + self._playlist = deque() + self._init_client() + self._init_bot() + self._init_misc() + + def _init_misc(self): + miscellaneous = self._configuration.get_directories() + self._download_dir = miscellaneous[0] + self._automatically_delete = miscellaneous[1] + + def _init_client(self): + client_info = self._configuration.get_ampache_config() + auth_info = self._configuration.get_auth() + self._client = AmpacheClient(client_info[0], auth_info, + client_info[1]) + + def _init_bot(self): + mumble_info = self._configuration.get_mumble_config() + host = mumble_info[0] + password = mumble_info[1] + certfile = mumble_info[2] + keyfile = mumble_info[3] + bot_nick = mumble_info[4] + self._bot = pymumble.Mumble(host, bot_nick, password=password, certfile=certfile, + keyfile=keyfile, reconnect=True, stereo=True) + + def read_command(self): + pass + + def parse_command(self): + pass + + def add_to_playlist(self, ): + pass + + def play_song(self, ): + pass + + # API methods. + def query_song(self, song_id): + pass + + def query_album(self, album_id): + pass + + def download_song(self): + pass diff --git a/src/config.py b/src/config.py index 4191c4d..0c4916d 100644 --- a/src/config.py +++ b/src/config.py @@ -2,15 +2,26 @@ import configparser class Config: """ - A cconfig reader class. + A config reader class. """ def __init__(self, config_file): self._parser = configparser.ConfigParser() self._parser.read(config_file) - def get_server_config(self): - host = self._parser['Server']['host'] - version = self._parser['Server']['api_version'] + # Mumble config + def get_mumble_config(self, ): + host = self._parser['MumbleServer']['host'] + password = self._parser['MumbleServer']['password'] + certfile = self._parser['MumbleServer']['certfile'] + keyfile = self._parser['MumbleServer']['keyfile'] + name = self._parser['MumbleServer']['name'] + + return (host, password, certfile, keyfile, name) + + # Ampache config + def get_ampache_config(self): + host = self._parser['AmpacheServer']['host'] + version = self._parser['AmpacheServer']['api_version'] return (host, version) @@ -20,8 +31,8 @@ class Config: return auth def get_directories(self): - download = self._parser['Directories']['download_folder'] - delete_after_playing = bool(self._parser['Directories'] + download = self._parser['Misc']['download_folder'] + delete_after_playing = bool(self._parser['Misc'] ['delete_after_playing']) return (download, delete_after_playing) diff --git a/src/main.py b/src/main.py index 118434b..4a87586 100644 --- a/src/main.py +++ b/src/main.py @@ -1,16 +1,22 @@ -import ampache +from config import Config import pymumble_py3 as pymumble +from ampache.client import AmpacheClient +# def main(): +# example_bot = pymumble.Mumble('192.168.15.18', 'ampache', password='cultoblandonianoEXTREME', certfile='../ampache.pem', keyfile='../ampachekey.pem', +# reconnect=True, stereo=True) +# example_bot.set_application_string('ampache-client') +# example_bot.start() +# example_bot.is_ready() +# example_bot.set_loop_rate(.01) + +# while True: +# continue def main(): - example_bot = pymumble.Mumble('192.168.15.18', 'ampache', password='cultoblandonianoEXTREME', certfile='../ampache.pem', keyfile='../ampachekey.pem', - reconnect=True, stereo=True) - example_bot.set_application_string('ampache-client') - example_bot.start() - example_bot.is_ready() - example_bot.set_loop_rate(.01) + config = Config('../example.cfg') + client = AmpacheClient('https://music.silosneeded.com', '4dee7fe5554cf581a3f69ea023ea378a', 53200) - while True: - continue + return 0 if __name__ == '__main__': -- cgit v1.2.3