Skip to content

Commit f6b812b

Browse files
author
Roman Miroshnychenko
committed
[script.service.next-episode] 1.0.0
1 parent 493b8fc commit f6b812b

File tree

16 files changed

+1699
-0
lines changed

16 files changed

+1699
-0
lines changed

script.service.next-episode/LICENSE.txt

Lines changed: 621 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<addon id="script.service.next-episode"
3+
name="next-episode.net"
4+
version="1.0.0"
5+
provider-name="next-episode.net">
6+
<requires>
7+
<import addon="xbmc.python" version="2.19.0" />
8+
<import addon="script.module.pyxbmct" version="1.1.0" />
9+
<import addon="script.module.requests" version="2.9.0" />
10+
</requires>
11+
<extension point="xbmc.service" library="service.py" start="[login|startup]" />
12+
<extension point="xbmc.python.script" library="main.py">
13+
<provides>executable</provides>
14+
</extension>
15+
<extension point="xbmc.addon.metadata">
16+
<platform>all</platform>
17+
<summary lang="en">The next-episode.net service for Kodi.</summary>
18+
<description lang="en">The next-episode.net service for Kodi allows to add your movies and TV episodes from
19+
media library to your inventory on next-episode.net. The service also monitors video playback and updates
20+
'watched' status of your movies and episodes on next-episode.net.</description>
21+
<summary lang="ru">Служба next-episode.net для Kodi.</summary>
22+
<description lang="ru">Служба next-episode.net для Kodi позволяет добавлять фильмы и сериалы из меедиатеки
23+
в вас список на next-episode.net. Служба также отслеживает проигрывание видео и обновляет состояние
24+
"просмотрено" на next-episode.net.</description>
25+
<summary lang="uk">Служба next-episode.net для Kodi.</summary>
26+
<description lang="uk">Служба next-episode.net для Kodi allows дозволяє додавати фільми та серіали з медіатеки
27+
на next-episode.net. Служба також відстежує перегляд відео та оновлює статус "переглянуто" на next-episode.net.</description>
28+
<license>GNU GPL v.3</license>
29+
<forum></forum>
30+
<website>http://next-episode.net/</website>
31+
<email>romanvm@yandex.ua</email>
32+
<source>https://github.com/santah/next-episode-kodi</source>
33+
</extension>
34+
</addon>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1.0.0:
2+
- Initial public release
16.1 KB
Loading
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# coding: utf-8
2+
# Created on: 15.03.2016
3+
# Author: Roman Miroshnychenko aka Roman V.M. (romanvm@yandex.ua)
4+
# License: GPL v. 3 <http://www.gnu.org/licenses/gpl-3.0.en.html>
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# coding: utf-8
2+
# Created on: 17.03.2016
3+
# Author: Roman Miroshnychenko aka Roman V.M. (romanvm@yandex.ua)
4+
# License: GPL v. 3 <http://www.gnu.org/licenses/gpl-3.0.en.html>
5+
6+
import os
7+
import sys
8+
from copy import deepcopy
9+
import xbmc
10+
from xbmcaddon import Addon
11+
from xbmcgui import Dialog
12+
import pyxbmct
13+
from medialibrary import (get_movies, get_tvshows, get_episodes, get_recent_movies,
14+
get_recent_episodes, get_tvdb_id, NoDataError)
15+
from nextepisode import (prepare_movies_list, prepare_episodes_list, update_data,
16+
get_password_hash, LoginError, DataUpdateError)
17+
from gui import NextEpDialog, ui_string
18+
19+
addon = Addon('script.service.next-episode')
20+
icon = os.path.join(addon.getAddonInfo('path'), 'icon.png')
21+
dialog = Dialog()
22+
23+
24+
class LoginDialog(NextEpDialog):
25+
"""
26+
Enter login/password dialog
27+
"""
28+
def __init__(self, title='', username=''):
29+
super(LoginDialog, self).__init__(450, 210, 3, 2, title)
30+
self.username = username
31+
self._username_field.setText(username)
32+
self.password = ''
33+
self.is_cancelled = True
34+
35+
def _set_controls(self):
36+
login_label = pyxbmct.Label(ui_string(32003))
37+
self.placeControl(login_label, 0, 0)
38+
password_label = pyxbmct.Label(ui_string(32004))
39+
self.placeControl(password_label, 1, 0)
40+
self._username_field = pyxbmct.Edit('')
41+
self.placeControl(self._username_field, 0, 1)
42+
self._password_field = pyxbmct.Edit('', isPassword=True)
43+
self.placeControl(self._password_field, 1, 1)
44+
self._ok_btn = pyxbmct.Button(ui_string(32005))
45+
self.placeControl(self._ok_btn, 2, 1)
46+
self._cancel_btn = pyxbmct.Button(ui_string(32006))
47+
self.placeControl(self._cancel_btn, 2, 0)
48+
49+
def _set_connections(self):
50+
super(LoginDialog, self)._set_connections()
51+
self.connect(self._ok_btn, self._ok)
52+
self.connect(self._cancel_btn, self.close)
53+
54+
def _set_navigation(self):
55+
self._username_field.controlUp(self._ok_btn)
56+
self._username_field.controlDown(self._password_field)
57+
self._password_field.controlUp(self._username_field)
58+
self._password_field.controlDown(self._ok_btn)
59+
self._ok_btn.setNavigation(self._password_field, self._username_field, self._cancel_btn, self._cancel_btn)
60+
self._cancel_btn.setNavigation(self._password_field, self._username_field, self._ok_btn, self._ok_btn)
61+
self.setFocus(self._username_field)
62+
63+
def _ok(self):
64+
self.is_cancelled = False
65+
self.username = self._username_field.getText()
66+
self.password = self._password_field.getText()
67+
self.close()
68+
69+
def close(self):
70+
if self.is_cancelled:
71+
self.username = self.password = ''
72+
super(LoginDialog, self).close()
73+
74+
75+
def send_data(data):
76+
"""
77+
Send data to next-episode.net and process possible errors
78+
79+
:param data: data to be sent
80+
:type data: dict
81+
"""
82+
try:
83+
update_data(data)
84+
except LoginError:
85+
xbmc.log('next-episode.net: login failed! Re-enter your username and password.', xbmc.LOGERROR)
86+
dialog.notification('next-episode.net', ui_string(32007), icon='error')
87+
except DataUpdateError as ex:
88+
xbmc.log('next-episode.net: {0}'.format(ex), xbmc.LOGERROR)
89+
if addon.getSetting('disable_error_dialogs') != 'true':
90+
dialog.ok('next-epsisode.net',
91+
ui_string(32020),
92+
ui_string(32021).format(ex.failed_movies),
93+
ui_string(32022).format(ex.failed_shows))
94+
else:
95+
dialog.notification('next-episode.net', ui_string(32008), icon='error')
96+
else:
97+
dialog.notification('next-episode.net', ui_string(32009), icon=icon, time=2000, sound=False)
98+
99+
100+
def log_data_sent(data):
101+
"""
102+
Log data sent to next-episode.net with sanitized username/hash
103+
104+
:param data: data to be sent
105+
:type data: dict
106+
"""
107+
logged_data = deepcopy(data)
108+
logged_data['user']['username'] = logged_data['user']['hash'] = '*****'
109+
xbmc.log('next-episode: data sent:\n{0}'.format(logged_data), xbmc.LOGNOTICE)
110+
111+
112+
def sync_library():
113+
"""
114+
Synchronize Kodi video library with next-episode.net
115+
"""
116+
xbmc.executebuiltin('ActivateWindow(10138)') # Busy dialog on
117+
data = {
118+
'user': {
119+
'username': addon.getSetting('username'),
120+
'hash': addon.getSetting('hash')
121+
}}
122+
try:
123+
data['movies'] = prepare_movies_list(get_movies())
124+
except NoDataError:
125+
pass
126+
try:
127+
tvshows = get_tvshows()
128+
except NoDataError:
129+
pass
130+
else:
131+
episodes = []
132+
for show in tvshows:
133+
try:
134+
episodes += prepare_episodes_list(get_episodes(show['tvshowid']))
135+
except NoDataError:
136+
continue
137+
data['tvshows'] = episodes
138+
if 'movies' in data or 'tvshows' in data:
139+
log_data_sent(data)
140+
send_data(data)
141+
else:
142+
xbmc.log('next-episode: Kodi video library has no movies and TV episodes.', xbmc.LOGWARNING)
143+
xbmc.executebuiltin('Dialog.Close(10138)') # Busy dialog off
144+
145+
146+
def sync_new_items():
147+
"""
148+
Synchronize new video items with next-episode.net
149+
"""
150+
data = {
151+
'user': {
152+
'username': addon.getSetting('username'),
153+
'hash': addon.getSetting('hash')
154+
}}
155+
try:
156+
data['movies'] = prepare_movies_list(get_recent_movies())
157+
except NoDataError:
158+
pass
159+
try:
160+
data['tvshows'] = prepare_episodes_list(get_recent_episodes())
161+
except NoDataError:
162+
pass
163+
if 'movies' in data or 'episodes' in data:
164+
log_data_sent(data)
165+
send_data(data)
166+
else:
167+
xbmc.log('next-episode.net: Kodi video library has no recent movies and episodes.', xbmc.LOGWARNING)
168+
169+
170+
def update_single_item(item):
171+
"""
172+
Synchronize single item (movie or episode) with next-episode-net
173+
174+
:param item: video item
175+
:type item: dict
176+
"""
177+
data = {
178+
'user': {
179+
'username': addon.getSetting('username'),
180+
'hash': addon.getSetting('hash')
181+
}}
182+
if item['type'] == 'episode':
183+
data['tvshows'] = [{
184+
'thetvdb_id': get_tvdb_id(item['tvshowid']),
185+
'season': str(item['season']),
186+
'episode': str(item['episode']),
187+
'watched': '1' if item['playcount'] else '0'
188+
}]
189+
elif item['type'] == 'movie':
190+
data['movies'] = [{
191+
'imdb_id': item['imdbnumber'],
192+
'watched': '1' if item['playcount'] else '0'
193+
}]
194+
log_data_sent(data)
195+
send_data(data)
196+
197+
198+
def login():
199+
"""
200+
Login to next-episode.net
201+
202+
:return: ``True`` on successful login, ``False`` if login is failed or cancelled
203+
:rtype: bool
204+
"""
205+
login_dialog = LoginDialog(ui_string(32001), username=addon.getSetting('username'))
206+
login_dialog.doModal()
207+
result = False
208+
if not login_dialog.is_cancelled:
209+
xbmc.executebuiltin('ActivateWindow(10138)')
210+
username = login_dialog.username
211+
password = login_dialog.password
212+
try:
213+
hash_ = get_password_hash(username, password)
214+
except LoginError:
215+
dialog.ok('next-episode.net', ui_string(32007), ui_string(32010))
216+
xbmc.log('next-episode.net: login failed!', xbmc.LOGERROR)
217+
else:
218+
addon.setSetting('username', username)
219+
addon.setSetting('hash', hash_)
220+
xbmc.log('next-episode.net: successful login', xbmc.LOGNOTICE)
221+
dialog.notification('next-episode.net', ui_string(32011), time=3000, sound=False)
222+
result = True
223+
xbmc.executebuiltin('Dialog.Close(10138)')
224+
del login_dialog
225+
return result
226+
227+
228+
if __name__ == '__main__':
229+
if sys.argv[1] == 'sync_library':
230+
sync_library()
231+
elif sys.argv[1] == 'login':
232+
login()
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# coding: utf-8
2+
# Created on: 15.03.2016
3+
# Author: Roman Miroshnychenko aka Roman V.M. (romanvm@yandex.ua)
4+
# License: GPL v. 3 <http://www.gnu.org/licenses/gpl-3.0.en.html>
5+
6+
from abc import ABCMeta, abstractmethod
7+
from xbmcaddon import Addon
8+
from xbmcgui import ACTION_NAV_BACK
9+
import pyxbmct
10+
11+
addon = Addon('script.service.next-episode')
12+
13+
14+
def ui_string(id_):
15+
"""
16+
Get localized UI string
17+
18+
:param id_: string ID
19+
:type id_: int
20+
:return: localized string
21+
:rtype: unicode
22+
"""
23+
return addon.getLocalizedString(id_).encode('utf-8')
24+
25+
26+
class NextEpDialog(pyxbmct.AddonDialogWindow):
27+
"""
28+
Base class for addon dialogs
29+
"""
30+
__metaclass__ = ABCMeta
31+
32+
def __init__(self, width, height, rows, columns, title=''):
33+
super(NextEpDialog, self).__init__(title)
34+
self.setGeometry(width, height, rows, columns)
35+
self._set_controls()
36+
self._set_connections()
37+
self._set_navigation()
38+
39+
@abstractmethod
40+
def _set_controls(self):
41+
pass
42+
43+
@abstractmethod
44+
def _set_connections(self):
45+
self.connect(ACTION_NAV_BACK, self.close)
46+
47+
@abstractmethod
48+
def _set_navigation(self):
49+
pass
50+
51+
def setAnimation(self, control):
52+
control.setAnimations([('WindowOpen', 'effect=fade start=0 end=100 time=250'),
53+
('WindowClose', 'effect=fade start=100 end=0 time=250')])

0 commit comments

Comments
 (0)