diff --git a/.gitignore b/.gitignore index e10c5d6..7d6ffc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ +/.vscode +__pycache__ .DS_Store update_repository.py /service.betaseries.com/lastdate.tmp /.vs *.sh + diff --git a/addons.xml b/addons.xml index 484c8f0..cebdb2c 100644 --- a/addons.xml +++ b/addons.xml @@ -1,79 +1,133 @@ - - - - - - - - all - tvsubtitles.net subtitles service - Service de sous-titres tvsubtitles.net - Search and download subtitles from tvsubtitles.net - Rechercher et télécharger des sous-titres depuis tvsubtitles.net - - + + + + + + + + all + tvsubtitles.net subtitles service + Service de sous-titres tvsubtitles.net + Search and download subtitles from tvsubtitles.net + Rechercher et télécharger des sous-titres depuis tvsubtitles.net + + - - - - - - - - all - BetaSeries subtitles service - Service de sous-titres BetaSeries - Search and download subtitles from BetaSeries.com - Rechercher et télécharger des sous-titres depuis BetaSeries.com - - + + + + + + + + all + BetaSeries subtitles service + Service de sous-titres BetaSeries + Search and download subtitles from BetaSeries.com + Rechercher et télécharger des sous-titres depuis BetaSeries.com + + - - - - - - - - Synchronization with BetaSeries.com - Synchronisation avec BetaSeries.com - This service automatically updates your profile on BetaSeries.com when you have finished watching an episode or a movie on Kodi - Ce service met à jour automatiquement votre profil sur le site BetaSeries.com lorsque vous venez de finir de regarder un épisode ou nu film dans Kodi - - all - http://www.betaseries.com/wiki/Plugin%20XBMC - - + + + + + + + + + Synchronization with BetaSeries.com + Synchronisation avec BetaSeries.com + This service automatically updates your profile on BetaSeries.com when you have finished watching an episode or a movie or just uploaded it in on Kodi + Ce service met à jour automatiquement votre profil sur le site BetaSeries.com lorsque vous venez de finir de regarder un épisode ou un film dans Kodi ou que vous venez de l'ajouter + + all + GPL-2.0-only + https://Betaseries.com + https://support.betaseries.com/article/38-les-applications-betaseries + + v2.0.0 (2021 02 23) + - refactoring for Kodi 19 Matrix + + + icon.png + fanart.png + + + - - - - - - - - all - Addic7ed subtitles service - Service de sous-titres Addic7ed - Search and download subtitles from Addic7ed.com - Rechercher et télécharger des sous-titres depuis Addic7ed.com - - + + + + + + + + Synchronization with BetaSeries.com + Synchronisation avec BetaSeries.com + This service automatically updates your profile on BetaSeries.com when you have finished watching an episode or a movie on Kodi + Ce service met à jour automatiquement votre profil sur le site BetaSeries.com lorsque vous venez de finir de regarder un épisode ou un film dans Kodi + + all + https://support.betaseries.com/article/38-les-applications-betaseries + + v1.0.14 (2020/03/30) + - add compatibility with Netflix addon + - add compatibility with Catchuptv addon + - add pseudo compatibility with themoviedb (not recommended) + - fix search from title + - add some traductions + - change to Betaseries API v3.0 + + + - - - https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml - https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml.md5 - https://raw.github.com/skylex/xbmc-betaseries/master/ - - - BetaSeries Add-on Repository - This repository contains the BetaSeries.com add-on for XBMC and maintains it up to date - Ce dépôt contient l'extension BetaSeries.com pour XBMC et permet de la maintenir à jour - If it breaks, you get to keep the pieces - all - - + + + + + + + + Synchronization with BetaSeries.com + Synchronisation avec BetaSeries.com + This service automatically updates your profile on BetaSeries.com when you have finished watching an episode or a movie on Kodi + Ce service met à jour automatiquement votre profil sur le site BetaSeries.com lorsque vous venez de finir de regarder un épisode ou nu film dans Kodi + + all + https://support.betaseries.com/article/38-les-applications-betaseries + + v1.0.13 (2020/03/24) + - convert from imbd_id to tvdb_id + - add auto-update from BS + + + + + + + + https://raw.github.com/skylex/xbmc-betaseries/leia/addons.xml + https://raw.github.com/skylex/xbmc-betaseries/leia/addons.xml.md5 + https://raw.github.com/skylex/xbmc-betaseries/leia + + + https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml + https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml.md5 + https://raw.github.com/skylex/xbmc-betaseries/master + + + + BetaSeries Add-on Repository + This repository contains the BetaSeries.com add-on for XBMC and maintains it up to date + Ce dépôt contient l'extension BetaSeries.com pour XBMC et permet de la maintenir à jour + If it breaks, you get to keep the pieces + all + + + icon.jpg + + diff --git a/addons.xml.md5 b/addons.xml.md5 index 6f108ba..92736dd 100644 --- a/addons.xml.md5 +++ b/addons.xml.md5 @@ -1 +1 @@ -36b8d1c242689b27643c91d4c47adf3a \ No newline at end of file +8e6bc84008dada18945fb8ffa79178a0 addons.xml diff --git a/repository.betaseries/LICENSE.txt b/repository.betaseries/LICENSE.txt old mode 100755 new mode 100644 diff --git a/repository.betaseries/addon.xml b/repository.betaseries/addon.xml index 4047222..3b2a266 100644 --- a/repository.betaseries/addon.xml +++ b/repository.betaseries/addon.xml @@ -1,9 +1,19 @@ - + + + + - https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml - https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml.md5 - https://raw.github.com/skylex/xbmc-betaseries/master/ + + https://raw.github.com/skylex/xbmc-betaseries/leia/addons.xml + https://raw.github.com/skylex/xbmc-betaseries/skylex/addons.xml.md5 + https://raw.github.com/skylex/xbmc-betaseries/leia + + + https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml + https://raw.github.com/skylex/xbmc-betaseries/master/addons.xml.md5 + https://raw.github.com/skylex/xbmc-betaseries/master + BetaSeries Add-on Repository @@ -12,4 +22,7 @@ If it breaks, you get to keep the pieces all + + icon.jpg + diff --git a/repository.betaseries/icon.png b/repository.betaseries/icon.png old mode 100755 new mode 100644 diff --git a/repository.betaseries/index.html b/repository.betaseries/index.html new file mode 100644 index 0000000..538f31c --- /dev/null +++ b/repository.betaseries/index.html @@ -0,0 +1,13 @@ + + + + Index of + + +

repository.betaseries

