Skip to content

Commit

Permalink
Full review and some refactoring (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
hdk5 authored and python273 committed Mar 31, 2018
1 parent c3b4f6f commit 9121047
Show file tree
Hide file tree
Showing 16 changed files with 425 additions and 233 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -8,6 +8,6 @@ MANIFEST
.pydevproject
.settings/
testing/
tests/
tests1/
vk_config.json
vk_config.v2.json
2 changes: 1 addition & 1 deletion examples/messages_bot/messages_bot.py
Expand Up @@ -34,7 +34,7 @@ def main():
longpoll = VkLongPoll(vk_session)

for event in longpoll.listen():
if event.type == VkEventType.MESSAGE_NEW and event.to_me:
if event.type == VkEventType.MESSAGE_NEW and event.to_me and event.text:
print('id{}: "{}"'.format(event.user_id, event.text), end=' ')

response = session.get(
Expand Down
8 changes: 4 additions & 4 deletions jconfig/base.py
Expand Up @@ -24,10 +24,10 @@ def __getattr__(self, name):
__getitem__ = __getattr__

def __setattr__(self, name, value):
if name in self.__slots__:
return super(BaseConfig, self).__setattr__(name, value)

self._section[name] = value
try:
super(BaseConfig, self).__setattr__(name, value)
except AttributeError:
self._section[name] = value

__setitem__ = __setattr__

Expand Down
2 changes: 1 addition & 1 deletion jconfig/jconfig.py
Expand Up @@ -14,7 +14,7 @@

class Config(BaseConfig):

__slots__ = BaseConfig.__slots__ + ('_filename',)
__slots__ = ('_filename',)

def __init__(self, section, filename='.jconfig'):
self._filename = filename
Expand Down
3 changes: 3 additions & 0 deletions jconfig/memory.py
Expand Up @@ -11,6 +11,9 @@


class MemoryConfig(BaseConfig):

__slots__ = tuple()

def load(self, settings=None, **kwargs):
return {} if settings is None else settings

Expand Down
2 changes: 2 additions & 0 deletions requirements_dev.txt
@@ -0,0 +1,2 @@
pytest
pytest-mock
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -22,7 +22,7 @@
license='Apache License, Version 2.0, see LICENSE file',

packages=['vk_api', 'jconfig'],
install_requires=['requests', 'enum34'],
install_requires=['requests', 'enum34', 'beautifulsoup4'],

classifiers=[
'License :: OSI Approved :: Apache Software License',
Expand Down
Empty file added tests/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions tests/test_jconfig.py
@@ -0,0 +1,19 @@
from jconfig.memory import MemoryConfig


def test_config_section():
c = MemoryConfig('secret')

assert 'secret' in c._settings

c.test = 'hello!'

assert c['test'] == 'hello!'
assert c.test == 'hello!'
assert c._section == {'test': 'hello!'}

c['test'] = '42'

assert c['test'] == '42'
assert c.test == '42'
assert c._section == {'test': '42'}
1 change: 1 addition & 0 deletions vk_api/__init__.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from .enums import *
from .exceptions import *
from .requests_pool import VkRequestsPool
from .tools import VkTools
Expand Down
184 changes: 125 additions & 59 deletions vk_api/audio.py
Expand Up @@ -6,83 +6,136 @@
from .audio_url_decoder import decode_audio_url
from .exceptions import AccessDenied

RE_AUDIO = re.compile(r'audio[-\d]+_\d+_audios\d+')
RE_AUDIO_ID = re.compile(r'audio(-?\d+)_(\d+)')
RE_ALBUM_ID = re.compile(r'act=audio_playlist(-?\d+)_(\d+)')

TRACKS_PER_USER_PAGE = 50
TRACKS_PER_ALBUM_PAGE = 100
ALBUMS_PER_USER_PAGE = 100

class VkAudio:

class VkAudio(object):

__slots__ = ('_vk', 'user_id')

def __init__(self, vk):
self.user_id = vk.get_api().users.get()[0]['id']
"""
:type vk: vk_api.VkApi
"""
self.user_id = vk.method('users.get')[0]['id']
self._vk = vk

def get(self, owner_id=None, album_id=None, offset=0):
""" Получить список аудиозаписей пользователя
def get_iter(self, owner_id=None, album_id=None):
""" Получить список аудиозаписей пользователя (по частям)
:param owner_id: ID владельца (отрицательные значения для групп)
:param album_id: ID альбома (отрицательные значения для групп)
:param offset: смещение
:param album_id: ID альбома
"""

if owner_id is None and album_id is None:
raise TypeError(
'get() missing 1 required argument: album_id or owner_id'
)
elif owner_id is not None and album_id is not None:
raise TypeError('get() too many arguments')
if owner_id is None:
owner_id = self.user_id

if album_id is not None:
url = 'https://m.vk.com/audio?act=audio_playlist{}'.format(album_id)
url = 'https://m.vk.com/audio?act=audio_playlist{}_{}'.format(
owner_id, album_id
)
offset_diff = TRACKS_PER_ALBUM_PAGE
else:
url = 'https://m.vk.com/audios{}'.format(owner_id)
offset_diff = TRACKS_PER_USER_PAGE

offset = 0
while True:
response = self._vk.http.get(
url,
params={
'offset': offset
},
allow_redirects=False
)

response = self._vk.http.get(
url,
params={
'offset': offset
},
allow_redirects=False
)
if not response.text:
raise AccessDenied(
'You don\'t have permissions to browse user\'s audio'
)

if not response.text:
raise AccessDenied(
'You don\'t have permissions to browse user\'s audio'
)
tracks = scrap_data(response.text, self.user_id)

return scrap_data(response.text, self.user_id)
if not tracks:
break

def get_albums(self, owner_id, offset=0):
""" Получить список альбомов пользователя
for i in tracks:
yield i

offset += offset_diff

def get(self, owner_id=None, album_id=None):
""" Получить список аудиозаписей пользователя
:param owner_id: ID владельца (отрицательные значения для групп)
:param offset: смещение
:param album_id: ID альбома
"""

response = self._vk.http.get(
'https://m.vk.com/audio?act=audio_playlists{}'.format(owner_id),
params={
'offset': offset
},
allow_redirects=False
)
return list(self.get_iter(owner_id, album_id))

if not response.text:
raise AccessDenied(
'You don\'t have permissions to browse {}\'s albums'.format(
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
)

return scrap_albums(response.text)
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

def search_user(self, owner_id, q=''):
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.get(
'https://m.vk.com/audio',
params={
Expand All @@ -101,7 +154,7 @@ def search_user(self, owner_id, q=''):

return [
i for i in scrap_data(response.text, self.user_id)
if RE_AUDIO.search(i['id'])
if i['owner_id'] == owner_id
]

def search(self, q='', offset=0):
Expand All @@ -128,26 +181,30 @@ def scrap_data(html, user_id):

soup = BeautifulSoup(html, 'html.parser')
tracks = []

for audio in soup.find_all('div', {'class': 'audio_item'}):
if 'audio_item_disabled' in audio["class"]:
# TODO: implement getting data of unavailable track
if 'audio_item_disabled' in audio['class']:
continue

artist = audio.select('.ai_artist')[0].text
title = audio.select('.ai_title')[0].text
duration = audio.select('.ai_dur')[0]['data-dur']
track_id = audio['id']
link = audio.select('.ai_body')[0].input['value']
artist = audio.select_one('.ai_artist').text
title = audio.select_one('.ai_title').text
duration = int(audio.select_one('.ai_dur')['data-dur'])
full_id = tuple(
int(i) for i in RE_AUDIO_ID.search(audio['id']).groups()
)
link = audio.select_one('.ai_body').input['value']

if 'audio_api_unavailable' in link:
link = decode_audio_url(link, user_id)

tracks.append({
'id': full_id[1],
'owner_id': full_id[0],
'url': link,

'artist': artist,
'title': title,
'dur': duration,
'id': track_id,
'url': link
'duration': duration,
})

return tracks
Expand All @@ -158,15 +215,24 @@ def scrap_albums(html):

soup = BeautifulSoup(html, 'html.parser')
albums = []

for album in soup.find_all('div', {'class': 'audioPlaylistsPage__item'}):
link = album.select('.audioPlaylistsPage__itemLink')[0]['href']

link = album.select_one('.audioPlaylistsPage__itemLink')['href']
full_id = tuple(int(i) for i in RE_ALBUM_ID.search(link).groups())

stats_text = album.select_one('.audioPlaylistsPage__stats').text
plays = int(stats_text.split(maxsplit=1)[0])

albums.append({
'artist': album.select('.audioPlaylistsPage__author')[0].text,
'title': album.select('.audioPlaylistsPage__title')[0].text,
'plays': album.select('.audioPlaylistsPage__stats')[0].text,
'id': album['class'][1][25:],
'url': 'https://m.vk.com/audio?act=audio_playlist{}'.format(link)
'id': full_id[1],
'owner_id': full_id[0],
'url': 'https://m.vk.com/audio?act=audio_playlist{}_{}'.format(
*full_id
),

'title': album.select_one('.audioPlaylistsPage__title').text,
'plays': plays
})

return albums
33 changes: 33 additions & 0 deletions vk_api/enums.py
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
"""
@author: python273
@contact: https://vk.com/python273
@license Apache License, Version 2.0, see LICENSE file
Copyright (C) 2018
"""

from enum import Enum


class VkUserPermissions(Enum):
NOTIFY = 1
FRIEND = 2
PHOTOS = 2**2
AUDIO = 2**3
VIDEO = 2**4
STORIES = 2**6
PAGES = 2**7
ADD_LINK = 2**8
STATUS = 2**10
NOTES = 2**11
MESSAGES = 2**12
WALL = 2**13
ADS = 2**15
OFFLINE = 2**16
DOCS = 2**17
GROUPS = 2**18
NOTIFICATIONS = 2**19
STATS = 2**20
EMAIL = 2**22
MARKET = 2**27

0 comments on commit 9121047

Please sign in to comment.