Browse Source

Merge branch 'master' into develop

pull/1289/head
JackDandy 5 years ago
parent
commit
b436f006f0
  1. 13
      CHANGES.md
  2. 2
      gui/slick/interfaces/default/config.tmpl
  3. 8
      gui/slick/interfaces/default/config_general.tmpl
  4. 20
      lib/socks/__init__.py
  5. 4
      sickbeard/__init__.py
  6. 20
      sickbeard/clients/kodi/service.sickgear.watchedstate.updater/addon.xml
  7. 18
      sickbeard/clients/kodi/service.sickgear.watchedstate.updater/changelog.txt
  8. 55
      sickbeard/clients/kodi/service.sickgear.watchedstate.updater/service.py
  9. 2
      sickbeard/image_cache.py
  10. 6
      sickbeard/processTV.py
  11. 44
      sickbeard/webserve.py

13
CHANGES.md

@ -22,6 +22,19 @@
### 0.21.6 (2020-01-21 22:30:00 UTC)
* Fix Kodi service addon + bump to 1.0.7 (select "Check for updates" on menu of "SickGear Add-on repository")
* Change Kodi Add-on/"What's new" list order to be latest version info at top
* Add output to SG log when a new Kodi Add-on version is available for upgrade
* Fix a rare post processing issue that created `dictionary changed size` error
* Fix ensure PySocks is available for Requests/urllib3
* Fix fanart image update issue
* Change add examples to config/general/advanced/"Proxy host" that show scheme and authentication usage
* Change add warning that Kodi Add-on requires IP to setting config/general/"Allow IP use for connections"
* Change About page version string
### 0.21.5 (2020-01-15 02:25:00 UTC)
* Update Fuzzywuzzy 0.17.0 (778162c) to 0.17.0 (0cfb2c8)

2
gui/slick/interfaces/default/config.tmpl

@ -22,7 +22,7 @@
<tr>
<td class="infoTableHeader">Version: </td>
<td class="infoTableCell">
BRANCH: #echo $sg_str('BRANCH') or 'UNKNOWN'# / COMMIT: #echo ($sg_str('CUR_COMMIT_HASH')[0:7] or 'UNKNOWN') + ('', ' @ ')[bool($version)]#$version<br />
BRANCH: #echo $sg_str('BRANCH') or 'UNKNOWN'# @ py#echo '.'.join(['%s' % x for x in sys.version_info[0:3]])# / COMMIT: #echo ($sg_str('CUR_COMMIT_HASH')[0:7] or 'UNKNOWN') + ('', ' @ ')[bool($version)]#$version<br />
<em class="red-text">This is BETA software</em><br />
#if not $sg_var('VERSION_NOTIFY') and not $sg_var('EXT_UPDATES'):
You don't have version checking turned on, see "Check software updates" in Config > General.

8
gui/slick/interfaces/default/config_general.tmpl

@ -619,8 +619,9 @@
<label for="allow_anyip">
<span class="component-title">Allow IP use for connections</span>
<span class="component-desc">
<input type="checkbox" name="allow_anyip""#echo ('', $checked)[$sg_var('ALLOW_ANYIP')]#>
<p>disable to only accept "Allowed browser hostnames" for connections</p>
<input type="checkbox" name="allow_anyip" id="allow_anyip"#echo ('', $checked)[$sg_var('ALLOW_ANYIP')]#>
<p>disable to only accept "Allowed browser hostnames" for connections<br>
Warning: Kodi Add-on requires IP and will correctly fail with this disabled</p>
</span>
</label>
</div>
@ -744,7 +745,8 @@
<span class="component-desc">
<input type="text" name="proxy_setting" value="$sg_str('PROXY_SETTING')" class="form-control input-sm input300">
<p>blank to disable</p>
<div class="clear-left"><p>proxy address for connecting to providers (use 'PAC:Url' for PAC support)</p></div>
<div class="clear-left"><p>proxy address for connecting to providers (use 'PAC:Url' for PAC support)<br>
e.g. socks5://host:port, socks5://user:pass@host:port, socks4a://user:pass@host:port</p></div>
</span>
</label>
</div>

20
lib/socks/__init__.py

@ -0,0 +1,20 @@
# coding=utf-8
#
# This file is part of SickGear.
#
# SickGear is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickGear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------------
# Note: Ensures PySocks is imported to be used by libs Requests/urllib3
from .socks import *

4
sickbeard/__init__.py

