# -*- coding: utf-8 -*- """ :authors: python273 :license: Apache License, Version 2.0, see LICENSE file :copyright: (c) 2019 python273 """ import re import json import time from itertools import islice from bs4 import BeautifulSoup from .audio_url_decoder import decode_audio_url from .exceptions import AccessDenied from .utils import set_cookies_from_list RE_ALBUM_ID = re.compile(r'act=audio_playlist(-?\d+)_(\d+)') RE_ACCESS_HASH = re.compile(r'access_hash=(\w+)') RE_M3U8_TO_MP3 = re.compile(r'/[0-9a-f]+(/audios)?/([0-9a-f]+)/index.m3u8') RPS_DELAY_RELOAD_AUDIO = 1.5 RPS_DELAY_LOAD_SECTION = 2.0 TRACKS_PER_USER_PAGE = 2000 TRACKS_PER_ALBUM_PAGE = 2000 ALBUMS_PER_USER_PAGE = 100 class VkAudio(object): """ Модуль для получения аудиозаписей без использования официального API. :param vk: Объект :class:`VkApi` """ __slots__ = ('_vk', 'user_id', 'convert_m3u8_links') DEFAULT_COOKIES = [ { # если не установлено, то первый запрос ломается 'version': 0, 'name': 'remixaudio_show_alert_today', 'value': '0', 'port': None, 'port_specified': False, 'domain': '.vk.com', 'domain_specified': True, 'domain_initial_dot': True, 'path': '/', 'path_specified': True, 'secure': True, 'expires': None, 'discard': False, 'comment': None, 'comment_url': None, 'rfc2109': False, 'rest': {} }, { # для аудио из постов 'version': 0, 'name': 'remixmdevice', 'value': '1920/1080/2/!!-!!!!', 'port': None, 'port_specified': False, 'domain': '.vk.com', 'domain_specified': True, 'domain_initial_dot': True, 'path': '/', 'path_specified': True, 'secure': True, 'expires': None, 'discard': False, 'comment': None, 'comment_url': None, 'rfc2109': False, 'rest': {} } ] def __init__(self, vk, convert_m3u8_links=True): self.user_id = vk.method('users.get')[0]['id'] self._vk = vk self.convert_m3u8_links = convert_m3u8_links set_cookies_from_list(self._vk.http.cookies, self.DEFAULT_COOKIES) self._vk.http.get('https://m.vk.com/') # load cookies def get_iter(self, owner_id=None, album_id=None, access_hash=None): """ Получить список аудиозаписей пользователя (по частям) :param owner_id: ID владельца (отрицательные значения для групп) :param album_id: ID альбома :param access_hash: ACCESS_HASH альбома """ if owner_id is None: owner_id = self.user_id if album_id is not None: offset_diff = TRACKS_PER_ALBUM_PAGE else: offset_diff = TRACKS_PER_USER_PAGE offset = 0 while True: response = self._vk.http.post( 'https://m.vk.com/audio', data={ 'act': 'load_section', 'owner_id': owner_id, 'playlist_id': album_id if album_id else -1, 'offset': offset, 'type': 'playlist', 'access_hash': access_hash, 'is_loading_all': 1 }, allow_redirects=False ).json() if not response['data'][0]: raise AccessDenied( 'You don\'t have permissions to browse {}\'s albums'.format( owner_id ) ) ids = scrap_ids( response['data'][0]['list'] ) tracks = scrap_tracks( ids, self.user_id, self._vk.http, convert_m3u8_links=self.convert_m3u8_links ) if not tracks: break for i in tracks: yield i if response['data'][0]['hasMore']: offset += offset_diff else: break def get(self, owner_id=None, album_id=None, access_hash=None): """ Получить список аудиозаписей пользователя :param owner_id: ID владельца (отрицательные значения для групп) :param album_id: ID альбома :param access_hash: ACCESS_HASH альбома """ return list(self.get_iter(owner_id, album_id, access_hash)) def get_albums_iter(self, owner_id=None): """ Получить список альбомов пользователя (по частям) :param owner_id: ID владельца (отрицательные значения для групп) """ if owner_id is None: owner_id = self.user_id offset = 0 while True: response = self._vk.http.get( 'https://m.vk.com/audio?act=audio_playlists{}'.format( owner_id ), params={ 'offset': offset }, allow_redirects=False ) if not response.text: raise AccessDenied( 'You don\'t have permissions to browse {}\'s albums'.format( owner_id ) ) albums = scrap_albums(response.text) if not albums: break for i in albums: yield i offset += ALBUMS_PER_USER_PAGE def get_albums(self, owner_id=None): """ Получить список альбомов пользователя :param owner_id: ID владельца (отрицательные значения для групп) """ return list(self.get_albums_iter(owner_id)) def search_user(self, owner_id=None, q=''): """ Искать по аудиозаписям пользователя :param owner_id: ID владельца (отрицательные значения для групп) :param q: запрос """ if owner_id is None: owner_id = self.user_id response = self._vk.http.post( 'https://vk.com/al_audio.php', data={ 'al': 1, 'act': 'section', 'claim': 0, 'is_layer': 0, 'owner_id': owner_id, 'section': 'search', 'q': q } ) json_response = json.loads(response.text.replace('