From d9b66fa941943e59b0c469ededac3216eda89355 Mon Sep 17 00:00:00 2001 From: Rafael Moraes <50295204+glomatico@users.noreply.github.com> Date: Thu, 26 Mar 2026 14:16:00 -0300 Subject: [PATCH] Enforce session-type constraints for media formats --- votify/interface/audio.py | 55 ++++++++++++++++++++++++---------- votify/interface/exceptions.py | 21 +++++++++++++ votify/interface/video.py | 13 +++++++- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/votify/interface/audio.py b/votify/interface/audio.py index b529e3c..8868380 100644 --- a/votify/interface/audio.py +++ b/votify/interface/audio.py @@ -1,11 +1,15 @@ +import asyncio import logging from pywidevine.license_protocol_pb2 import WidevinePsshData +from ..api.enums import SessionType from .base import SpotifyBaseInterface from .enums import AudioQuality -import asyncio -from .exceptions import VotifyMediaFormatNotAvailableException +from .exceptions import ( + VotifyMediaFormatNotAvailableException, + VotifyMediaFormatNotAvailableForSessionTypeException, +) from .types import DecryptionKey, StreamInfo, StreamInfoAv logger = logging.getLogger(__name__) @@ -48,18 +52,31 @@ class SpotifyAudioInterface(SpotifyBaseInterface): ) -> StreamInfoAv: for audio_quality in self.audio_quality_priority: if audio_quality.mp4: - stream_info = await self._get_stream_info_web( - media_id=media_id, - media_type=media_type, - skip_pssh=skip_pssh, - audio_quality=audio_quality, - ) - else: - stream_info = await self._get_stream_info_librespot( - media_id=media_id, - media_type=media_type, - audio_quality=audio_quality, - ) + if self.api.session_type in {SessionType.LIBRESPOT, SessionType.WEB}: + stream_info = await self._get_stream_info_web( + media_id=media_id, + media_type=media_type, + skip_pssh=skip_pssh, + audio_quality=audio_quality, + ) + else: + raise VotifyMediaFormatNotAvailableForSessionTypeException( + media_id=media_id, + session_type=self.api.session_type, + ) + + elif audio_quality.ogg: + if self.api.session_type == SessionType.LIBRESPOT: + stream_info = await self._get_stream_info_librespot( + media_id=media_id, + media_type=media_type, + audio_quality=audio_quality, + ) + else: + raise VotifyMediaFormatNotAvailableForSessionTypeException( + media_id=media_id, + session_type=self.api.session_type, + ) if stream_info: return stream_info @@ -201,11 +218,17 @@ class SpotifyAudioInterface(SpotifyBaseInterface): stream_info: StreamInfoAv, media_id: str, ): - if stream_info.audio_track.widevine_pssh: + if ( + self.api.session_type in {SessionType.LIBRESPOT, SessionType.WEB} + and stream_info.audio_track.widevine_pssh + ): return await self.get_widevine_decryption_key( stream_info.audio_track.widevine_pssh ) - elif stream_info.audio_track.file_id: + elif ( + stream_info.audio_track.file_id + and self.api.session_type == SessionType.LIBRESPOT + ): return await self.get_librespot_decryption_key( media_id=media_id, file_id=stream_info.audio_track.file_id, diff --git a/votify/interface/exceptions.py b/votify/interface/exceptions.py index f79c8e0..0b21d2c 100644 --- a/votify/interface/exceptions.py +++ b/votify/interface/exceptions.py @@ -1,5 +1,6 @@ from typing import Any +from ..api.enums import SessionType from ..utils import VotiyException @@ -79,3 +80,23 @@ class VotifyMediaFormatNotAvailableException(VotifyMediaException): media_id=media_id, media_metadata=media_metadata, ) + + +class VotifyMediaFormatNotAvailableForSessionTypeException( + VotifyMediaFormatNotAvailableException +): + def __init__( + self, + media_id: str, + media_metadata: dict | None = None, + session_type: SessionType | None = None, + ): + message = "Selected format is not available for session type" + if session_type: + message += f": {session_type.value}" + + super().__init__( + media_id=media_id, + media_metadata=media_metadata, + message=message, + ) diff --git a/votify/interface/video.py b/votify/interface/video.py index 5367518..68b15d0 100644 --- a/votify/interface/video.py +++ b/votify/interface/video.py @@ -3,9 +3,11 @@ import logging from InquirerPy import inquirer from InquirerPy.base.control import Choice +from ..api.enums import SessionType from .base import SpotifyBaseInterface from .constants import COVER_SIZE_ID_MAP_VIDEO from .enums import VideoFormat, VideoResolution +from .exceptions import VotifyMediaFormatNotAvailableForSessionTypeException from .types import DecryptionKey, StreamInfo, StreamInfoAv logger = logging.getLogger(__name__) @@ -191,7 +193,16 @@ class SpotifyVideoInterface(SpotifyBaseInterface): media_id: str, media_type: str, playback_info: dict | None = None, - ) -> StreamInfoAv | None: + ) -> StreamInfoAv: + if ( + not self.api.session_type in {SessionType.LIBRESPOT, SessionType.WEB} + and media_type != "episode" + ): + raise VotifyMediaFormatNotAvailableForSessionTypeException( + media_id=media_id, + session_type=self.api.session_type, + ) + if not playback_info: playback_info = await self._get_playback_info( media_id=media_id,