@ -565,6 +565,8 @@ CACHE_IMAGE_URL_LIST = classes.ImageUrlList()
__INITIALIZED__ = False
__INIT_STAGE__ = 0
MEMCACHE = {}
def get_backlog_cycle_time():
cycletime = RECENTSEARCH_FREQUENCY * 2 + 7
@ -1366,7 +1368,7 @@ def init_stage_1(console_logging):
def init_stage_2():
# Misc
global __INITIALIZED__, RECENTSEARCH_STARTUP
global __INITIALIZED__, MEMCACHE, RECENTSEARCH_STARTUP
# Schedulers
# global traktCheckerScheduler
global recentSearchScheduler, backlogSearchScheduler, showUpdateScheduler, \

20
sickbeard/clients/kodi/service.sickgear.watchedstate.updater/addon.xml

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="service.sickgear.watchedstate.updater" name="SickGear Watched State Updater" version="1.0.4" provider-name="SickGear">
<addon id="service.sickgear.watchedstate.updater" name="SickGear Watched State Updater" version="%(ADDON_VERSION)s" provider-name="SickGear">
<requires>
<import addon="xbmc.python" version="2.19.0" />
<import addon="xbmc.json" version="6.20.0" />
@ -24,14 +24,20 @@
<assets>
<icon>icon.png</icon>
</assets>
<news>[B]1.0.0[/B] (2017-10-04)
- Initial release
[B]1.0.2[/B] (2017-11-15)
- Devel release for an SG API change
<news>[B]1.0.7[/B] (2020-01-21)
- Public release
[B]1.0.6[/B] (2020-01-18)
- Public test release
[B]1.0.5[/B] (2020-01-16)
- Fix service code for py3
[B]1.0.4[/B] (2019-08-13)
- Tidy up service code
[B]1.0.3[/B] (2018-02-28)
- Add episodeid to payload
[B]1.0.4[/B] (2019-08-13)
- Tidy up Service code
[B]1.0.2[/B] (2017-11-15)
- Devel release for an SG API change
[B]1.0.0[/B] (2017-10-04)
- Initial release
</news>
</extension>
</addon>

18
sickbeard/clients/kodi/service.sickgear.watchedstate.updater/changelog.txt

@ -1,8 +1,14 @@
[B]1.0.0[/B] (2017-10-04)
- Initial release
[B]1.0.2[/B] (2017-11-15)
- Devel release for an SG API change
[B]1.0.3[/B] (2018-02-28)
- Add episodeid to payload
[B]1.0.7[/B] (2020-01-21)
- Public release
[B]1.0.6[/B] (2020-01-18)
- Public test release
[B]1.0.5[/B] (2020-01-16)
- Fix service code for py3
[B]1.0.4[/B] (2019-08-13)
- Service code cleanup
[B]1.0.3[/B] (2018-02-28)
- Add episodeid to payload
[B]1.0.2[/B] (2017-11-15)
- Devel release for an SG API change
[B]1.0.0[/B] (2017-10-04)
- Initial release

55
sickbeard/clients/kodi/service.sickgear.watchedstate.updater/service.py

