From 8a616c666e1d2607a24d35ec6a660ccdbbf63801 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Wed, 22 Apr 2020 02:53:28 +0100 Subject: [PATCH] =?UTF-8?q?Update=20TZlocal=202.0.0b3=20(410a838)=20?= =?UTF-8?q?=E2=86=92=202.1b1=20(dd79171).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change Emby notifier to add unofficial support for Jellyfin. Change Filelist torrent provider. --- CHANGES.md | 9 +- HACKS.txt | 1 - lib/tzlocal/unix.py | 2 +- lib/tzlocal/utils.py | 12 ++- lib/tzlocal/windows_tz.py | 12 ++- sickbeard/notifiers/emby.py | 4 +- sickbeard/providers/filelist.py | 221 ++++++++++++++++++++-------------------- 7 files changed, 138 insertions(+), 123 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f35c59a..9a49489 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,11 @@ -### 0.21.26 (2020-04-13 00:30:00 UTC) +### 0.21.27 (2020-04-22 02:50:00 UTC) + +* Update TZlocal 2.0.0b3 (410a838) to 2.1b1 (dd79171) +* Change Emby notifier to add unofficial support for Jellyfin +* Change Filelist torrent provider + + +### 0.21.26 (2020-04-13 00:30:00 UTC) * Fix AttributeError in anime manager while editing show (part deux) * Fix use lib logger instead of global logger diff --git a/HACKS.txt b/HACKS.txt index 9b331cc..2de46cc 100644 --- a/HACKS.txt +++ b/HACKS.txt @@ -25,4 +25,3 @@ Libs with customisations... /lib/tmdbsimple /lib/tornado /lib/tvdb_api/tvdb_api.py -/lib/tzlocal/unix.py diff --git a/lib/tzlocal/unix.py b/lib/tzlocal/unix.py index 388273c..8574965 100644 --- a/lib/tzlocal/unix.py +++ b/lib/tzlocal/unix.py @@ -138,7 +138,7 @@ def _get_localzone(_root='/'): if os.path.exists(tzpath) and os.path.islink(tzpath): tzpath = os.path.realpath(tzpath) start = tzpath.find("/")+1 - while start is not 0: + while start != 0: tzpath = tzpath[start:] try: return pytz.timezone(tzpath) diff --git a/lib/tzlocal/utils.py b/lib/tzlocal/utils.py index bd9d663..5a67799 100644 --- a/lib/tzlocal/utils.py +++ b/lib/tzlocal/utils.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +import time import datetime +import calendar def get_system_offset(): @@ -11,8 +13,14 @@ def get_system_offset(): To keep compatibility with Windows, we're always importing time module here. """ - import time - if time.daylight and time.localtime().tm_isdst > 0: + + localtime = calendar.timegm(time.localtime()) + gmtime = calendar.timegm(time.gmtime()) + offset = gmtime - localtime + # We could get the localtime and gmtime on either side of a second switch + # so we check that the difference is less than one minute, because nobody + # has that small DST differences. + if abs(offset - time.altzone) < 60: return -time.altzone else: return -time.timezone diff --git a/lib/tzlocal/windows_tz.py b/lib/tzlocal/windows_tz.py index 3d691c8..86ba807 100644 --- a/lib/tzlocal/windows_tz.py +++ b/lib/tzlocal/windows_tz.py @@ -87,6 +87,7 @@ win_tz = {'AUS Central Standard Time': 'Australia/Darwin', 'Pacific Standard Time (Mexico)': 'America/Tijuana', 'Pakistan Standard Time': 'Asia/Karachi', 'Paraguay Standard Time': 'America/Asuncion', + 'Qyzylorda Standard Time': 'Asia/Qyzylorda', 'Romance Standard Time': 'Europe/Paris', 'Russia Time Zone 10': 'Asia/Srednekolymsk', 'Russia Time Zone 11': 'Asia/Kamchatka', @@ -127,6 +128,7 @@ win_tz = {'AUS Central Standard Time': 'Australia/Darwin', 'Ulaanbaatar Standard Time': 'Asia/Ulaanbaatar', 'Venezuela Standard Time': 'America/Caracas', 'Vladivostok Standard Time': 'Asia/Vladivostok', + 'Volgograd Standard Time': 'Europe/Volgograd', 'W. Australia Standard Time': 'Australia/Perth', 'W. Central Africa Standard Time': 'Africa/Lagos', 'W. Europe Standard Time': 'Europe/Berlin', @@ -287,7 +289,7 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time', 'America/Mendoza': 'Argentina Standard Time', 'America/Menominee': 'Central Standard Time', 'America/Merida': 'Central Standard Time (Mexico)', - 'America/Metlakatla': 'Pacific Standard Time', + 'America/Metlakatla': 'Alaskan Standard Time', 'America/Mexico_City': 'Central Standard Time (Mexico)', 'America/Miquelon': 'Saint Pierre Standard Time', 'America/Moncton': 'Atlantic Standard Time', @@ -347,13 +349,13 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time', 'America/Winnipeg': 'Central Standard Time', 'America/Yakutat': 'Alaskan Standard Time', 'America/Yellowknife': 'Mountain Standard Time', - 'Antarctica/Casey': 'W. Australia Standard Time', + 'Antarctica/Casey': 'Singapore Standard Time', 'Antarctica/Davis': 'SE Asia Standard Time', 'Antarctica/DumontDUrville': 'West Pacific Standard Time', 'Antarctica/Macquarie': 'Central Pacific Standard Time', 'Antarctica/Mawson': 'West Asia Standard Time', 'Antarctica/McMurdo': 'New Zealand Standard Time', - 'Antarctica/Palmer': 'Magallanes Standard Time', + 'Antarctica/Palmer': 'SA Eastern Standard Time', 'Antarctica/Rothera': 'SA Eastern Standard Time', 'Antarctica/South_Pole': 'New Zealand Standard Time', 'Antarctica/Syowa': 'E. Africa Standard Time', @@ -424,7 +426,7 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time', 'Asia/Pyongyang': 'North Korea Standard Time', 'Asia/Qatar': 'Arab Standard Time', 'Asia/Qostanay': 'Central Asia Standard Time', - 'Asia/Qyzylorda': 'West Asia Standard Time', + 'Asia/Qyzylorda': 'Qyzylorda Standard Time', 'Asia/Rangoon': 'Myanmar Standard Time', 'Asia/Riyadh': 'Arab Standard Time', 'Asia/Saigon': 'SE Asia Standard Time', @@ -592,7 +594,7 @@ tz_win = {'Africa/Abidjan': 'Greenwich Standard Time', 'Europe/Vatican': 'W. Europe Standard Time', 'Europe/Vienna': 'W. Europe Standard Time', 'Europe/Vilnius': 'FLE Standard Time', - 'Europe/Volgograd': 'Russian Standard Time', + 'Europe/Volgograd': 'Volgograd Standard Time', 'Europe/Warsaw': 'Central European Standard Time', 'Europe/Zagreb': 'Central European Standard Time', 'Europe/Zaporozhye': 'FLE Standard Time', diff --git a/sickbeard/notifiers/emby.py b/sickbeard/notifiers/emby.py index b21452d..b59d647 100644 --- a/sickbeard/notifiers/emby.py +++ b/sickbeard/notifiers/emby.py @@ -53,7 +53,7 @@ class EmbyNotifier(Notifier): timeout=20, hooks=dict(response=self._cb_response), json=True) return self.response and self.response.get('ok') and 200 == self.response.get('status_code') and \ - version <= map_list(lambda x: int(x), response.get('Version', '0.0.0.0').split('.')) + version <= map_list(lambda x: int(x), (response and response.get('Version') or '0.0.0.0').split('.')) def update_library(self, show_obj=None, **kwargs): """ Update library function @@ -140,7 +140,7 @@ class EmbyNotifier(Notifier): cs.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) cs.settimeout(10) result, sock_issue = '', None - for server in ('EmbyServer', 'MediaBrowserServer'): + for server in ('EmbyServer', 'MediaBrowserServer', 'JellyfinServer'): bufr = 'who is %s?' % server try: assert len(bufr) == cs.sendto(bufr, ('255.255.255.255', mb_listen_port)), \ diff --git a/sickbeard/providers/filelist.py b/sickbeard/providers/filelist.py index 6db79c7..2dbf720 100644 --- a/sickbeard/providers/filelist.py +++ b/sickbeard/providers/filelist.py @@ -1,111 +1,110 @@ -# 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 . - -import re -import traceback - -from . import generic -from .. import logger -from ..helpers import try_int -from bs4_parser import BS4Parser - -from _23 import unidecode -from six import iteritems - - -class FLProvider(generic.TorrentProvider): - - def __init__(self): - generic.TorrentProvider.__init__(self, 'FileList') - - self.url_base = 'https://filelist.ro/' - self.urls = {'config_provider_home_uri': self.url_base, - 'login_action': self.url_base + 'login.php', - 'search': self.url_base + 'browse.php?search=%s&%s&incldead=0'} - - self.categories = {'Season': [14], 'Episode': [13, 21, 23], 'anime': [24]} - self.categories['Cache'] = self.categories['Season'] + self.categories['Episode'] - - self.url = self.urls['config_provider_home_uri'] - - self.username, self.password, self.minseed, self.minleech = 4 * [None] - - def _authorised(self, **kwargs): - - return super(FLProvider, self)._authorised() - - def _search_provider(self, search_params, **kwargs): - - results = [] - if not self._authorised(): - return results - - items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []} - - rc = dict([(k, re.compile('(?i)' + v)) for (k, v) in iteritems({'info': 'details', 'get': 'download'})]) - for mode in search_params: - rc['cats'] = re.compile('(?i)cat=(?:%s)' % self._categories_string(mode, template='', delimiter='|')) - for search_string in search_params[mode]: - search_string = unidecode(search_string) - - html = self.get_url(self.urls['search'] % ('+'.join(search_string.split()), - self._categories_string(mode, template='cats[]=%s'))) - if self.should_skip(): - return results - - cnt = len(items[mode]) - try: - if not html or self._has_no_results(html): - raise generic.HaltParseException - - with BS4Parser(html) as soup: - tbl_rows = soup.find_all('div', 'torrentrow') - - if not len(tbl_rows): - raise generic.HaltParseException - - for tr in tbl_rows: - cells = tr.select('span[style*="cell"]') - if 6 > len(cells): - continue - try: - seeders, leechers, size = [try_int(n, n) for n in [ - cells[x].get_text().strip() for x in (-3, -2, -5)]] - if not tr.find('a', href=rc['cats']) or self._reject_item(seeders, leechers): - continue - - title = tr.find('a', href=rc['info']).get_text().strip() - download_url = self._link(tr.find('a', href=rc['get'])['href']) - except (AttributeError, TypeError, ValueError, IndexError): - continue - - if title and download_url: - items[mode].append((title, download_url, seeders, self._bytesizer(size))) - - except generic.HaltParseException: - pass - except (BaseException, Exception): - logger.log(u'Failed to parse. Traceback: %s' % traceback.format_exc(), logger.ERROR) - - self._log_search(mode, len(items[mode]) - cnt, self.session.response.get('url')) - - results = self._sort_seeding(mode, results + items[mode]) - - return results - - -provider = FLProvider() +# 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 . + +import re +import traceback + +from . import generic +from .. import logger +from ..helpers import try_int +from bs4_parser import BS4Parser + +from _23 import unidecode +from six import iteritems + + +class FLProvider(generic.TorrentProvider): + + def __init__(self): + generic.TorrentProvider.__init__(self, 'FileList') + + self.url_base = 'https://filelist.io/' + self.urls = {'config_provider_home_uri': self.url_base, + 'login_action': self.url_base + 'login.php', + 'search': self.url_base + 'browse.php?search=%s&%s&incldead=0'} + + self.categories = dict(shows=[13, 21, 23, 27], anime=[24]) + + self.url = self.urls['config_provider_home_uri'] + + self.username, self.password, self.minseed, self.minleech = 4 * [None] + + def _authorised(self, **kwargs): + + return super(FLProvider, self)._authorised() + + def _search_provider(self, search_params, **kwargs): + + results = [] + if not self._authorised(): + return results + + items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []} + + rc = dict([(k, re.compile('(?i)' + v)) for (k, v) in iteritems({'info': 'details', 'get': 'download'})]) + for mode in search_params: + rc['cats'] = re.compile('(?i)cat=(?:%s)' % self._categories_string(mode, template='', delimiter='|')) + for search_string in search_params[mode]: + search_string = unidecode(search_string) + + html = self.get_url(self.urls['search'] % ('+'.join(search_string.split()), + self._categories_string(mode, template='cats[]=%s'))) + if self.should_skip(): + return results + + cnt = len(items[mode]) + try: + if not html or self._has_no_results(html): + raise generic.HaltParseException + + with BS4Parser(html) as soup: + tbl_rows = soup.find_all('div', 'torrentrow') + + if not len(tbl_rows): + raise generic.HaltParseException + + for tr in tbl_rows: + cells = tr.select('span[style*="cell"]') + if 6 > len(cells): + continue + try: + seeders, leechers, size = [try_int(n, n) for n in [ + cells[x].get_text().strip() for x in (-3, -2, -5)]] + if not tr.find('a', href=rc['cats']) or self._reject_item(seeders, leechers): + continue + + title = tr.find('a', href=rc['info']).get_text().strip() + download_url = self._link(tr.find('a', href=rc['get'])['href']) + except (AttributeError, TypeError, ValueError, IndexError): + continue + + if title and download_url: + items[mode].append((title, download_url, seeders, self._bytesizer(size))) + + except generic.HaltParseException: + pass + except (BaseException, Exception): + logger.log(u'Failed to parse. Traceback: %s' % traceback.format_exc(), logger.ERROR) + + self._log_search(mode, len(items[mode]) - cnt, self.session.response.get('url')) + + results = self._sort_seeding(mode, results + items[mode]) + + return results + + +provider = FLProvider()