+ + + +
repository.betaseries-1.0.1.zip
repository.betaseries-1.0.2.zip
+ + diff --git a/repository.betaseries/repository.betaseries-1.0.0.zip b/repository.betaseries/repository.betaseries-1.0.1.zip similarity index 89% rename from repository.betaseries/repository.betaseries-1.0.0.zip rename to repository.betaseries/repository.betaseries-1.0.1.zip index 1fc507e..72f630f 100644 Binary files a/repository.betaseries/repository.betaseries-1.0.0.zip and b/repository.betaseries/repository.betaseries-1.0.1.zip differ diff --git a/repository.betaseries/repository.betaseries-1.0.2.zip b/repository.betaseries/repository.betaseries-1.0.2.zip new file mode 100644 index 0000000..8c5d6a0 Binary files /dev/null and b/repository.betaseries/repository.betaseries-1.0.2.zip differ diff --git a/service.betaseries.com/LICENSE.txt b/service.betaseries.com/LICENSE.txt index 4f8e8eb..8159d9e 100644 --- a/service.betaseries.com/LICENSE.txt +++ b/service.betaseries.com/LICENSE.txt @@ -1,13 +1,12 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -16,7 +15,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -56,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - - GNU GENERAL PUBLIC LICENSE + + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -111,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -169,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -226,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -256,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -278,5 +277,4 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS -------------------------------------------------------------------------- + END OF TERMS AND CONDITIONS diff --git a/service.betaseries.com/README.md b/service.betaseries.com/README.md new file mode 100644 index 0000000..c97d122 --- /dev/null +++ b/service.betaseries.com/README.md @@ -0,0 +1,8 @@ +BetaSeries sync for Kodi +========================== + +Automatically update your Betaseries account from Kodi. +Watching UnWatch or just added a movie or a TV episode and this add-on will update automatically into your betasseries account + + +Thanks a lot to PCCV for the work they done : https://github.com/PCCV/xbmc-betaseries diff --git a/service.betaseries.com/addon.xml b/service.betaseries.com/addon.xml index f2ad4a9..28b425a 100644 --- a/service.betaseries.com/addon.xml +++ b/service.betaseries.com/addon.xml @@ -1,17 +1,28 @@ - - - - - - - - Synchronization with BetaSeries.com - Synchronisation avec BetaSeries.com - This service automatically updates your profile on BetaSeries.com when you have finished watching an episode or a movie on Kodi - Ce service met à jour automatiquement votre profil sur le site BetaSeries.com lorsque vous venez de finir de regarder un épisode ou nu film dans Kodi - - all - http://www.betaseries.com/wiki/Plugin%20XBMC - + + + + + + + + + Synchronization with BetaSeries.com + Synchronisation avec BetaSeries.com + This service automatically updates your profile on BetaSeries.com when you have finished watching an episode or a movie or just uploaded it in on Kodi + Ce service met à jour automatiquement votre profil sur le site BetaSeries.com lorsque vous venez de finir de regarder un épisode ou un film dans Kodi ou que vous venez de l'ajouter + + all + GPL-2.0-only + https://Betaseries.com + https://github.com/skylex/xbmc-betaseries + + v2.0.0 (2021 02 23) + - refactoring for Kodi 19 Matrix + + + icon.png + fanart.png + + diff --git a/service.betaseries.com/betaseries.py b/service.betaseries.com/betaseries.py deleted file mode 100644 index bccf2a5..0000000 --- a/service.betaseries.com/betaseries.py +++ /dev/null @@ -1,508 +0,0 @@ -# * This Program 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 2, or (at your option) -# * any later version. -# * -# * This Program 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 XBMC; see the file COPYING. If not, write to -# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -# * http://www.gnu.org/copyleft/gpl.html -# * -# * code structure and portions of code based on service.scrobbler.librefm by Team-XBMC - -import urllib, urllib2, socket, hashlib, time, platform -import xbmc, xbmcgui, xbmcaddon -from xml.dom import minidom -import simplejson as json - -__addon__ = xbmcaddon.Addon() -__addonid__ = __addon__.getAddonInfo('id') -__addonname__ = __addon__.getAddonInfo('name') -__addonversion__ = __addon__.getAddonInfo('version') -__icon__ = __addon__.getAddonInfo('icon') -__platform__ = platform.system() + " " + platform.release() -__language__ = __addon__.getLocalizedString - -socket.setdefaulttimeout(10) - -def log(txt, loglevel=xbmc.LOGDEBUG): - if isinstance (txt,str): - txt = txt.decode("utf-8") - message = u'%s: %s' % (__addonid__, txt) - xbmc.log(msg=message.encode("utf-8"), level=loglevel) - -def set_user_agent(): - json_query = json.loads(xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["version", "name"]}, "id": 1 }')) - try: - major = str(json_query['result']['version']['major']) - minor = str(json_query['result']['version']['minor']) - name = "Kodi" if int(major) >= 14 else "XBMC" - version = "%s %s.%s" % (name, major, minor) - except: - log("could not get app version") - version = "XBMC" - return "Mozilla/5.0 (compatible; " + __platform__ + "; " + version + "; " + __addonid__ + "/" + __addonversion__ + ")" - -def get_urldata(url, urldata, method): - # create a handler - handler = urllib2.HTTPSHandler() - # create an openerdirector instance - opener = urllib2.build_opener(handler) - # encode urldata - body = urllib.urlencode(urldata) - # build a request - req = urllib2.Request(url, data=body) - # add any other information you want - req.add_header('Accept', 'application/json') - req.add_header('User-Agent', __useragent__) - # overload the get method function - req.get_method = lambda: method - try: - #response = urllib2.urlopen(req) - connection = opener.open(req) - except urllib2.HTTPError,e: - connection = e - if connection.code: - response = connection.read() - return response - else: - log('response empty') - return 0 - -class Main: - def __init__( self ): - self._service_setup() - while (not xbmc.abortRequested): - xbmc.sleep(1000) - - def _service_setup( self ): - self.apikey = '5a85a0adc953' - self.apiurl = 'https://api.betaseries.com' - self.apiver = '2.2' - self.Monitor = MyMonitor(action = self._get_settings) - self._get_settings() - - def _get_settings( self ): - log('reading settings') - service = [] - BetaActive = __addon__.getSetting('betaactive') == 'true' - BetaFirst = __addon__.getSetting('betafirst') == 'true' - BetaUser = __addon__.getSetting('betauser') - BetaPass = __addon__.getSetting('betapass') - BetaBulk = __addon__.getSetting('betabulk') == 'true' - BetaMark = __addon__.getSetting('betamark') == 'true' - BetaUnMark = __addon__.getSetting('betaunmark') == 'true' - BetaFollow = __addon__.getSetting('betafollow') == 'true' - BetaNotify = __addon__.getSetting('betanotify') == 'true' - if BetaActive and BetaUser and BetaPass: - # [service, api-url, api-key, user, pass, first-only, token, auth-fail, failurecount, timercounter, timerexpiretime, bulk, mark, unmark, follow] - service = ['betaseries', self.apiurl, self.apikey, BetaUser, BetaPass, BetaFirst, '', False, 0, 0, 0, BetaBulk, BetaMark, BetaUnMark, BetaFollow, BetaNotify] - self.Player = MyPlayer(action = self._service_betaserie, service = service) - if service[15]: - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(30003), 750, __icon__)).encode('utf-8', 'ignore')) - - def _service_betaserie( self, episode, service ): - tstamp = int(time.time()) - # don't proceed if we had an authentication failure - if not service[7]: - # test if we are authenticated - if not service[6]: - # authenticate - service = self._service_authenticate(service, str(tstamp)) - # only proceed if authentication was succesful - if service[6]: - # mark as watched if we still have a valid session key after submission and have episode info - if episode[0] and episode[1]: - # first-only check - if not service[5] or (service[5] and episode[2] <= 1): - # mark as watched - service = self._service_mark(service, episode) - - def _service_authenticate( self, service, timestamp ): - # don't proceed if timeout timer has not expired - if service[10] > int(timestamp): - return service - # create a pass hash - md5pass = hashlib.md5() - md5pass.update(service[4]) - url = service[1] + '/members/auth' - urldata = {'v':self.apiver, 'key':service[2], 'login':service[3], 'password':md5pass.hexdigest()} - try: - # authentication request - response = get_urldata(url, urldata, "POST") - # authentication response - data = json.loads(response) - log('successfully authenticated') - except: - service = self._service_fail( service, True ) - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(32003), 750, __icon__)).encode('utf-8', 'ignore')) - log('failed to connect for authentication', xbmc.LOGNOTICE) - return service - # parse results - if 'token' in data: - # get token - service[6] = str(data['token']) - # reset failure count - service[8] = 0 - # reset timer - service[9] = 0 - service[10] = 0 - if data['errors']: - log("%s error %s : %s" % (service[0], data['errors'][0]['code'], data['errors'][0]['text']), xbmc.LOGNOTICE) - if data['errors'][0]['code'] < 2000: - # API error - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(32002), 750, __icon__)).encode('utf-8', 'ignore')) - log('bad API usage', xbmc.LOGNOTICE) - # disable the service, the monitor class will pick up the changes - __addon__.setSetting('betaactive', 'false') - elif data['errors'][0]['code'] > 4001: - # login error - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(32004), 750, __icon__)).encode('utf-8', 'ignore')) - log('login or password incorrect', xbmc.LOGNOTICE) - service[7] = True - else: - # everything else - service = self._service_fail( service, True ) - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(32001), 750, __icon__)).encode('utf-8', 'ignore')) - log('server error while authenticating', xbmc.LOGNOTICE) - return service - - def _service_mark( self, service, episode ): - # abort if betamark = false and playcount > 0 and play = false - if not service[12] and episode[2] > 0 and not episode[3]: - log("abort marking, as play = %s" % episode[3]) - return service - # abort if betaunmark = false and playcount = 0 and play = false - elif not service[13] and episode[2] == 0 and not episode[3]: - log("abort unmarking, as play = %s" % episode[3]) - return service - if episode[6]=='episode': - # follow show if BetaFollow = true - if service[14] and episode[2] != -1: - url = service[1] + "/shows/show" - urldata = {'v':self.apiver, 'key':service[2], 'token':service[6], 'thetvdb_id':episode[0]} - try: - # marking request - response = get_urldata(url, urldata, "POST") - # marking response - data = json.loads(response) - except: - service = self._service_fail( service, False ) - log('failed to follow show %s' % episode[4], xbmc.LOGNOTICE) - return service - # parse results - if data['errors']: - log("%s error : %s %s" % (service[0], data['errors'][0]['code'], data['errors'][0]['text']), xbmc.LOGNOTICE) - if data['errors'][0]['code'] == 2001: - # drop our session key - service[6] = '' - log('bad token while following show', xbmc.LOGNOTICE) - return service - elif data['errors'][0]['code'] == 2003: - log('already following show %s' % episode[4]) - else: - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(32005) + episode[4].decode('utf-8'), 750, __icon__)).encode('utf-8', 'ignore')) - log('failed to follow show %s' % episode[4], xbmc.LOGNOTICE) - return service - else: - if service[15]: - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(30013) + episode[4].decode('utf-8'), 750, __icon__)).encode('utf-8', 'ignore')) - log('now following show %s' % (episode[4])) - if episode[6]=='movie': - # mark movie as watched - url = service[1] + "/movies/movie" - urldata = {'v':self.apiver, 'key':service[2], 'token':service[6], 'id':episode[0],'state': episode[2]} - method = "POST" - if episode[2] == 0: - act = "not watched" - actlang = 30017 - else: - act = "watched" - actlang = 30016 - elif episode[6]=='episode': - # mark episode as watched - urldata = {'v':self.apiver, 'key':service[2], 'token':service[6], 'thetvdb_id':episode[1]} - if service[11]: - urldata.update({'bulk': 1}) - if episode[2] == 0: - url = service[1] + "/episodes/watched" - method = "DELETE" - act = "not watched" - actlang = 30015 - elif episode[2] == -1: - url = service[1] + "/episodes/downloaded" - method = "POST" - act = "downloaded" - actlang = 30101 - else: - url = service[1] + "/episodes/watched" - method = "POST" - act = "watched" - actlang = 30014 - try: - # marking request - response = get_urldata(url, urldata, method) - # marking response - data = json.loads(response) - except: - service = self._service_fail( service, False ) - log('failed to mark as %s' % act, xbmc.LOGNOTICE) - return service - # parse results - if data['errors']: - log("%s error : %s %s" % (service[0], data['errors'][0]['code'], data['errors'][0]['text']), xbmc.LOGNOTICE) - if data['errors'][0]['code'] == 2001: - # drop our session key - service[6] = '' - log('bad token while marking %s' % (episode[6]), xbmc.LOGNOTICE) - elif data['errors'][0]['code'] == 0: - if episode[6]=='movie': - log('%s already marked as %s' % (episode[5], act), xbmc.LOGNOTICE) - else: - log('not following show, or %s %s already marked as %s' % (episode[6],episode[5], act), xbmc.LOGNOTICE) - - else: - if episode[6]=='movie': - actlang = 32007 - else: - actlang = 32006 - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(actlang), 750, __icon__)).encode('utf-8', 'ignore')) - log('error marking %s %s as %s' % (episode[6],episode[5], act), xbmc.LOGNOTICE) - else: - if service[15]: - xbmc.executebuiltin((u'Notification(%s,%s,%s,%s)' % (__addonname__, __language__(actlang), 750, __icon__)).encode('utf-8', 'ignore')) - log('%s %s %s marked as %s' % (episode[4], episode[6], episode[5], act)) - return service - - def _service_fail( self, service, timer ): - timestamp = int(time.time()) - # increment failure counter - service[8] += 1 - # drop our session key if we encouter three failures - if service[8] > 2: - service[6] = '' - # set a timer if failure occurred during authentication phase - if timer: - # wrap timer if we cycled through all timeout values - if service[9] == 0 or service[9] == 7680: - service[9] = 60 - else: - # increment timer - service[9] = 2 * service[9] - # set timer expire time - service[10] = timestamp + service[9] - return service - -# monitor notifications -class MyPlayer(xbmc.Monitor): - def __init__( self, *args, **kwargs ): - xbmc.Monitor.__init__( self ) - self.action = kwargs['action'] - self.service = kwargs['service'] - self.Play = False - log('Player Class Init') - self.ScanRecentlyadded() - - def onNotification( self, sender, method, data ): - if sender == 'xbmc': - if method == 'VideoLibrary.OnScanFinished': - self.ScanRecentlyadded() - elif method == 'Player.OnPlay': - result = json.loads(data) - if 'item' in result: - if result['item']['type'] == 'episode': - # in case Player.OnPlay comes to fast after Player.OnStop - xbmc.sleep(1000) - log("watching episode, library id = %s" % result['item']['id']) - self.Play = True - elif result['item']['type'] == 'movie': - # in case Player.OnPlay comes to fast after Player.OnStop - xbmc.sleep(1000) - log("watching movie, library id = %s" % result['item']['id']) - self.Play = True - - elif method == 'Player.OnStop': - result = json.loads(data) - # if viewing in file mode and playback stopped at the end - if 'item' in result and 'title' in result["item"] and result["end"]: - if result['item']['type'] == 'episode': - # scrap episode infos from filename (item -> title) - scraper_url = "%s/episodes/scraper?file=%s&key=%s" % (self.service[1], result["item"]["title"], self.service[2]) - scraper_data = json.loads(get_urldata(scraper_url,"","GET"))["episode"] - title = str(scraper_data["season"]) + "x" + str(scraper_data["episode"]) - # get show's tvdbid from showid - show_url = "%s/shows/display?id=%s&key=%s" % (self.service[1], scraper_data["show_id"], self.service[2]) - tvdbid = json.loads(get_urldata(show_url,"","GET"))["show"]["thetvdb_id"] - # set episode infos - episode = [int(tvdbid), int(scraper_data["thetvdb_id"]), 1, True, str(scraper_data["show_title"]), title,'episode'] - # mark episode as watched - self.action(episode, self.service) - elif result['item']['type'] == 'movie': - # scrap movie infos from filename (item -> title) - scraper_url = "%s/movies/scraper?file=%s&key=%s" % (self.service[1], result["item"]["title"], self.service[2]) - scraper_data = json.loads(get_urldata(scraper_url,"","GET"))["movie"] - # set movie infos - movie = [int(scraper_data["id"]), int(scraper_data["thetvdb_id"]), 1, True, '', str(scraper_data["title"]), 'movie'] - # mark movie as watched - self.action(movie, self.service) - else: - # wait 1s to avoid setting Play=False before marking episode - xbmc.sleep(1000) - self.Play = False - elif method == 'VideoLibrary.OnUpdate': - result = json.loads(data) - if 'playcount' in result: - if 'item' in result: - if result['item']['type'] == 'episode': - log("episode status changed for library id = %s, playcount = %s" % (result['item']['id'], result['playcount'])) - episode = self._get_episode_info( result['item']['id'], result['playcount'], self.Play) - if episode: - if result['playcount']==0: - # mark as downloaded - episode[2]=-1 - self.action(episode, self.service) - # mark as watched or not, depending on playcount - self.action(episode, self.service) - self.Play = False - elif result['item']['type'] == 'movie': - log("movie status changed for library id = %s, playcount = %s" % (result['item']['id'], result['playcount'])) - movie = self._get_movie_info( result['item']['id'], result['playcount'], self.Play) - if movie: - # mark as watched or not, depending on playcount - self.action(movie, self.service) - self.Play = False - - def ScanRecentlyadded ( self): - f = __addon__.getAddonInfo('path') + '/lastdate.tmp' - try: - with open (f,"r") as fic: - lastdate = fic.read() - - except: - lastdate = '2001-01-01 00:00:00' - newdate = lastdate - # result_movies = json.loads(xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "method": "VideoLibrary.GetRecentlyAddedMovies", "params": {"properties": ["dateadded"]}, "id": 1 }')) - # if 'result' in result_movies: - # log("VideoLirary GetRecentlyAddedMovies : %s" % result_movies['result']['movies']) - # for movie in result_movies['result']['movies']: - # log("id %s has been added %s" % (movie['movieid'],movie['dateadded'])) - # else: - # log("VideoLirary GetRecentlyAddedMovies in ERROR : %s" % result_movies) - result_episodes = json.loads(xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "method": "VideoLibrary.GetRecentlyAddedEpisodes", "params": {"properties": ["dateadded"]}, "id": 1 }')) - if 'result' in result_episodes: - # log("VideoLirary GetRecentlyAddedEpisodes : %s" % result_episodes['result']['episodes']) - for episode in result_episodes['result']['episodes']: - if episode['dateadded'] > lastdate: - if episode['dateadded'] > newdate: - newdate = episode['dateadded'] - log("%s with id %s has been added %s" % (episode['label'],episode['episodeid'],episode['dateadded'])) - episode = self._get_episode_info( episode['episodeid'], -1, self.Play) - if episode and type(episode) is list: - episode[2]=-1 - self.action(episode, self.service) - with open (f,'wb') as fic: - fic.write(newdate) - else: - log("VideoLirary GetRecentlyAddedEpisodes in ERROR : %s" % result_episodes) - - - - def _get_episode_info( self, episodeid, playcount, playstatus ): - tvdbid = False - tvdbepid = False - try: - tvshow_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodeDetails", "params": {"episodeid": ' + str(episodeid) + ', "properties": ["tvshowid", "showtitle", "season", "episode", "uniqueid"]}, "id": 1}' - tvshow = json.loads(xbmc.executeJSONRPC (tvshow_query))['result']['episodedetails'] - if 'uniqueid' in tvshow and 'unknown' in tvshow['uniqueid']: - tvdbepid = tvshow['uniqueid']['unknown'] - log("theTvDb episode id : %s" % tvdbepid) - tvdbid_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShowDetails", "params": {"tvshowid": ' + str(tvshow['tvshowid']) + ', "properties": ["imdbnumber"]}, "id": 1}' - tvdbid = json.loads(xbmc.executeJSONRPC (tvdbid_query))['result']['tvshowdetails']['imdbnumber'] - showtitle = tvshow['showtitle'].encode("utf-8") - epname = str(tvshow['season']) + 'x' + str(tvshow['episode']) - except: - log("could not get tvshow/episode details", xbmc.LOGNOTICE) - if not tvdbid: - url = self.service[1] + '/shows/search' - urldata = '?v=2.2&key=' + self.service[2] + '&title=' + showtitle - try: - tvdbid_query = get_urldata(url + urldata, '', "GET") - tvdbid_query = json.loads(tvdbid_query) - tvdbid = tvdbid_query['shows'][0]['thetvdb_id'] - except: - log("could not fetch tvshow's thetvdb_id", xbmc.LOGNOTICE) - return False - if not tvdbepid: - url = self.service[1] + '/shows/episodes' - urldata = '?v=2.2&key=' + self.service[2] + '&thetvdb_id=' + str(tvdbid) + '&season=' + str(tvshow['season']) + '&episode=' + str(tvshow['episode']) - try: - tvdbepid_query = get_urldata(url + urldata, '', "GET") - tvdbepid_query = json.loads(tvdbepid_query) - tvdbepid = tvdbepid_query['episodes'][0]['thetvdb_id'] - except: - log("could not fetch episode's thetvdb_id", xbmc.LOGNOTICE) - return False - - epinfo = [int(tvdbid), int(tvdbepid), int(playcount), bool(playstatus), showtitle, epname, 'episode'] - return epinfo - - def _get_movie_info( self, episodeid, playcount, playstatus ): - method = 'VideoLibrary.GetMovieDetails' - params = '"movieid": ' + str(episodeid) + ', "properties": ["imdbnumber", "originaltitle", "sorttitle", "title", "uniqueid"]' - returncode = 'moviedetails' - movie_query = '{"jsonrpc": "2.0", "method": "' + str(method) + '", "params": {' + str(params) + '}, "id": 1}' - movie = json.loads(xbmc.executeJSONRPC (movie_query))['result']['moviedetails'] - imdbid = '' - tvdbid = '' - id = '' - try: - imdbid = movie['imdbnumber'] - moviename = movie['originaltitle'].encode("utf-8") - log('movie found= %s %s' % (moviename, imdbid)) - except: - log("could not get movie details", xbmc.LOGNOTICE) - if not imdbid: - url = self.service[1] + '/movies/search' - urldata = '?v=2.2&key=' + self.service[2] + '&title=' + moviename - try: - imdbid_query = get_urldata(url + urldata, '', "GET") - imdbid_query = json.loads(imdbid_query) - imdbid = imdbid_query['movies'][0]['imdb_id'] - except: - log("could not fetch movie's %s imdb_id : %s " % (moviename, imdbid), xbmc.LOGNOTICE) - return False - url = self.service[1] + '/movies/movie' - urldata = '?key=' + self.service[2] + '&imdb_id=' + str(imdbid) - try: - tvdbid_query = get_urldata(url + urldata, '', "GET") - log(tvdbid_query) - tvdbid_query = json.loads(tvdbid_query) - tvdbid = tvdbid_query['movie']['tmdb_id'] - id = tvdbid_query['movie']['id'] - except: - log("could not fetch movie %s thetmdb_id : %s , %s" % (imdbid, tvdbid , id ), xbmc.LOGNOTICE) - return False - epinfo = [int(id), int(tvdbid), int(playcount), bool(playstatus), '', moviename, 'movie'] - return epinfo - -# monitor settings change -class MyMonitor(xbmc.Monitor): - def __init__( self, *args, **kwargs ): - xbmc.Monitor.__init__( self ) - self.action = kwargs['action'] - - def onSettingsChanged( self ): - log('onSettingsChanged') - self.action() - -# start script -if ( __name__ == "__main__" ): - log('script version %s started' % __addonversion__) - __useragent__ = set_user_agent() - Main() - diff --git a/service.betaseries.com/changelog.txt b/service.betaseries.com/changelog.txt deleted file mode 100644 index 840c0ba..0000000 --- a/service.betaseries.com/changelog.txt +++ /dev/null @@ -1,38 +0,0 @@ -1.0.12 -- syntax error, missing ":" - -1.0.11 -- fix another bug in issue #15 - -1.0.10 -- fix issue 15 - -1.0.9 -- version bump for some fixes - -1.0.8 -- allow marking movies - -1.0.7 -- support for movies - -1.0.6 -- allow marking episode when in file mode - -1.0.5 -- change name, api key and add data on ua - -1.0.4 -- rename plugin because of conflict with subtitles addon - -1.0.3 -- add import of simplejson module - -1.0.2 -- fix notification icon - -1.0.1 -- bug fixes, new notification option, code cleanup - -1.0.0 -- initial release diff --git a/service.betaseries.com/default.py b/service.betaseries.com/default.py new file mode 100644 index 0000000..e883792 --- /dev/null +++ b/service.betaseries.com/default.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +# coding: utf-8 +# import web_pdb; web_pdb.set_trace() +import logging +import xbmcaddon +from resources.lib import kodilogging +from resources.lib.service import betaseriesService +from resources.lib.kodiUtilities import setSetting, getSetting +from resources.lib.utilities import createError, checkIfNewVersion + +__addon__ = xbmcaddon.Addon("service.betaseries.com") +__addonversion__ = __addon__.getAddonInfo("version") +__addonid__ = __addon__.getAddonInfo("id") +kodilogging.config() +logger = logging.getLogger(__name__) + + +logger.debug(f"Loading '{__addonid__}' version '{__addonversion__}'") +if checkIfNewVersion(str(getSetting("version")), str(__addonversion__)): + setSetting("version", __addonversion__) + +try: + betaseriesService() +except Exception as ex: + message = createError(ex) + logger.fatal(message) + +logger.debug(f"'{__addonid__}' shutting down.") diff --git a/service.betaseries.com/fanart.png b/service.betaseries.com/fanart.png new file mode 100644 index 0000000..5ecb6f5 Binary files /dev/null and b/service.betaseries.com/fanart.png differ diff --git a/service.betaseries.com/resources/language/English (US)/strings.po b/service.betaseries.com/resources/language/English (US)/strings.po deleted file mode 100644 index 40568b8..0000000 --- a/service.betaseries.com/resources/language/English (US)/strings.po +++ /dev/null @@ -1,21 +0,0 @@ -# XBMC Media Center language file -# Addon Name: BetaSeries Service -# Addon id: service.betaseries -# Addon Provider: skylex -msgid "" -msgstr "" -"Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" -"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: XBMC Translation Team\n" -"Language-Team: English (US) (http://www.transifex.com/projects/p/xbmc-addons/language/en_US/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: en_US\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -msgctxt "#30000" -msgid "General" -msgstr "General" diff --git a/service.betaseries.com/resources/language/English/strings.po b/service.betaseries.com/resources/language/English/strings.po deleted file mode 100644 index 984ea1a..0000000 --- a/service.betaseries.com/resources/language/English/strings.po +++ /dev/null @@ -1,117 +0,0 @@ -# XBMC Media Center language file -# Addon Name: BetaSeries Service -# Addon id: service.betaseries -# Addon Provider: skylex -msgid "" -msgstr "" -"Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" -"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: XBMC Translation Team\n" -"Language-Team: English (http://www.transifex.com/projects/p/xbmc-addons/language/en/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: en\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -msgctxt "#30000" -msgid "General" -msgstr "" - -#empty strings from id 30001 to 30002 - -msgctxt "#30003" -msgid "Successfully loaded !" -msgstr "" - -msgctxt "#30004" -msgid "Activate BetaSeries.com service" -msgstr "" - -msgctxt "#30005" -msgid "Mark as viewed only when watched for the first time" -msgstr "" - -msgctxt "#30006" -msgid "BetaSeries.com username" -msgstr "" - -msgctxt "#30007" -msgid "BetaSeries.com password" -msgstr "" - -msgctxt "#30008" -msgid "Mark previous episodes as watched" -msgstr "" - -msgctxt "#30009" -msgid "Update status when marking as viewed in XBMC" -msgstr "" - -msgctxt "#30010" -msgid "Update status when marking as not viewed in XBMC" -msgstr "" - -msgctxt "#30011" -msgid "Automatically add new watched shows" -msgstr "" - -msgctxt "#30012" -msgid "Notify on screen when marking episodes" -msgstr "" - -msgctxt "#30013" -msgid "Show added : " -msgstr "" - -msgctxt "#30014" -msgid "Episode marked as watched !" -msgstr "" - -msgctxt "#30015" -msgid "Episode marked as not watched !" -msgstr "" - -msgctxt "#30016" -msgid "Movie marked as watched !" -msgstr "" - -msgctxt "#30017" -msgid "Movie marked as not watched !" -msgstr "" - -#empty strings from id 30018 to 32000 - -msgctxt "#30101" -msgid "Episode marked as downloaded !" -msgstr "" - -msgctxt "#32001" -msgid "Authentication error" -msgstr "" - -msgctxt "#32002" -msgid "Please update the add-on" -msgstr "" - -msgctxt "#32003" -msgid "Failed to connect to the site" -msgstr "" - -msgctxt "#32004" -msgid "Login or password incorrect" -msgstr "" - -msgctxt "#32005" -msgid "Failed to add show : " -msgstr "" - -msgctxt "#32006" -msgid "Failed to mark episode !" -msgstr "" - -msgctxt "#32007" -msgid "Failed to mark movie !" -msgstr "" diff --git a/service.betaseries.com/resources/language/resource.language.en_US/strings.po b/service.betaseries.com/resources/language/resource.language.en_US/strings.po new file mode 100644 index 0000000..ccfa16f --- /dev/null +++ b/service.betaseries.com/resources/language/resource.language.en_US/strings.po @@ -0,0 +1,143 @@ +# XBMC Media Center language file +# Addon Name: BetaSeries Service +# Addon id: service.betaseries +# Addon Provider: skylex +msgid "" +msgstr "" +"Project-Id-Version: XBMC Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: XBMC Translation Team\n" +"Language-Team: French (http://www.transifex.com/projects/p/xbmc-addons/language/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +msgctxt "#30000" +msgid "General" +msgstr "General" + +msgctxt "#30003" +msgid "Successfully loaded !" +msgstr "Successfully loaded !" + +msgctxt "#30004" +msgid "Activate BetaSeries.com service" +msgstr "Activate BetaSeries.com service" + +msgctxt "#30005" +msgid "Mark as viewed only when watched for the first time" +msgstr "Mark as viewed only when watched for the first time" + +msgctxt "#30006" +msgid "BetaSeries.com username" +msgstr "BetaSeries.com username" + +msgctxt "#30007" +msgid "BetaSeries.com password" +msgstr "BetaSeries.com password" + +msgctxt "#30008" +msgid "Mark previous episodes as watched" +msgstr "Mark previous episodes as watched" + +msgctxt "#30009" +msgid "Update status when marking as viewed in KODI" +msgstr "Update status when marking as viewed in KODI" + +msgctxt "#30010" +msgid "Update status when marking as not viewed in KODI" +msgstr "Update status when marking as not viewed in KODI" + +msgctxt "#30011" +msgid "Automatically add new watched shows" +msgstr "Automatically add new watched shows" + +msgctxt "#30012" +msgid "Notify on screen when marking episodes" +msgstr "Notify on screen when marking episodes" + +msgctxt "#30013" +msgid "Show added : %s" +msgstr "Show added : %s" + +msgctxt "#30014" +msgid "Episode marked as watched !" +msgstr "Episode marked as watched !" + +msgctxt "#30015" +msgid "Episode marked as not watched !" +msgstr "Episode marked as not watched !" + +msgctxt "#30016" +msgid "Movie marked as watched !" +msgstr "Movie marked as watched !" + +msgctxt "#30017" +msgid "Movie marked as not watched !" +msgstr "Movie marked as not watched !" + +msgctxt "#30018" +msgid "Verbose logging" +msgstr "Verbose logging" + +msgctxt "#30019" +msgid "Update episodes seen from BS at each start" +msgstr "Update episodes seen from BS at each start" + +msgctxt "#30020" +msgid "Mark recently added file as downloaded on BS at each start" +msgstr "Mark recently added file as downloaded on BS at each start" + +msgctxt "#30021" +msgid "%s episode(s) marked as watched" +msgstr "%s episode(s) marked as watched" + +msgctxt "#30101" +msgid "Episode marked as downloaded !" +msgstr "Episode marked as downloaded !" + +msgctxt "#32001" +msgid "Authentication error" +msgstr "Authentication error" + +msgctxt "#32002" +msgid "bad communication with BetaSeries.com" +msgstr "bad communication with BetaSeries.com" + +msgctxt "#32003" +msgid "Failed to connect to the site" +msgstr "Failed to connect to the site" + +msgctxt "#32004" +msgid "Login or password incorrect" +msgstr "Login or password incorrect" + +msgctxt "#32005" +msgid "Failed to add show : " +msgstr "Failed to add show : " + +msgctxt "#32006" +msgid "Failed to mark episode !" +msgstr "Failed to mark episode !" + +msgctxt "#32007" +msgid "Failed to mark movie !" +msgstr "Failed to mark movie !" + +msgctxt "#32008" +msgid "Authentication succeeded" +msgstr "Authentication succeeded" + +msgctxt "#32009" +msgid "Authentication failed" +msgstr "Authentication failed" + +msgctxt "#32010" +msgid "BetaSeries Agent" +msgstr "BetaSeries Agent" + + diff --git a/service.betaseries.com/resources/language/French/strings.po b/service.betaseries.com/resources/language/resource.language.fr_FR/strings.po similarity index 65% rename from service.betaseries.com/resources/language/French/strings.po rename to service.betaseries.com/resources/language/resource.language.fr_FR/strings.po index c1bc9ff..969d40c 100644 --- a/service.betaseries.com/resources/language/French/strings.po +++ b/service.betaseries.com/resources/language/resource.language.fr_FR/strings.po @@ -1,15 +1,16 @@ -# XBMC Media Center language file +# 0101 Revoir la méthode à employer pour les traductions" +# KODI Media Center language file # Addon Name: BetaSeries Service # Addon id: service.betaseries -# Addon Provider: skylex +# Addon Provider: kugan49 msgid "" msgstr "" -"Project-Id-Version: XBMC Addons\n" -"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"Project-Id-Version: KODI Addons\n" +"Report-Msgid-Bugs-To: alanwww1@KODI.org\n" "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: XBMC Translation Team\n" -"Language-Team: French (http://www.transifex.com/projects/p/xbmc-addons/language/fr/)\n" +"Last-Translator: KODI Translation Team\n" +"Language-Team: French (http://www.transifex.com/projects/p/KODI-addons/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -45,12 +46,12 @@ msgid "Mark previous episodes as watched" msgstr "Marquer les épisodes précédents comme vus" msgctxt "#30009" -msgid "Update status when marking as viewed in XBMC" -msgstr "Mettre à jour quand l'épisode est marqué comme vu dans XBMC" +msgid "Update status when marking as viewed in KODI" +msgstr "Mettre à jour quand l'épisode est marqué comme vu dans KODI" msgctxt "#30010" -msgid "Update status when marking as not viewed in XBMC" -msgstr "Mettre à jour quand l'épisode est marqué comme non vu dans XBMC" +msgid "Update status when marking as not viewed in KODI" +msgstr "Mettre à jour quand l'épisode est marqué comme non vu dans KODI" msgctxt "#30011" msgid "Automatically add new watched shows" @@ -61,8 +62,8 @@ msgid "Notify on screen when marking episodes" msgstr "Notification visuelle lors du marquage d'un épisode" msgctxt "#30013" -msgid "Show added : " -msgstr "Série ajoutée : " +msgid "Show added : %s" +msgstr "Série ajoutée : %s" msgctxt "#30014" msgid "Episode marked as watched !" @@ -80,6 +81,22 @@ msgctxt "#30017" msgid "Movie marked as not watched !" msgstr "Film marqué comme non vu !" +msgctxt "#30018" +msgid "Verbose logging" +msgstr "Log verbeux" + +msgctxt "#30019" +msgid "Update episodes seen from BS at each start" +msgstr "Mettre à jour les épisodes marqués vus sur BS à chaque démarrage" + +msgctxt "#30020" +msgid "Mark recently added file as downloaded on BS at each start" +msgstr "Note les fichiers récents comme téléchargés sur BS à chaque démarrage" + +msgctxt "#30021" +msgid "%s episode(s) marked as watched" +msgstr "%s episode(s) marqué(s) comme vu !" + msgctxt "#30101" msgid "Episode marked as downloaded !" msgstr "Episode marqué récupéré !" @@ -89,8 +106,8 @@ msgid "Authentication error" msgstr "Erreur d'authentification" msgctxt "#32002" -msgid "Please update the add-on" -msgstr "Veuillez mettre à jour l'extension" +msgid "bad communication with BetaSeries.com" +msgstr "Problème de communication avec BetaSeries.com" msgctxt "#32003" msgid "Failed to connect to the site" @@ -111,3 +128,17 @@ msgstr "Erreur lors du marquage de l'épisode !" msgctxt "#32007" msgid "Failed to mark movie !" msgstr "Erreur lors du marquage du film !" + +msgctxt "#32008" +msgid "Authentication succeeded" +msgstr "Authentification réussie" + +msgctxt "#32009" +msgid "Authentication failed" +msgstr "Echec d'authentification" + +msgctxt "#32010" +msgid "BetaSeries Agent" +msgstr "Agent BetaSeries" + + diff --git a/service.betaseries.com/resources/lib/globals.py b/service.betaseries.com/resources/lib/globals.py new file mode 100644 index 0000000..4c91711 --- /dev/null +++ b/service.betaseries.com/resources/lib/globals.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# + +# Declaration to integrate same serviceApi instance for all system +betaseriesapi = None + + +class Service: + """Describes settings""" + + def __init__( + self, + active, + first, + user, + password, + bulk, + mark, + unMark, + follow, + notify, + update, + ): + self.name = "Betaseries" + self.active = active + self.first = first + self.user = user + self.password = password + self.bulk = bulk + self.mark = mark + self.unMark = unMark + self.follow = follow + self.notify = notify + self.update = update diff --git a/service.betaseries.com/resources/lib/kodiUtilities.py b/service.betaseries.com/resources/lib/kodiUtilities.py new file mode 100644 index 0000000..1bc9a07 --- /dev/null +++ b/service.betaseries.com/resources/lib/kodiUtilities.py @@ -0,0 +1,344 @@ +# -*- coding: utf-8 -*- +# + +import xbmc +import xbmcaddon +import json + +import logging + + +# read settings +__addon__ = xbmcaddon.Addon("service.betaseries.com") + +logger = logging.getLogger(__name__) + + +def notification( + header: str, message: str, time=5000, icon=__addon__.getAddonInfo("icon") +): + """On Screen notification + + Args: + header (str): title + message (str): body + time (int, optional): time before disappears. Defaults to 5000. + icon ([type], optional): icon. Defaults to __addon__.getAddonInfo("icon"). + """ + # 0101 définir le temps dans les settings + xbmc.executebuiltin("Notification(%s,%s,%d,%s)" % (header, message, time, icon)) + + +def showSettings(): + """Open settings dialog""" + __addon__.openSettings() + + +def getSetting(setting): + """retrieve user setting + + Args: + setting (str): setting name + + Returns: + str: setting value + """ + return __addon__.getSetting(setting).strip() + + +def setSetting(setting, value): + """update user setting + + Args: + setting (str): setting name + value (): setting value (transfromed in str) + """ + __addon__.setSetting(setting, str(value)) + + +def getSettingAsBool(setting): + """retrieve user setting + + Args: + setting (str): setting name + + Returns: + bool: setting value + """ + return getSetting(setting).lower() == "true" + + +def getSettingAsFloat(setting): + """retrieve user setting + + Args: + setting (str): setting name + + Returns: + float: setting value + """ + try: + return float(getSetting(setting)) + except ValueError: + return 0 + + +def getSettingAsInt(setting): + """retrieve user setting + + Args: + setting (str): setting name + + Returns: + int: setting value + """ + try: + return int(getSettingAsFloat(setting)) + except ValueError: + return 0 + + +def getString(string_id, var=None): + """return localized string by id + + Args: + string_id (int): id of the text in string.po + var ([type], optional): variable added into string. Defaults to None. + + Returns: + str: text localized + """ + if var: + return __addon__.getLocalizedString(string_id) % var + else: + return __addon__.getLocalizedString(string_id) + + +def kodiJsonRequest(params): + """get json for kodi request + + Args: + params (dict): jso informations + + Returns: + dict: response founded in ["result"] + """ + data = json.dumps(params) + request = xbmc.executeJSONRPC(data) + + response = json.loads(request) + + try: + if "result" in response: + return response["result"] + return None + except KeyError: + logger.warn(f"[{params['method']}] {response['error']['message']}") + return None + + +def getEpisodesFromKodi(): + """return list of episodes in Kodi + + Returns: + dict: list of episodes + """ + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetEpisodes", + "id": 1, + } + ) + logger.debug("getEpisodesFromKodi(): %s" % str(result)) + + if not result: + logger.debug("getEpisodesFromKodi(): Result from Kodi was empty.") + return None + + try: + return result + except KeyError: + logger.debug("getEpisodesFromKodi(): KeyError: result") + return None + + +def GetRecentlyAddedEpisodesFromKodi(fields): + """Return list of recently added episodes in Kodi + + Args: + fields (dict): list of field to return + + Returns: + dict: list of episodes + """ + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetRecentlyAddedEpisodes", + "params": fields, + "id": 1, + } + ) + logger.debug("GetRecentlyAddedEpisodesFromKodi(): %s" % str(result)) + + if not result: + logger.debug("GetRecentlyAddedEpisodesFromKodi(): Result from Kodi was empty.") + return None + + try: + return result["episodes"] + except KeyError: + logger.debug("GetRecentlyAddedEpisodesFromKodi(): KeyError: result['episodes']") + return None + + +def getShowDetailsFromKodi(showID, fields): + """return detailled Show information from Kodi based on TVShowID + + Args: + showID (int): internal ID + fields (dict): list of field to return + + Returns: + dict: list of informations requested in field + """ + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetTVShowDetails", + "params": {"tvshowid": showID, "properties": fields}, + "id": 1, + } + ) + logger.debug("getShowDetailsFromKodi(): %s" % str(result)) + + if not result: + logger.debug("getShowDetailsFromKodi(): Result from Kodi was empty.") + return None + + try: + return result["tvshowdetails"] + except KeyError: + logger.debug("getShowDetailsFromKodi(): KeyError: result['tvshowdetails']") + return None + + +def getSeasonDetailsFromKodi(seasonID, fields): + """return detailled Season information from Kodi based on SeasonID + + Args: + seasonID (int): internal ID + fields (dict): list of field to return + Returns: + dict: list of informations requested in field + """ + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetSeasonDetails", + "params": {"seasonid": seasonID, "properties": fields}, + "id": 1, + } + ) + logger.debug("getSeasonDetailsFromKodi(): %s" % str(result)) + + if not result: + logger.debug("getSeasonDetailsFromKodi(): Result from Kodi was empty.") + return None + + try: + return result["seasondetails"] + except KeyError: + logger.debug("getSeasonDetailsFromKodi(): KeyError: result['seasondetails']") + return None + + +def setEpisodeDetailsOnKodi(libraryId, fields): + """Set info on a single episode from kodi given the id + + Args: + libraryId (int): internal ID + fields (dict): list of field and value to update + Returns: + str: return + - True if update is ok + - False or None if problem + """ + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.SetEpisodeDetails", + "params": dict({"episodeid": libraryId}, **fields), + "id": 1, + } + ) + logger.debug("setEpisodeDetailsOnKodi(): %s" % str(result)) + + if not result: + logger.debug("setEpisodeDetailsOnKodi(): Result from Kodi was empty.") + return None + try: + return result == "OK" + except KeyError: + logger.debug("setEpisodeDetailsOnKodi(): KeyError: result") + return None + + +def getEpisodeDetailsFromKodi(libraryId, fields): + """Get a single episode from kodi given the id + + Args: + libraryId (int): internal ID + fields (dict): list of field to return + Returns: + dict: list of informations requested in field + """ + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetEpisodeDetails", + "params": {"episodeid": libraryId, "properties": fields}, + "id": 1, + } + ) + logger.debug("getEpisodeDetailsFromKodi(): %s" % str(result)) + + if not result: + logger.debug("getEpisodeDetailsFromKodi(): Result from Kodi was empty.") + return None + try: + return result["episodedetails"] + except KeyError: + logger.debug("getEpisodeDetailsFromKodi(): KeyError: result['episodedetails']") + return None + + +def getMovieDetailsFromKodi(libraryId, fields): + """Get a single movie from kodi given the id + + Args: + libraryId (int): internal ID + fields (dict): list of field to return + Returns: + dict: list of informations requested in field + """ + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetMovieDetails", + "params": {"movieid": libraryId, "properties": fields}, + "id": 1, + } + ) + logger.debug("getMovieDetailsFromKodi(): %s" % str(result)) + + if not result: + logger.debug("getMovieDetailsFromKodi(): Result from Kodi was empty.") + return None + + try: + return result["moviedetails"] + except KeyError: + logger.debug("getMovieDetailsFromKodi(): KeyError: result['moviedetails']") + return None diff --git a/service.betaseries.com/resources/lib/kodilogging.py b/service.betaseries.com/resources/lib/kodilogging.py new file mode 100644 index 0000000..ad368dd --- /dev/null +++ b/service.betaseries.com/resources/lib/kodilogging.py @@ -0,0 +1,44 @@ +# coding: utf-8 + +from resources.lib.kodiUtilities import getSettingAsBool + +import logging +import xbmc +import xbmcaddon + + +class KodiLogHandler(logging.StreamHandler): + """Generate Handler for logging + + Args: + logging (logging.StreamHandler): informations about logging + """ + + def __init__(self): + logging.StreamHandler.__init__(self) + addon_id = xbmcaddon.Addon().getAddonInfo("id") + prefix = f"[{addon_id}] " + formatter = logging.Formatter(prefix + "%(name)s: %(message)s") + self.setFormatter(formatter) + + def emit(self, record): + levels = { + logging.CRITICAL: xbmc.LOGFATAL, + logging.ERROR: xbmc.LOGERROR, + logging.WARNING: xbmc.LOGWARNING, + logging.INFO: xbmc.LOGINFO, + logging.DEBUG: xbmc.LOGDEBUG, + logging.NOTSET: xbmc.LOGNONE, + } + if getSettingAsBool("betaverbose"): + xbmc.log(self.format(record), levels[record.levelno]) + + def flush(self): + pass + + +def config(): + """generate logger""" + logger = logging.getLogger() + logger.addHandler(KodiLogHandler()) + logger.setLevel(logging.DEBUG) diff --git a/service.betaseries.com/resources/lib/media.py b/service.betaseries.com/resources/lib/media.py new file mode 100644 index 0000000..10b50cb --- /dev/null +++ b/service.betaseries.com/resources/lib/media.py @@ -0,0 +1,256 @@ +#!/usr/bin/python +# coding: utf-8 +# import web_pdb + +import logging +from resources.lib import kodiUtilities +from resources.lib import globals + +logger = logging.getLogger(__name__) + + +class Media: + """Provides information about media (episode or movie)""" + + type_media = None + + def __init__(self, media_id, playcount, playstatus): + self.media_id = media_id + self.playcount = playcount + self.playstatus = playstatus + + def get_media_info(self, type_media): + """Provides information about media + + Args: + type_media (str): episode or movie + + Returns: + [dict]: media information + """ + if type_media == "episode": + self.type_media = type_media + return self.__get_episode_info() + if type_media == "movie": + self.type_media = type_media + return self.__get_movie_info() + + def __get_episode_info(self): + """Provides information about episode + + Returns: + [dict]: media information + """ + tvdbid = False + tmdbid = False + tvdbepid = False + logstr = "" + try: + tvshow = kodiUtilities.getEpisodeDetailsFromKodi( + self.media_id, + [ + "tvshowid", + "showtitle", + "season", + "episode", + "uniqueid", + "playcount", + ], + ) + if "uniqueid" in tvshow: + if "tvdb" in tvshow["uniqueid"]: + tvdbepid = tvshow["uniqueid"]["tvdb"] + elif "imdb" in tvshow["uniqueid"]: + tvdbepid = tvshow["uniqueid"]["imdb"] + elif "tmdb" in tvshow["uniqueid"]: + tmdbid = tvshow["uniqueid"]["tmdb"] + elif "unknown" in tvshow["uniqueid"]: + # suppose to be tvdbid !!! + tvdbepid = tvshow["uniqueid"]["unknown"] + if tvdbepid: + if (tvdbepid).startswith("tt"): + tvdbepid = False + showtitle = tvshow["showtitle"] + epname = str(tvshow["season"]) + "x" + str(tvshow["episode"]) + logstr += f"Id: {self.media_id} Title: {tvshow['showtitle']} tvshowid: {tvshow['tvshowid']} tvdbepid: {tvdbepid}" + except Exception as e: + logger.error(f"getEpisodeDetailsFromKodi error for {self.media_id} : {e}") + return None + + if tvshow["tvshowid"] != -1: + try: + tvdbid_query = kodiUtilities.getShowDetailsFromKodi( + tvshow["tvshowid"], ["imdbnumber", "uniqueid"] + ) + if "uniqueid" in tvdbid_query: + if "tvdb" in tvdbid_query["uniqueid"]: + tvdbid = tvdbid_query["uniqueid"]["tvdb"] + elif "imdb" in tvdbid_query["uniqueid"]: + tvdbid = tvdbid_query["uniqueid"]["imdb"] + else: + tvdbid = tvdbid_query["imdbnumber"] + else: + tvdbid = tvdbid_query["imdbnumber"] + if tvdbid: + logstr += f"\ntvdbid: {tvdbid}" + if tmdbid: + logstr += f"\ntmdbid: {tmdbid}" + except Exception: + logger.info(logstr) + tvdbid = False + logger.error( + f"could not get tvshow/episode details for {self.media_id}" + ) + + # si imbd_id, convert to thetvdb_id + if tvdbid: + # logger.info('tvdbid: %s' % ( tvdbid) ) + if (tvdbid).startswith("tt"): + tvdbid = globals.betaseriesapi.tvdbidFromimbd(tvdbid, logstr) + + if not tvdbid: + # if tvdbepid, convert to thetvdb_id + if tvdbepid: + tvdbid = globals.betaseriesapi.tvdbidFromtvdbepid( + tvdbepid, showtitle, logstr + ) + + # si aucun tvdbid, chercher avec le Titre !!! + if not tvdbid: + tvdbid = globals.betaseriesapi.tvdbidFromTitle(showtitle, logstr) + if tvdbid: + # logger.info("found tvdbid "+ str(tvdbid ) +" for " + showtitle ) + logstr += f"\nBS tvdbid from title: {tvdbid}" + else: + logger.error( + "could not fetch tvshow's thetvdb_id from title for " + showtitle + ) + return None + follow = globals.betaseriesapi.followFromtvdbid(tvdbid, showtitle, logstr) + if not tvdbepid: + tvdbepid = globals.betaseriesapi.tvdbepidFromtvdbid( + tvdbid, showtitle, tvshow["season"], tvshow["episode"], logstr + ) + if not tvdbepid: + return None + + seen = False + dl = False + if tvshow["playcount"] < 1 and follow: + try: + seen_query = globals.betaseriesapi.tvdbInfFromtvdbepid( + tvdbepid, showtitle, logstr + ) + seen = seen_query["user"]["seen"] + dl = seen_query["user"]["downloaded"] + logstr += f" dl status: {dl} seen status: {seen}" + except Exception as e: + logger.info(logstr) + logger.warning(f"failed to get status for {showtitle} - {epname} : {e}") + logger.debug(logstr) + epinfo = { + "int_id": int(tvdbid), + "remote_id": int(tvdbepid), + "playcount": int(self.playcount), + "playstatus": bool(self.playstatus), + "showtitle": showtitle, + "title": epname, + "type": "episode", + "tvshow_playcount": tvshow["playcount"], + "followed": bool(follow), + "downloaded": bool(dl), + "seen": bool(seen), + } + return epinfo + + def __get_movie_info(self): + """Provides information about movie + + Returns: + [dict]: media information + """ + imdbid = False + tvdbid = False + id = False + seen = False + follow = False + epinfo = False + logstr = "" + if isinstance(self.media_id, int): + movie = kodiUtilities.getMovieDetailsFromKodi( + self.media_id, + [ + "uniqueid", + "imdbnumber", + "originaltitle", + "sorttitle", + "title", + ], + ) + try: + imdbid = movie["imdbnumber"] + moviename = movie["originaltitle"] + logstr += f"movie found= {moviename} {imdbid}" + except Exception: + logger.info("could not get movie details") + + if not imdbid: + imdbid = globals.betaseriesapi.imdbidFromTitle(moviename, logstr) + if not imdbid: + return None + + try: + tvdbid_query = globals.betaseriesapi.imdbidFromId(imdbid, logstr) + tvdbid = tvdbid_query["movie"]["tmdb_id"] + id = tvdbid_query["movie"]["id"] + except Exception: + logger.error( + "could not fetch movie {imdbid} thetmdb_id : {tvdbid} , {id}" + ) + return None + if "user" in tvdbid_query["movie"]: + follow = tvdbid_query["movie"]["user"]["in_account"] + seen = tvdbid_query["movie"]["user"]["status"] + epinfo = { + "int_id": int(id), + "remote_id": tvdbid, + "playcount": int(self.playcount), + "playstatus": bool(self.playstatus), + "showtitle": "", + "title": moviename, + "type": "movie", + "tvshow_playcount": 0, + "followed": bool(follow), + "downloaded": True, + "seen": bool(seen), + } + else: + try: + found = globals.betaseriesapi.imdbFromId(self.media_id, logstr) + if ( + found["title"] == self.media_id + or found["title"].lower() == self.media_id.lower() + ): + logstr += "found " + found["title"] + if "imdb_id" in found: + tvdbid = found["imdb_id"] + logstr += " imdb_id: " + str(tvdbid) + if "user" in found: + seen = found["user"]["status"] + epinfo = { + "int_id": int(found["id"]), + "remote_id": str(tvdbid), + "playcount": int(self.playcount), + "playstatus": bool(self.playstatus), + "showtitle": "", + "title": found["original_title"], + "type": "movie", + "tvshow_playcount": 0, + "followed": bool(follow), + "downloaded": True, + "seen": bool(seen), + } + except Exception: + logger.error(f"could not fetch movie's {self.media_id} imdb_id") + return None + return epinfo diff --git a/service.betaseries.com/resources/lib/monitor.py b/service.betaseries.com/resources/lib/monitor.py new file mode 100644 index 0000000..a5ddbb6 --- /dev/null +++ b/service.betaseries.com/resources/lib/monitor.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +# coding: utf-8 + +import xbmc +import logging + +logger = logging.getLogger(__name__) + + +class MyMonitor(xbmc.Monitor): + """monitor settings change""" + + def __init__(self, *args, **kwargs): + xbmc.Monitor.__init__(self) + self.action = kwargs["action"] + + def onSettingsChanged(self): + logger.debug("onSettingsChanged") + self.action() diff --git a/service.betaseries.com/resources/lib/player.py b/service.betaseries.com/resources/lib/player.py new file mode 100644 index 0000000..268c9b2 --- /dev/null +++ b/service.betaseries.com/resources/lib/player.py @@ -0,0 +1,212 @@ +#!/usr/bin/python +# coding: utf-8 +# import web_pdb + +import json +import xbmc +import xbmcaddon + +import logging +from resources.lib import utilities +from resources.lib.kodiUtilities import notification, getString +from resources.lib import kodiUtilities +from resources.lib.media import Media +from resources.lib import globals + +__addon__ = xbmcaddon.Addon("service.betaseries.com") +logger = logging.getLogger(__name__) + + +class MyPlayer(xbmc.Monitor): + """Monitoring Kodi Notifications""" + + def __init__(self, *args, **kwargs): + xbmc.Monitor.__init__(self) + self.action = kwargs["action"] + self.service = kwargs["service"] + self.Play = False + logger.debug("Player Class Init") + if self.service.update: + self.ScanBSMarkedEpisode() + + def onNotification(self, sender, method, data): + if sender == "xbmc": + if method == "VideoLibrary.OnScanFinished": + if self.service.update: + self.ScanBSMarkedEpisode() + elif method == "Player.OnPlay": + result = json.loads(data) + logger.debug("OnPlay: " + str(result)) + if "item" in result: + if utilities.isEpisode(result["item"]["type"]) or utilities.isMovie( + result["item"]["type"] + ): + # in case Player.OnPlay comes to fast after Player.OnStop + xbmc.sleep(1000) + self.Play = True + elif method == "Player.OnStop": + result = json.loads(data) + logger.debug("OnStop: " + str(result)) + # if viewing in file mode and playback stopped at the end + if "item" in result and result["end"]: + item = result["item"] + if utilities.isEpisode(item["type"]): + episode = False + if "id" in item: + episode = Media(item["id"], 1, self.Play).get_media_info( + "episode" + ) + elif ( + "episode" in item + and "season" in item + and "showtitle" in item + ): + logstr = "" + tvdbid = globals.betaseriesapi.tvdbidFromTitle( + item["showtitle"], logstr + ) + if tvdbid: + tvdbepid = globals.betaseriesapi.tvdbepidFromtvdbid( + tvdbid, + item["showtitle"], + item["season"], + item["episode"], + logstr, + ) + if tvdbepid: + episode = Media( + int(tvdbepid), 1, True + ).get_media_info("episode") + if not episode and "title" in item: + episode = Media(item["title"], 1, True).get_media_info( + "episode" + ) + # mark episode as watched + if episode: + logger.debug(episode) + self.action(episode, self.service) + elif utilities.isMovie(item["type"]): + movie = False + if "id" in item: + movie = Media(item["id"], 1, self.Play).get_media_info( + "movie" + ) + elif "title" in item: + movie = Media(item["title"], 1, self.Play).get_media_info( + "movie" + ) + # mark movie as watched + if movie: + self.action(movie, self.service) + else: + # wait 1s to avoid setting Play=False before marking episode + xbmc.sleep(1000) + self.Play = False + elif method == "VideoLibrary.OnUpdate": + result = json.loads(data) + logger.debug("OnUpdate: " + str(result)) + if "playcount" in result: + if "item" in result: + item = result["item"] + if utilities.isEpisode(item["type"]): + logger.debug( + "episode status changed for library id = %s, playcount = %s" + % (item["id"], result["playcount"]) + ) + episode = Media( + item["id"], result["playcount"], self.Play + ).get_media_info("episode") + logger.debug(episode) + if episode: + if result["playcount"] == 0 and not episode["seen"]: + # mark as downloaded + episode["playcount"] = -1 + self.action(episode, self.service) + self.action(episode, self.service) + self.Play = False + elif utilities.isMovie(item["type"]): + logger.debug( + "movie status changed for library id = %s, playcount = %s" + % (item["id"], result["playcount"]) + ) + movie = Media( + item["id"], result["playcount"], self.Play + ).get_media_info("movie") + logger.debug(movie) + if movie: + # mark as watched or not, depending on playcount + self.action(movie, self.service) + self.Play = False + + # rechercher les episodes non marques + def ScanBSMarkedEpisode(self): + """Do self.action on recently added media in kodi based on lastdate treatment""" + f = __addon__.getAddonInfo("path") + "/lastdate.tmp" + try: + with open(f, "r") as fic: + lastdate = fic.read() + except Exception: + lastdate = "2001-01-01 00:00:00" + newdate = lastdate + new = False + counter = 0 + # cree table de tous les episodes + result_episodes = kodiUtilities.getEpisodesFromKodi() + + if "episodes" in result_episodes: + logger.debug( + "Start scanning BS for viewed episode and compare with Kodi database" + ) + for media in result_episodes["episodes"]: + # web_pdb.set_trace() + ep_id = media["episodeid"] + seen = False + try: + tvshow = kodiUtilities.getEpisodeDetailsFromKodi( + ep_id, ["dateadded", "playcount"] + ) + if tvshow["playcount"] > 0: + seen = True + except Exception as e: + logger.error(f"getEpisodeDetailsFromKodi error for {ep_id} : {e}") + # passe au suivant si erreur + continue + + if tvshow["dateadded"] > lastdate: + new = True + if tvshow["dateadded"] > newdate: + newdate = tvshow["dateadded"] + # 0101 voir pour l'interet de not seen + if new: # or not seen: + # si pas vu, regarder sur BS si marque + + episode = Media(ep_id, -1, self.Play).get_media_info("episode") + if episode: + if seen: + episode["playcount"] = 1 + # follow ? # downloaded ? + if not episode["followed"] or not episode["downloaded"]: + self.action(episode, self.service) + if episode["tvshow_playcount"] < 1 and episode["seen"]: + result = kodiUtilities.setEpisodeDetailsOnKodi( + ep_id, {"playcount": 1} + ) + logger.info( + f"info :{episode['showtitle']},{episode['title']}" + ) + if result == "OK": + # logger.info(result,) + logger.info( + "episode marked watched or downloaded on BetaSeries.com", + ) + counter += 1 + else: + logger.error( + "error: failed to mark watched or downloaded on Betaseries.com", + ) + if counter > 0: + notification(getString(32010), getString(30021, str(counter))) + else: + logger.info("Scan finished, all episodes updated") + with open(f, "w") as fic: + fic.write(newdate) diff --git a/service.betaseries.com/resources/lib/service.py b/service.betaseries.com/resources/lib/service.py new file mode 100644 index 0000000..ec38530 --- /dev/null +++ b/service.betaseries.com/resources/lib/service.py @@ -0,0 +1,57 @@ +#!/usr/bin/python +# coding: utf-8 +# import web_pdb + +import xbmc +import logging +from resources.lib.monitor import MyMonitor +from resources.lib.player import MyPlayer +from resources.lib.serviceapi import ServiceApi +from resources.lib import globals +from resources.lib.kodiUtilities import ( + getSettingAsBool, + getSetting, + notification, + getString, +) + +logger = logging.getLogger(__name__) + + +class betaseriesService: + """Main Service""" + + def __init__(self): + """instanciate class""" + self.Monitor = MyMonitor(action=self.__get_settings) + self.service = None + self.__get_settings() + + while not xbmc.Monitor().abortRequested(): + xbmc.sleep(1000) + + def __get_settings(self): + """get users settings and launch actions""" + logger.debug("reading settings") + self.service = globals.Service( + getSettingAsBool("betaactive"), + getSettingAsBool("betafirst"), + getSetting("betauser"), + getSetting("betapass"), + getSettingAsBool("betabulk"), + getSettingAsBool("betamark"), + getSettingAsBool("betaunmark"), + getSettingAsBool("betafollow"), + getSettingAsBool("betanotify"), + getSettingAsBool("betaupdate"), + ) + if self.service.active and self.service.user and self.service.password: + globals.betaseriesapi = ServiceApi(self) + globals.betaseriesapi._service_authenticate( + self.service.user, self.service.password + ) + self.Player = MyPlayer( + action=globals.betaseriesapi._service_betaserie, service=self.service + ) + if self.service.notify: + notification(getString(32010), getString(30003)) diff --git a/service.betaseries.com/resources/lib/serviceapi.py b/service.betaseries.com/resources/lib/serviceapi.py new file mode 100644 index 0000000..9d2ed9b --- /dev/null +++ b/service.betaseries.com/resources/lib/serviceapi.py @@ -0,0 +1,596 @@ +#!/usr/bin/python +# coding: utf-8 +# import web_pdb + +import logging +import hashlib +import json +import time +from resources.lib import utilities +from resources.lib.kodiUtilities import notification, getString # , setSetting + +logger = logging.getLogger(__name__) + + +class ServiceApi: + """Class to exchange with betaseries.com api""" + + apikey = "2b78f54a7cc3" + apiurl = "https://api.betaseries.com" + apiver = "3.0" + + def __init__(self, betaseriesService): + # web_pdb.set_trace() + self.user = "" + self.pw = "" + self.bsService = betaseriesService + self.token = "" + self.auth_fail = False + self.failurecount = 0 + self.timercounter = 0 + self.timerexpiretime = 0 + + def __auth(self): + """low level Authenticate into betaseries.com + + Returns: + True or None + """ + # create a pass hash + md5pass = hashlib.md5() + md5pass.update(self.pw.encode("utf8")) + url = self.apiurl + "/members/auth" + urldata = { + "v": self.apiver, + "key": self.apikey, + "login": self.user, + "password": md5pass.hexdigest(), + } + try: + # authentication request + response = utilities.get_urldata(url, urldata, "POST") + # authentication response + data = json.loads(response) + logger.info("successfully authenticated") + except Exception: + logger.error("failed to connect for authentication") + return None + + # parse results + if "token" in data: + # get token + self.token = str(data["token"]) + # reset failure count + self.failurecount = 0 + # reset timer + self.timercounter = 0 + self.timerexpiretime = 0 + if data["errors"]: + self.__checkerrors(data["errors"][0], auth=True) + return None + else: + logger.debug("token find:" + self.token) + notification(getString(32010), getString(32008)) + return True + + def __checkerrors(self, error, auth=False, infos=""): + """treatment about error returned by betaseries API + + Args: + error (dict): error returned by API + auth (bool, optional): error occured during authentication process. Defaults to False. + infos (str, optional): text to add to message. Defaults to "". + Returns: + True on real error + False on non real error (informations returned by API with 0 code) + """ + if auth is False and error["code"] == 0: + return False + if error["code"] < 2000: + # API error + notification(getString(32010), getString(32002)) + logger.warning(f"bad API usage : [{error['code']}] - {error['text']}") + # disable the service, the monitor class will pick up the changes + # setSetting("betaactive", "false") + elif error["code"] > 4001: + # login error + notification(getString(32010), getString(32004)) + logger.warning("login or password incorrect") + self.auth_fail = True + elif error["code"] == 2001: + # drop our session key + self.token = "" + logger.info("bad token") + return True + elif error["code"] == 2003: + logger.info(f"already following show {infos}") + # everything else + elif auth is True: + self.__service_fail(True) + notification(getString(32010), getString(32001)) + logger.error("server error while authenticating") + notification(getString(32010), getString(32009)) + else: + notification(getString(32010), getString(32005, infos)) + logger.info(f"failed to follow show {infos}") + return True + + def __service_fail(self, timer): + """update informations when API auth failure + + Args: + timer (bool): set a timer if failure occurred during authentication phase + """ + timestamp = int(time.time()) + # increment failure counter + self.failurecount += 1 + # drop our session key if we encouter three failures + if self.failurecount > 2: + self.token = "" + # set a timer if failure occurred during authentication phase + if timer: + # wrap timer if we cycled through all timeout values + if self.timercounter == 0 or self.timercounter == 7680: + self.timercounter = 60 + else: + # increment timer + self.timercounter = 2 * self.timercounter + # set timer expire time + self.timerexpiretime = timestamp + self.timercounter + + def followFromtvdbid(self, tvdbid, showtitle, logstr): + """Find if Show is followed + + Args: + tvdbid (int): thetvdb_id for show + showtitle (str): show title for message + logstr (str): process information returned + + Returns: + bool + """ + url = self.apiurl + "/shows/display" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "thetvdb_id": tvdbid, + } + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + follow = tvdbid_query["show"]["in_account"] + logstr += f" follow status: {follow}" + return follow + except Exception: + logger.warning("could not get follow tvshow's status for " + showtitle) + return False + + def tvdbidFromTitle(self, showtitle, logstr): + """Find TVdbId from title for a Show + + Args: + showtitle (str): show title + logstr (str): process information returned + + Returns: + int or None: TVdbId Show + """ + url = self.apiurl + "/shows/list" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "order": "popularity", + "summary": "true", + "starting": showtitle.replace(" ", "+").encode( + "ascii", "xmlcharrefreplace" + ), + } + logger.info(urldata) + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + for found in tvdbid_query["shows"]: + if ( + found["title"] == showtitle + or found["title"].lower() == showtitle.lower() + ): + if "thetvdb_id" in found: + tvdbid = found["thetvdb_id"] + logstr += "tvdbid: " + str(tvdbid) + return tvdbid + except Exception: + logger.info(logstr) + logger.warning("No search result for tvshow's " + showtitle) + + def tvdbidFromimbd(self, imdbid, logstr): + """Find TVdbId from imdbId for a Show + + Args: + imdbid (int): thetvdb_id for show + logstr (str): process information returned + + Returns: + int or None: TVdbId Show + """ + url = self.apiurl + "/shows/display" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "imdb_id": imdbid, + } + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + tvdbid = tvdbid_query["show"]["thetvdb_id"] + logstr += f" convert to {tvdbid} " + except Exception: + logger.info(logstr) + logger.error(f"Cannot convert from {imdbid}") + return False + return tvdbid + + def tvdbInfFromtvdbepid(self, tvdbepid, showtitle, logstr): + """Find Information Episode from TVdbId episode + + Args: + tvdbepid (int): TVdbId episode + showtitle (str): show title for message + logstr (str): process information returned + + Returns: + dict: Betaseries information on media + """ + url = self.apiurl + "/episodes/display" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "thetvdb_id": tvdbepid, + } + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + # logger.info(tvdbid_query) + return tvdbid_query["episode"] + except Exception: + logger.info(logstr) + logger.error("failed to get Episode information for " + showtitle) + + def tvdbidFromtvdbepid(self, tvdbepid, showtitle, logstr): + """Find TVdbId Show from TVdbId episode + + Args: + tvdbepid (int): TVdbId episode + showtitle (str): show title for message + logstr (str): process information returned + + Returns: + int or None: TVdbId Show + """ + showid = False + url = self.apiurl + "/episodes/display" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "thetvdb_id": tvdbepid, + } + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + # logger.info(tvdbid_query) + if "show" in tvdbid_query["episode"]: + tvdbid = tvdbid_query["episode"]["show"]["thetvdb_id"] + logstr += "\nBS tvdbid from tvdbepid: %s" % (tvdbid) + return tvdbid + else: + showid = tvdbid_query["episode"]["show_id"] + except Exception: + logger.info(logstr) + logger.error("failed to get show_id for " + showtitle) + + if showid: + url = self.apiurl + "/shows/display" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "id": showid, + } + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + tvdbid = tvdbid_query["show"]["thetvdb_id"] + logstr += f"\nBS tvdbid from tvdbepid: {tvdbid}" + return tvdbid + except Exception: + logger.info(logstr) + logger.error( + "could not fetch tvshow's thetvdb_id from show_id for " + showtitle + ) + return False + + def tvdbepidFromtvdbid(self, tvdbid, showtitle, season, episode, logstr): + """Find TVdbId episode from TVdbId show, season and episode num + + Args: + tvdbid (int): TVdbId show + showtitle (str): show title for message + season (int): season number + episode (int): episode number + logstr (str): process information returned + + Returns: + int or None: TVdbId Episode + """ + epname = str(season) + "x" + str(episode) + url = self.apiurl + "/shows/episodes" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "thetvdb_id": tvdbid, + "season": season, + "episode": episode, + } + try: + tvdbepid_query = utilities.get_urldata(url, urldata, "GET") + tvdbepid_query = json.loads(tvdbepid_query) + tvdbepid = tvdbepid_query["episodes"][0]["thetvdb_id"] + logstr += f" tvdbepid: {tvdbepid} for Ep {epname}" + except Exception: + if logstr: + logger.info(logstr) + logger.error( + f"could not fetch episode's thetvdb_id for {showtitle}-{epname}" + ) + return False + return tvdbepid + + def imdbFromId(self, showtitle, logstr): + """Find imdbId from filename for a movie + + Args: + showtitle (str): movie filename + logstr (str): process information returned + + Returns: + int or None: imdbId Show + """ + url = self.apiurl + "/movies/scraper" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "file": showtitle.replace(" ", "+").encode("ascii", "xmlcharrefreplace"), + } + logger.debug(urldata) + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + return tvdbid_query["movie"] + except Exception: + logger.info(logstr) + logger.info(f"could not fetch movie's {showtitle} imdb_id") + return False + + def imdbidFromTitle(self, showtitle, logstr): + """Find imdbId from title for a movie + + Args: + showtitle (str): movie title + logstr (str): process information returned + + Returns: + int or None: imdbId Show + """ + url = self.apiurl + "/movies/scraper" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "file": showtitle.replace(" ", "+").encode("ascii", "xmlcharrefreplace"), + } + logger.debug(urldata) + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + for found in tvdbid_query["movies"]: + # logger.info(found) + logger.info("testing " + found["title"]) + if ( + found["title"] == showtitle + or found["title"].lower() == showtitle.lower() + ): + logger.info("found " + found["title"]) + if "imdb_id" in found: + tvdbid = found["imdb_id"] + logger.info("imdb_id: " + str(tvdbid)) + return tvdbid + except Exception: + logger.info(logstr) + logger.info(f"could not fetch movie's {showtitle} imdb_id") + return False + + def imdbidFromId(self, showid, logstr): + """Find information from imdbid for a movie + + Args: + showid (int): imdbid + logstr (str): process information returned + + Returns: + [dict]: movie informations + """ + url = self.apiurl + "/movies/movie" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "imdb_id": showid, + } + logger.debug(urldata) + try: + tvdbid_query = utilities.get_urldata(url, urldata, "GET") + tvdbid_query = json.loads(tvdbid_query) + return tvdbid_query + except Exception: + logger.info(logstr) + logger.info(f"could not fetch movie's {showid} imdb_infos") + return False + + def _service_betaserie(self, episode, service): + """mark as watched + + Args: + episode (dict): information on media + service (class): user settings + """ + # web_pdb.set_trace() + + # don't proceed if we had an authentication failure + if not self.auth_fail: + # test if we are authenticated + if not self.token: + # authenticate + self._service_authenticate() + # only proceed if authentication was succesful + if self.token: + # mark as watched if we still have a valid session key after submission and have episode info + if episode["int_id"] and episode["remote_id"]: + # first-only check + if not service.first or ( + service.first and episode["playcount"] <= 1 + ): + # mark as watched + self._service_mark(episode, service) + + def _service_authenticate(self, user=None, pw=None): + """authenticate if necessary into betaseries.com + + Args: + user ([str], optional): username. Defaults to None. + pw ([str], optional): password. Defaults to None. + """ + if user: + self.user = user + if pw: + self.pw = pw + # don't proceed if timeout timer has not expired + if self.timerexpiretime > int(time.time()): + return + if self.__auth(): + logger.info("successfully authenticated") + else: + notification(getString(32010), getString(32003)) + logger.error("failed to connect for authentication") + + def _service_mark(self, episode, service): + # abort if betamark = false and playcount > 0 and play = false + if not service.mark and episode["playcount"] > 0 and not episode["playstatus"]: + logger.info(f"abort marking, as play = {episode['playstatus']}") + return + # abort if betaunmark = false and playcount = 0 and play = false + elif ( + not service.unMark + and episode["playcount"] == 0 + and not episode["playstatus"] + ): + logger.info(f"abort unmarking, as play = {episode['playstatus']}") + return + if utilities.isEpisode(episode["type"]): + # follow show if BetaFollow = true + # if service.follow and episode["playcount"] != -1: + if service.follow and not episode["followed"]: + url = self.apiurl + "/shows/show" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "thetvdb_id": episode["int_id"], + } + try: + # marking request + response = utilities.get_urldata(url, urldata, "POST") + # marking response + data = json.loads(response) + except Exception: + self.__service_fail(False) + logger.info(f"failed to follow TV show {episode['showtitle']}") + # parse results + if data["errors"]: + if self.__checkerrors( + data["errors"][0], infos=episode["showtitle"] + ): + return None + + if service.notify: + notification( + getString(32010), getString(30013, episode["showtitle"]) + ) + logger.info(f"now following show {episode['showtitle']}") + if utilities.isMovie(episode["type"]): + # mark movie as watched + url = self.apiurl + "/movies/movie" + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "id": episode["int_id"], + "state": episode["playcount"], + } + method = "POST" + if episode["playcount"] == 0: + act = "not watched" + actlang = 30017 + else: + act = "watched" + actlang = 30016 + elif utilities.isEpisode(episode["type"]): + # mark episode as watched, unwatched or downloaded + urldata = { + "v": self.apiver, + "key": self.apikey, + "token": self.token, + "thetvdb_id": episode["remote_id"], + } + if service.bulk: + urldata.update({"bulk": 1}) + if episode["playcount"] == 0: + url = self.apiurl + "/episodes/watched" + method = "DELETE" + act = "not watched" + actlang = 30015 + elif episode["playcount"] == -1: + url = self.apiurl + "/episodes/downloaded" + method = "POST" + act = "downloaded" + actlang = 30101 + else: + url = self.apiurl + "/episodes/watched" + method = "POST" + act = "watched" + actlang = 30014 + try: + # marking request + response = utilities.get_urldata(url, urldata, method) + # marking response + data = json.loads(response) + except Exception: + self.__service_fail(False) + logger.warning(f"failed to mark as {act}") + return + # parse results + if data["errors"]: + if self.__checkerrors( + data["errors"][0], infos=episode["type"] + episode["title"] + ): + return None + + if service.notify: + notification(getString(32010), getString(actlang)) + logger.info(f"{episode['showtitle']} {episode['title']} marked as {act}") + return None diff --git a/service.betaseries.com/resources/lib/utilities.py b/service.betaseries.com/resources/lib/utilities.py new file mode 100644 index 0000000..c972770 --- /dev/null +++ b/service.betaseries.com/resources/lib/utilities.py @@ -0,0 +1,185 @@ +# coding: utf-8 +# +"""Some useful base functions""" +import logging +import traceback +import urllib.request +import urllib.parse +import urllib.error + +import json +import xbmc +import xbmcaddon +import platform + +__addon__ = xbmcaddon.Addon("service.betaseries.com") +logger = logging.getLogger(__name__) + + +def user_agent(): + """return appropriate header for user agent header information + + Returns: + str: text to put in user agent header + """ + json_query = json.loads( + xbmc.executeJSONRPC( + '{ "jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["version", "name"]}, "id": 1 }' + ) + ) + try: + major = str(json_query["result"]["version"]["major"]) + minor = str(json_query["result"]["version"]["minor"]) + name = "Kodi" + version = f"{name} {major}.{minor}" + except Exception: + logger.error("could not get app version") + version = "XBMC" + splatform = platform.system() + " " + platform.release() + addon_id = __addon__.getAddonInfo("id") + addon_version = __addon__.getAddonInfo("version") + return ( + f"Mozilla/5.0 (compatible; {splatform}; {version}; {addon_id}/{addon_version})" + ) + + +def get_urldata(url, urldata, method): + """get json informations + + Args: + url (str): address + urldata (dict): body to send + method (str): HTTP method (GET, POST, PUT, DELETE, ....) + + Returns: + dict: informations from url + """ + # create a handler + handler = urllib.request.HTTPSHandler() + # create an openerdirector instance + opener = urllib.request.build_opener(handler) + # encode urldata + body = urllib.parse.urlencode(urldata).encode("utf-8") + # build a request + req = urllib.request.Request(url, data=body) + # add any other information you want + req.add_header("Accept", "application/json") + req.add_header("User-Agent", user_agent()) + # overload the get method function + req.get_method = lambda: method + try: + # response = urllib2.urlopen(req) + connection = opener.open(req) + except urllib.error.HTTPError as e: + connection = e + if connection.code: + response = connection.read() + return response + else: + logger.error("response empty") + return 0 + + +def isMovie(type): + """know if type is a movie + + Args: + type (str): information to test + + Returns: + bool: True if Ok, else False + """ + return type == "movie" + + +def isEpisode(type): + """know if type is an episode + + Args: + type (str): information to test + + Returns: + bool: True if Ok, else False + """ + return type == "episode" + + +def isShow(type): + """know if type is a show + + Args: + type (str): information to test + + Returns: + bool: True if Ok, else False + """ + return type == "show" + + +def isSeason(type): + """know if type is a season + + Args: + type (str): information to test + + Returns: + bool: True if Ok, else False + """ + return type == "season" + + +def isValidMediaType(type): + """know if type is ok + + Args: + type (str): information to test + + Returns: + bool: True if Ok, else False + """ + return type in ["movie", "show", "season", "episode"] + + +def createError(ex): + """Templating for error logger + + Args: + ex (object): Exception information + + Returns: + str: message to put in logger + """ + template = ( + "EXCEPTION Thrown (PythonToCppException) : -->Python callback/script returned the following error<--\n" + " - NOTE: IGNORING THIS CAN LEAD TO MEMORY LEAKS!\n" + "Error Type: \n" + "Error Contents: {1!r}\n" + "{2}" + "-->End of Python script error report<--" + ) + return template.format(type(ex).__name__, ex.args, traceback.format_exc()) + + +def checkIfNewVersion(old, new): + """compare old and new version + + Args: + old (str): old version must be 0.0.0 format + new ([type]): new version must be 0.0.0 format + + Returns: + bool: True if new version + """ + # Check if old is empty, it might be the first time we check + if old == "": + return True + # Major + if old[0] < new[0]: + return True + # Minor + if old[1] < new[1]: + return True + # Revision + if old[2] < new[2]: + return True + return False diff --git a/service.betaseries.com/resources/settings.xml b/service.betaseries.com/resources/settings.xml index 5b89cf8..b6b39c0 100644 --- a/service.betaseries.com/resources/settings.xml +++ b/service.betaseries.com/resources/settings.xml @@ -1,14 +1,17 @@ + - - + + - + + + - + \ No newline at end of file diff --git a/service.betaseries.com/service.betaseries.com-1.0.10.zip b/service.betaseries.com/service.betaseries.com-1.0.10.zip deleted file mode 100644 index 0d61dbe..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.10.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.11.zip b/service.betaseries.com/service.betaseries.com-1.0.11.zip deleted file mode 100644 index 2f553c1..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.11.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.12.zip b/service.betaseries.com/service.betaseries.com-1.0.12.zip deleted file mode 100644 index 8adb587..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.12.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.13.zip b/service.betaseries.com/service.betaseries.com-1.0.13.zip new file mode 100755 index 0000000..6ef4c24 Binary files /dev/null and b/service.betaseries.com/service.betaseries.com-1.0.13.zip differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.14.zip b/service.betaseries.com/service.betaseries.com-1.0.14.zip new file mode 100755 index 0000000..6e0ebb1 Binary files /dev/null and b/service.betaseries.com/service.betaseries.com-1.0.14.zip differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.5.zip b/service.betaseries.com/service.betaseries.com-1.0.5.zip deleted file mode 100644 index b82e086..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.5.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.6.zip b/service.betaseries.com/service.betaseries.com-1.0.6.zip deleted file mode 100644 index 34f8999..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.6.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.7.zip b/service.betaseries.com/service.betaseries.com-1.0.7.zip deleted file mode 100644 index 61d026c..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.7.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.8.zip b/service.betaseries.com/service.betaseries.com-1.0.8.zip deleted file mode 100644 index a95fdd7..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.8.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-1.0.9.zip b/service.betaseries.com/service.betaseries.com-1.0.9.zip deleted file mode 100644 index 31cbad7..0000000 Binary files a/service.betaseries.com/service.betaseries.com-1.0.9.zip and /dev/null differ diff --git a/service.betaseries.com/service.betaseries.com-2.0.0.zip b/service.betaseries.com/service.betaseries.com-2.0.0.zip new file mode 100644 index 0000000..ea78a58 Binary files /dev/null and b/service.betaseries.com/service.betaseries.com-2.0.0.zip differ diff --git a/service.subtitles.addic7ed/addon.xml b/service.subtitles.addic7ed/addon.xml index d799850..c950dce 100644 --- a/service.subtitles.addic7ed/addon.xml +++ b/service.subtitles.addic7ed/addon.xml @@ -1,15 +1,15 @@ - - - - - - - - - all - Addic7ed subtitles service - Service de sous-titres Addic7ed - Search and download subtitles from Addic7ed.com - Rechercher et télécharger des sous-titres depuis Addic7ed.com - - + + + + + + + + + all + Addic7ed subtitles service + Service de sous-titres Addic7ed + Search and download subtitles from Addic7ed.com + Rechercher et télécharger des sous-titres depuis Addic7ed.com + + diff --git a/service.subtitles.addic7ed/changelog.txt b/service.subtitles.addic7ed/changelog.txt index 37b7cb8..ae23226 100644 --- a/service.subtitles.addic7ed/changelog.txt +++ b/service.subtitles.addic7ed/changelog.txt @@ -1,26 +1,26 @@ -1.0.8 -- option did not work properly - -1.0.7 -- small fix for last option label - -1.0.6 -- fixed rmtree with accents, added option to hide incomplete subtitles - -1.0.5 -- remove httplib usage - -1.0.4 -- add missing httplib in imports - -1.0.3 -- add some settings and localize things - -1.0.2 -- major corrections and optimizations - -1.0.1 -- minor corrections - -1.0.0 -- initial release +1.0.8 +- option did not work properly + +1.0.7 +- small fix for last option label + +1.0.6 +- fixed rmtree with accents, added option to hide incomplete subtitles + +1.0.5 +- remove httplib usage + +1.0.4 +- add missing httplib in imports + +1.0.3 +- add some settings and localize things + +1.0.2 +- major corrections and optimizations + +1.0.1 +- minor corrections + +1.0.0 +- initial release diff --git a/service.subtitles.betaseries/addon.xml b/service.subtitles.betaseries/addon.xml index 08704c9..073697d 100644 --- a/service.subtitles.betaseries/addon.xml +++ b/service.subtitles.betaseries/addon.xml @@ -1,15 +1,15 @@ - - - - - - - - - all - BetaSeries subtitles service - Service de sous-titres BetaSeries - Search and download subtitles from BetaSeries.com - Rechercher et télécharger des sous-titres depuis BetaSeries.com - - + + + + + + + + + all + BetaSeries subtitles service + Service de sous-titres BetaSeries + Search and download subtitles from BetaSeries.com + Rechercher et télécharger des sous-titres depuis BetaSeries.com + + diff --git a/service.subtitles.betaseries/changelog.txt b/service.subtitles.betaseries/changelog.txt index 394a84b..f84be8d 100644 --- a/service.subtitles.betaseries/changelog.txt +++ b/service.subtitles.betaseries/changelog.txt @@ -1,32 +1,32 @@ -1.0.8 -- fix issue #17 - -1.0.7 -- fixed rmtree with accents - -1.0.6 -- fix unicode, other fixes and big cleanup - -1.0.5 -- fix temp dir cleanup - -1.0.4 -- fix CC and zip langs - -1.0.3 -- small improvements and add data on ua - -1.0.2 -- improve sorting and a few things - -1.0.1 -- lang fix (ignore case) - -1.0.0 -- complete rewriting - -0.2 -- some changes - -0.1 -- initial release +1.0.8 +- fix issue #17 + +1.0.7 +- fixed rmtree with accents + +1.0.6 +- fix unicode, other fixes and big cleanup + +1.0.5 +- fix temp dir cleanup + +1.0.4 +- fix CC and zip langs + +1.0.3 +- small improvements and add data on ua + +1.0.2 +- improve sorting and a few things + +1.0.1 +- lang fix (ignore case) + +1.0.0 +- complete rewriting + +0.2 +- some changes + +0.1 +- initial release diff --git a/service.subtitles.tvsubtitles/addon.xml b/service.subtitles.tvsubtitles/addon.xml index 6292d8f..4a2c15d 100644 --- a/service.subtitles.tvsubtitles/addon.xml +++ b/service.subtitles.tvsubtitles/addon.xml @@ -1,15 +1,15 @@ - - - - - - - - - all - tvsubtitles.net subtitles service - Service de sous-titres tvsubtitles.net - Search and download subtitles from tvsubtitles.net - Rechercher et télécharger des sous-titres depuis tvsubtitles.net - - + + + + + + + + + all + tvsubtitles.net subtitles service + Service de sous-titres tvsubtitles.net + Search and download subtitles from tvsubtitles.net + Rechercher et télécharger des sous-titres depuis tvsubtitles.net + + diff --git a/service.subtitles.tvsubtitles/changelog.txt b/service.subtitles.tvsubtitles/changelog.txt index ac9bf54..7925235 100644 --- a/service.subtitles.tvsubtitles/changelog.txt +++ b/service.subtitles.tvsubtitles/changelog.txt @@ -1,8 +1,8 @@ -1.0.2 -- fixed rmtree with accents - -1.0.1 -- remove httplib usage - -1.0.0 -- initial release +1.0.2 +- fixed rmtree with accents + +1.0.1 +- remove httplib usage + +1.0.0 +- initial release diff --git a/service.subtitles.tvsubtitles/lib/BeautifulSoup.py b/service.subtitles.tvsubtitles/lib/BeautifulSoup.py old mode 100755 new mode 100644 diff --git a/service.subtitles.tvsubtitles/lib/LICENSE.txt b/service.subtitles.tvsubtitles/lib/LICENSE.txt old mode 100755 new mode 100644