@ -36,14 +36,19 @@ import xbmcgui
# noinspection PyUnresolvedReferences
import xbmcvfs
ADDON_VERSION = '1.0.7'
PY2 = 2 == sys.version_info[0]
if PY2:
# noinspection PyCompatibility,PyUnresolvedReferences
from urllib2 import Request, urlopen, URLError, urlencode
from urllib2 import Request, urlopen, URLError
# noinspection PyUnresolvedReferences
from urllib import urlencode
# noinspection PyCompatibility,PyUnresolvedReferences
string_types = (basestring,)
binary_type = str
def iterkeys(d, **kw):
# noinspection PyCompatibility
@ -57,8 +62,12 @@ if PY2:
# noinspection PyCompatibility
return d.iteritems(**kw)
def iterlists(d, **kw):
return d.iterlists(**kw)
# noinspection PyUnusedLocal
def decode_bytes(d, **kw):
if not isinstance(d, binary_type):
return bytes(d)
return d
else:
# noinspection PyCompatibility,PyUnresolvedReferences
from urllib.error import URLError
@ -68,6 +77,7 @@ else:
from urllib.request import Request, urlopen
string_types = (str,)
binary_type = bytes
def iterkeys(d, **kw):
return iter(d.keys(**kw))
@ -78,8 +88,19 @@ else:
def iteritems(d, **kw):
return iter(d.items(**kw))
def iterlists(d, **kw):
return iter(d.lists(**kw))
def decode_bytes(d, encoding='utf-8', errors='replace'):
if not isinstance(d, binary_type):
# noinspection PyArgumentList
return bytes(d, encoding=encoding, errors=errors)
return d
def decode_str(s, encoding='utf-8', errors=None):
if isinstance(s, binary_type):
if None is errors:
return s.decode(encoding)
return s.decode(encoding, errors)
return s
class SickGearWatchedStateUpdater(object):
@ -128,13 +149,20 @@ class SickGearWatchedStateUpdater(object):
self.log('Started')
self.notify('Started in background')
cache_pkg = 'special://home/addons/packages/service.sickgear.watchedstate.updater-%s.zip' % ADDON_VERSION
if xbmcvfs.exists(cache_pkg):
try:
xbmcvfs.delete(cache_pkg)
except (BaseException, Exception):
pass
self.kodi_events = xbmc.Monitor()
sock_buffer, depth, methods, method = '', 0, {'VideoLibrary.OnUpdate': self.video_library_on_update}, None
# socks listener parsing Kodi json output into action to perform
while not self.kodi_events.abortRequested():
chunk = self.sock_kodi.recv(1)
chunk = decode_str(self.sock_kodi.recv(1))
sock_buffer += chunk
if chunk in '{}':
if '{' == chunk:
@ -229,7 +257,7 @@ class SickGearWatchedStateUpdater(object):
def video_library_on_update(self, json_msg):
"""
Actions to perform for: Kodi Notifications / VideoLibrary/ VideoLibrary.OnUpdate
Actions to perform for: Kodi Notifications / VideoLibrary / VideoLibrary.OnUpdate
invoked in Kodi when: A video item has been updated
source: http://kodi.wiki/view/JSON-RPC_API/v8#VideoLibrary.OnUpdate
@ -270,9 +298,9 @@ class SickGearWatchedStateUpdater(object):
payload_json = self.payload_prep(dict(media_id=media_id, path_file=path_file, played=play_count, label=profile))
if payload_json:
payload = urlencode(dict(payload=payload_json))
payload = urlencode(dict(payload=payload_json, version=ADDON_VERSION))
try:
rq = Request(url, data=payload)
rq = Request(url, data=decode_bytes(payload))
r = urlopen(rq)
response = json.load(r)
r.close()
@ -282,7 +310,7 @@ class SickGearWatchedStateUpdater(object):
msg = 'Success, watched state updated'
else:
msg = 'Success, %s/%s watched stated updated' % (
len([v for v in itervalues(response) if v]), len(itervalues(response)))
len([None for v in itervalues(response) if v]), len([None for _ in itervalues(response)]))
self.log(msg)
self.notify(msg, error=False)
else:
@ -362,18 +390,19 @@ class SickGearWatchedStateUpdater(object):
# setting esallinterfaces: allow remote control by programs on other systems
settings = [dict(esenabled=True), dict(esallinterfaces=True)]
for setting in settings:
name = next(iterkeys(setting))
if not self.kodi_request(dict(
method='Settings.SetSettingValue',
params=dict(setting='services.%s' % iterkeys(setting)[0], value=itervalues(setting)[0])
params=dict(setting='services.%s' % name, value=next(itervalues(setting)))
)).get('result', {}):
settings[setting] = self.kodi_request(dict(
method='Settings.GetSettingValue',
params=dict(setting='services.%s' % iterkeys(setting)[0])
params=dict(setting='services.%s' % name)
)).get('result', {}).get('value')
except (BaseException, Exception):
return
setting_states = [itervalues(setting)[0] for setting in settings]
setting_states = [next(itervalues(setting)) for setting in settings]
if not all(setting_states):
if not (any(setting_states)):
msg = 'Please enable *all* Kodi settings to allow remote control by programs...'

2
sickbeard/image_cache.py

@ -213,7 +213,7 @@ class ImageCache(object):
:return: true if a cached fanart exists for the given tvid prodid
:rtype: bool
"""
return self.has_file(self.fanart_path(tvid, prodid).replace('fanart.jpg', '*.001.*.fanart.jpg'))
return self.has_file(self.fanart_path(tvid, prodid).replace('fanart.jpg', '001.*.fanart.jpg'))
def has_poster_thumbnail(self, tvid, prodid):
# type: (int, int) -> bool

6
sickbeard/processTV.py

@ -43,7 +43,7 @@ from .history import reset_status
from .name_parser.parser import InvalidNameException, InvalidShowException, NameParser
from _23 import filter_list, filter_iter, list_values, map_iter
from six import iterkeys, string_types, PY2, text_type
from six import iteritems, iterkeys, string_types, PY2, text_type
from sg_helpers import long_path
import lib.rarfile.rarfile as rarfile
@ -566,9 +566,7 @@ class ProcessTVShow(object):
init_history_cnt = len(archive_history)
for archive in archive_history:
if not ek.ek(os.path.isfile, archive):
del archive_history[archive]
archive_history = {k_arc: v for k_arc, v in iteritems(archive_history) if ek.ek(os.path.isfile, k_arc)}
unused_files = list(set([ek.ek(os.path.join, path, x) for x in archives]) - set(iterkeys(archive_history)))
archives = [ek.ek(os.path.basename, x) for x in unused_files]

