Skip to content

Commit 9121047

Browse files
hdk5python273
authored andcommitted
Full review and some refactoring (#124)
1 parent c3b4f6f commit 9121047

16 files changed

+425
-233
lines changed

Diff for: .gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ MANIFEST
88
.pydevproject
99
.settings/
1010
testing/
11-
tests/
11+
tests1/
1212
vk_config.json
1313
vk_config.v2.json

Diff for: examples/messages_bot/messages_bot.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def main():
3434
longpoll = VkLongPoll(vk_session)
3535

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

4040
response = session.get(

Diff for: jconfig/base.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ def __getattr__(self, name):
2424
__getitem__ = __getattr__
2525

2626
def __setattr__(self, name, value):
27-
if name in self.__slots__:
28-
return super(BaseConfig, self).__setattr__(name, value)
29-
30-
self._section[name] = value
27+
try:
28+
super(BaseConfig, self).__setattr__(name, value)
29+
except AttributeError:
30+
self._section[name] = value
3131

3232
__setitem__ = __setattr__
3333

Diff for: jconfig/jconfig.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
class Config(BaseConfig):
1616

17-
__slots__ = BaseConfig.__slots__ + ('_filename',)
17+
__slots__ = ('_filename',)
1818

1919
def __init__(self, section, filename='.jconfig'):
2020
self._filename = filename

Diff for: jconfig/memory.py

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212

1313
class MemoryConfig(BaseConfig):
14+
15+
__slots__ = tuple()
16+
1417
def load(self, settings=None, **kwargs):
1518
return {} if settings is None else settings
1619

Diff for: requirements_dev.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pytest
2+
pytest-mock

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
license='Apache License, Version 2.0, see LICENSE file',
2323

2424
packages=['vk_api', 'jconfig'],
25-
install_requires=['requests', 'enum34'],
25+
install_requires=['requests', 'enum34', 'beautifulsoup4'],
2626

2727
classifiers=[
2828
'License :: OSI Approved :: Apache Software License',

Diff for: tests/__init__.py

Whitespace-only changes.

Diff for: tests/test_jconfig.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from jconfig.memory import MemoryConfig
2+
3+
4+
def test_config_section():
5+
c = MemoryConfig('secret')
6+
7+
assert 'secret' in c._settings
8+
9+
c.test = 'hello!'
10+
11+
assert c['test'] == 'hello!'
12+
assert c.test == 'hello!'
13+
assert c._section == {'test': 'hello!'}
14+
15+
c['test'] = '42'
16+
17+
assert c['test'] == '42'
18+
assert c.test == '42'
19+
assert c._section == {'test': '42'}

Diff for: vk_api/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: utf-8 -*-
2+
from .enums import *
23
from .exceptions import *
34
from .requests_pool import VkRequestsPool
45
from .tools import VkTools

Diff for: vk_api/audio.py

+125-59
Original file line numberDiff line numberDiff line change
@@ -6,83 +6,136 @@
66
from .audio_url_decoder import decode_audio_url
77
from .exceptions import AccessDenied
88

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

12+
TRACKS_PER_USER_PAGE = 50
13+
TRACKS_PER_ALBUM_PAGE = 100
14+
ALBUMS_PER_USER_PAGE = 100
1115

12-
class VkAudio:
16+
17+
class VkAudio(object):
1318

1419
__slots__ = ('_vk', 'user_id')
1520

1621
def __init__(self, vk):
17-
self.user_id = vk.get_api().users.get()[0]['id']
22+
"""
23+
24+
:type vk: vk_api.VkApi
25+
"""
26+
self.user_id = vk.method('users.get')[0]['id']
1827
self._vk = vk
1928

20-
def get(self, owner_id=None, album_id=None, offset=0):
21-
""" Получить список аудиозаписей пользователя
29+
def get_iter(self, owner_id=None, album_id=None):
30+
""" Получить список аудиозаписей пользователя (по частям)
2231
2332
:param owner_id: ID владельца (отрицательные значения для групп)
24-
:param album_id: ID альбома (отрицательные значения для групп)
25-
:param offset: смещение
33+
:param album_id: ID альбома
2634
"""
2735

28-
if owner_id is None and album_id is None:
29-
raise TypeError(
30-
'get() missing 1 required argument: album_id or owner_id'
31-
)
32-
elif owner_id is not None and album_id is not None:
33-
raise TypeError('get() too many arguments')
36+
if owner_id is None:
37+
owner_id = self.user_id
3438

3539
if album_id is not None:
36-
url = 'https://m.vk.com/audio?act=audio_playlist{}'.format(album_id)
40+
url = 'https://m.vk.com/audio?act=audio_playlist{}_{}'.format(
41+
owner_id, album_id
42+
)
43+
offset_diff = TRACKS_PER_ALBUM_PAGE
3744
else:
3845
url = 'https://m.vk.com/audios{}'.format(owner_id)
46+
offset_diff = TRACKS_PER_USER_PAGE
47+
48+
offset = 0
49+
while True:
50+
response = self._vk.http.get(
51+
url,
52+
params={
53+
'offset': offset
54+
},
55+
allow_redirects=False
56+
)
3957

40-
response = self._vk.http.get(
41-
url,
42-
params={
43-
'offset': offset
44-
},
45-
allow_redirects=False
46-
)
58+
if not response.text:
59+
raise AccessDenied(
60+
'You don\'t have permissions to browse user\'s audio'
61+
)
4762

48-
if not response.text:
49-
raise AccessDenied(
50-
'You don\'t have permissions to browse user\'s audio'
51-
)
63+
tracks = scrap_data(response.text, self.user_id)
5264

53-
return scrap_data(response.text, self.user_id)
65+
if not tracks:
66+
break
5467

55-
def get_albums(self, owner_id, offset=0):
56-
""" Получить список альбомов пользователя
68+
for i in tracks:
69+
yield i
70+
71+
offset += offset_diff
72+
73+
def get(self, owner_id=None, album_id=None):
74+
""" Получить список аудиозаписей пользователя
5775
5876
:param owner_id: ID владельца (отрицательные значения для групп)
59-
:param offset: смещение
77+
:param album_id: ID альбома
6078
"""
6179

62-
response = self._vk.http.get(
63-
'https://m.vk.com/audio?act=audio_playlists{}'.format(owner_id),
64-
params={
65-
'offset': offset
66-
},
67-
allow_redirects=False
68-
)
80+
return list(self.get_iter(owner_id, album_id))
6981

70-
if not response.text:
71-
raise AccessDenied(
72-
'You don\'t have permissions to browse {}\'s albums'.format(
82+
def get_albums_iter(self, owner_id=None):
83+
""" Получить список альбомов пользователя (по частям)
84+
85+
:param owner_id: ID владельца (отрицательные значения для групп)
86+
"""
87+
88+
if owner_id is None:
89+
owner_id = self.user_id
90+
91+
offset = 0
92+
93+
while True:
94+
response = self._vk.http.get(
95+
'https://m.vk.com/audio?act=audio_playlists{}'.format(
7396
owner_id
74-
)
97+
),
98+
params={
99+
'offset': offset
100+
},
101+
allow_redirects=False
75102
)
76103

77-
return scrap_albums(response.text)
104+
if not response.text:
105+
raise AccessDenied(
106+
'You don\'t have permissions to browse {}\'s albums'.format(
107+
owner_id
108+
)
109+
)
110+
111+
albums = scrap_albums(response.text)
112+
113+
if not albums:
114+
break
115+
116+
for i in albums:
117+
yield i
78118

79-
def search_user(self, owner_id, q=''):
119+
offset += ALBUMS_PER_USER_PAGE
120+
121+
def get_albums(self, owner_id=None):
122+
""" Получить список альбомов пользователя
123+
124+
:param owner_id: ID владельца (отрицательные значения для групп)
125+
"""
126+
127+
return list(self.get_albums_iter(owner_id))
128+
129+
def search_user(self, owner_id=None, q=''):
80130
""" Искать по аудиозаписям пользователя
81131
82132
:param owner_id: ID владельца (отрицательные значения для групп)
83133
:param q: запрос
84134
"""
85135

136+
if owner_id is None:
137+
owner_id = self.user_id
138+
86139
response = self._vk.http.get(
87140
'https://m.vk.com/audio',
88141
params={
@@ -101,7 +154,7 @@ def search_user(self, owner_id, q=''):
101154

102155
return [
103156
i for i in scrap_data(response.text, self.user_id)
104-
if RE_AUDIO.search(i['id'])
157+
if i['owner_id'] == owner_id
105158
]
106159

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

129182
soup = BeautifulSoup(html, 'html.parser')
130183
tracks = []
184+
131185
for audio in soup.find_all('div', {'class': 'audio_item'}):
132-
if 'audio_item_disabled' in audio["class"]:
133-
# TODO: implement getting data of unavailable track
186+
if 'audio_item_disabled' in audio['class']:
134187
continue
135188

136-
artist = audio.select('.ai_artist')[0].text
137-
title = audio.select('.ai_title')[0].text
138-
duration = audio.select('.ai_dur')[0]['data-dur']
139-
track_id = audio['id']
140-
link = audio.select('.ai_body')[0].input['value']
189+
artist = audio.select_one('.ai_artist').text
190+
title = audio.select_one('.ai_title').text
191+
duration = int(audio.select_one('.ai_dur')['data-dur'])
192+
full_id = tuple(
193+
int(i) for i in RE_AUDIO_ID.search(audio['id']).groups()
194+
)
195+
link = audio.select_one('.ai_body').input['value']
141196

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

145200
tracks.append({
201+
'id': full_id[1],
202+
'owner_id': full_id[0],
203+
'url': link,
204+
146205
'artist': artist,
147206
'title': title,
148-
'dur': duration,
149-
'id': track_id,
150-
'url': link
207+
'duration': duration,
151208
})
152209

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

159216
soup = BeautifulSoup(html, 'html.parser')
160217
albums = []
218+
161219
for album in soup.find_all('div', {'class': 'audioPlaylistsPage__item'}):
162-
link = album.select('.audioPlaylistsPage__itemLink')[0]['href']
220+
221+
link = album.select_one('.audioPlaylistsPage__itemLink')['href']
222+
full_id = tuple(int(i) for i in RE_ALBUM_ID.search(link).groups())
223+
224+
stats_text = album.select_one('.audioPlaylistsPage__stats').text
225+
plays = int(stats_text.split(maxsplit=1)[0])
163226

164227
albums.append({
165-
'artist': album.select('.audioPlaylistsPage__author')[0].text,
166-
'title': album.select('.audioPlaylistsPage__title')[0].text,
167-
'plays': album.select('.audioPlaylistsPage__stats')[0].text,
168-
'id': album['class'][1][25:],
169-
'url': 'https://m.vk.com/audio?act=audio_playlist{}'.format(link)
228+
'id': full_id[1],
229+
'owner_id': full_id[0],
230+
'url': 'https://m.vk.com/audio?act=audio_playlist{}_{}'.format(
231+
*full_id
232+
),
233+
234+
'title': album.select_one('.audioPlaylistsPage__title').text,
235+
'plays': plays
170236
})
171237

172238
return albums

Diff for: vk_api/enums.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
@author: python273
4+
@contact: https://vk.com/python273
5+
@license Apache License, Version 2.0, see LICENSE file
6+
7+
Copyright (C) 2018
8+
"""
9+
10+
from enum import Enum
11+
12+
13+
class VkUserPermissions(Enum):
14+
NOTIFY = 1
15+
FRIEND = 2
16+
PHOTOS = 2**2
17+
AUDIO = 2**3
18+
VIDEO = 2**4
19+
STORIES = 2**6
20+
PAGES = 2**7
21+
ADD_LINK = 2**8
22+
STATUS = 2**10
23+
NOTES = 2**11
24+
MESSAGES = 2**12
25+
WALL = 2**13
26+
ADS = 2**15
27+
OFFLINE = 2**16
28+
DOCS = 2**17
29+
GROUPS = 2**18
30+
NOTIFICATIONS = 2**19
31+
STATS = 2**20
32+
EMAIL = 2**22
33+
MARKET = 2**27

0 commit comments

Comments
 (0)