From 4c7a8856651f6863ac0f0cc5802f4667e17b992f Mon Sep 17 00:00:00 2001 From: BarsTiger Date: Tue, 24 Oct 2023 23:19:09 +0300 Subject: [PATCH] Add deezer as default search --- bot/__init__.py | 3 ++ .../inline_default/on_inline_default.py | 35 +++------------- bot/handlers/on_chosen/__init__.py | 12 +++++- bot/handlers/on_chosen/deezer.py | 40 +++++++++++++++++++ bot/markups/__init__.py | 0 bot/markups/deezer/__init__.py | 4 ++ bot/markups/deezer/search.py | 33 +++++++++++++++ bot/markups/spotify/__init__.py | 6 +++ bot/markups/spotify/search.py | 33 +++++++++++++++ bot/modules/database/db.py | 1 + bot/modules/deezer/__init__.py | 3 +- bot/modules/deezer/downloader.py | 8 ++-- bot/modules/deezer/driver.py | 4 +- bot/modules/deezer/song.py | 10 +++-- bot/modules/deezer/util.py | 4 +- pyproject.toml | 1 + 16 files changed, 155 insertions(+), 42 deletions(-) create mode 100644 bot/handlers/on_chosen/deezer.py create mode 100644 bot/markups/__init__.py create mode 100644 bot/markups/deezer/__init__.py create mode 100644 bot/markups/deezer/search.py create mode 100644 bot/markups/spotify/__init__.py create mode 100644 bot/markups/spotify/search.py diff --git a/bot/__init__.py b/bot/__init__.py index 9090de9..4428f57 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -21,5 +21,8 @@ def main(): from rich.traceback import install install(show_locals=True) + from nest_asyncio import apply + apply() + print('Starting...') asyncio.run(runner()) diff --git a/bot/handlers/inline_default/on_inline_default.py b/bot/handlers/inline_default/on_inline_default.py index 42ab749..52e3b03 100644 --- a/bot/handlers/inline_default/on_inline_default.py +++ b/bot/handlers/inline_default/on_inline_default.py @@ -1,39 +1,16 @@ -from aiogram import Router, Bot, F -from aiogram.types import ( - InlineQuery, InlineQueryResultDocument, InlineQueryResultCachedAudio, - InlineKeyboardMarkup, InlineKeyboardButton, -) +from aiogram import Router, F -from bot.modules.spotify import spotify -from bot.modules.database import db +from aiogram.types import InlineQuery + +from bot.markups.deezer import get_deezer_search_results router = Router() @router.inline_query(F.query != '') -async def default_inline_query(inline_query: InlineQuery, bot: Bot): +async def default_inline_query(inline_query: InlineQuery): await inline_query.answer( - [ - InlineQueryResultDocument( - id='spot::' + audio.id, - title=audio.name, - description=audio.all_artists, - thumb_url=audio.thumbnail, - document_url=audio.preview_url or audio.thumbnail, - mime_type='application/zip', - reply_markup=InlineKeyboardMarkup( - inline_keyboard=[ - [InlineKeyboardButton(text='Downloading...', callback_data='.')] - ] - ), - caption=audio.full_name, - ) if audio.id not in list(db.spotify.keys()) else - InlineQueryResultCachedAudio( - id='spotc::' + audio.id, - audio_file_id=db.spotify[audio.id], - ) - for audio in spotify.songs.search(inline_query.query, limit=50) - ], + await get_deezer_search_results(inline_query.query), cache_time=0, is_personal=True ) diff --git a/bot/handlers/on_chosen/__init__.py b/bot/handlers/on_chosen/__init__.py index c20b9c0..65c73da 100644 --- a/bot/handlers/on_chosen/__init__.py +++ b/bot/handlers/on_chosen/__init__.py @@ -1 +1,11 @@ -from .spotify import router +from aiogram import Router +from . import spotify, deezer + +router = Router() + +router.include_routers( + spotify.router, + deezer.router, +) + +__all__ = ['router'] diff --git a/bot/handlers/on_chosen/deezer.py b/bot/handlers/on_chosen/deezer.py new file mode 100644 index 0000000..92bac83 --- /dev/null +++ b/bot/handlers/on_chosen/deezer.py @@ -0,0 +1,40 @@ +from aiogram import Router, Bot, F +from aiogram.types import ( + BufferedInputFile, URLInputFile, InputMediaAudio, + ChosenInlineResult, +) + +from bot.modules.deezer import deezer, DeezerBytestream +from bot.utils.config import config +from bot.modules.database import db + +router = Router() + + +@router.chosen_inline_result(F.result_id.startswith('deez::')) +async def on_new_chosen(chosen_result: ChosenInlineResult, bot: Bot): + bytestream: DeezerBytestream = await (await deezer.downloader.from_id( + chosen_result.result_id.removeprefix('deez::') + )).to_bytestream() + + audio = await bot.send_audio( + chat_id=config.telegram.files_chat, + audio=BufferedInputFile( + file=bytestream.file, + filename=bytestream.filename, + ), + thumbnail=URLInputFile(bytestream.song.thumbnail), + performer=bytestream.song.all_artists, + title=bytestream.song.name, + duration=bytestream.song.duration, + ) + + db.spotify[bytestream.song.id] = audio.audio.file_id + + await bot.edit_message_media( + inline_message_id=chosen_result.inline_message_id, + media=InputMediaAudio(media=audio.audio.file_id), + reply_markup=None + ) + + await db.occasionally_write() diff --git a/bot/markups/__init__.py b/bot/markups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bot/markups/deezer/__init__.py b/bot/markups/deezer/__init__.py new file mode 100644 index 0000000..af09ab9 --- /dev/null +++ b/bot/markups/deezer/__init__.py @@ -0,0 +1,4 @@ +from .search import get_deezer_search_results + + +__all__ = ['get_deezer_search_results'] diff --git a/bot/markups/deezer/search.py b/bot/markups/deezer/search.py new file mode 100644 index 0000000..f2ca2f7 --- /dev/null +++ b/bot/markups/deezer/search.py @@ -0,0 +1,33 @@ +from aiogram.types import ( + InlineQueryResultDocument, InlineQueryResultCachedAudio, + InlineKeyboardMarkup, InlineKeyboardButton +) + +from bot.modules.deezer import deezer +from bot.modules.database import db + + +async def get_deezer_search_results(query: str) -> list[ + InlineQueryResultDocument | InlineQueryResultCachedAudio +]: + return [ + InlineQueryResultDocument( + id='deez::' + audio.id_s, + title=audio.name, + description=audio.artist, + thumb_url=audio.thumbnail, + document_url=audio.preview_url or audio.thumbnail, + mime_type='application/zip', + reply_markup=InlineKeyboardMarkup( + inline_keyboard=[ + [InlineKeyboardButton(text='Downloading...', callback_data='.')] + ] + ), + caption=audio.full_name, + ) if audio.id_s not in list(db.deezer.keys()) else + InlineQueryResultCachedAudio( + id='deezc::' + audio.id_s, + audio_file_id=db.deezer[audio.id_s], + ) + for audio in await deezer.songs.search(query, limit=50) + ] diff --git a/bot/markups/spotify/__init__.py b/bot/markups/spotify/__init__.py new file mode 100644 index 0000000..eacb443 --- /dev/null +++ b/bot/markups/spotify/__init__.py @@ -0,0 +1,6 @@ +from .search import get_spotify_search_results + + +__all__ = [ + 'get_spotify_search_results' +] diff --git a/bot/markups/spotify/search.py b/bot/markups/spotify/search.py new file mode 100644 index 0000000..bf23245 --- /dev/null +++ b/bot/markups/spotify/search.py @@ -0,0 +1,33 @@ +from aiogram.types import ( + InlineQueryResultDocument, InlineQueryResultCachedAudio, + InlineKeyboardMarkup, InlineKeyboardButton, InlineQueryResult +) + +from bot.modules.spotify import spotify +from bot.modules.database import db + + +async def get_spotify_search_results(query: str) -> list[ + InlineQueryResultDocument | InlineQueryResultCachedAudio +]: + return [ + InlineQueryResultDocument( + id='spot::' + audio.id, + title=audio.name, + description=audio.all_artists, + thumb_url=audio.thumbnail, + document_url=audio.preview_url or audio.thumbnail, + mime_type='application/zip', + reply_markup=InlineKeyboardMarkup( + inline_keyboard=[ + [InlineKeyboardButton(text='Downloading...', callback_data='.')] + ] + ), + caption=audio.full_name, + ) if audio.id not in list(db.spotify.keys()) else + InlineQueryResultCachedAudio( + id='spotc::' + audio.id, + audio_file_id=db.spotify[audio.id], + ) + for audio in spotify.songs.search(query, limit=50) + ] diff --git a/bot/modules/database/db.py b/bot/modules/database/db.py index 5102c54..c5f28e7 100644 --- a/bot/modules/database/db.py +++ b/bot/modules/database/db.py @@ -16,6 +16,7 @@ class Db(object): self.config = DBDict('config') self.inline = DBDict('inline') self.spotify = DBDict('spotify') + self.deezer = DBDict('deezer') async def write(self): await self.config.write() diff --git a/bot/modules/deezer/__init__.py b/bot/modules/deezer/__init__.py index 28d5efd..53e4f3c 100644 --- a/bot/modules/deezer/__init__.py +++ b/bot/modules/deezer/__init__.py @@ -1,4 +1,5 @@ from .deezer import Deezer +from .downloader import DeezerBytestream from bot.utils.config import config @@ -6,4 +7,4 @@ deezer = Deezer( arl=config.tokens.deezer.arl, ) -__all__ = ['deezer'] +__all__ = ['deezer', 'DeezerBytestream'] diff --git a/bot/modules/deezer/downloader.py b/bot/modules/deezer/downloader.py index a3ea1a7..a1b4b1e 100644 --- a/bot/modules/deezer/downloader.py +++ b/bot/modules/deezer/downloader.py @@ -33,19 +33,19 @@ class DeezerBytestream: @define class Downloader: driver: DeezerDriver - song_id: int + song_id: str track: dict song: FullSongItem @classmethod async def build( cls, - song_id: int, + song_id: str, driver: DeezerDriver ): track = await driver.reverse_get_track(song_id) return cls( - song_id=song_id, + song_id=str(song_id), driver=driver, track=track['results'], song=await FullSongItem.from_deezer(track) @@ -91,7 +91,7 @@ class Downloader: class DownloaderBuilder: driver: DeezerDriver - async def from_id(self, song_id: int): + async def from_id(self, song_id: str): return await Downloader.build( song_id=song_id, driver=self.driver diff --git a/bot/modules/deezer/driver.py b/bot/modules/deezer/driver.py index 6350dd0..39fb738 100644 --- a/bot/modules/deezer/driver.py +++ b/bot/modules/deezer/driver.py @@ -16,11 +16,11 @@ class DeezerDriver: return data - async def reverse_get_track(self, track_id: int | str): + async def reverse_get_track(self, track_id: str): return await self.engine.call_api( 'song.getData', params={ - 'SNG_ID': str(track_id) + 'SNG_ID': track_id } ) diff --git a/bot/modules/deezer/song.py b/bot/modules/deezer/song.py index 6334295..0464f6a 100644 --- a/bot/modules/deezer/song.py +++ b/bot/modules/deezer/song.py @@ -7,6 +7,7 @@ from .driver import DeezerDriver class SongItem: name: str id: int + id_s: str artist: str preview_url: str | None thumbnail: str @@ -16,6 +17,7 @@ class SongItem: return cls( name=song_item['title'], id=song_item['id'], + id_s=str(song_item['id']), artist=song_item['artist']['name'], preview_url=song_item.get('preview'), thumbnail=song_item['album']['cover_medium'] @@ -32,11 +34,12 @@ class SongItem: @define class FullSongItem: name: str - id: int + id: str artists: list[str] preview_url: str | None duration: int thumbnail: str + track_dict: dict @classmethod async def from_deezer(cls, song_item: dict): @@ -53,7 +56,8 @@ class FullSongItem: else None), thumbnail=f'https://e-cdns-images.dzcdn.net/images/cover/' f'{song_item["ALB_PICTURE"]}/320x320.jpg', - duration=int(song_item['DURATION']) + duration=int(song_item['DURATION']), + track_dict=song_item ) @property @@ -83,7 +87,7 @@ class Songs(object): async def search_one(self, query: str) -> SongItem | None: return (await self.search(query, limit=1) or [None])[0] - async def from_id(self, song_id: int) -> FullSongItem | None: + async def from_id(self, song_id: str) -> FullSongItem | None: r = await self.driver.reverse_get_track(song_id) if r is None: diff --git a/bot/modules/deezer/util.py b/bot/modules/deezer/util.py index 2308a6c..d0c50f7 100644 --- a/bot/modules/deezer/util.py +++ b/bot/modules/deezer/util.py @@ -63,9 +63,9 @@ class ChunkDecrypter: cipher: Cipher @classmethod - def from_track_id(cls, track_id: int): + def from_track_id(cls, track_id: str): cipher = Cipher( - algorithms.Blowfish(get_blowfish_key(str(track_id))), + algorithms.Blowfish(get_blowfish_key(track_id)), modes.CBC(bytes([i for i in range(8)])), default_backend() ) diff --git a/pyproject.toml b/pyproject.toml index a7ff357..a41ffe0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ ytmusicapi = "^1.3.0" pytube = "^15.0.0" pydub = "^0.25.1" aiohttp = "^3.8.6" +nest-asyncio = "^1.5.8" [build-system]