kodi-vk.inpos.ru/default.py

1162 lines
48 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals
import sys, os, vk, time, pickle, binascii
from vk.exceptions import VkAPIError
from vk.api import Session, AuthSession
from math import ceil
import xbmc, xbmcplugin, xbmcaddon, xbmcgui
import urllib.parse
import urllib.request, urllib.error, urllib.parse
from urllib.parse import urlencode
from urllib.request import urlopen
import simplejson as json
import re
_VERSION = '1.2.5'
_ADDON_NAME = 'kodi-vk.inpos.ru'
_addon = xbmcaddon.Addon(id=_ADDON_NAME)
_addon_id = int(sys.argv[1])
_addon_url = sys.argv[0]
_addon_path = _addon.getAddonInfo('path')
_APP_ID = '4353740'
_APP_SEC = 'GRDdDhHJ8qzyPQczrwqC'
_SCOPE = 'friends,photos,audio,video,groups,messages,offline'
_SETTINGS_ID_TOKEN = 'vk_token'
_SETTINGS_ID_MAX_RES = 'video_resolution'
_SETTINGS_ID_LIST_LEN = 'list_len'
_SETTINGS_ID_VIDEO_SEARCH_SORT = 'v_search_sort'
_SETTINGS_ID_VIDEO_SEARCH_HD = 'search_hd_video'
_SETTINGS_ID_VIDEO_SEARCH_ADULT = 'dont_search_adult_video'
_SETTINGS_BOOL = {'true': 1, 'false': 0}
_SETTINGS_INV_BOOL = {'true': 0, 'false': 1}
_SETTINGS_PAGE_ITEMS = int(_addon.getSetting(_SETTINGS_ID_LIST_LEN))
# _SETTINGS_PAGE_ITEMS = 50
_SETTINGS_MAX_RES = int(_addon.getSetting(_SETTINGS_ID_MAX_RES))
# _SETTINGS_MAX_RES = 1080
_SETTINGS_VIDEO_SEARCH_SORT = int(_addon.getSetting(_SETTINGS_ID_VIDEO_SEARCH_SORT))
# _SETTINGS_VIDEO_SEARCH_SORT = 0
_SETTINGS_VIDEO_SEARCH_HD = _SETTINGS_BOOL[_addon.getSetting(_SETTINGS_ID_VIDEO_SEARCH_HD)]
# _SETTINGS_VIDEO_SEARCH_HD = False
_SETTINGS_VIDEO_SEARCH_ADULT = _SETTINGS_INV_BOOL[_addon.getSetting(_SETTINGS_ID_VIDEO_SEARCH_ADULT)]
# _SETTINGS_VIDEO_SEARCH_ADULT = True
_FILE_VIDEO_SEARCH_HISTORY = _ADDON_NAME + '_vsh.pkl'
_FILE_GROUP_SEARCH_HISTORY = _ADDON_NAME + '_gsh.pkl'
_FILE_USER_SEARCH_HISTORY = _ADDON_NAME + '_ush.pkl'
_USERNAME = 'vk_username'
_LOGIN_RETRY = 3
_VK_API_VERSION = '5.95'
_PHOTO_THUMB_KEY = 'photo_130'
_CTYPE_VIDEO = 'video'
_CTYPE_AUDIO = 'audio'
_CTYPE_IMAGE = 'image'
_DO_HOME = 'home'
_DO_MAIN_VIDEO = 'main_video'
_DO_VIDEO = 'video'
_DO_VIDEO_ALBUMS = 'video_albums'
_DO_MAIN_VIDEO_SEARCH = 'main_video_search'
_DO_VIDEO_SEARCH = 'video_search'
_DO_PLAY_VIDEO = 'play_video'
_DO_MAIN_AUDIO = 'main_audio'
_DO_MAIN_PHOTO = 'main_photo'
_DO_PHOTO = 'photo'
_DO_PHOTO_ALBUMS = 'photo_albums'
_DO_FRIENDS = 'friends'
_DO_GROUPS = 'groups'
_DO_MAIN_GROUP_SEARCH = 'main_group_search'
_DO_GROUP_SEARCH = 'group_search'
_DO_MEMBERS = 'members'
_DO_MAIN_USER_SEARCH = 'main_user_search'
_DO_USER_SEARCH = 'user_search'
_DO_MAIN_FAVE = 'main_fave'
_DO_FAVE_VIDEO = 'fave_video'
_DO_FAVE_PHOTO = 'fave_photo'
_DO_FAVE_GROUPS = 'fave_groups'
_DO_FAVE_USERS = 'fave_users'
_DO_LOGOUT = 'logout'
_NO_OWNER = '__no_owner__'
_VK_VIDEO_SOURCE = 'vk_video'
_YOUTUBE_VIDEO_SOURCE = 'youtube_video'
_UNKNOWN_VIDEO_SOURCE = 'unknown_video'
DELAY = 1.0 / 3 # 3 запроса в секунду
# Служебные классы
class APIMethod(object):
__slots__ = ['conn', '_method_name']
def __init__(self, conn, method_name):
self.conn = conn
self._method_name = method_name
def __getattr__(self, method_name):
return APIMethod(self.conn, self._method_name + '.' + method_name)
def __call__(self, **method_kwargs):
return self.conn(self._method_name, **method_kwargs)
class Connection(object):
"""Соединяемся с сайтом"""
def __init__(self, app_id, username=None, password=None, access_token=None, scope=''):
if access_token is not None:
session = Session(access_token=access_token)
else:
session = AuthSession(app_id, username, password, scope=scope)
self.conn = vk.API(session)
self.last_request = 0.0
def __getattr__(self, method_name):
return APIMethod(self, method_name)
def __call__(self, method_name, **method_kwargs):
# Ограничение 3 запроса в секунду
delay = DELAY - (time.time() - self.last_request)
if delay > 0:
time.sleep(delay)
if 'v' not in method_kwargs: method_kwargs['v'] = _VK_API_VERSION
res = self.conn(method_name, **method_kwargs)
self.last_request = time.time()
return res
class Group(object):
"""Группа"""
def __init__(self, gid, conn):
self.conn = conn
self.id = gid
self.info = {}
def set_info(self):
self.info = self.conn.groups.getById(group_id=int(self.id) * -1)[0]
def members(self, page_items=_SETTINGS_PAGE_ITEMS, page=1):
m = self.conn.groups.getMembers(group_id=self.id,
offset=((page_items * page) - page_items),
fields='first_name,last_name,photo_50,photo_100,photo_200',
count=page_items)
count = m['count']
pages = ceil(float(count) / float(page_items))
l = []
for i in m['items']:
member = User(i['id'], self.conn)
member.info = i
l.append(member)
return {'pages': pages, 'total': count, 'items': l}
# Благодарю автора статьи https://habrahabr.ru/post/193374/
def switch_view():
skin_used = xbmc.getSkinDir()
if skin_used == 'skin.confluence':
xbmc.executebuiltin('Container.SetViewMode(500)') # Вид "Эскизы".
elif skin_used == 'skin.aeon.nox':
xbmc.executebuiltin('Container.SetViewMode(512)') # Вид "Инфо-стена"
def get_search_history(h_file_name):
path = os.path.join(xbmc.translatePath('special://temp/'), h_file_name)
if not os.path.exists(path):
return []
with open(path, 'rb') as f:
history = pickle.load(f)
return history
def put_search_history(history, h_file_name):
path = os.path.join(xbmc.translatePath('special://temp/'), h_file_name)
with open(path, 'wb') as f:
pickle.dump(history, f, -1)
def media_entries(e_method, conn, oid, **kwargs):
page_items = kwargs.pop('page_items', _SETTINGS_PAGE_ITEMS)
page = kwargs.pop('page', 1)
album = kwargs.pop('album', None)
kwargs.update({
'offset': ((page_items * page) - page_items),
'count': page_items
})
if oid != _NO_OWNER:
kwargs['owner_id'] = oid
if album:
kwargs['album_id'] = album
try:
entries = getattr(conn, e_method)(**kwargs)
except VkAPIError as exc:
if exc.code == 15 or (200 <= exc.code < 300):
entries = {'count': 0, 'items': []}
else:
raise
count = entries['count']
pages = int(ceil(count / float(page_items)))
l = []
for i in entries['items']:
if e_method.split('.')[-1] in ['getAlbums', 'getUsers', 'getLinks']:
entry_id = str(i['id'])
else:
entry_id = str(i['owner_id']) + '_' + str(i['id'])
ent = Entry(e_method, entry_id, conn)
ent.info = i
l.append(ent)
del entries['count']
del entries['items']
res = {'pages': pages, 'total': count, 'items': l}
for k in list(entries.keys()):
res[k] = entries[k]
return res
class Entry(object):
def __init__(self, e_method, eid, conn):
self.method = e_method
self.id = eid
self.conn = conn
self.info = {}
def set_info(self):
if self.method == 'video.get':
self.info = self.conn.video.get(videos=self.id)['items'][0]
elif self.method == 'audio.get':
self.info = self.conn.audio.getById(audios=self.id)['items'][0]
elif self.method == 'photos.get':
self.info = self.conn.photos.getById(photos=self.id)['items'][0]
class User(object):
"""Этот класс описывает свойства и методы пользователя."""
def __init__(self, uid, conn):
self.conn = conn
self.id = uid
self.info = {}
def set_info(self):
self.info = self.conn.users.get(user_id=self.id, fields='first_name,last_name,photo_50,photo_100,photo_200')[0]
def friends(self, page_items=_SETTINGS_PAGE_ITEMS, page=1, order='hints'):
f = self.conn.friends.get(user_id=self.id,
offset=((page_items * page) - page_items),
count=page_items,
fields='first_name,last_name,photo_50,photo_100,photo_200',
order=order)
count = f['count']
pages = ceil(float(count) / float(page_items))
l = []
for i in f['items']:
u = User(i['id'], self.conn)
u.info = i
l.append(u)
return {'pages': pages, 'total': count, 'items': l}
def user_search(self, q='', page_items=_SETTINGS_PAGE_ITEMS, page=1):
usr = self.conn.users.search(q=q,
offset=((page_items * page) - page_items),
count=page_items,
fields='first_name,last_name,photo_50,photo_100,photo_200')
count = usr['count']
pages = ceil(float(count) / float(page_items))
l = []
for i in usr['items']:
u = User(i['id'], self.conn)
u.info = i
l.append(u)
return {'pages': pages, 'total': count, 'items': l}
def groups(self, page_items=_SETTINGS_PAGE_ITEMS, page=1):
gr = self.conn.groups.get(user_id=self.id,
offset=((page_items * page) - page_items),
count=page_items,
extended=1)
count = gr['count']
pages = ceil(float(count) / float(page_items))
l = []
for i in gr['items']:
if i['is_closed'] > 0 and i['is_member'] == 0: continue
g = Group(i['id'], self.conn)
g.info = i
l.append(g)
return {'pages': pages, 'total': count, 'items': l}
def group_search(self, q='', page_items=_SETTINGS_PAGE_ITEMS, page=1):
gr = self.conn.groups.search(q=q,
offset=((page_items * page) - page_items),
count=page_items)
count = gr['count']
pages = ceil(float(count) / float(page_items))
l = []
for i in gr['items']:
if i['is_closed'] > 0 and i['is_member'] == 0: continue
g = Group(i['id'], self.conn)
g.info = i
l.append(g)
return {'pages': pages, 'total': count, 'items': l}
class KodiVKGUIFave(object):
def __init__(self, root):
self.root = root
def m_main_fave(self):
if self.root.c_type == _CTYPE_VIDEO:
self.root.add_folder(self.root.gui.m_string(400502), {'do': _DO_FAVE_VIDEO, 'page': 1})
elif self.root.c_type == _CTYPE_IMAGE:
self.root.add_folder(self.root.gui.m_string(400504), {'do': _DO_FAVE_PHOTO, 'page': 1})
self.root.add_folder(self.root.gui.m_string(400513), {'do': _DO_FAVE_USERS, 'page': 1})
self.root.add_folder(self.root.gui.m_string(400506), {'do': _DO_FAVE_GROUPS, 'page': 1})
xbmcplugin.endOfDirectory(_addon_id)
def m_video(self):
page = int(self.root.params['page'])
kwargs = {'page': page, 'extended': 1}
vids = media_entries('fave.getVideos', self.root.conn, _NO_OWNER, **kwargs)
if page < vids['pages']:
params = {'do': _DO_FAVE_VIDEO, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
for v in vids['items']:
list_item = xbmcgui.ListItem(v.info['title'])
list_item.setInfo('video', {
'title': v.info['title'],
'duration': int(v.info['duration']),
'plot': v.info['description']
}
)
p_key = 'photo_%d' % (
max([int(x.split('_')[1]) for x in [x for x in list(v.info.keys()) if x.startswith('photo_')]]),)
list_item.setArt({'thumb': v.info['photo_130'], 'icon': v.info['photo_130'], 'fanart': v.info[p_key]})
list_item.setProperty('IsPlayable', 'true')
if 'files' in list(v.info.keys()):
if 'external' in v.info['files']:
v_source = self.root.gui.videos.m_get_video_source(v.info['files']['external'])
else:
v_source = _VK_VIDEO_SOURCE
else:
v_source = self.root.gui.videos.m_get_video_source(v.info['player'])
if v_source == _VK_VIDEO_SOURCE:
params = {'do': _DO_PLAY_VIDEO, 'vid': v.id, 'source': _VK_VIDEO_SOURCE}
url = self.root.url(**params)
elif v_source == _YOUTUBE_VIDEO_SOURCE:
if 'files' in list(v.info.keys()):
y_url = v.info['files']['external']
else:
y_url = v.info['player']
s = re.compile('^http.*youtube.*(v=|\/embed\/)([^\?\&]+)[\?\&]?.*$')
sr = s.findall(y_url)
if len(sr) < 0:
xbmc.log('WARN: Unknown youtube url: %s' % (y_url,))
continue
y_id = sr[0][1]
url = 'plugin://plugin.video.youtube/?action=play_video&videoid=' + y_id
else:
continue
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=False)
if page < vids['pages']:
params = {'do': _DO_FAVE_VIDEO, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_photo(self):
page = int(self.root.params['page'])
kwargs = {'page': page}
photos = media_entries('photos.getAll', self.root.conn, _NO_OWNER, **kwargs)
if page < photos['pages']:
params = {'do': _DO_FAVE_PHOTO, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
for index in range(len(photos['items'])):
p = photos['items'][index]
num = (_SETTINGS_PAGE_ITEMS * page) + index + 1
list_item = xbmcgui.ListItem('%04d' % (num,))
p_info = {
'title': '%04d' % (num,),
'tagline': p.info['text']
}
if 'width' in list(p.info.keys()): p_info['exif:resolution'] = '%d,%d' % (p.info['width'], p.info['height'])
list_item.setInfo('pictures', p_info)
list_item.setArt({'thumb': p.info['photo_130'], 'icon': p.info['photo_75']})
r = [int(x.split('_')[1]) for x in [x for x in list(p.info.keys()) if x.startswith('photo_')]]
### Здесь надо подумать над настройкой
url_key = max(r)
url = p.info['photo_%d' % (url_key,)]
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=False)
if page < photos['pages']:
params = {'do': _DO_FAVE_PHOTO, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
switch_view()
def m_users(self):
page = int(self.root.params['page'])
users = media_entries('fave.getUsers', self.root.conn, _NO_OWNER, page=page)
if page < users['pages']:
params = {'do': _DO_FAVE_USERS, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
for u in users['items']:
list_item = xbmcgui.ListItem('%s %s' % (u.info['last_name'], u.info['first_name']))
# p_key = 'photo_%d' % (max(map(lambda x: int(x.split('_')[1]), filter(lambda x: x.startswith('photo_'), m.info.keys()))),)
# list_item.setArt({'thumb': m.info[p_key], 'icon': m.info[p_key]})
params = {'do': _DO_HOME, 'oid': u.id}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
if page < users['pages']:
params = {'do': _DO_FAVE_USERS, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_groups(self):
page = int(self.root.params['page'])
links = media_entries('fave.getLinks', self.root.conn, _NO_OWNER, page=page)
if page < links['pages']:
params = {'do': _DO_FAVE_GROUPS, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
for l in links['items']:
l_id = l.info['id'].split('_')
if l_id[0] == '2':
list_item = xbmcgui.ListItem(l.info['title'])
p_key = 'photo_%d' % (
max([int(x.split('_')[1]) for x in [x for x in list(l.info.keys()) if x.startswith('photo_')]]),)
list_item.setArt({'thumb': l.info[p_key], 'icon': l.info[p_key]})
params = {'do': _DO_HOME, 'oid': -int(l_id[-1])}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
if page < links['pages']:
params = {'do': _DO_FAVE_GROUPS, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
class KodiVKGUIPhotos(object):
def __init__(self, root):
self.root = root
def m_main_photo(self):
oid = self.root.params['oid']
self.root.add_folder(self.root.gui.m_string(400508), {'do': _DO_PHOTO, 'oid': oid, 'page': 1})
self.root.add_folder(self.root.gui.m_string(400511), {'do': _DO_PHOTO_ALBUMS, 'oid': oid, 'page': 1})
xbmcplugin.endOfDirectory(_addon_id)
def m_photo_albums(self):
page = int(self.root.params['page'])
oid = self.root.params['oid']
kwargs = {'page': page, 'need_covers': 1, 'need_system': 1}
albums = media_entries('photos.getAlbums', self.root.conn, oid, **kwargs)
if page < albums['pages']:
params = {'do': _DO_PHOTO_ALBUMS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
for a in albums['items']:
list_item = xbmcgui.ListItem(a.info['title'])
list_item.setInfo('pictures', {'title': a.info['title']})
list_item.setArt({'thumb': a.info['thumb_src'], 'icon': a.info['thumb_src']})
params = {'do': _DO_PHOTO, 'oid': oid, 'album': a.id, 'page': 1}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
if page < albums['pages']:
params = {'do': _DO_PHOTO_ALBUMS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
switch_view()
def m_photo(self):
page = int(self.root.params['page'])
oid = self.root.params['oid']
album = self.root.params.get('album', None)
kwargs = {'page': page}
if album:
kwargs['album'] = album
photos = media_entries('photos.get', self.root.conn, oid, **kwargs)
else:
photos = media_entries('photos.getAll', self.root.conn, oid, **kwargs)
if page < photos['pages']:
params = {'do': _DO_PHOTO, 'oid': oid, 'page': page + 1}
if album: params['album'] = album
self.root.add_folder(self.root.gui.m_string(400602), params)
for index in range(len(photos['items'])):
p = photos['items'][index]
num = (_SETTINGS_PAGE_ITEMS * (page - 1)) + index + 1
list_item = xbmcgui.ListItem('%04d' % (num,))
p_info = {
'title': '%04d' % (num,),
'tagline': p.info['text']
}
if 'width' in list(p.info.keys()): p_info['exif:resolution'] = '%d,%d' % (p.info['width'], p.info['height'])
list_item.setInfo('pictures', p_info)
list_item.setArt({'thumb': p.info['photo_130'], 'icon': p.info['photo_75']})
r = [int(x.split('_')[1]) for x in [x for x in list(p.info.keys()) if x.startswith('photo_')]]
# Здесь надо подумать над настройкой
url_key = max(r)
url = p.info['photo_%d' % (url_key,)]
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=False)
if page < photos['pages']:
params = {'do': _DO_PHOTO, 'oid': oid, 'page': page + 1}
if album: params['album'] = album
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
switch_view()
class KodiVKGUIVideos(object):
def __init__(self, root):
self.root = root
def m_get_video_source(self, v_url):
is_vk_url_re = re.compile('https?\:\/\/[^\/]*vk.com\/.*')
is_youtube_url_re = re.compile('https\:\/\/www.youtube.com\/.*')
if len(is_vk_url_re.findall(v_url)) > 0: return _VK_VIDEO_SOURCE
if len(is_youtube_url_re.findall(v_url)) > 0: return _YOUTUBE_VIDEO_SOURCE
return _UNKNOWN_VIDEO_SOURCE
def m_main_video_search(self):
page = int(self.root.params['page'])
self.root.add_folder(self.root.gui.m_string(400516), {'do': _DO_VIDEO_SEARCH, 'q': 'none', 'page': 1})
history = get_search_history(_FILE_VIDEO_SEARCH_HISTORY)
count = len(history)
pages = int(ceil(count / float(_SETTINGS_PAGE_ITEMS)))
if page < pages:
params = {'do': _DO_MAIN_VIDEO_SEARCH, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
h_start = _SETTINGS_PAGE_ITEMS * (page - 1)
h_end = h_start + _SETTINGS_PAGE_ITEMS
history = history[h_start:h_end]
for h in history:
query_hex = binascii.hexlify(pickle.dumps(h, -1))
list_item = xbmcgui.ListItem(h)
params = {'do': _DO_VIDEO_SEARCH, 'q': query_hex, 'page': 1}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
if page < pages:
params = {'do': _DO_MAIN_VIDEO_SEARCH, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_video_search(self):
page = int(self.root.params['page'])
q = self.root.params['q']
if q == 'none':
s_win = xbmc.Keyboard()
s_win.setHeading(self.root.gui.m_string(400515))
s_win.setHiddenInput(False)
s_win.doModal()
if s_win.isConfirmed():
q = s_win.getText()
else:
self.root.gui.notify(self.root.gui.m_string(400525), '')
return
else:
q = pickle.loads(binascii.unhexlify(q))
history = get_search_history(_FILE_VIDEO_SEARCH_HISTORY)
try:
del history[history.index(q)]
except ValueError:
pass
history = [q] + history
put_search_history(history, _FILE_VIDEO_SEARCH_HISTORY)
query_hex = binascii.hexlify(pickle.dumps(q, -1))
kwargs = {
'page': page,
'sort': _SETTINGS_VIDEO_SEARCH_SORT,
'hd': _SETTINGS_VIDEO_SEARCH_HD,
'adult': _SETTINGS_VIDEO_SEARCH_ADULT,
'q': q,
'extended': 1
}
search_res = media_entries('video.search', self.root.conn, _NO_OWNER, **kwargs)
if page < search_res['pages']:
params = {'do': _DO_VIDEO_SEARCH, 'q': query_hex, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
self.m__create_video_list_(search_res)
if page < search_res['pages']:
params = {'do': _DO_VIDEO_SEARCH, 'q': query_hex, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_main_video(self):
oid = self.root.params['oid']
self.root.add_folder(self.root.gui.m_string(400509), {'do': _DO_VIDEO, 'oid': oid, 'page': 1})
self.root.add_folder(self.root.gui.m_string(400510), {'do': _DO_VIDEO_ALBUMS, 'oid': oid, 'page': 1})
if int(oid) == self.root.u.id:
self.root.add_folder(self.root.gui.m_string(400515), {'do': _DO_MAIN_VIDEO_SEARCH, 'page': 1})
xbmcplugin.endOfDirectory(_addon_id)
def m__create_video_list_(self, vids):
for v in vids['items']:
list_item = xbmcgui.ListItem(v.info['title'])
oid = v.info['owner_id']
if int(oid) < 0:
gid = oid * -1
g = [x for x in vids['groups'] if x['id'] == gid][0]
cm_title = '%s [I]%s[/I]' % (self.root.gui.m_string(400604), g['name'])
else:
u = [x for x in vids['profiles'] if x['id'] == oid][0]
cm_title = '%s [I]%s %s[/I]' % (self.root.gui.m_string(400603), u['last_name'], u['first_name'])
cm_params = {'do': _DO_HOME, 'oid': oid}
cm_url = self.root.url(**cm_params)
list_item.addContextMenuItems([(cm_title, 'xbmc.Container.update(%s)' % (cm_url,))])
list_item.setInfo('video', {
'title': v.info['title'],
'duration': int(v.info['duration']),
'plot': v.info['description']
}
)
p_key = 'photo_%d' % (max([int(x.split('_')[1]) for x in [x for x in list(v.info.keys()) if x.startswith('photo_')]]),)
list_item.setArt({'thumb': v.info['photo_130'], 'icon': v.info['photo_130'], 'fanart': v.info[p_key]})
list_item.setProperty('IsPlayable', 'true')
if 'files' in list(v.info.keys()):
if 'external' in v.info['files']:
v_source = self.m_get_video_source(v.info['files']['external'])
else:
v_source = _VK_VIDEO_SOURCE
else:
v_source = self.m_get_video_source(v.info['player'])
if v_source == _VK_VIDEO_SOURCE:
params = {'do': _DO_PLAY_VIDEO, 'vid': v.id, 'source': _VK_VIDEO_SOURCE}
url = self.root.url(**params)
elif v_source == _YOUTUBE_VIDEO_SOURCE:
if 'files' in list(v.info.keys()):
y_url = v.info['files']['external']
else:
y_url = v.info['player']
s = re.compile('^http.*youtube.*(v=|\/embed\/)([^\?\&]+)[\?\&]?.*$')
sr = s.findall(y_url)
if len(sr) < 0:
xbmc.log('WARN: Unknown youtube url: %s' % (y_url,))
continue
y_id = sr[0][1]
url = 'plugin://plugin.video.youtube/?action=play_video&videoid=' + y_id
else:
continue
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=False)
def m_video_albums(self):
page = int(self.root.params['page'])
oid = self.root.params['oid']
kwargs = {
'page': page,
'extended': 1
}
albums = media_entries('video.getAlbums', self.root.conn, oid, **kwargs)
if page < albums['pages']:
params = {'do': _DO_VIDEO_ALBUMS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
for a in albums['items']:
list_item = xbmcgui.ListItem(a.info['title'])
list_item.setInfo('video', {'title': a.info['title']})
if 'photo_320' in list(a.info.keys()):
list_item.setArt(
{'thumb': a.info['photo_160'], 'icon': a.info['photo_160'], 'fanart': a.info['photo_320']})
params = {'do': _DO_VIDEO, 'oid': oid, 'album': a.id, 'page': 1}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
if page < albums['pages']:
params = {'do': _DO_VIDEO_ALBUMS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_video(self):
page = int(self.root.params['page'])
oid = self.root.params['oid']
album = self.root.params.get('album', None)
kwargs = {'page': page, 'extended': 1}
if album: kwargs['album'] = album
vids = media_entries('video.get', self.root.conn, oid, **kwargs)
if page < vids['pages']:
params = {'do': _DO_VIDEO, 'oid': oid, 'page': page + 1}
if album: params['album'] = album
self.root.add_folder(self.root.gui.m_string(400602), params)
self.m__create_video_list_(vids)
if page < vids['pages']:
params = {'do': _DO_VIDEO, 'oid': oid, 'page': page + 1}
if album: params['album'] = album
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_play_video(self):
vid = self.root.params['vid']
src = self.root.params['source']
v = Entry('video.get', vid, self.root.conn)
try:
v.set_info()
except:
self.root.gui.notify(self.root.gui.m_string(400524), '')
return
if 'files' in list(v.info.keys()):
paths = {}
if src == _VK_VIDEO_SOURCE:
for k in list(v.info['files'].keys()):
if '_' not in k:
try:
local_idx = int(k)
except:
continue
else:
local_idx = int(k.split('_')[1])
paths[local_idx] = v.info['files'][k]
else:
v_url = v.info['player']
if src == _VK_VIDEO_SOURCE:
paths = self.root.parse_vk_player_html(v_url)
# Здесь должно браться разрешение из настроек
k = max([x for x in list(paths.keys()) if x <= _SETTINGS_MAX_RES])
path = paths[k]
play_item = xbmcgui.ListItem(path=path)
xbmcplugin.setResolvedUrl(_addon_id, True, listitem=play_item)
class KodiVkGUI:
"""Окошки, диалоги, сообщения"""
def __init__(self, root):
self.root = root
self.photos = KodiVKGUIPhotos(self.root)
self.videos = KodiVKGUIVideos(self.root)
self.faves = KodiVKGUIFave(self.root)
def notify(self, title, msg):
dialog = xbmcgui.Dialog()
dialog.notification(title, msg,
xbmcgui.NOTIFICATION_WARNING, 3000)
def m_string(self, string_id):
return _addon.getLocalizedString(string_id)
def m_login_form(self):
login_window = xbmc.Keyboard()
login_window.setHeading(self.m_string(400500))
login_window.setHiddenInput(False)
login_window.setDefault(_addon.getSetting(_USERNAME))
login_window.doModal()
if login_window.isConfirmed():
username = login_window.getText()
password_window = xbmc.Keyboard()
password_window.setHeading(self.m_string(400501))
password_window.setHiddenInput(True)
password_window.doModal()
if password_window.isConfirmed():
return username, password_window.getText()
else:
raise Exception("Password input was cancelled.")
else:
raise Exception("Login input was cancelled.")
def m_home(self):
c_type = self.root.params.get('content_type', None)
oid = self.root.params['oid']
if not c_type:
xbmc.log('No content_type')
return
if int(oid) < 0:
g = Group(oid, self.root.conn)
g.set_info()
header_string = '%s [I]%s[/I]' % (self.m_string(400604), g.info['name'])
p_key = 'photo_%d' % (
max([int(x.split('_')[1]) for x in [x for x in list(g.info.keys()) if x.startswith('photo_')]]),)
thumb_url = g.info[p_key]
icon_url = g.info[p_key]
else:
u = User(oid, self.root.conn)
u.set_info()
header_string = '%s [I]%s %s[/I]' % (self.m_string(400603), u.info['last_name'], u.info['first_name'])
p_key = 'photo_%d' % (max([int(x.split('_')[1]) for x in [x for x in list(u.info.keys()) if x.startswith('photo_')]]),)
thumb_url = u.info[p_key]
icon_url = u.info[p_key]
list_item = xbmcgui.ListItem(header_string)
list_item.setArt({'thumb': thumb_url, 'icon': icon_url})
h_url = self.root.url({'do': _DO_HOME, 'oid': oid})
xbmcplugin.addDirectoryItem(_addon_id, h_url, list_item, isFolder=True)
if c_type == _CTYPE_VIDEO:
self.root.add_folder(self.m_string(400502), {'do': _DO_MAIN_VIDEO, 'oid': oid})
# elif c_type == _CTYPE_AUDIO:
# self.root.add_folder(self._string(400503), {'do': _DO_MAIN_AUDIO, 'oid': oid})
elif c_type == _CTYPE_IMAGE:
self.root.add_folder(self.m_string(400504), {'do': _DO_MAIN_PHOTO, 'oid': oid})
else:
xbmc.log('Unknown content_type: %s' % (c_type,))
return
if int(oid) > 0:
self.root.add_folder(self.m_string(400505), {'do': _DO_FRIENDS, 'oid': oid, 'page': 1})
self.root.add_folder(self.m_string(400506), {'do': _DO_GROUPS, 'oid': oid, 'page': 1})
else:
self.root.add_folder(self.m_string(400512), {'do': _DO_MEMBERS, 'oid': -int(oid), 'page': 1})
if oid == self.root.u.id:
self.root.add_folder(self.m_string(400514), {'do': _DO_MAIN_FAVE})
xbmcplugin.addDirectoryItem(_addon_id, '', xbmcgui.ListItem(''), isFolder=False)
xbmcplugin.addDirectoryItem(_addon_id, '', xbmcgui.ListItem(''), isFolder=False)
xbmcplugin.addDirectoryItem(_addon_id, '', xbmcgui.ListItem(''), isFolder=False)
self.root.add_folder(self.m_string(400526), {'do': _DO_LOGOUT})
xbmcplugin.endOfDirectory(_addon_id)
def m__create_group_list_(self, groups):
for g in groups['items']:
list_item = xbmcgui.ListItem(g.info['name'])
p_key = 'photo_%d' % (
max([int(x.split('_')[1]) for x in [x for x in list(g.info.keys()) if x.startswith('photo_')]]),)
list_item.setArt({'thumb': g.info[p_key], 'icon': g.info[p_key]})
params = {'do': _DO_HOME, 'oid': -g.id}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
def m_groups(self):
oid = self.root.params['oid']
page = int(self.root.params['page'])
if int(oid) == self.root.u.id:
self.root.add_folder(self.root.gui.m_string(400515), {'do': _DO_MAIN_GROUP_SEARCH, 'page': 1})
user = User(oid, self.root.conn)
groups = user.groups(page=page)
if page < groups['pages']:
params = {'do': _DO_GROUPS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
self.m__create_group_list_(groups)
if page < groups['pages']:
params = {'do': _DO_GROUPS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m__create_user_list_(self, users):
for f in users['items']:
list_item = xbmcgui.ListItem('%s %s' % (f.info['last_name'], f.info['first_name']))
p_key = 'photo_%d' % (max([int(x.split('_')[1]) for x in [x for x in list(f.info.keys()) if x.startswith('photo_')]]),)
list_item.setArt({'thumb': f.info[p_key], 'icon': f.info[p_key]})
params = {'do': _DO_HOME, 'oid': f.id}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
def m_friends(self):
oid = self.root.params['oid']
page = int(self.root.params['page'])
if int(oid) == self.root.u.id:
self.root.add_folder(self.root.gui.m_string(400515), {'do': _DO_MAIN_USER_SEARCH, 'page': 1})
user = User(oid, self.root.conn)
friends = user.friends(page=page)
if page < friends['pages']:
params = {'do': _DO_FRIENDS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
self.m__create_user_list_(friends)
if page < friends['pages']:
params = {'do': _DO_FRIENDS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_members(self):
oid = self.root.params['oid']
page = int(self.root.params['page'])
group = Group(oid, self.root.conn)
members = group.members(page=page)
if page < members['pages']:
params = {'do': _DO_MEMBERS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
for m in members['items']:
list_item = xbmcgui.ListItem('%s %s' % (m.info['last_name'], m.info['first_name']))
p_key = 'photo_%d' % (
max([int(x.split('_')[1]) for x in [x for x in list(m.info.keys()) if x.startswith('photo_')]]),)
list_item.setArt({'thumb': m.info[p_key], 'icon': m.info[p_key]})
params = {'do': _DO_HOME, 'oid': m.id}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
if page < members['pages']:
params = {'do': _DO_MEMBERS, 'oid': oid, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m__create_user_group_search_page_(self, do_current, do_target, h_file):
page = int(self.root.params['page'])
self.root.add_folder(self.root.gui.m_string(400516), {'do': do_target, 'q': 'none', 'page': 1})
history = get_search_history(h_file)
count = len(history)
pages = int(ceil(count / float(_SETTINGS_PAGE_ITEMS)))
if page < pages:
params = {'do': do_current, 'page': page + 1}
self.root.add_folder(self.m_string(400602), params)
h_start = _SETTINGS_PAGE_ITEMS * (page - 1)
h_end = h_start + _SETTINGS_PAGE_ITEMS
history = history[h_start:h_end]
for h in history:
query_hex = binascii.hexlify(pickle.dumps(h, -1))
list_item = xbmcgui.ListItem(h)
params = {'do': do_target, 'q': query_hex, 'page': 1}
url = self.root.url(**params)
xbmcplugin.addDirectoryItem(_addon_id, url, list_item, isFolder=True)
if page < pages:
params = {'do': do_current, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_main_group_search(self):
self.m__create_user_group_search_page_(_DO_MAIN_GROUP_SEARCH, _DO_GROUP_SEARCH, _FILE_GROUP_SEARCH_HISTORY)
def m_group_search(self):
page = int(self.root.params['page'])
q = self.root.params['q']
if q == 'none':
s_win = xbmc.Keyboard()
s_win.setHeading(self.m_string(400515))
s_win.setHiddenInput(False)
s_win.doModal()
if s_win.isConfirmed():
q = s_win.getText()
else:
self.notify(self.m_string(400525), '')
return
else:
q = pickle.loads(binascii.unhexlify(q))
history = get_search_history(_FILE_GROUP_SEARCH_HISTORY)
try:
del history[history.index(q)]
except ValueError:
pass
history = [q] + history
put_search_history(history, _FILE_GROUP_SEARCH_HISTORY)
query_hex = binascii.hexlify(pickle.dumps(q, -1))
kwargs = {
'page': page,
'q': q
}
u = User(self.root.u.id, self.root.conn)
search_res = u.group_search(**kwargs)
if page < search_res['pages']:
params = {'do': _DO_GROUP_SEARCH, 'q': query_hex, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
self.m__create_group_list_(search_res)
if page < search_res['pages']:
params = {'do': _DO_GROUP_SEARCH, 'q': query_hex, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_main_user_search(self):
self.m__create_user_group_search_page_(_DO_MAIN_USER_SEARCH, _DO_USER_SEARCH, _FILE_USER_SEARCH_HISTORY)
def m_user_search(self):
page = int(self.root.params['page'])
q = self.root.params['q']
if q == 'none':
s_win = xbmc.Keyboard()
s_win.setHeading(self.root.gui.m_string(400515))
s_win.setHiddenInput(False)
s_win.doModal()
if s_win.isConfirmed():
q = s_win.getText()
else:
self.notify(self.m_string(400525), '')
return
else:
q = pickle.loads(binascii.unhexlify(q))
history = get_search_history(_FILE_USER_SEARCH_HISTORY)
try:
del history[history.index(q)]
except ValueError:
pass
history = [q] + history
put_search_history(history, _FILE_USER_SEARCH_HISTORY)
query_hex = binascii.hexlify(pickle.dumps(q, -1))
kwargs = {
'page': page,
'q': q
}
u = User(self.root.u.id, self.root.conn)
search_res = u.user_search(**kwargs)
if page < search_res['pages']:
params = {'do': _DO_USER_SEARCH, 'q': query_hex, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
self.m__create_user_list_(search_res)
if page < search_res['pages']:
params = {'do': _DO_USER_SEARCH, 'q': query_hex, 'page': page + 1}
self.root.add_folder(self.root.gui.m_string(400602), params)
xbmcplugin.endOfDirectory(_addon_id)
def m_logout(self):
dialog = xbmcgui.Dialog()
ret = dialog.yesno(self.m_string(400526), self.m_string(400527), nolabel=self.m_string(400529),
yeslabel=self.m_string(400528))
if ret:
_addon.setSetting(_SETTINGS_ID_TOKEN, '')
xbmc.executebuiltin("XBMC.Container.Update(path,replace)")
xbmc.executebuiltin("XBMC.ActivateWindow(Home)")
else:
return
class KodiVk:
conn = None
def __init__(self):
self.gui = KodiVkGUI(self)
self.conn = self.__connect_()
if not self.conn: raise Exception()
u_info = self.conn.users.get()[0]
self.u = User(u_info['id'], self.conn)
self.u.set_info()
p = {'do': _DO_HOME}
if sys.argv[2]:
p.update(dict(urllib.parse.parse_qsl(sys.argv[2][1:])))
p['oid'] = int(p.get('oid', self.u.info['id']))
self.params = p
if 'content_type' not in list(self.params.keys()):
cw_id = xbmcgui.getCurrentWindowId()
if cw_id in (10006, 10024, 10025, 10028):
self.params['content_type'] = _CTYPE_VIDEO
# elif id in (10005, 10500, 10501, 10502):
# self.params['content_type'] = _CTYPE_AUDIO
elif id in (10002,):
self.params['content_type'] = _CTYPE_IMAGE
self.c_type = self.params.get('content_type', None)
def url(self, params={}, **kwparams):
if self.c_type:
kwparams['content_type'] = self.c_type
params.update(kwparams)
return _addon_url + "?" + urlencode(params)
def add_folder(self, name, params):
url = self.url(**params)
item = xbmcgui.ListItem(name)
xbmcplugin.addDirectoryItem(_addon_id, url, item, isFolder=True)
def __connect_(self):
token = _addon.getSetting(_SETTINGS_ID_TOKEN)
conn = Connection(_APP_ID, access_token=token)
try:
tmp__ = conn.users.get()[0]
except vk.exceptions.VkAPIError as e:
if e.code == 5:
token = None
else:
raise
if token in [None, '']:
token = None
count = _LOGIN_RETRY
while token in [None, ''] and count > 0:
count -= 1
try:
login, password = self.gui.m_login_form()
except:
self.gui.notify(self.gui.m_string(400525), '')
try:
token = auth(login, password, _APP_ID, _APP_SEC, _SCOPE,"0")
if token == '-1':
code = self.m_ask_code()
token = auth(login, password, _APP_ID, _APP_SEC, _SCOPE, code)
if token not in (None, ''):
_addon.setSetting(_SETTINGS_ID_TOKEN, token)
conn = Connection(_APP_ID, login, password, scope=_SCOPE)
else:
raise vk.api.VkAuthError
except vk.api.VkAuthError:
continue
if token in [None, '']:
return
return conn
def m_ask_code(self):
code_keyboard = xbmc.Keyboard()
code_keyboard.setHeading(self.gui.m_string(400700))
code_keyboard.setHiddenInput(False)
code_keyboard.doModal()
if code_keyboard.isConfirmed():
return code_keyboard.getText()
else:
raise Exception("2FA Code input was cancelled")
def parse_vk_player_html(self, v_url):
p = re.compile('"url(\d+)":"([^"]+)"')
headers = {'User-Agent': 'Kodi-vk/%s (linux gnu)' % (_VERSION,)}
req = urllib.request.Request(v_url, None, headers)
http_res = urllib.request.urlopen(req)
if http_res.code != 200:
return None
html = http_res.read()
re_res = p.findall(html)
if len(re_res) < 1:
return None
res = {}
for tup in re_res:
res[int(tup[0])] = tup[1].replace('\\', '')
return res
# взято с https://github.com/VkKodi/vkkodi/blob/master/xbmc-vk.svoka.com/vk_auth.py
def auth(email, password, client_id, secret,scope,code):
if code == "0":
try:
url = urlopen("https://oauth.vk.com/token?" + urlencode({
"grant_type": "password",
"client_id": client_id,
"client_secret": secret,
"username": email,
"password": password,
"scope": scope,
"2fa_supported": "1"
}))
out = json.load(url)
except IOError as e:
xbmc.log(e.message)
xbmc.log("===VK 2FA Code requested===")
return "-1"
else:
url = urlopen("https://oauth.vk.com/token?" + urlencode({
"grant_type": "password",
"client_id": client_id,
"client_secret": secret,
"username": email,
"password": password,
"scope": scope,
"2fa_supported": "1",
"code": code
}))
out = json.load(url)
if "access_token" not in out:
xbmc.log(f'kodi-vk auth: {out}')
return out["access_token"]
if __name__ == '__main__':
try:
kvk = KodiVk()
except Exception as e:
import traceback
xbmc.log(traceback.format_exc())
sys.exit()
_DO = {
_DO_HOME: kvk.gui.m_home,
_DO_MAIN_PHOTO: kvk.gui.photos.m_main_photo,
_DO_PHOTO: kvk.gui.photos.m_photo,
_DO_PHOTO_ALBUMS: kvk.gui.photos.m_photo_albums,
_DO_MAIN_VIDEO: kvk.gui.videos.m_main_video,
_DO_VIDEO: kvk.gui.videos.m_video,
_DO_VIDEO_ALBUMS: kvk.gui.videos.m_video_albums,
_DO_PLAY_VIDEO: kvk.gui.videos.m_play_video,
_DO_MAIN_VIDEO_SEARCH: kvk.gui.videos.m_main_video_search,
_DO_VIDEO_SEARCH: kvk.gui.videos.m_video_search,
_DO_GROUPS: kvk.gui.m_groups,
_DO_MAIN_GROUP_SEARCH: kvk.gui.m_main_group_search,
_DO_GROUP_SEARCH: kvk.gui.m_group_search,
_DO_FRIENDS: kvk.gui.m_friends,
_DO_MAIN_USER_SEARCH: kvk.gui.m_main_user_search,
_DO_USER_SEARCH: kvk.gui.m_user_search,
_DO_MEMBERS: kvk.gui.m_members,
_DO_MAIN_FAVE: kvk.gui.faves.m_main_fave,
_DO_FAVE_VIDEO: kvk.gui.faves.m_video,
_DO_FAVE_PHOTO: kvk.gui.faves.m_photo,
_DO_FAVE_USERS: kvk.gui.faves.m_users,
_DO_FAVE_GROUPS: kvk.gui.faves.m_groups,
_DO_LOGOUT: kvk.gui.m_logout
}
_do_method = kvk.params['do']
if _do_method in list(_DO.keys()):
_DO[_do_method]()