diff --git a/examples/player/main.py b/examples/player/main.py index 1c1c0dd..0b123fc 100644 --- a/examples/player/main.py +++ b/examples/player/main.py @@ -5,6 +5,7 @@ import subprocess import time import requests +from requests.structures import CaseInsensitiveDict from librespot.audio.decoders import AudioQuality, VorbisOnlyAudioQuality from librespot.core import Session @@ -66,7 +67,7 @@ def client(): "q": cmd[2:], "type": "track" }, - headers={"Authorization": "Bearer %s" % token}, + headers=CaseInsensitiveDict({"Authorization": "Bearer %s" % token}), ) i = 1 tracks = resp.json()["tracks"]["items"] diff --git a/examples/server/main.py b/examples/server/main.py index 1f707bd..f31e528 100644 --- a/examples/server/main.py +++ b/examples/server/main.py @@ -2,6 +2,7 @@ import os import re import socket import threading +from requests.structures import CaseInsensitiveDict from librespot.audio.decoders import AudioQuality, VorbisOnlyAudioQuality from librespot.core import Session @@ -23,7 +24,7 @@ def handler(client: socket.socket, address: str): req_method = req_http_arr[0] req_uri = req_http_arr[1] req_http_version = req_http_arr[2] - req_header = {} + req_header = CaseInsensitiveDict() for header in req_header_str.split(b"\r\n"): try: key, value = header.split(b": ") @@ -73,7 +74,7 @@ def main(): threading.Thread(target=handler, args=sock.accept()).start() -def response(client: socket.socket, uri: str, header: dict, +def response(client: socket.socket, uri: str, header: CaseInsensitiveDict, body: bytes) -> tuple[str, list, bytes, bool]: if re.search(r"^/audio/track/([0-9a-zA-Z]{22})$", uri) is not None: track_id_search = re.search( diff --git a/librespot/audio/__init__.py b/librespot/audio/__init__.py index 97c9282..3d10a6f 100644 --- a/librespot/audio/__init__.py +++ b/librespot/audio/__init__.py @@ -8,6 +8,7 @@ from librespot.crypto import Packet from librespot.metadata import EpisodeId, PlayableId, TrackId from librespot.proto import Metadata_pb2 as Metadata, StorageResolve_pb2 as StorageResolve from librespot.structure import AudioDecrypt, AudioQualityPicker, Closeable, FeederException, GeneralAudioStream, GeneralWritableStream, HaltListener, NoopAudioDecrypt, PacketsReceiver +from requests.structures import CaseInsensitiveDict import concurrent.futures import io import logging @@ -471,9 +472,9 @@ class CdnManager: class InternalResponse: buffer: bytes - headers: typing.Dict[str, str] + headers: CaseInsensitiveDict[str, str] - def __init__(self, buffer: bytes, headers: typing.Dict[str, str]): + def __init__(self, buffer: bytes, headers: CaseInsensitiveDict[str, str]): self.buffer = buffer self.headers = headers @@ -578,8 +579,6 @@ class CdnManager: response = self.request(range_start=0, range_end=ChannelManager.chunk_size - 1) content_range = response.headers.get("Content-Range") - if content_range is None: - content_range = response.headers.get("content-range") if content_range is None: raise IOError("Missing Content-Range header!") split = content_range.split("/") @@ -633,16 +632,16 @@ class CdnManager: range_end = (chunk + 1) * ChannelManager.chunk_size - 1 response = self.__session.client().get( self.__cdn_url.url, - headers={ + headers=CaseInsensitiveDict({ "Range": "bytes={}-{}".format(range_start, range_end) - }, + }), ) if response.status_code != 206: raise IOError(response.status_code) body = response.content if body is None: raise IOError("Response body is empty!") - return CdnManager.InternalResponse(body, dict(response.headers)) + return CdnManager.InternalResponse(body, response.headers) class InternalStream(AbsChunkedInputStream): streamer: CdnManager.Streamer diff --git a/librespot/core.py b/librespot/core.py index 893d0a0..91dc51c 100644 --- a/librespot/core.py +++ b/librespot/core.py @@ -28,6 +28,7 @@ from Cryptodome.Hash import SHA1 from Cryptodome.Protocol.KDF import PBKDF2 from Cryptodome.PublicKey import RSA from Cryptodome.Signature import PKCS1_v1_5 +from requests.structures import CaseInsensitiveDict from librespot import util from librespot import Version @@ -80,7 +81,7 @@ class ApiClient(Closeable): self, method: str, suffix: str, - headers: typing.Union[None, typing.Dict[str, str]], + headers: typing.Union[None, CaseInsensitiveDict[str, str]], body: typing.Union[None, bytes], url: typing.Union[None, str], ) -> requests.PreparedRequest: @@ -89,7 +90,7 @@ class ApiClient(Closeable): :param method: str: :param suffix: str: :param headers: typing.Union[None: - :param typing.Dict[str: + :param CaseInsensitiveDict[str: :param str]]: :param body: typing.Union[None: :param bytes]: @@ -106,7 +107,7 @@ class ApiClient(Closeable): request = requests.PreparedRequest() request.method = method request.data = body - request.headers = {} + request.headers = CaseInsensitiveDict() if headers is not None: request.headers = headers request.headers["Authorization"] = "Bearer {}".format( @@ -122,7 +123,7 @@ class ApiClient(Closeable): self, method: str, suffix: str, - headers: typing.Union[None, typing.Dict[str, str]], + headers: typing.Union[None, CaseInsensitiveDict[str, str]], body: typing.Union[None, bytes], ) -> requests.Response: """ @@ -130,7 +131,7 @@ class ApiClient(Closeable): :param method: str: :param suffix: str: :param headers: typing.Union[None: - :param typing.Dict[str: + :param CaseInsensitiveDict[str: :param str]]: :param body: typing.Union[None: :param bytes]: @@ -145,7 +146,7 @@ class ApiClient(Closeable): method: str, url: str, suffix: str, - headers: typing.Union[None, typing.Dict[str, str]], + headers: typing.Union[None, CaseInsensitiveDict[str, str]], body: typing.Union[None, bytes], ) -> requests.Response: """ @@ -154,7 +155,7 @@ class ApiClient(Closeable): :param url: str: :param suffix: str: :param headers: typing.Union[None: - :param typing.Dict[str: + :param CaseInsensitiveDict[str: :param str]]: :param body: typing.Union[None: :param bytes]: @@ -327,10 +328,10 @@ class ApiClient(Closeable): resp = requests.post( "https://clienttoken.spotify.com/v1/clienttoken", proto_req.SerializeToString(), - headers={ + headers=CaseInsensitiveDict({ "Accept": "application/x-protobuf", "Content-Encoding": "", - }, + }), ) ApiClient.StatusCodeException.check_status(resp) @@ -604,10 +605,10 @@ class DealerClient(Closeable): return self.__message_listeners_lock.wait() - def __get_headers(self, obj: typing.Any) -> dict[str, str]: + def __get_headers(self, obj: typing.Any) -> CaseInsensitiveDict[str, str]: headers = obj.get("headers") if headers is None: - return {} + return CaseInsensitiveDict() return headers class ConnectionHolder(Closeable): @@ -1212,12 +1213,12 @@ class Session(Closeable, MessageListener, SubListener): raise RuntimeError("Session isn't authenticated!") return self.__mercury_client - def on_message(self, uri: str, headers: typing.Dict[str, str], + def on_message(self, uri: str, headers: CaseInsensitiveDict[str, str], payload: bytes): """ :param uri: str: - :param headers: typing.Dict[str: + :param headers: CaseInsensitiveDict[str: :param str]: :param payload: bytes: @@ -2331,10 +2332,10 @@ class TokenProvider: response = requests.post( "https://login5.spotify.com/v3/login", data=login5_request.SerializeToString(), - headers={ + headers=CaseInsensitiveDict({ "Content-Type": "application/x-protobuf", "Accept": "application/x-protobuf" - }) + })) if response.status_code == 200: login5_response = Login5.LoginResponse() diff --git a/librespot/mercury.py b/librespot/mercury.py index 450b98f..6084d28 100644 --- a/librespot/mercury.py +++ b/librespot/mercury.py @@ -3,6 +3,7 @@ from librespot import util from librespot.crypto import Packet from librespot.proto import Mercury_pb2 as Mercury, Pubsub_pb2 as Pubsub from librespot.structure import Closeable, PacketsReceiver, SubListener +from requests.structures import CaseInsensitiveDict import io import json import logging @@ -346,11 +347,11 @@ class RawMercuryRequest: return RawMercuryRequest.Builder() class Builder: - header_dict: dict + header_dict: CaseInsensitiveDict payload: typing.List[bytes] def __init__(self): - self.header_dict = {} + self.header_dict = CaseInsensitiveDict() self.payload = [] def set_uri(self, uri: str): diff --git a/librespot/structure.py b/librespot/structure.py index 9171ada..4e18c73 100644 --- a/librespot/structure.py +++ b/librespot/structure.py @@ -8,6 +8,7 @@ if typing.TYPE_CHECKING: from librespot.crypto import Packet from librespot.mercury import MercuryClient from librespot.proto import Metadata_pb2 as Metadata + from requests.structures import CaseInsensitiveDict class AudioDecrypt: @@ -61,7 +62,7 @@ class HaltListener: class MessageListener: - def on_message(self, uri: str, headers: typing.Dict[str, str], + def on_message(self, uri: str, headers: CaseInsensitiveDict[str, str], payload: bytes): raise NotImplementedError diff --git a/librespot/zeroconf.py b/librespot/zeroconf.py index afe6847..e2277bb 100644 --- a/librespot/zeroconf.py +++ b/librespot/zeroconf.py @@ -7,6 +7,7 @@ from librespot.core import Session from librespot.crypto import DiffieHellman from librespot.proto import Connect_pb2 as Connect from librespot.structure import Closeable, Runnable, SessionListener +from requests.structures import CaseInsensitiveDict import base64 import concurrent.futures import copy @@ -275,7 +276,7 @@ class ZeroconfServer(Closeable): method = request_line[0].decode() path = request_line[1].decode() http_version = request_line[2].decode() - headers = {} + headers = CaseInsensitiveDict() while True: header = request.readline().strip() if not header: diff --git a/librespot_player/__init__.py b/librespot_player/__init__.py index 630986e..0dc6b7c 100644 --- a/librespot_player/__init__.py +++ b/librespot_player/__init__.py @@ -5,6 +5,7 @@ from librespot.core import Session from librespot.mercury import MercuryRequests from librespot.proto import Connect_pb2 as Connect from librespot.structure import Closeable, MessageListener, RequestListener +from requests.structures import CaseInsensitiveDict import concurrent.futures import logging import typing @@ -200,6 +201,6 @@ class StateWrapper(MessageListener): "hm://collection/collection/" + session.username() + "/json" ]) - def on_message(self, uri: str, headers: typing.Dict[str, str], + def on_message(self, uri: str, headers: CaseInsensitiveDict[str, str], payload: bytes): pass