Add deezer as default search

This commit is contained in:
BarsTiger
2023-10-24 23:19:09 +03:00
parent 495202d949
commit 4c7a885665
16 changed files with 155 additions and 42 deletions

View File

@@ -21,5 +21,8 @@ def main():
from rich.traceback import install from rich.traceback import install
install(show_locals=True) install(show_locals=True)
from nest_asyncio import apply
apply()
print('Starting...') print('Starting...')
asyncio.run(runner()) asyncio.run(runner())

View File

@@ -1,39 +1,16 @@
from aiogram import Router, Bot, F from aiogram import Router, F
from aiogram.types import (
InlineQuery, InlineQueryResultDocument, InlineQueryResultCachedAudio,
InlineKeyboardMarkup, InlineKeyboardButton,
)
from bot.modules.spotify import spotify from aiogram.types import InlineQuery
from bot.modules.database import db
from bot.markups.deezer import get_deezer_search_results
router = Router() router = Router()
@router.inline_query(F.query != '') @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( await inline_query.answer(
[ await get_deezer_search_results(inline_query.query),
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)
],
cache_time=0, cache_time=0,
is_personal=True is_personal=True
) )

View File

@@ -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']

View File

@@ -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()

0
bot/markups/__init__.py Normal file
View File

View File

@@ -0,0 +1,4 @@
from .search import get_deezer_search_results
__all__ = ['get_deezer_search_results']

View File

@@ -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)
]

View File

@@ -0,0 +1,6 @@
from .search import get_spotify_search_results
__all__ = [
'get_spotify_search_results'
]

View File

@@ -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)
]

View File

@@ -16,6 +16,7 @@ class Db(object):
self.config = DBDict('config') self.config = DBDict('config')
self.inline = DBDict('inline') self.inline = DBDict('inline')
self.spotify = DBDict('spotify') self.spotify = DBDict('spotify')
self.deezer = DBDict('deezer')
async def write(self): async def write(self):
await self.config.write() await self.config.write()

View File

@@ -1,4 +1,5 @@
from .deezer import Deezer from .deezer import Deezer
from .downloader import DeezerBytestream
from bot.utils.config import config from bot.utils.config import config
@@ -6,4 +7,4 @@ deezer = Deezer(
arl=config.tokens.deezer.arl, arl=config.tokens.deezer.arl,
) )
__all__ = ['deezer'] __all__ = ['deezer', 'DeezerBytestream']

View File

@@ -33,19 +33,19 @@ class DeezerBytestream:
@define @define
class Downloader: class Downloader:
driver: DeezerDriver driver: DeezerDriver
song_id: int song_id: str
track: dict track: dict
song: FullSongItem song: FullSongItem
@classmethod @classmethod
async def build( async def build(
cls, cls,
song_id: int, song_id: str,
driver: DeezerDriver driver: DeezerDriver
): ):
track = await driver.reverse_get_track(song_id) track = await driver.reverse_get_track(song_id)
return cls( return cls(
song_id=song_id, song_id=str(song_id),
driver=driver, driver=driver,
track=track['results'], track=track['results'],
song=await FullSongItem.from_deezer(track) song=await FullSongItem.from_deezer(track)
@@ -91,7 +91,7 @@ class Downloader:
class DownloaderBuilder: class DownloaderBuilder:
driver: DeezerDriver driver: DeezerDriver
async def from_id(self, song_id: int): async def from_id(self, song_id: str):
return await Downloader.build( return await Downloader.build(
song_id=song_id, song_id=song_id,
driver=self.driver driver=self.driver

View File

@@ -16,11 +16,11 @@ class DeezerDriver:
return data 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( return await self.engine.call_api(
'song.getData', 'song.getData',
params={ params={
'SNG_ID': str(track_id) 'SNG_ID': track_id
} }
) )

View File

@@ -7,6 +7,7 @@ from .driver import DeezerDriver
class SongItem: class SongItem:
name: str name: str
id: int id: int
id_s: str
artist: str artist: str
preview_url: str | None preview_url: str | None
thumbnail: str thumbnail: str
@@ -16,6 +17,7 @@ class SongItem:
return cls( return cls(
name=song_item['title'], name=song_item['title'],
id=song_item['id'], id=song_item['id'],
id_s=str(song_item['id']),
artist=song_item['artist']['name'], artist=song_item['artist']['name'],
preview_url=song_item.get('preview'), preview_url=song_item.get('preview'),
thumbnail=song_item['album']['cover_medium'] thumbnail=song_item['album']['cover_medium']
@@ -32,11 +34,12 @@ class SongItem:
@define @define
class FullSongItem: class FullSongItem:
name: str name: str
id: int id: str
artists: list[str] artists: list[str]
preview_url: str | None preview_url: str | None
duration: int duration: int
thumbnail: str thumbnail: str
track_dict: dict
@classmethod @classmethod
async def from_deezer(cls, song_item: dict): async def from_deezer(cls, song_item: dict):
@@ -53,7 +56,8 @@ class FullSongItem:
else None), else None),
thumbnail=f'https://e-cdns-images.dzcdn.net/images/cover/' thumbnail=f'https://e-cdns-images.dzcdn.net/images/cover/'
f'{song_item["ALB_PICTURE"]}/320x320.jpg', f'{song_item["ALB_PICTURE"]}/320x320.jpg',
duration=int(song_item['DURATION']) duration=int(song_item['DURATION']),
track_dict=song_item
) )
@property @property
@@ -83,7 +87,7 @@ class Songs(object):
async def search_one(self, query: str) -> SongItem | None: async def search_one(self, query: str) -> SongItem | None:
return (await self.search(query, limit=1) or [None])[0] 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) r = await self.driver.reverse_get_track(song_id)
if r is None: if r is None:

View File

@@ -63,9 +63,9 @@ class ChunkDecrypter:
cipher: Cipher cipher: Cipher
@classmethod @classmethod
def from_track_id(cls, track_id: int): def from_track_id(cls, track_id: str):
cipher = Cipher( 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)])), modes.CBC(bytes([i for i in range(8)])),
default_backend() default_backend()
) )

View File

@@ -19,6 +19,7 @@ ytmusicapi = "^1.3.0"
pytube = "^15.0.0" pytube = "^15.0.0"
pydub = "^0.25.1" pydub = "^0.25.1"
aiohttp = "^3.8.6" aiohttp = "^3.8.6"
nest-asyncio = "^1.5.8"
[build-system] [build-system]