import random import string import hashlib import urllib.parse import time import xml.etree import xml.etree.ElementTree import requests # import libsonic # import simpleaudio import pyaudio u = 'pseudo' p = 'telegram9dock-MICHEL' class Artist: id: str name: str albumCount: int coverArt: str artistImageUrl: str def __init__(self, id: str, name: str, albumCount: str | int, coverArt: str, artistImageUrl: str): self.id = id self.name = name self.albumCount = int(albumCount) self.coverArt = coverArt self.artistImageUrl = artistImageUrl class ArtistIndex: name: str artists: list[Artist] def __init__(self, indexInfo: dict, artists: list[dict]): self.name = indexInfo['name'] self.artists = [] for artist in artists: self.artists.append(Artist( artist['id'], artist['name'], artist['albumCount'], artist['coverArt'], artist['artistImageUrl'] )) class ArtistIndexes: indexes: list[ArtistIndex] def __init__(self, xml_data: bytes): self._parseXml(xml_data.decode('utf-8')) def _parseXml(self, xml_data: str): root = xml.etree.ElementTree.fromstring(xml_data) self.indexes: list[ArtistIndex] = [] if root.get('status') != 'ok': return None in_index = False artists = [] index_info = None for item in root.iter(): # print(item.tag, dict(item.items())) if item.tag == '{http://subsonic.org/restapi}index': if in_index: in_index = not in_index self.indexes.append(ArtistIndex(index_info, artists)) artists = [] else: in_index = not in_index index_info = dict(item.items()) elif item.tag == '{http://subsonic.org/restapi}artist': artists.append(dict(item.items())) class Album: id: str title: str name: str album: str coverArtId: str duration: int artistId: str songCount: int def __init__(self, album_info: dict): self.id = album_info['id'] self.title = album_info['title'] self.name = album_info['name'] self.album = album_info['album'] self.coverArtId = album_info['coverArt'] self.duration = int(album_info['duration']) self.artistId = album_info['artistId'] self.songCount = int(album_info['songCount']) class Song: id: str title: str album: str artist: str trackNumber: int contentType: str suffix: str duration: int bitRate: int path: str discNumber: int albumId: str artistId: str comment: str channelCount: int samplingRate: int def __init__(self, song_info: dict): self.id = song_info['id'] self.title = song_info['title'] self.album = song_info['album'] self.artist = song_info['artist'] self.trackNumber = int(song_info['track']) self.contentType = song_info['contentType'] self.suffix = song_info['suffix'] self.duration = int(song_info['duration']) self.bitRate = int(song_info['bitRate']) self.path = song_info['path'] self.discNumber = int(song_info['discNumber']) self.albumId = song_info['albumId'] self.artistId = song_info['artistId'] self.comment = song_info['comment'] self.channelCount = int(song_info['channelCount']) self.samplingRate = int(song_info['samplingRate']) class SubpyConn: username: str password: str base_url: str salt: str salt_len: int = 8 version: str = "1.16.1" client_name: str = "testing" def __init__(self, u: str, p: str, base_url: str, salt_len: int | None = 8): self.username = u self.password = p self.base_url = base_url self.salt_len = salt_len def _saltGenerator(self) -> None: self.salt = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(self.salt_len)) def _hashPass(self) -> str: self._saltGenerator() return hashlib.md5((self.password + self.salt).encode('utf-8')).hexdigest() def _getBasePayload(self) -> dict: return { "u" : self.username, "t" : self._hashPass(), "s" : self.salt, "v" : self.version, "c" : self.client_name } def _encodePayload(self, payload: dict) -> str: return urllib.parse.urlencode(payload, quote_via=urllib.parse.quote_plus) def getIndexes(self) -> ArtistIndexes: url = self.base_url + '/rest/getIndexes?' return ArtistIndexes(requests.get(url + self._encodePayload(self._getBasePayload())).content) def _getMusicDirectory(self, id: str): url = self.base_url + '/rest/getMusicDirectory?' payload = self._getBasePayload() payload['id'] = id return requests.get(url + self._encodePayload(payload)) def getArtistAlbums(self, artist_id: str) -> list[Album]: r: requests.Response = self._getMusicDirectory(artist_id) xml_data: str = r.content.decode('utf-8') root: xml.etree.ElementTree.Element = xml.etree.ElementTree.fromstring(xml_data) albums: list[Album] = [] for item in root.iter(): # print(item.tag, dict(item.items())) if item.tag == '{http://subsonic.org/restapi}child': albums.append(Album(dict(item.items()))) return albums def getAlbumSongs(self, album_id: str) -> list[Song]: r: requests.Response = self._getMusicDirectory(album_id) xml_data: str = r.content.decode('utf-8') root: xml.etree.ElementTree.ElementTree = xml.etree.ElementTree.fromstring(xml_data) songs: list[Song] = [] for item in root.iter(): if item.tag == '{http://subsonic.org/restapi}child': songs.append(Song(dict(item.items()))) return songs def getArtists(self) -> list[Artist]: artistIndexes: ArtistIndexes = self.getIndexes() artists: list[Artist] = [] for artistIndex in artistIndexes.indexes: artists.extend(artistIndex.artists) return artists # Returns a requests Response object, the data stream can be found in Response.content def stream(self, song_id: str) -> requests.Response: url = self.base_url + '/rest/stream?' payload = self._getBasePayload() payload['id'] = song_id return requests.get(url + self._encodePayload(payload)) conn = SubpyConn(u, p, "https://music.0x01fe.net") # artistIndexes = conn.getIndexes() # for artistIndex in artistIndexes.indexes: # print(artistIndex.name, artistIndex.artists) helios_id = 'c69cdc593487ed813f1e11ecb270b228' yakui_id = "7f6ca9951af294420382442061a2d6ee" # r = conn.getMusicDirectory(helios_id) # with open("getHelios.xml", 'wb') as file: # file.write(r.content) # yakui_albums = conn.getArtistAlbums(yakui_id) # for album in yakui_albums: # print(album.name) # helios_songs = conn.getAlbumSongs(helios_id) # for song in helios_songs: # print(song.trackNumber, song.title) # artists = conn.getArtists() # for artist in artists: # print(artist.name) echidna_id = "937004317800a879f0cddd471fb82387" # print(conn.stream(echidna_id).content) # def getArtists() -> list[Artist]: # tree = xml.etree.ElementTree.parse("artist_response.xml") # root = tree.getroot() # artists: list[Artist] = [] # if root.get('status') != 'ok': # return [] # for item in root.iter(): # print(item.tag, dict(item.items())) # if item.tag == '{http://subsonic.org/restapi}artist': # a = Artist() # a.fromDict(dict(item.items())) # artists.append(a) # return artists # artists = getArtists() # for artist in artists: # print(artist.name) # conn = libsonic.Connection('https://music.0x01fe.net', 'pseudo', p, port=443) # song = conn.getRandomSongs(1)['randomSongs']['song'][0] # song_id = song['id'] # rate = song['samplingRate'] # print(song_id) # stream = conn.stream(song_id) # while not stream.fp: # pass # pya = pyaudio.PyAudio() # pya_stream = pya.open(format=) # wave_obj = simpleaudio.WaveObject(stream.fp) # play_obj = wave_obj.play() # play_obj.wait_done()