44
sickbeard/webserve.py

@ -583,11 +583,30 @@ class RepoHandler(BaseStaticFileHandler):
return re.findall(r'(?si)addon\sid="([^"]+)[^>]+version="([^"]+)',
self.get_watchedstate_updater_addon_xml())[0]
@staticmethod
def get_watchedstate_updater_addon_xml():
def get_watchedstate_updater_addon_xml(self):
mem_key = 'kodi_xml'
if SGDatetime.now().totimestamp(default=0) < sickbeard.MEMCACHE.get(mem_key, {}).get('last_update', 0):
return sickbeard.MEMCACHE.get(mem_key).get('data')
with io.open(ek.ek(os.path.join, sickbeard.PROG_DIR, 'sickbeard', 'clients',
'kodi', 'service.sickgear.watchedstate.updater', 'addon.xml'), 'r') as fh:
return fh.read().strip()
xml = fh.read().strip() % dict(ADDON_VERSION=self.get_addon_version())
sickbeard.MEMCACHE[mem_key] = dict(last_update=30 + SGDatetime.now().totimestamp(default=0), data=xml)
return xml
@staticmethod
def get_addon_version():
mem_key = 'kodi_ver'
if SGDatetime.now().totimestamp(default=0) < sickbeard.MEMCACHE.get(mem_key, {}).get('last_update', 0):
return sickbeard.MEMCACHE.get(mem_key).get('data')
with io.open(ek.ek(os.path.join, sickbeard.PROG_DIR, 'sickbeard', 'clients',
'kodi', 'service.sickgear.watchedstate.updater', 'service.py'), 'r') as fh:
version = re.findall(r'ADDON_VERSION\s*?=\s*?\'([^\']+)', fh.read())[0]
sickbeard.MEMCACHE[mem_key] = dict(last_update=30 + SGDatetime.now().totimestamp(default=0), data=version)
return version
def render_kodi_repo_addon_xml(self):
t = PageTemplate(web_handler=self, file='repo_kodi_addon.tmpl')
@ -633,8 +652,7 @@ class RepoHandler(BaseStaticFileHandler):
bfr.close()
return zip_data
@staticmethod
def kodi_service_sickgear_watchedstate_updater_zip():
def kodi_service_sickgear_watchedstate_updater_zip(self):
bfr = io.BytesIO()
basepath = ek.ek(os.path.join, sickbeard.PROG_DIR, 'sickbeard', 'clients', 'kodi')
@ -650,8 +668,12 @@ class RepoHandler(BaseStaticFileHandler):
for f in helpers.scantree(zip_path):
if f.is_file(follow_symlinks=False) and f.name[-4:] not in '.xcf':
try:
with io.open(f.path, 'rb') as fh:
infile = fh.read()
infile = None
if 'service.sickgear.watchedstate.updater' in f.path and f.path.endswith('addon.xml'):
infile = self.get_watchedstate_updater_addon_xml()
if not infile:
with io.open(f.path, 'rb') as fh:
infile = fh.read()
with zipfile.ZipFile(bfr, 'a') as zh:
zh.writestr(ek.ek(os.path.relpath, f.path, basepath), infile, zipfile.ZIP_DEFLATED)
@ -680,7 +702,7 @@ class NoXSRFHandler(RouteHandler):
self.route_method(route, limit_route=False, xsrf_filter=False)
@staticmethod
def update_watched_state_kodi(payload=None, as_json=True):
def update_watched_state_kodi(payload=None, as_json=True, **kwargs):
data = {}
try:
data = json.loads(payload)
@ -713,6 +735,12 @@ class NoXSRFHandler(RouteHandler):
logger.log('Folder mappings used, the first of %s is [%s] in Kodi is [%s] in SickGear' %
(mapped, mapping[0], mapping[1]))
req_version = tuple([int(x) for x in kwargs.get('version', '0.0.0').split('.')])
this_version = RepoHandler.get_addon_version()
if not kwargs or (req_version < tuple([int(x) for x in this_version.split('.')])):
logger.log('Kodi Add-on update available. To upgrade to version %s; '
'select "Check for updates" on menu of "SickGear Add-on repository"' % this_version)
return MainHandler.update_watched_state(data, as_json)

Loading…
Cancel
Save