├── resources ├── proxy │ ├── __init__.py │ ├── win_inet_pton.py │ ├── immunicity.py │ └── antizapret.py ├── contenters │ ├── __init__.py │ ├── unused │ │ ├── __init__.py │ │ ├── EZTV.py │ │ └── CXZ.py │ ├── RiperAM.py │ ├── FastTorrent.py │ ├── ThePirateBaySe.py │ └── SWESUB.py ├── scrapers │ ├── __init__.py │ ├── fuzzywuzzy │ │ ├── __init__.py │ │ ├── string_processing.py │ │ ├── utils.py │ │ ├── StringMatcher.py │ │ ├── process.py │ │ └── fuzz.py │ ├── kinopoisk │ │ ├── LOGGER.py │ │ ├── pluginsettings.py │ │ ├── __init__.py │ │ ├── pageparser.py │ │ ├── HTTP.py │ │ └── translit.py │ ├── html.py │ ├── scrapers.py │ └── cache.py ├── images │ └── black.png ├── __init__.py ├── utorrent │ ├── __init__.py │ └── dopal │ │ ├── logutils.py │ │ ├── persistency.py │ │ ├── __init__.py │ │ ├── main.py │ │ ├── interact.py │ │ ├── debug.py │ │ ├── utils.py │ │ └── xmlutils.py ├── searchers │ └── README.txt ├── language │ ├── Ukrainian │ │ └── strings.xml │ ├── Hebrew │ │ └── strings.xml │ ├── slovak │ │ └── strings.po │ ├── Hungarian │ │ └── strings.xml │ ├── English │ │ └── strings.xml │ ├── spanish │ │ └── strings.xml │ └── Russian │ │ └── strings.xml └── settings.xml ├── icon.png ├── icons ├── fav.png ├── clear.png ├── kodi.png ├── list.png ├── media.png ├── unfav.png ├── video.png ├── vuze.png ├── deluge.png ├── download.png ├── history2.png ├── magnet.png ├── search.png ├── settings.png ├── watched.png ├── pause-icon.png ├── stop-icon.png ├── ContentPanel.png ├── download-icon.png ├── qbittorrent.png ├── torrentPlayer.png ├── transmission.png ├── upload-icon.png ├── torrent-client.png └── searchwindow │ ├── fvuze.png │ ├── button.psd │ ├── fdeluge.png │ ├── fsearch.png │ ├── nfvuze.png │ ├── fhistory.png │ ├── fkeyboard.png │ ├── fwatched.png │ ├── nfdeluge.png │ ├── nfhistory.png │ ├── nfkeyboard.png │ ├── nfsearch.png │ ├── nfwatched.png │ ├── fqbittorrent.png │ ├── fcontrolcenter.png │ ├── fdownloadstatus.png │ ├── ftorrent-client.png │ ├── ftransmission.png │ ├── nfcontrolcenter.png │ ├── nfqbittorrent.png │ ├── nftransmission.png │ ├── nfdownloadstatus.png │ └── nftorrent-client.png ├── .idea ├── scopes │ └── scope_settings.xml ├── vcs.xml ├── misc.xml ├── encodings.xml ├── modules.xml └── plugin.video.torrenter.iml ├── README.txt ├── .gitattributes ├── default.py ├── addon.xml ├── .gitignore ├── Downloader.py └── AceStream.py /resources/proxy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/contenters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/scrapers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/contenters/unused/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icon.png -------------------------------------------------------------------------------- /icons/fav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/fav.png -------------------------------------------------------------------------------- /resources/scrapers/fuzzywuzzy/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '0.4.0' 3 | -------------------------------------------------------------------------------- /icons/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/clear.png -------------------------------------------------------------------------------- /icons/kodi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/kodi.png -------------------------------------------------------------------------------- /icons/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/list.png -------------------------------------------------------------------------------- /icons/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/media.png -------------------------------------------------------------------------------- /icons/unfav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/unfav.png -------------------------------------------------------------------------------- /icons/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/video.png -------------------------------------------------------------------------------- /icons/vuze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/vuze.png -------------------------------------------------------------------------------- /icons/deluge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/deluge.png -------------------------------------------------------------------------------- /icons/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/download.png -------------------------------------------------------------------------------- /icons/history2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/history2.png -------------------------------------------------------------------------------- /icons/magnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/magnet.png -------------------------------------------------------------------------------- /icons/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/search.png -------------------------------------------------------------------------------- /icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/settings.png -------------------------------------------------------------------------------- /icons/watched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/watched.png -------------------------------------------------------------------------------- /icons/pause-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/pause-icon.png -------------------------------------------------------------------------------- /icons/stop-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/stop-icon.png -------------------------------------------------------------------------------- /icons/ContentPanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/ContentPanel.png -------------------------------------------------------------------------------- /icons/download-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/download-icon.png -------------------------------------------------------------------------------- /icons/qbittorrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/qbittorrent.png -------------------------------------------------------------------------------- /icons/torrentPlayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/torrentPlayer.png -------------------------------------------------------------------------------- /icons/transmission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/transmission.png -------------------------------------------------------------------------------- /icons/upload-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/upload-icon.png -------------------------------------------------------------------------------- /icons/torrent-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/torrent-client.png -------------------------------------------------------------------------------- /icons/searchwindow/fvuze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fvuze.png -------------------------------------------------------------------------------- /resources/images/black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/resources/images/black.png -------------------------------------------------------------------------------- /icons/searchwindow/button.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/button.psd -------------------------------------------------------------------------------- /icons/searchwindow/fdeluge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fdeluge.png -------------------------------------------------------------------------------- /icons/searchwindow/fsearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fsearch.png -------------------------------------------------------------------------------- /icons/searchwindow/nfvuze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfvuze.png -------------------------------------------------------------------------------- /icons/searchwindow/fhistory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fhistory.png -------------------------------------------------------------------------------- /icons/searchwindow/fkeyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fkeyboard.png -------------------------------------------------------------------------------- /icons/searchwindow/fwatched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fwatched.png -------------------------------------------------------------------------------- /icons/searchwindow/nfdeluge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfdeluge.png -------------------------------------------------------------------------------- /icons/searchwindow/nfhistory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfhistory.png -------------------------------------------------------------------------------- /icons/searchwindow/nfkeyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfkeyboard.png -------------------------------------------------------------------------------- /icons/searchwindow/nfsearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfsearch.png -------------------------------------------------------------------------------- /icons/searchwindow/nfwatched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfwatched.png -------------------------------------------------------------------------------- /resources/__init__.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | ''' 3 | Torrenter v2 plugin for XBMC/Kodi 4 | Copyright (C) 2015 DiMartino 5 | ''' -------------------------------------------------------------------------------- /icons/searchwindow/fqbittorrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fqbittorrent.png -------------------------------------------------------------------------------- /icons/searchwindow/fcontrolcenter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fcontrolcenter.png -------------------------------------------------------------------------------- /icons/searchwindow/fdownloadstatus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/fdownloadstatus.png -------------------------------------------------------------------------------- /icons/searchwindow/ftorrent-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/ftorrent-client.png -------------------------------------------------------------------------------- /icons/searchwindow/ftransmission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/ftransmission.png -------------------------------------------------------------------------------- /icons/searchwindow/nfcontrolcenter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfcontrolcenter.png -------------------------------------------------------------------------------- /icons/searchwindow/nfqbittorrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfqbittorrent.png -------------------------------------------------------------------------------- /icons/searchwindow/nftransmission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nftransmission.png -------------------------------------------------------------------------------- /resources/utorrent/__init__.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | ''' 3 | Torrenter v2 plugin for XBMC/Kodi 4 | Copyright (C) 2015 DiMartino 5 | ''' -------------------------------------------------------------------------------- /icons/searchwindow/nfdownloadstatus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nfdownloadstatus.png -------------------------------------------------------------------------------- /icons/searchwindow/nftorrent-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiMartinoXBMC/plugin.video.torrenter/HEAD/icons/searchwindow/nftorrent-client.png -------------------------------------------------------------------------------- /resources/searchers/README.txt: -------------------------------------------------------------------------------- 1 | Moved to MyShows.me Kodi Repo, please install from it. 2 | 3 | https://bitbucket.org/DiMartino/myshows.me-kodi-repo/downloads/repository.myshows.me.zip -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/plugin.video.torrenter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Plugin helps you to watch videos from p2p torrent-networks, without full predownload (uses inner python-libtorrent) or Ace Stream. It also can add, control torrents and play downloaded files with external uTorrent, Transmission, Vuze or Deluge. 2 | Official forum thread and FAQ: http://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html 3 | 4 | Инструкции и обсуждение: http://xbmc.ru/forum/showthread.php?t=6837 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /resources/scrapers/kinopoisk/LOGGER.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | try: 4 | import xbmcaddon 5 | from functions import log, debug 6 | 7 | __settings__ = xbmcaddon.Addon("plugin.video.torrenter") 8 | force_debug = __settings__.getSetting("debug") 9 | except: 10 | force_debug = 'true' 11 | 12 | 13 | def Log(msg, force=False): 14 | log(msg) 15 | 16 | 17 | def Debug(msg, force=False): 18 | if force_debug == 'true' or force: 19 | debug(msg, True) 20 | 21 | 22 | def Info(msg, force=False): 23 | if force_debug == 'true' or force: 24 | debug(msg, True) 25 | 26 | 27 | def Warn(msg, force=False): 28 | if force_debug == 'true' or force: 29 | debug(msg, True) -------------------------------------------------------------------------------- /resources/scrapers/fuzzywuzzy/string_processing.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import re 3 | 4 | 5 | class StringProcessor(object): 6 | """ 7 | This class defines method to process strings in the most 8 | efficient way. Ideally all the methods below use unicode strings 9 | for both input and output. 10 | """ 11 | 12 | @classmethod 13 | def replace_non_letters_non_numbers_with_whitespace(cls, a_string): 14 | """ 15 | This function replaces any sequence of non letters and non 16 | numbers with a single white space. 17 | """ 18 | regex = re.compile(r"(?ui)\W") 19 | return regex.sub(" ", a_string) 20 | 21 | @classmethod 22 | def strip(cls, a_string): 23 | """ 24 | This function strips leading and trailing white space. 25 | """ 26 | 27 | return a_string.strip() 28 | 29 | @classmethod 30 | def to_lower_case(cls, a_string): 31 | """ 32 | This function returns the lower-cased version of the string given. 33 | """ 34 | return a_string.lower() 35 | 36 | @classmethod 37 | def to_upper_case(cls, a_string): 38 | """ 39 | This function returns the upper-cased version of the string given. 40 | """ 41 | return a_string.upper() 42 | -------------------------------------------------------------------------------- /resources/utorrent/dopal/logutils.py: -------------------------------------------------------------------------------- 1 | # File: logutils.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | ''' 18 | Module containing various logging-related utilities and classes. 19 | ''' 20 | # From: 21 | # http://news.hping.org/comp.lang.python.archive/19937.html 22 | def noConfig(): 23 | ''' 24 | This function should be called to indicate that you are explicitly 25 | notifying the logging module that you do not intend to add any 26 | handlers to the root logger. 27 | 28 | This suppresses the warning C{No handlers could be found for logger 29 | "root"} from being emitted. This function performs the following 30 | call to disable the warning:: 31 | logging.root.manager.emittedNoHandlerWarning = True 32 | ''' 33 | import logging 34 | 35 | logging.root.manager.emittedNoHandlerWarning = True 36 | -------------------------------------------------------------------------------- /default.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter v2 plugin for XBMC/Kodi 4 | Copyright (C) 2012-2015 Vadim Skorba v1 - DiMartino v2 5 | https://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | 21 | import sys 22 | import gc 23 | import xbmcaddon 24 | from functions import log 25 | 26 | 27 | __settings__ = xbmcaddon.Addon(id='plugin.video.torrenter') 28 | __version__ = __settings__.getAddonInfo('version') 29 | __plugin__ = __settings__.getAddonInfo('name') + " v." + __version__ 30 | __root__ = __settings__.getAddonInfo('path') 31 | 32 | if (__name__ == "__main__" ): 33 | log(__plugin__) 34 | import Core 35 | 36 | core = Core.Core() 37 | if (not sys.argv[2]): 38 | core.sectionMenu() 39 | else: 40 | params = core.getParameters(sys.argv[2]) 41 | core.executeAction(params) 42 | del core 43 | 44 | collected = gc.collect() 45 | log("Garbage collector: collected %d objects." % (collected)) -------------------------------------------------------------------------------- /resources/scrapers/html.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | import htmlentitydefs 5 | 6 | RE = { 7 | 'space': re.compile('[ ]{2,}', re.U | re.S), 8 | 'cl': re.compile('[\n]{2,}', re.U | re.S), 9 | 'br': re.compile('<\s*br[\s/]*>', re.U | re.S), 10 | 'inner': re.compile('<[^>]*>[^<]+<\s*/[^>]*>', re.U | re.S), 11 | 'html': re.compile('<[^>]*>', re.U | re.S), 12 | 'entity': re.compile('&#?\w+;', re.U) 13 | } 14 | 15 | UNSUPPORT = { 16 | '—': '-' 17 | } 18 | 19 | 20 | class Clear: 21 | def text(self, text, inner=False): 22 | text = self._unsupport(text).replace(u'\r', u'\n') 23 | text = RE['br'].sub(u'\n', text) 24 | if inner: 25 | text = RE['inner'].sub(u'', text) 26 | text = RE['html'].sub(u'', text) 27 | text = self.char(text) 28 | text = RE['space'].sub(u' ', text) 29 | return RE['cl'].sub(u'\n', text).strip() 30 | 31 | def string(self, text, space=u''): 32 | return self.text(text).replace(u'\n', space).strip() 33 | 34 | def char(self, text): 35 | return RE['entity'].sub(self._unescape, self._unsupport(text)) 36 | 37 | def _unsupport(self, text): 38 | for tag, value in UNSUPPORT.iteritems(): 39 | text = text.replace(tag, value) 40 | return text 41 | 42 | def _unescape(self, m): 43 | text = m.group(0) 44 | if text[:2] == u"&#": 45 | try: 46 | if text[:3] == u"&#x": 47 | return unichr(int(text[3:-1], 16)) 48 | else: 49 | return unichr(int(text[2:-1])) 50 | except ValueError: 51 | pass 52 | else: 53 | try: 54 | text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) 55 | except KeyError: 56 | pass 57 | return text 58 | -------------------------------------------------------------------------------- /resources/scrapers/fuzzywuzzy/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import sys 3 | 4 | from fuzzywuzzy.string_processing import StringProcessor 5 | 6 | 7 | PY3 = sys.version_info[0] == 3 8 | 9 | 10 | def validate_string(s): 11 | try: 12 | if len(s) > 0: 13 | return True 14 | else: 15 | return False 16 | except: 17 | return False 18 | 19 | bad_chars = str('') # ascii dammit! 20 | for i in range(128, 256): 21 | bad_chars += chr(i) 22 | if PY3: 23 | translation_table = dict((ord(c), None) for c in bad_chars) 24 | 25 | 26 | def asciionly(s): 27 | if PY3: 28 | return s.translate(translation_table) 29 | else: 30 | return s.translate(None, bad_chars) 31 | 32 | 33 | def asciidammit(s): 34 | if type(s) is str: 35 | return asciionly(s) 36 | elif type(s) is unicode: 37 | return asciionly(s.encode('ascii', 'ignore')) 38 | else: 39 | return asciidammit(unicode(s)) 40 | 41 | 42 | def make_type_consistent(s1, s2): 43 | if isinstance(s1, str) and isinstance(s2, str): 44 | return s1, s2 45 | 46 | elif isinstance(s1, unicode) and isinstance(s2, unicode): 47 | return s1, s2 48 | 49 | else: 50 | return unicode(s1), unicode(s2) 51 | 52 | 53 | def full_process(s, force_ascii=False): 54 | """Process string by 55 | -- removing all but letters and numbers 56 | -- trim whitespace 57 | -- force to lower case 58 | if force_ascii == True, force convert to ascii""" 59 | 60 | if s is None: 61 | return "" 62 | 63 | if force_ascii: 64 | s = asciidammit(s) 65 | # Keep only Letters and Numbres (see Unicode docs). 66 | string_out = StringProcessor.replace_non_letters_non_numbers_with_whitespace(s) 67 | # Force into lowercase. 68 | string_out = StringProcessor.to_lower_case(string_out) 69 | # Remove leading and trailing whitespaces. 70 | string_out = StringProcessor.strip(string_out) 71 | return string_out 72 | 73 | 74 | def intr(n): 75 | '''Returns a correctly rounded integer''' 76 | return int(round(n)) 77 | -------------------------------------------------------------------------------- /resources/scrapers/kinopoisk/pluginsettings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Global definitions for KinoPoiskRu plugin. 4 | # Copyright (C) 2013 Yevgeny Nyden 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 | # 02110-1301, USA. 20 | # 21 | # @author zhenya (Yevgeny Nyden) 22 | # @version 1.52 23 | # @revision 148 24 | 25 | # Default plugin preferences. When modifying, please also change 26 | # corresponding values in the ../DefaultPrefs.json file. 27 | KINOPOISK_PREF_DEFAULT_MAX_POSTERS = 1 28 | KINOPOISK_PREF_DEFAULT_MAX_ART = 2 29 | KINOPOISK_PREF_DEFAULT_GET_ALL_ACTORS = False 30 | KINOPOISK_PREF_DEFAULT_IMDB_SUPPORT = True 31 | KINOPOISK_PREF_DEFAULT_IMDB_RATING = False 32 | KINOPOISK_PREF_DEFAULT_KP_RATING = False 33 | 34 | ENCODING_KINOPOISK_PAGE = 'cp1251' 35 | 36 | # Разные страницы сайта. 37 | KINOPOISK_SITE_BASE = 'http://www.kinopoisk.ru/' 38 | KINOPOISK_RESOURCE_BASE = 'http://st.kinopoisk.ru/' 39 | KINOPOISK_TITLE_PAGE_URL = KINOPOISK_SITE_BASE + 'film/%s/' 40 | KINOPOISK_CAST_PAGE_URL = KINOPOISK_SITE_BASE + 'film/%s/cast/' 41 | KINOPOISK_STUDIO_PAGE_URL = KINOPOISK_SITE_BASE + 'film/%s/studio/' 42 | KINOPOISK_THUMBNAIL_BIG_URL = KINOPOISK_RESOURCE_BASE + 'images/film_big/%s.jpg' 43 | KINOPOISK_THUMBNAIL_SMALL_URL = KINOPOISK_RESOURCE_BASE + 'images/film/%s.jpg' 44 | KINOPOISK_POSTERS_URL = KINOPOISK_SITE_BASE + 'film/%s/posters/page/%d/' 45 | KINOPOISK_STILLS_URL = KINOPOISK_SITE_BASE + 'film/%s/stills/page/%d/' 46 | 47 | # Страница поиска. 48 | KINOPOISK_SEARCH = KINOPOISK_SITE_BASE + 'index.php?first=no&kp_query=%s' 49 | KINOPOISK_SEARCH_SIMPLE = 'http://m.kinopoisk.ru/search/%s/' -------------------------------------------------------------------------------- /addon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | video 15 | 16 | 17 | all 18 | Plugin helps you to watch videos from p2p torrent-networks, without full predownload. 19 | 20 | It also can add, control torrents and play downloaded files with external torrent-client (uTorrent, Transmisson, Vuse, Deluge, qBittorrent) or python-libtorrent. 21 | 22 | GNU GPLv3 http://www.gnu.org/licenses/ 23 | Плагин позволяет просматривать видео из пиринговых торрент-сетей, не дожидаясь полного скачивания. 24 | 25 | Так же плагин может добавлять, проигрывать и управлять скачками в торрент клиентах (uTorrent, Transmisson, Deluge и Vuse, qBittorrent) или средставми python-libtorrent. 26 | 27 | GNU GPLv3 http://www.gnu.org/licenses/ 28 | Complemento que nos permite ver vídeos que se distribuyen en redes BitTorrent sin necesidad de descargarlos previamente por completo. 29 | 30 | También gestiona torrents y nos deja elegir entre su propio cliente interno y otros externos (μTorrent, Transmission, Deluge, Vuze, qBittorrent), el cliente BitTorrent con el que realizar las descargas. 31 | 32 | https://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html 33 | http://xbmc.ru/forum/showthread.php?t=6837 34 | https://github.com/inpos/plugin.video.torrenter 35 | 36 | 37 | -------------------------------------------------------------------------------- /resources/utorrent/dopal/persistency.py: -------------------------------------------------------------------------------- 1 | # File: persistency.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | ''' 17 | Support module containing code which supports the persistency functionality offered by DOPAL. 18 | ''' 19 | _refresh_methods = { 20 | 21 | # Simple ones. 22 | 'PluginConfig': lambda pi, obj: \ 23 | pi.getPluginconfig(), 24 | 'DownloadManager': lambda pi, obj: \ 25 | pi.getDownloadManager(), 26 | 'IPFilter': lambda pi, obj: \ 27 | pi.getIPFilter(), 28 | 'ShortCuts': lambda pi, obj: \ 29 | pi.getShortCuts(), 30 | 'TorrentManager': lambda pi, obj: \ 31 | pi.getTorrentManager(), 32 | 'PluginInterface': lambda pi, obj: \ 33 | pi, 34 | 35 | # Not so simple ones. 36 | 'Download': lambda pi, obj: \ 37 | pi.getShortCuts().getDownload(obj.torrent.hash), 38 | 'Torrent': lambda pi, obj: \ 39 | pi.getShortCuts().getDownload(obj.hash).torrent, 40 | } 41 | 42 | # XXX: Test and document. 43 | def get_equivalent_object_from_root(original_object, plugin_interface): 44 | import dopal.objects 45 | 46 | if not isinstance(original_object, dopal.objects.RemoteObject): 47 | raise ValueError, "%s is not a RemoteObject" % (original_object,) 48 | 49 | from dopal.errors import NonRefreshableObjectTypeError, \ 50 | MissingRemoteAttributeError, NonRefreshableIncompleteObjectError 51 | 52 | remote_type = original_object.get_remote_type() 53 | try: 54 | refresh_function = _refresh_methods[remote_type] 55 | except KeyError: 56 | raise NonRefreshableObjectTypeError(obj=original_object) 57 | 58 | try: 59 | return refresh_function(plugin_interface, original_object) 60 | except MissingRemoteAttributeError, error: 61 | raise NonRefreshableIncompleteObjectError(obj=original_object, error=error) 62 | -------------------------------------------------------------------------------- /resources/utorrent/dopal/__init__.py: -------------------------------------------------------------------------------- 1 | # File: __init__.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | __version__ = (0, 6, 0) 18 | __version_str__ = '%s.%s' % (__version__[0], ''.join([str(part) for part in __version__[1:]])) 19 | __user_agent__ = 'DOPAL/' + __version_str__ 20 | 21 | __all__ = [ 22 | 23 | # Module variables. 24 | '__version__', '__version_str__', '__user_agent__', 25 | 26 | # Front-end modules. 27 | 'interact', 'main', 'scripting', 28 | 29 | # Core-level modules. 30 | 'aztypes', 'core', 'debug', 'errors', 'utils', 'xmlutils', 31 | 32 | # Object-level modules. 33 | 'classes', 'class_defs', 'convert', 'objects', 'obj_impl', 'persistency', 34 | 'logutils', 35 | ] 36 | 37 | # Mode definitions: 38 | # 0 - Normal behaviour - should always be distributed with this value. 39 | # 1 - Debug mode - raise debug errors when appropriate. 40 | # 2 - Epydoc mode - used when Epydoc API documentation is being generated. 41 | __dopal_mode__ = 0 42 | 43 | __doc__ = ''' 44 | DOPAL - DO Python Azureus Library (version %(__version_str__)s) 45 | 46 | @var __version__: DOPAL version as a tuple. 47 | @var __version_str__: DOPAL version as a string. 48 | @var __user_agent__: User agent string used by DOPAL when communicating with 49 | Azureus. 50 | @var __dopal_mode__: Debug internal variable which controls some of the 51 | behaviour of how DOPAL works - not meant for external use. 52 | 53 | @group Front-end modules: interact, main, scripting 54 | @group Core-level modules: aztypes, core, debug, errors, utils, xmlutils 55 | @group Object-level modules: classes, class_defs, convert, objects, obj_impl, 56 | persistency, logutils 57 | ''' % vars() 58 | 59 | # If we are in debug mode, auto-detect whether Epydoc is running and adjust the 60 | # mode accordingly. 61 | import sys 62 | 63 | if __dopal_mode__ == 1 and 'epydoc' in sys.modules: 64 | __dopal_mode__ = 2 65 | del sys 66 | -------------------------------------------------------------------------------- /resources/scrapers/fuzzywuzzy/StringMatcher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | StringMatcher.py 5 | 6 | ported from python-Levenshtein 7 | [https://github.com/miohtama/python-Levenshtein] 8 | """ 9 | 10 | from warnings import warn 11 | 12 | from Levenshtein import * 13 | 14 | 15 | class StringMatcher: 16 | """A SequenceMatcher-like class built on the top of Levenshtein""" 17 | 18 | def _reset_cache(self): 19 | self._ratio = self._distance = None 20 | self._opcodes = self._editops = self._matching_blocks = None 21 | 22 | def __init__(self, isjunk=None, seq1='', seq2=''): 23 | if isjunk: 24 | warn("isjunk not NOT implemented, it will be ignored") 25 | self._str1, self._str2 = seq1, seq2 26 | self._reset_cache() 27 | 28 | def set_seqs(self, seq1, seq2): 29 | self._str1, self._str2 = seq1, seq2 30 | self._reset_cache() 31 | 32 | def set_seq1(self, seq1): 33 | self._str1 = seq1 34 | self._reset_cache() 35 | 36 | def set_seq2(self, seq2): 37 | self._str2 = seq2 38 | self._reset_cache() 39 | 40 | def get_opcodes(self): 41 | if not self._opcodes: 42 | if self._editops: 43 | self._opcodes = opcodes(self._editops, self._str1, self._str2) 44 | else: 45 | self._opcodes = opcodes(self._str1, self._str2) 46 | return self._opcodes 47 | 48 | def get_editops(self): 49 | if not self._editops: 50 | if self._opcodes: 51 | self._editops = editops(self._opcodes, self._str1, self._str2) 52 | else: 53 | self._editops = editops(self._str1, self._str2) 54 | return self._editops 55 | 56 | def get_matching_blocks(self): 57 | if not self._matching_blocks: 58 | self._matching_blocks = matching_blocks(self.get_opcodes(), 59 | self._str1, self._str2) 60 | return self._matching_blocks 61 | 62 | def ratio(self): 63 | if not self._ratio: 64 | self._ratio = ratio(self._str1, self._str2) 65 | return self._ratio 66 | 67 | def quick_ratio(self): 68 | # This is usually quick enough :o) 69 | if not self._ratio: 70 | self._ratio = ratio(self._str1, self._str2) 71 | return self._ratio 72 | 73 | def real_quick_ratio(self): 74 | len1, len2 = len(self._str1), len(self._str2) 75 | return 2.0 * min(len1, len2) / (len1 + len2) 76 | 77 | def distance(self): 78 | if not self._distance: 79 | self._distance = distance(self._str1, self._str2) 80 | return self._distance -------------------------------------------------------------------------------- /resources/proxy/win_inet_pton.py: -------------------------------------------------------------------------------- 1 | # This software released into the public domain. Anyone is free to copy, 2 | # modify, publish, use, compile, sell, or distribute this software, 3 | # either in source code form or as a compiled binary, for any purpose, 4 | # commercial or non-commercial, and by any means. 5 | 6 | import socket 7 | import ctypes 8 | import os 9 | 10 | 11 | class sockaddr(ctypes.Structure): 12 | _fields_ = [("sa_family", ctypes.c_short), 13 | ("__pad1", ctypes.c_ushort), 14 | ("ipv4_addr", ctypes.c_byte * 4), 15 | ("ipv6_addr", ctypes.c_byte * 16), 16 | ("__pad2", ctypes.c_ulong)] 17 | 18 | if hasattr(ctypes, 'windll'): 19 | WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA 20 | WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringA 21 | else: 22 | def not_windows(): 23 | raise SystemError( 24 | "Invalid platform. ctypes.windll must be available." 25 | ) 26 | WSAStringToAddressA = not_windows 27 | WSAAddressToStringA = not_windows 28 | 29 | 30 | def inet_pton(address_family, ip_string): 31 | addr = sockaddr() 32 | addr.sa_family = address_family 33 | addr_size = ctypes.c_int(ctypes.sizeof(addr)) 34 | 35 | if WSAStringToAddressA( 36 | ip_string, 37 | address_family, 38 | None, 39 | ctypes.byref(addr), 40 | ctypes.byref(addr_size) 41 | ) != 0: 42 | raise socket.error(ctypes.FormatError()) 43 | 44 | if address_family == socket.AF_INET: 45 | return ctypes.string_at(addr.ipv4_addr, 4) 46 | if address_family == socket.AF_INET6: 47 | return ctypes.string_at(addr.ipv6_addr, 16) 48 | 49 | raise socket.error('unknown address family') 50 | 51 | 52 | def inet_ntop(address_family, packed_ip): 53 | addr = sockaddr() 54 | addr.sa_family = address_family 55 | addr_size = ctypes.c_int(ctypes.sizeof(addr)) 56 | ip_string = ctypes.create_string_buffer(128) 57 | ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string)) 58 | 59 | if address_family == socket.AF_INET: 60 | if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr): 61 | raise socket.error('packed IP wrong length for inet_ntoa') 62 | ctypes.memmove(addr.ipv4_addr, packed_ip, 4) 63 | elif address_family == socket.AF_INET6: 64 | if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr): 65 | raise socket.error('packed IP wrong length for inet_ntoa') 66 | ctypes.memmove(addr.ipv6_addr, packed_ip, 16) 67 | else: 68 | raise socket.error('unknown address family') 69 | 70 | if WSAAddressToStringA( 71 | ctypes.byref(addr), 72 | addr_size, 73 | None, 74 | ip_string, 75 | ctypes.byref(ip_string_size) 76 | ) != 0: 77 | raise socket.error(ctypes.FormatError()) 78 | 79 | return ip_string[:ip_string_size.value - 1] 80 | 81 | # Adding our two functions to the socket library 82 | if os.name == 'nt': 83 | socket.inet_pton = inet_pton 84 | socket.inet_ntop = inet_ntop 85 | -------------------------------------------------------------------------------- /resources/utorrent/dopal/main.py: -------------------------------------------------------------------------------- 1 | # File: main.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | ''' 18 | Main module for using DOPAL with little effort. 19 | ''' 20 | 21 | 22 | def make_connection(persistent=False, **kwargs): 23 | ''' 24 | Generate a L{DopalConnection} to an Azureus server, whose location is 25 | specified by keyword arguments. 26 | 27 | To see what keywords are accepted, see the 28 | L{set_link_details} method. This 29 | method also takes an additional keyword - C{persistent}, which determines 30 | whether the connection should be persistent or not (by default, it is not 31 | persistent). 32 | 33 | This function will return a DopalConnection instance. 34 | 35 | @rtype: L{DopalConnection} 36 | @see: L{set_link_details} 37 | ''' 38 | connection = DopalConnection() 39 | connection.set_link_details(**kwargs) 40 | 41 | if not persistent: 42 | connection.is_persistent_connection = False 43 | return connection 44 | 45 | 46 | from dopal.objects import AzureusObjectConnection 47 | 48 | 49 | class DopalConnection(AzureusObjectConnection): 50 | ''' 51 | A subclass of 52 | L{AzureusObjectConnection} which 53 | contains an extended API. 54 | 55 | This class defines an extended API, similar to the way that C{Dopal} 56 | classes contain additional methods compared to their C{Azureus} 57 | counterparts. It also sets up some different default behaviours (compared 58 | to L{AzureusObjectConnection}): 59 | - All instances are I{persistent} connections by default. 60 | - A L{RemoteObjectConverter} 61 | instance is installed as the default handler for converting XML to 62 | its appropriate object representation. 63 | - The L{DOPAL class map} is used as the 64 | standard class mapping. 65 | 66 | @see: The L{obj_impl} module documentation. 67 | ''' 68 | 69 | def __init__(self): 70 | super(DopalConnection, self).__init__() 71 | 72 | from dopal.convert import RemoteObjectConverter 73 | 74 | converter = RemoteObjectConverter(self) 75 | 76 | from dopal.obj_impl import DOPAL_CLASS_MAP 77 | 78 | converter.class_map = DOPAL_CLASS_MAP 79 | self.converter = converter 80 | 81 | self.is_persistent_connection = True 82 | 83 | 84 | del AzureusObjectConnection 85 | -------------------------------------------------------------------------------- /resources/proxy/immunicity.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os, re, fnmatch, threading, urllib2 4 | from contextlib import contextmanager, closing 5 | from functions import log, debug, tempdir 6 | 7 | LOCKS = {} 8 | PAC_URL = "http://clientconfig.immunicity.org/pacs/all.pac" 9 | CACHE_DIR = tempdir() 10 | USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36" 11 | 12 | if not os.path.exists(CACHE_DIR): 13 | os.makedirs(CACHE_DIR) 14 | 15 | CACHE = 24 * 3600 # 24 hour caching 16 | 17 | @contextmanager 18 | def shelf(filename, ttl=0): 19 | import shelve 20 | filename = os.path.join(CACHE_DIR, filename) 21 | with LOCKS.get(filename, threading.RLock()): 22 | with closing(shelve.open(filename, writeback=True)) as d: 23 | import time 24 | if not d: 25 | d.update({ 26 | "created_at": time.time(), 27 | "data": {}, 28 | }) 29 | elif ttl > 0 and (time.time() - d["created_at"]) > ttl: 30 | d["data"] = {} 31 | yield d["data"] 32 | 33 | _config = {} 34 | 35 | def config(): 36 | global _config 37 | if not _config: 38 | with shelf("xbmctorrent.immunicity.pac_config", ttl=CACHE) as pac_config: 39 | log("Fetching Immunicity PAC file") 40 | pac_data = urllib2.urlopen(PAC_URL).read() 41 | pac_config["server"] = re.search(r"var proxyserver = '(.*)'", pac_data).group(1) 42 | pac_config["domains"] = map(lambda x: x.replace(r"\Z(?ms)", ""), map(fnmatch.translate, re.findall(r"\"(.*?)\",", pac_data))) 43 | _config = pac_config 44 | return _config 45 | 46 | class ImmunicityProxyHandler(urllib2.ProxyHandler, object): 47 | def __init__(self): 48 | self.config = config() 49 | urllib2.ProxyHandler.__init__(self, { 50 | "http" : "", 51 | "https": "", 52 | "ftp" : "", 53 | }) 54 | def proxy_open(self, req, proxy, type): 55 | import socket 56 | 57 | if socket.gethostbyname(req.get_host().split(":")[0]) in self.config["domains"]: 58 | debug("[immunicity]: Pass request through proxy " + self.config["server"]) 59 | return urllib2.ProxyHandler.proxy_open(self, req, self.config["server"], type) 60 | 61 | return None 62 | 63 | def url_get(url, params={}, headers={}, post = None): 64 | 65 | if params: 66 | import urllib 67 | url = "%s?%s" % (url, urllib.urlencode(params)) 68 | 69 | if post: 70 | import urllib 71 | post = urllib.urlencode(post) 72 | 73 | req = urllib2.Request(url, post) 74 | req.add_header("User-Agent", USER_AGENT) 75 | 76 | for k, v in headers.items(): 77 | req.add_header(k, v) 78 | 79 | try: 80 | with closing(urllib2.urlopen(req)) as response: 81 | data = response.read() 82 | if response.headers.get("Content-Encoding", "") == "gzip": 83 | import zlib 84 | return zlib.decompressobj(16 + zlib.MAX_WBITS).decompress(data) 85 | return data 86 | except urllib2.HTTPError as e: 87 | log("[immunicity]: HTTP Error(%s): %s" % (e.errno, e.strerror)) 88 | return None 89 | -------------------------------------------------------------------------------- /resources/contenters/unused/EZTV.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter plugin for XBMC 4 | Copyright (C) 2012 Vadim Skorba 5 | vadim.skorba@gmail.com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | 21 | import re 22 | 23 | import Content 24 | 25 | 26 | class EZTV(Content.Content): 27 | category_dict = { 28 | 'hot': ('Most Recent', '/', {'page': '/page_%d', 'increase': 1, 'second_page': 1}), 29 | } 30 | 31 | baseurl = "https://eztv.ag" 32 | headers = [('User-Agent', 33 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ 34 | ' YaBrowser/14.10.2062.12061 Safari/537.36'), 35 | ('Referer', 'https://eztv.ch/'), ('Accept-Encoding', 'gzip')] 36 | ''' 37 | Weight of source with this searcher provided. 38 | Will be multiplied on default weight. 39 | Default weight is seeds number 40 | ''' 41 | sourceWeight = 1 42 | 43 | def isTracker(self): 44 | return True 45 | 46 | def isSearcher(self): 47 | return False 48 | 49 | def isScrappable(self): 50 | return False 51 | 52 | def isInfoLink(self): 53 | return True 54 | 55 | def isPages(self): 56 | return True 57 | 58 | def isSearchOption(self): 59 | return False 60 | 61 | def get_contentList(self, category, subcategory=None, apps_property=None): 62 | contentList = [] 63 | url = self.get_url(category, subcategory, apps_property) 64 | 65 | response = self.makeRequest(url, headers=self.headers) 66 | 67 | if None != response and 0 < len(response): 68 | # print response 69 | if category in ['hot']: 70 | contentList = self.mode(response) 71 | # print str(contentList) 72 | return contentList 73 | 74 | def mode(self, response): 75 | contentList = [] 76 | # print str(result) 77 | num = 51 78 | result = re.compile( 79 | r'''class="epinfo">(.+?).+?(.+?) ''', 80 | re.DOTALL).findall(response) 81 | for title, link, date in result: 82 | # main 83 | info = {} 84 | num = num - 1 85 | original_title = None 86 | year = 0 87 | img = '' 88 | # info 89 | 90 | info['label'] = info['title'] = title 91 | info['link'] = link 92 | info['plot'] = info['title'] + '\r\nAge: %s' % (date) 93 | 94 | contentList.append(( 95 | int(int(self.sourceWeight) * (int(num))), 96 | original_title, title, int(year), img, info, 97 | )) 98 | return contentList 99 | -------------------------------------------------------------------------------- /resources/utorrent/dopal/interact.py: -------------------------------------------------------------------------------- 1 | # File: interact.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | ''' 18 | Interactive Python application which initialises DOPAL to connect with a chosen 19 | Azureus server. 20 | ''' 21 | 22 | 23 | def main(): 24 | '''Function to invoke this application.''' 25 | # Get host and port. 26 | connection_details = {} 27 | connection_details['host'] = raw_input('Enter host: ') 28 | port_text = raw_input('Enter port (default is 6884): ') 29 | if port_text: 30 | connection_details['port'] = int(port_text) 31 | 32 | # Username and password. 33 | username = raw_input('Enter user name (leave blank if not applicable): ') 34 | password = None 35 | if username: 36 | import getpass 37 | 38 | connection_details['user'] = username 39 | connection_details['password'] = getpass.getpass('Enter password: ') 40 | 41 | my_locals = {} 42 | from dopal.main import make_connection 43 | 44 | connection = make_connection(**connection_details) 45 | connection.is_persistent_connection = True 46 | 47 | from dopal.errors import LinkError 48 | 49 | try: 50 | interface = connection.get_plugin_interface() 51 | except LinkError, error: 52 | interface = None 53 | connection_error = error 54 | else: 55 | connection_error = None 56 | 57 | from dopal import __version_str__ 58 | 59 | banner = "DOPAL %s - interact module\n\n" % __version_str__ 60 | banner += "Connection object stored in 'connection' variable.\n" 61 | 62 | if connection_error is None: 63 | banner += "Plugin interface stored in 'interface' variable.\n" 64 | else: 65 | banner += "\nError getting plugin interface object - could not connect to Azureus, error:\n %s" % connection_error.to_error_string() 66 | 67 | import dopal 68 | 69 | if dopal.__dopal_mode__ == 1: 70 | banner += "\nRunning in DEBUG mode.\n" 71 | elif dopal.__dopal_mode__ == 2: 72 | banner += '\nWARNING: Running in "epydoc" mode.\n' 73 | 74 | my_locals['connection'] = connection 75 | if interface is not None: 76 | my_locals['interface'] = interface 77 | my_locals['__import__'] = __import__ 78 | 79 | print 80 | print '------------------------' 81 | print 82 | 83 | import code 84 | 85 | code.interact(banner, local=my_locals) 86 | 87 | 88 | if __name__ == '__main__': 89 | def _main(env): 90 | return main() 91 | 92 | import dopal.scripting 93 | 94 | dopal.scripting.ext_run( 95 | 'dopal.interact', _main, 96 | make_connection=False, 97 | setup_logging=False, 98 | timeout=8, 99 | pause_on_exit=2, 100 | ) 101 | -------------------------------------------------------------------------------- /resources/scrapers/scrapers.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | # import xbmc, xbmcgui, xbmcplugin, xbmcvfs 6 | from tvdb import TvDb 7 | from tmdbs import TmDb 8 | from kinopoisks import KinoPoisk 9 | 10 | STATUS = { 11 | 'moder': (40501, 'FFFF0000'), 12 | 'check': (40502, 'FFFF0000'), 13 | 'repeat': (40503, 'FFFF0000'), 14 | 'nodesc': (40504, 'FFFF0000'), 15 | 'copyright': (40505, 'FFFF0000'), 16 | 'close': (40506, 'FFFF0000'), 17 | 'absorb': (40507, 'FFFF0000'), 18 | 19 | 'nocheck': (40508, 'FFFF9900'), 20 | 'neededit': (40509, 'FFFF9900'), 21 | 'doubtful': (40510, 'FFFF9900'), 22 | 'temp': (40511, 'FFFF9900'), 23 | 24 | 'ok': (40512, 'FF339933') 25 | } 26 | 27 | GENRE = ( 28 | ('anime', 80102), 29 | ('biography', 80103), 30 | ('action', 80104), 31 | ('western', 80105), 32 | ('military', 80106), 33 | ('detective', 80107), 34 | ('children', 80108), 35 | ('documentary', 80109), 36 | ('drama', 80110), 37 | ('game', 80111), 38 | ('history', 80112), 39 | ('comedy', 80113), 40 | ('concert', 80114), 41 | ('short', 80115), 42 | ('criminal', 80116), 43 | ('romance', 80117), 44 | ('music', 80118), 45 | ('cartoon', 80119), 46 | ('musical', 80120), 47 | ('news', 80121), 48 | ('adventures', 80122), 49 | ('realitytv', 80123), 50 | ('family', 80124), 51 | ('sports', 80125), 52 | ('talkshows', 80126), 53 | ('thriller', 80127), 54 | ('horror', 80128), 55 | ('fiction', 80129), 56 | ('filmnoir', 80130), 57 | ('fantasy', 80131) 58 | ) 59 | 60 | WORK = ( 61 | ('actor', u'Актер'), 62 | ('director', u'Режиссер'), 63 | ('writer', u'Сценарист'), 64 | ('producer', u'Продюсер'), 65 | ('composer', u'Композитор'), 66 | ('operator', u'Оператор'), 67 | ('editor', u'Монтажер'), 68 | ('design', u'Художник'), 69 | ('voice', u'Актер дубляжа'), 70 | ('voice_director', u'Режиссер дубляжа') 71 | ) 72 | 73 | MPAA = ('G', 'PG', 'PG-13', 'R', 'NC-17', 'C', 'GP') 74 | 75 | 76 | class Scrapers(): 77 | RE = { 78 | 'year': re.compile(r'\(([1-2]{1}[0-9]{3})\)', re.U), 79 | 'second': re.compile(r'^([^\[]*)\[(.+)\]([^\]]*)$', re.U) 80 | } 81 | 82 | def scraper(self, content, item, language='en'): 83 | # если есть специализированный скрабер, то запускаем его... 84 | 85 | scraped_item = self.scraper_default(item) 86 | 87 | if content == 'tvdb': 88 | scraper = TvDb(language) 89 | elif content == 'tmdb': 90 | scraper = TmDb(language) 91 | else: # if content == 'kinopoisk': 92 | scraper = KinoPoisk(language) 93 | 94 | name, search, year = item['label'], item['search'], item['year'] 95 | 96 | if not search: 97 | return scraped_item 98 | 99 | scraper_item = scraper.scraper(search, year) 100 | if not scraper_item: 101 | scraped_item['label'] = name 102 | return scraped_item 103 | 104 | scraped_item.update(scraper_item) 105 | scraped_item['label'] = name 106 | 107 | return scraped_item 108 | 109 | def default(self, item): 110 | scraper = self.scraper_default(item) 111 | name, search, year = item['label'], item['search'], item['year'] 112 | scraper['label'] = name 113 | 114 | item.update(scraper) 115 | return item 116 | 117 | def scraper_default(self, item): 118 | return { 119 | 'label': item['label'], 120 | 'icon': None, 121 | 'thumbnail': None, 122 | 'info': {}, 123 | 'properties': { 124 | 'fanart_image': None 125 | }, 126 | } 127 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | *.publishproj 131 | 132 | # NuGet Packages Directory 133 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 134 | #packages/ 135 | 136 | # Windows Azure Build Output 137 | csx 138 | *.build.csdef 139 | 140 | # Windows Store app package directory 141 | AppPackages/ 142 | 143 | # Others 144 | sql/ 145 | *.Cache 146 | ClientBin/ 147 | [Ss]tyle[Cc]op.* 148 | ~$* 149 | *~ 150 | *.dbmdl 151 | *.[Pp]ublish.xml 152 | *.pfx 153 | *.publishsettings 154 | 155 | # RIA/Silverlight projects 156 | Generated_Code/ 157 | 158 | # Backup & report files from converting an old project file to a newer 159 | # Visual Studio version. Backup files are not needed, because we have git ;-) 160 | _UpgradeReport_Files/ 161 | Backup*/ 162 | UpgradeLog*.XML 163 | UpgradeLog*.htm 164 | 165 | # SQL Server files 166 | App_Data/*.mdf 167 | App_Data/*.ldf 168 | 169 | ############# 170 | ## Windows detritus 171 | ############# 172 | 173 | # Windows image file caches 174 | Thumbs.db 175 | ehthumbs.db 176 | 177 | # Folder config file 178 | Desktop.ini 179 | 180 | # Recycle Bin used on file shares 181 | $RECYCLE.BIN/ 182 | 183 | # Mac crap 184 | .DS_Store 185 | 186 | 187 | ############# 188 | ## Python 189 | ############# 190 | 191 | *.py[cod] 192 | 193 | # Packages 194 | *.egg 195 | *.egg-info 196 | dist/ 197 | build/ 198 | eggs/ 199 | parts/ 200 | var/ 201 | sdist/ 202 | develop-eggs/ 203 | .installed.cfg 204 | 205 | # Installer logs 206 | pip-log.txt 207 | 208 | # Unit test / coverage reports 209 | .coverage 210 | .tox 211 | 212 | #Translations 213 | *.mo 214 | 215 | #Mr Developer 216 | .mr.developer.cfg 217 | -------------------------------------------------------------------------------- /resources/language/Ukrainian/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Мова інтерфейсу 4 | Утримувати стиль відображення 5 | Вимкнено 6 | Зберігати файли в каталог 7 | Використовувати магнет-посилання 8 | Зберігати завантажені файли 9 | Залишатись на роздачі завантажених файлів 10 | Обмежити швидкість роздачі Мбіт/сек (0 - необмежено) 11 | Обмежити швидкість завантаження Мбіт/сек (0 - необмежено) 12 | Використовувати тільки системний libtorrent 13 | Завантажити і відтворити наступний епізод 14 | Завантажувати мета-дані для Списків медіа 15 | Налагоджування (Режим розробника) 16 | Confluence (від slng) 17 | Transperency (від slng) 18 | Confluence (від DiMartino) 19 | Confluence (від RussakHH) 20 | Увімкнути Історію Пошуку 21 | python-libtorrent (рекомендовано) 22 | Ace Stream (без магнет-посилань) 23 | P2P Програвач 24 | Решта налаштувань в "Програми - AceStream Client" 25 | Таймаут пошуку 26 | Короткий (10сек) 27 | Звичайний (20сек) 28 | Довгий (30сек) 29 | Попередньо завантажувати субтитри 30 | Продовжувати роздачу після перегляду 31 | Пропонувати зміну місця зберігання 32 | Вилучити російські трекери 33 | Повернути російські трекери 34 | Дія при натисканні на торрент 35 | Відкрити торрент-файл 36 | Відкрити контекстне меню 37 | Завантажити торрент-клієнтом 38 | Завантажити за допомогою python-libtorrent 39 | Сортувати результати пошуку по кількості роздач 40 | Додаткова пошукова фраза в варіантах пошуку 41 | Шифрування 42 | Інтерфейс 43 | P2P Мережа 44 | Додатково 45 | Шлях для зберігання 46 | Викликати діалог 47 | За замовчанням 48 | Шлях 49 | Створювати підкаталог для скрапера 50 | Хост 51 | Порт 52 | URL 53 | Логін 54 | Пароль 55 | Торрент-клієнт 56 | Заміна шляху (тільки для віддаленого режиму) 57 | Закрити 58 | Відкрити налаштування 59 | Браузер торрент-клієнта 60 | Статус плагіна 61 | Установить Трекеры 62 | Настройки Трекеров 63 | Очистити сховище 64 | Оберіть трекер 65 | У вас нет внешних серчеров. Пожалуйста, сначало установите их. 66 | 67 | -------------------------------------------------------------------------------- /resources/language/Hebrew/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | שפת ממשק 4 | נעילת סגנון תצוגת תיקיות 5 | כבוי 6 | שמור קבצים לתיקייה 7 | השתמש בקישורי-מגנט 8 | שמור קבצים שהורדו 9 | המשך זריעה של קבצים שהורדו 10 | הגבלת מהירות של העלה MBits/לשניה (0 - ללא הגבלה) 11 | הגבלת מהירות של הורדה MBits/לשניה (0 - ללא הגבלה) 12 | השתמש רק במערכת libtorrent 13 | הורדה מראש ונגן את הפרק הבא 14 | הורד מטה-נתונים עבור רשימות תוכן 15 | איתור באגים (מצב מפתח) 16 | Confluence (by slng) 17 | Transperency (by slng) 18 | Confluence (by DiMartino) 19 | Confluence (by RussakHH) 20 | אפשר היסטוריית חיפוש 21 | python-libtorrent (מומלץ) 22 | Ace Stream (ללא מגנטים) 23 | נגן P2P 24 | איפוס הגדרות של תוכניות - AceStream 25 | זמן קצוב לחיפוש 26 | קצר (10ש) 27 | רגיל (20ש) 28 | ארוך (30ש) 29 | הורדה מראש של כתוביות מכל התיקיות 30 | המשך להזריע לאחר צפייה 31 | שאל כדי לשנות אחסון לפני ניגון 32 | מחק דברים רוסים 33 | החזר דברים רוסים 34 | פעולת לחיצה על טורנט 35 | פתח קובץ טורנט 36 | פתח תפריט הקשר 37 | הורד באמצעות טורנט 38 | הורד באמצעות python-libtorrent 39 | מיין תוצאות חיפוש על ידי זרעים 40 | חיפוש מותאם אישית 41 | הצפנה 42 | חיפוש מספר אשכול 43 | מחיקת קבצים 44 | שמור קבצים 45 | שאל לשמירה 46 | Torrent2HTTP (python-libtorrent via http) 47 | Proxy 48 | ללא 49 | Anti-zapret 50 | Tor 51 | Immunicity 52 | ממשק 53 | רשת P2P 54 | מתקדם 55 | נתיב לשמירה 56 | דו-שיח שיחה 57 | ברירת מחדל 58 | נתיב 59 | צור תיקייה עבור scrapper 60 | מארח 61 | יציאה 62 | כתובת 63 | התחבר 64 | סיסמה 65 | לקוח טורנט 66 | כתובת URL (רק לא SSL) 67 | נתיב חלופי (מרחוק בלבד) 68 | סגור 69 | פתח הגדרות 70 | דפדפן לקוח טורנט 71 | התקן עוקבים 72 | הגדרות עוקבים 73 | נקה אכסון 74 | בחר מנוע חיפוש 75 | אין לך מנוע חיפוש חיצוני. התקן אותו תחילה. 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /resources/proxy/antizapret.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os, re, fnmatch, threading, urllib2, time, shelve, anydbm 4 | from contextlib import contextmanager, closing 5 | from functions import log, debug, tempdir 6 | 7 | PAC_URL = "http://antizapret.prostovpn.org/proxy.pac" 8 | CACHE_DIR = tempdir() 9 | USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36" 10 | CONFIG_LOCK = threading.Lock() 11 | 12 | if not os.path.exists(CACHE_DIR): 13 | os.makedirs(CACHE_DIR) 14 | 15 | CACHE_LIFETIME = 24 * 3600 # 24 hour caching 16 | 17 | def config(): 18 | shelf = None 19 | try: 20 | CONFIG_LOCK.acquire() 21 | filename = os.path.join(CACHE_DIR, "antizapret.pac_config2") 22 | try: 23 | shelf = shelve.open(filename) 24 | except anydbm.error: 25 | os.remove(filename) 26 | shelf = shelve.open(filename) 27 | 28 | created_at = 0 29 | data = {} 30 | 31 | if 'created_at' in shelf: 32 | created_at = shelf['created_at'] 33 | 34 | if 'data' in shelf: 35 | data = shelf['data'] 36 | 37 | if((time.time() - created_at) <= CACHE_LIFETIME 38 | and 'domains' in data 39 | and len(data['domains']) > 0): 40 | return data 41 | 42 | log("[antizapret]: Fetching Antizapret PAC file on %s" %PAC_URL) 43 | try: 44 | pac_data = urllib2.urlopen(PAC_URL).read() 45 | except: 46 | pac_data = "" 47 | 48 | r = re.search(r"\"PROXY (.*); DIRECT", pac_data) 49 | if r: 50 | data["server"] = r.group(1) 51 | data["domains"] = map(lambda x: x.replace(r"\Z(?ms)", "").replace("\\", ""), map(fnmatch.translate, re.findall(r"\"(.*?)\",", pac_data))) 52 | else: 53 | data["server"] = None 54 | data["domains"] = [] 55 | 56 | shelf.clear() 57 | shelf.update({ 58 | "created_at": time.time(), 59 | "data": data, 60 | }) 61 | return data 62 | except Exception as ex: 63 | debug("[antizapret]: " + str(ex)) 64 | raise 65 | finally: 66 | if shelf: 67 | shelf.close() 68 | if CONFIG_LOCK.locked(): 69 | CONFIG_LOCK.release() 70 | 71 | class AntizapretProxyHandler(urllib2.ProxyHandler, object): 72 | def __init__(self): 73 | self.config = config() 74 | urllib2.ProxyHandler.__init__(self, { 75 | "http" : "", 76 | "https": "", 77 | "ftp" : "", 78 | }) 79 | def proxy_open(self, req, proxy, type): 80 | import socket 81 | 82 | hostname = req.get_host().split(":")[0] 83 | if socket.gethostbyname(hostname) in self.config["domains"] or hostname in self.config["domains"]: 84 | debug("[antizapret]: Pass request through proxy " + self.config["server"]) 85 | return urllib2.ProxyHandler.proxy_open(self, req, self.config["server"], type) 86 | 87 | return None 88 | 89 | def url_get(url, params={}, headers={}, post = None): 90 | 91 | if params: 92 | import urllib 93 | url = "%s?%s" % (url, urllib.urlencode(params)) 94 | 95 | if post: 96 | import urllib 97 | post = urllib.urlencode(post) 98 | 99 | req = urllib2.Request(url, post) 100 | req.add_header("User-Agent", USER_AGENT) 101 | 102 | for k, v in headers.items(): 103 | req.add_header(k, v) 104 | 105 | try: 106 | with closing(urllib2.urlopen(req)) as response: 107 | data = response.read() 108 | if response.headers.get("Content-Encoding", "") == "gzip": 109 | import zlib 110 | return zlib.decompressobj(16 + zlib.MAX_WBITS).decompress(data) 111 | return data 112 | except urllib2.HTTPError as e: 113 | log("[antizapret]: HTTP Error(%s): %s" % (e.errno, e.strerror)) 114 | return None -------------------------------------------------------------------------------- /resources/scrapers/kinopoisk/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Russian metadata plugin for Plex, which uses http://www.kinopoisk.ru/ to get the tag data. 4 | # Плагин для обновления информации о фильмах использующий КиноПоиск (http://www.kinopoisk.ru/). 5 | # Copyright (C) 2012 Yevgeny Nyden 6 | # 7 | # This program is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU General Public License 9 | # as published by the Free Software Foundation; either version 2 10 | # of the License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | # 21 | # @author zhenya (Yevgeny Nyden) 22 | # @version 1.52 23 | # @revision 148 24 | 25 | import common 26 | import pageparser 27 | import LOGGER 28 | import pluginsettings as S 29 | from translit import provide_unicode 30 | 31 | 32 | IS_DEBUG = True # TODO - DON'T FORGET TO SET IT TO FALSE FOR A DISTRO. 33 | 34 | # Plugin preferences. 35 | # When changing default values here, also update the DefaultPrefs.json file. 36 | PREFS = common.Preferences( 37 | ('kinopoisk_pref_max_posters', S.KINOPOISK_PREF_DEFAULT_MAX_POSTERS), 38 | ('kinopoisk_pref_max_art', S.KINOPOISK_PREF_DEFAULT_MAX_ART), 39 | ('kinopoisk_pref_get_all_actors', S.KINOPOISK_PREF_DEFAULT_GET_ALL_ACTORS), 40 | ('kinopoisk_pref_imdb_support', S.KINOPOISK_PREF_DEFAULT_IMDB_SUPPORT), 41 | (None, None), 42 | ('kinopoisk_pref_imdb_rating', S.KINOPOISK_PREF_DEFAULT_IMDB_RATING), 43 | ('kinopoisk_pref_kp_rating', S.KINOPOISK_PREF_DEFAULT_KP_RATING)) 44 | 45 | 46 | def Start(): 47 | LOGGER.Info('***** START ***** %s' % common.USER_AGENT) 48 | PREFS.readPluginPreferences() 49 | 50 | 51 | def ValidatePrefs(): 52 | LOGGER.Info('***** updating preferences...') 53 | PREFS.readPluginPreferences() 54 | 55 | 56 | class KinoPoiskRuAgent(): 57 | name = 'KinoPoiskRu' 58 | primary_provider = True 59 | fallback_agent = False 60 | accepts_from = ['com.plexapp.agents.localmedia'] 61 | contributes_to = None 62 | parser = pageparser.PageParser( 63 | LOGGER, common.HttpUtils(S.ENCODING_KINOPOISK_PAGE, pageparser.USER_AGENT), IS_DEBUG) 64 | 65 | 66 | # ############################################################################# 67 | ############################# S E A R C H #################################### 68 | ############################################################################## 69 | def search(self, results, media, lang, manual=False): 70 | """ Searches for matches on KinoPoisk using the title and year 71 | passed via the media object. All matches are saved in a list of results 72 | as MetadataSearchResult objects. For each results, we determine a 73 | page id, title, year, and the score (how good we think the match 74 | is on the scale of 1 - 100). 75 | """ 76 | LOGGER.Debug('SEARCH START <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<') 77 | try: 78 | mediaName = media['name'].decode('utf-8') 79 | except: 80 | mediaName = media['name'] 81 | mediaYear = media['year'] 82 | LOGGER.Debug('searching for name="%s", year="%s"...' % 83 | (mediaName, str(mediaYear))) 84 | 85 | # Look for matches on KinoPisk (result is returned as an array of tuples [kinoPoiskId, title, year, score]). 86 | titleResults = KinoPoiskRuAgent.parser.fetchAndParseSearchResults(mediaName, mediaYear) 87 | for titleResult in titleResults: 88 | results.append((titleResult[0], titleResult[1], titleResult[2], lang, titleResult[3])) 89 | 90 | # Sort results according to their score (Сортируем результаты). 91 | LOGGER.Debug('SEARCH END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<') 92 | return results 93 | 94 | #k=KinoPoiskRuAgent() 95 | 96 | #print str(k.search([],{'name':'Django Unchained','year':'2012'},'English')) -------------------------------------------------------------------------------- /resources/contenters/RiperAM.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter plugin for XBMC 4 | Copyright (C) 2012 Vadim Skorba 5 | vadim.skorba@gmail.com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | 21 | import re 22 | 23 | import Content 24 | 25 | 26 | class RiperAM(Content.Content): 27 | category_dict = { 28 | 'hot': ('Most Recent', '/', {'page': '/portal.php?tp=%d', 'increase': 30, 'second_page': 30}), 29 | } 30 | 31 | baseurl = "http://riperam.org/" 32 | headers = [('User-Agent', 33 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ 34 | ' YaBrowser/14.10.2062.12061 Safari/537.36'), 35 | ('Referer', baseurl+'/'), ('Accept-Encoding', 'gzip,deflate,sdch'), 36 | ('Accept-Language', 'ru,en;q=0.8')] 37 | ''' 38 | Weight of source with this searcher provided. 39 | Will be multiplied on default weight. 40 | Default weight is seeds number 41 | ''' 42 | sourceWeight = 1 43 | 44 | def isTracker(self): 45 | return True 46 | 47 | def isSearcher(self): 48 | return True 49 | 50 | def isScrappable(self): 51 | return False 52 | 53 | def isInfoLink(self): 54 | return True 55 | 56 | def isPages(self): 57 | return True 58 | 59 | def isSearchOption(self): 60 | return False 61 | 62 | def get_contentList(self, category, subcategory=None, apps_property=None): 63 | #self.debug=self.log 64 | contentList = [] 65 | url = self.get_url(category, subcategory, apps_property) 66 | 67 | response = self.makeRequest(url, headers=self.headers) 68 | 69 | if None != response and 0 < len(response): 70 | response=response.decode('utf-8') 71 | #self.debug(str(response)) 72 | if category in ['hot']: 73 | contentList = self.popmode(response) 74 | self.debug('[get_contentList] contentList '+str(contentList)) 75 | return contentList 76 | 77 | def popmode(self, response): 78 | contentList = [] 79 | num = 31 80 | bad_forum = [u'Безопасность', u'Книги и журналы', u'Action & Shooter', u'RPG/MMORPG', u'Книги', u'Журналы'] 81 | 82 | regex = u'' 83 | regex_tr = u'''.+?

(.+?)

.+?
.+?(.+?)''' 84 | for tr in re.compile(regex, re.DOTALL).findall(response): 85 | 86 | result=re.compile(regex_tr, re.DOTALL).findall(tr) 87 | self.debug(tr+' -> '+str(result)) 88 | if result: 89 | (img, link, label, forum)=result[0] 90 | info = {} 91 | if forum and forum in bad_forum: 92 | continue 93 | num = num - 1 94 | original_title = None 95 | year = 0 96 | title = self.unescape(label) 97 | if img: 98 | img = img.replace('.webp', '.jpg') 99 | 100 | #info 101 | 102 | info['label'] = label 103 | info['link'] = link 104 | info['title'] = title 105 | info['year'] = int(year) 106 | 107 | contentList.append(( 108 | int(int(self.sourceWeight) * (int(num))), 109 | original_title, title, int(year), img, info, 110 | )) 111 | return contentList 112 | -------------------------------------------------------------------------------- /resources/language/slovak/strings.po: -------------------------------------------------------------------------------- 1 | 2 | 3 | Jazyk rozhrania 4 | Lock Folders View Style 5 | Off 6 | Uložiť súbory do zložky 7 | Použiť magnet-linky 8 | Ponechať stiahnuté súbory 9 | Ponechať seedovať stiahnuté súbory 10 | Limit rýchlosti odosielania MBits/sec (0 - neobmedzene) 11 | Limit rýchlosti stahovania MBits/sec (0 - neobmedzene) 12 | Použiť iba system libtorrent 13 | Predstiahnút a prehrať ďaľšiu epizódu 14 | Download metadata for Content Lists 15 | Debug (Developer Mode) 16 | Confluence (by slng) 17 | Transperency (by slng) 18 | Confluence (by DiMartino) 19 | Confluence (by RussakHH) 20 | Povoliť históriu vyhľadávania 21 | python-libtorrent 22 | Ace Stream 23 | P2P Player 24 | Zvyšné nastavenia v programoch - AceStream Client 25 | Časový horizontvyhľadávania 26 | Krátky (10s) 27 | Normálny (20s) 28 | Dlhý (30s) 29 | Predstiahnuť titulky zo všetkých zložiek 30 | Ponechať seedovať po prezretí 31 | Opýtať sa na zmenu úložneho miesta pred prezretím 32 | Vymazať ruské veci 33 | Vratiť ruské veci 34 | Akcia onClick pre torrent 35 | Otvoriť Torrent s´bor 36 | Otvoriť Contextové menu 37 | Stiahnúť cez Torrent-klient 38 | Stiahnúť cez python-libtorrent 39 | Zoradiť výsledok vyhľadávania podľa počtu seederov 40 | Custom search option phrase 41 | Šifrovanie 42 | Search Thread Number 43 | Vymazať súbory 44 | Uložiť súbory 45 | Ponúknuť uloženie 46 | Torrent2HTTP (libtorrent cez http) 47 | Proxy 48 | 49 | Anti-zapret 50 | Tor 51 | Immunicity 52 | Max. pripojenia (0 - neobmedzene) 53 | Požit náhodný port 54 | Port pre prichádzajúce pripojenia 55 | Veľkost dát uložených do vyrovnávacej pamäte, pred sputením, Mb 56 | Automatické prehratie ďaľšej epizódy (alebo sa opýtať) 57 | Nastavenie zariadenian 58 | Priemerné/dobré PC 59 | Podpriemerné PC/router 60 | Rozhranie 61 | P2P siet 62 | Pokročilé 63 | Uložiť cestu 64 | vyvolať dialog 65 | Základné 66 | Cesta 67 | Vytvoriť podzložku pre scrapper 68 | Host 69 | Port 70 | URL 71 | Login 72 | heslo 73 | Torrent Client 74 | URL (iba bez SSL) 75 | Path replacement (remote only) 76 | Zavrieť 77 | Otvoriť nastavenia 78 | Torrent Client prehliadač 79 | Instalvať Trackery 80 | Nastavenie trackerov 81 | Vymazať uložný priestor 82 | Vybrať vyhľadávač 83 | Nemáš externý vyhľadávač. Prosím najprv si ho naištaluj. 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /resources/scrapers/fuzzywuzzy/process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | process.py 5 | 6 | Copyright (c) 2011 Adam Cohen 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | """ 27 | import itertools 28 | 29 | from . import fuzz 30 | from . import utils 31 | 32 | 33 | def extract(query, choices, processor=None, scorer=None, limit=5): 34 | """Find best matches in a list or dictionary of choices, return a 35 | list of tuples containing the match and it's score. If a dictionery 36 | is used, also returns the key for each match. 37 | 38 | Arguments: 39 | query -- an object representing the thing we want to find 40 | choices -- a list or dictionary of objects we are attempting 41 | to extract values from. The dictionary should 42 | consist of {key: str} pairs. 43 | scorer -- f(OBJ, QUERY) --> INT. We will return the objects 44 | with the highest score by default, we use 45 | score.WRatio() and both OBJ and QUERY should be 46 | strings 47 | processor -- f(OBJ_A) --> OBJ_B, where the output is an input 48 | to scorer for example, "processor = lambda x: 49 | x[0]" would return the first element in a 50 | collection x (of, say, strings) this would then 51 | be used in the scoring collection by default, we 52 | use utils.full_process() 53 | 54 | """ 55 | if choices is None: 56 | return [] 57 | 58 | try: 59 | if len(choices) == 0: 60 | return [] 61 | except TypeError: 62 | pass 63 | 64 | # default, turn whatever the choice is into a workable string 65 | if processor is None: 66 | processor = lambda x: utils.full_process(x) 67 | 68 | # default: wratio 69 | if scorer is None: 70 | scorer = fuzz.WRatio 71 | 72 | sl = list() 73 | 74 | if isinstance(choices, dict): 75 | for key, choice in choices.items(): 76 | processed = processor(choice) 77 | score = scorer(query, processed) 78 | tuple = (choice, score, key) 79 | sl.append(tuple) 80 | 81 | else: 82 | for choice in choices: 83 | processed = processor(choice) 84 | score = scorer(query, processed) 85 | tuple = (choice, score) 86 | sl.append(tuple) 87 | 88 | sl.sort(key=lambda i: i[1], reverse=True) 89 | return sl[:limit] 90 | 91 | 92 | def extractBests(query, choices, processor=None, scorer=None, score_cutoff=0, limit=5): 93 | """Find best matches above a score in a list of choices, return a 94 | list of tuples containing the match and it's score. 95 | 96 | Convenience method which returns the choices with best scores, see 97 | extract() for full arguments list 98 | 99 | Optional parameter: score_cutoff. 100 | If the choice has a score of less than or equal to score_cutoff 101 | it will not be included on result list 102 | 103 | """ 104 | 105 | best_list = extract(query, choices, processor, scorer, limit) 106 | if len(best_list) > 0: 107 | return list(itertools.takewhile(lambda x: x[1] >= score_cutoff, best_list)) 108 | else: 109 | return [] 110 | 111 | 112 | def extractOne(query, choices, processor=None, scorer=None, score_cutoff=0): 113 | """Find the best match above a score in a list of choices, return a 114 | tuple containing the match and it's score if it's above the treshold 115 | or None. 116 | 117 | Convenience method which returns the single best choice, see 118 | extract() for full arguments list 119 | 120 | Optional parameter: score_cutoff. 121 | If the best choice has a score of less than or equal to 122 | score_cutoff we will return none (intuition: not a good enough 123 | match) 124 | 125 | """ 126 | 127 | best_list = extract(query, choices, processor, scorer, limit=1) 128 | if len(best_list) > 0: 129 | best = best_list[0] 130 | if best[1] >= score_cutoff: 131 | return best 132 | else: 133 | return None 134 | else: 135 | return None 136 | -------------------------------------------------------------------------------- /Downloader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter v2 plugin for XBMC/Kodi 4 | Copyright (C) 2012-2015 Vadim Skorba v1 - DiMartino v2 5 | https://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | 21 | import hashlib 22 | import sys 23 | 24 | from functions import log 25 | 26 | class Torrent(): 27 | __settings__ = sys.modules["__main__"].__settings__ 28 | 29 | def __init__(self, storageDirectory='', torrentFile='', torrentFilesDirectory='torrents'): 30 | self.get_torrent_client() 31 | if self.player == 'libtorrent': 32 | import SkorbaLoader 33 | self.player = SkorbaLoader.SkorbaLoader(storageDirectory, torrentFile, torrentFilesDirectory) 34 | 35 | elif self.player == 'acestream': 36 | import AceStream 37 | self.player = AceStream.AceStream(storageDirectory, torrentFile, torrentFilesDirectory) 38 | 39 | elif self.player == 'anteo': 40 | import Anteoloader 41 | self.player = Anteoloader.AnteoLoader(storageDirectory, torrentFile, torrentFilesDirectory) 42 | 43 | elif self.player == 'inpos': 44 | import Inposloader 45 | self.player = Inposloader.InposLoader(storageDirectory, torrentFile, torrentFilesDirectory) 46 | 47 | def __exit__(self): 48 | self.player.__exit__() 49 | 50 | def get_torrent_client(self): 51 | player = self.__settings__.getSetting("torrent_player") 52 | if player == '0' or player == '4': 53 | self.player = 'libtorrent' 54 | elif player == '1': 55 | self.player = 'acestream' 56 | elif player == '2': 57 | self.player = 'anteo' 58 | elif player == '3': 59 | self.player = 'inpos' 60 | 61 | def play_url_ind(self, ind, label, icon): 62 | return self.player.play_url_ind(int(ind), label, str(icon)) 63 | 64 | def saveTorrent(self, torrentUrl): 65 | return self.player.saveTorrent(torrentUrl) 66 | 67 | def getMagnetInfo(self): 68 | return self.player.getMagnetInfo() 69 | 70 | def magnetToTorrent(self, magnet): 71 | return self.player.magnetToTorrent(magnet) 72 | 73 | def getUploadRate(self): 74 | return self.player.getUploadRate() 75 | 76 | def getDownloadRate(self): 77 | return self.player.getDownloadRate() 78 | 79 | def getPeers(self): 80 | return self.player.getPeers() 81 | 82 | def getSeeds(self): 83 | return self.player.getSeeds() 84 | 85 | def getFileSize(self, contentId=0): 86 | return self.player.getFileSize(contentId) 87 | 88 | def getFilePath(self, contentId=0): 89 | return self.player.getFilePath(contentId) 90 | 91 | def getContentList(self): 92 | # print str(self.player.getContentList()) 93 | return self.player.getContentList() 94 | 95 | def setUploadLimit(self, bytesPerSecond): 96 | return self.player.setUploadLimit(bytesPerSecond) 97 | 98 | def setDownloadLimit(self, bytesPerSecond): 99 | return self.player.setDownloadLimit(bytesPerSecond) 100 | 101 | def stopSession(self): 102 | return self.player.stopSession() 103 | 104 | def md5(self, string): 105 | hasher = hashlib.md5() 106 | try: 107 | hasher.update(string) 108 | except: 109 | hasher.update(string.encode('utf-8', 'ignore')) 110 | return hasher.hexdigest() 111 | 112 | def downloadProcess(self, contentId=None, encrytion=True): 113 | return self.player.downloadProcess(contentId, encrytion) 114 | 115 | def initSession(self): 116 | return self.player.initSession() 117 | 118 | def encryptSession(self): 119 | return self.player.encryptSession() 120 | 121 | def startSession(self): 122 | return self.player.startSession() 123 | 124 | def continueSession(self, contentId=0, Offset=155, seeding=True): 125 | return self.player.continueSession(contentId, Offset, seeding) 126 | 127 | def addToSeeding(self): 128 | return self.player.addToSeeding() 129 | 130 | def fetchParts(self): 131 | return self.player.fetchParts() 132 | 133 | def checkThread(self): 134 | return self.player.checkThread() 135 | 136 | def _makedirs(self, _path): 137 | return self.player._makedirs(_path) 138 | 139 | def debug(self): 140 | return self.player.debug() 141 | 142 | def dump(self, obj): 143 | for attr in dir(obj): 144 | try: 145 | log("'%s':'%s'," % (attr, getattr(obj, attr))) 146 | except: 147 | pass 148 | -------------------------------------------------------------------------------- /resources/contenters/FastTorrent.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter plugin for XBMC 4 | Copyright (C) 2012 Vadim Skorba 5 | vadim.skorba@gmail.com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | 21 | import re 22 | 23 | import Content 24 | from BeautifulSoup import BeautifulSoup 25 | 26 | 27 | class FastTorrent(Content.Content): 28 | category_dict = { 29 | #'movies':('Movies', '/popular/'), 30 | 'tvshows': ( 31 | 'TV Shows', '/last-tv-torrent/', {'page': '/last-tv-torrent/%d.html', 'increase': 1, 'second_page': 2}), 32 | 'cartoons': ('Cartoons', '/last-multfilm-torrent/', 33 | {'page': '/last-multfilm-torrent/%d.html', 'increase': 1, 'second_page': 2}), 34 | 'anime': ('Anime', '/anime/multfilm/', {'page': '/anime/multfilm/%d.html', 'increase': 1, 'second_page': 2}), 35 | 'hot': ('Most Recent', '/new-films/', {'page': '/new-films/%d.html', 'increase': 1, 'second_page': 2}), 36 | 'genre': {'genre': 'by Genre', 37 | 'amime_series': ('Anime Series', '/anime-serialy/multfilm/', 38 | {'page': '/anime-serialy/multfilm/%d.html', 'increase': 1, 'second_page': 2}), 39 | } 40 | #'top':('Top 250 Movies', '/top/'), 41 | } 42 | baseurl = "http://www.fast-torrent.ru" 43 | headers = [('User-Agent', 44 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ 45 | ' YaBrowser/14.10.2062.12061 Safari/537.36'), 46 | ('Referer', baseurl), ('Accept-Encoding', 'gzip'), ('Accept-Language', 'ru,en;q=0.8')] 47 | ''' 48 | Weight of source with this searcher provided. 49 | Will be multiplied on default weight. 50 | Default weight is seeds number 51 | ''' 52 | sourceWeight = 1 53 | 54 | def isTracker(self): 55 | return False 56 | 57 | def isSearcher(self): 58 | return False 59 | 60 | def isScrappable(self): 61 | return True 62 | 63 | def isSearchOption(self): 64 | return True 65 | 66 | def isInfoLink(self): 67 | return False 68 | 69 | def isPages(self): 70 | return True 71 | 72 | def get_contentList(self, category, subcategory=None, apps_property=None): 73 | contentList = [] 74 | url = self.get_url(category, subcategory, apps_property) 75 | 76 | response = self.makeRequest(url, headers=self.headers) 77 | 78 | if None != response and 0 < len(response): 79 | #print response 80 | if category: # in ['hot']: 81 | contentList = self.popmode(response) 82 | #print str(contentList) 83 | return contentList 84 | 85 | def popmode(self, response): 86 | contentList = [] 87 | Soup = BeautifulSoup(response.decode('utf-8')) 88 | result = Soup.findAll('div', {'class': 'film-wrap'}) 89 | num = 16 90 | for tr in result: 91 | #main 92 | info = {} 93 | num = num - 1 94 | original_title = None 95 | year = 0 96 | h2 = tr.find('h2') 97 | #print str(h2) 98 | label = h2.find('span', {'itemprop': 'name'}) 99 | if label: 100 | title = label.text 101 | year = re.compile('\((\d\d\d\d)\)').findall(h2.text) 102 | if year: 103 | year = year[0] 104 | original_title = h2.find('span', {'itemprop': 'alternativeHeadline'}) 105 | if original_title: 106 | original_title = original_title.text 107 | else: 108 | try: 109 | title, year, original_title = \ 110 | re.compile(u'

(.+?) \((\d\d\d\d)\) \((.+?)\)<', re.DOTALL | re.I).findall(unicode(h2))[0] 111 | except: 112 | try: 113 | title, year = re.compile(u'

(.+?) \((\d\d\d\d)\)<', re.DOTALL | re.I).findall(unicode(h2))[0] 114 | except: 115 | pass 116 | a = tr.find('div', 'film-image').find('a') 117 | link = a.get('href') 118 | img = a.get('style') 119 | if img: 120 | img = img.replace('background: url(', '').rstrip(')') 121 | 122 | #info 123 | 124 | info['label'] = title 125 | info['link'] = link 126 | info['title'] = title 127 | genre = tr.find('div', 'film-genre').text 128 | tv = [u'Зарубежный сериал', u'Русский сериал', u'Аниме сериалы', u'Мультсериалы'] 129 | for i in tv: 130 | if re.search(i, genre): 131 | info['tvshowtitle'] = title 132 | info['year'] = int(year) 133 | 134 | contentList.append(( 135 | int(int(self.sourceWeight) * (int(num))), 136 | original_title, title, int(year), img, info, 137 | )) 138 | return contentList 139 | -------------------------------------------------------------------------------- /resources/language/Hungarian/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Kezelőfelület nyelve 4 | Mappák nézetének lezárása 5 | Ki 6 | Fájlok mentése mappába (nem FAT32) 7 | Magnet-linkek használata 8 | Letöltött fájlok megtartása 9 | Letöltött fájlok seedben tartása 10 | Feltöltési sebességkorlát MBits/sec (0 - korlátlan) 11 | Letöltési sebességkorlát MBits/sec (0 - korlátlan) 12 | Csak rendszerszintű libtorrent használata 13 | A következő epizód előtöltése és lejátszása 14 | Metaadatok letöltése a Tartalmak Listájába 15 | Hibakeresés (Fejlesztői Mód) 16 | Confluence (by slng) 17 | Transperency (by slng) 18 | Confluence (by DiMartino) 19 | Confluence (by RussakHH) 20 | Keresési Előzmények engedélyezése 21 | python-libtorrent 22 | Ace Stream 23 | P2P Lejátszó 24 | Egyéb beállítások a programban - AceStream Client 25 | Keresés időkorlátja 26 | Rövid (10s) 27 | Normál (20s) 28 | Hosszú (30s) 29 | Feliratok előtöltése az összes mappából 30 | Seedben tartás megtekintés után 31 | Kérdezzen rá a tárolóhely váltására lejátszás előtt 32 | Orosz tartalmak törlése 33 | Orosz tartalmak visszaállítása 34 | Torrentre kattintás művelete 35 | Torrent fájl megnyitása 36 | Helyi menü megnyitása 37 | Letöltés torrentkliensen keresztül 38 | Letöltés python-libtorrenten keresztül 39 | Keresések rendezése seed alapján 40 | Egyéni keresési lehetőség kifejezésre 41 | Titkosítás 42 | Keresési szálak száma 43 | Törölje a fájlokat 44 | Mentse a fájlokat 45 | Kérdezzen rá 46 | Torrent2HTTP (libtorrent via http) 47 | Proxy 48 | Ki 49 | Anti-zapret 50 | Tor 51 | Immunicity 52 | Max. kapcsolatok (0 - korlátlan) 53 | Véletlen port használata 54 | Port a bejövő kapcsolatokhoz 55 | Előtöltendő adat lejátszás előtt (MB) 56 | Következő epizód automatikus lejátszása (vagy kérdezzen) 57 | Eszköz konfiguráció 58 | Átlagos/Jó PC 59 | Átlag alatti PC/router 60 | Tárolóhely legkisebb mérete automatikus tisztítás miatt (GB) 61 | Videó szüneteltetése elindítás után 62 | Keresési eredmények rendezése 63 | Seed alapján 64 | Ne rendezze 65 | Név alapján 66 | Ne adja Megtek. Előzm.-hez ennél kevesebb lejátszásnál (%) 67 | Confluence (by safonov_ivan) 68 | Aeon Nox (by joyrider) 69 | pyrrent2http (python-libtorrent via http) 70 | Méret mellékelése a fájl nevéhez 71 | DHT engedélyezése 72 | Kezelőfelület 73 | P2P Hálózat 74 | Haladó 75 | Torrentkliens 76 | Hentai (finomhangolás) 77 | Mentési útvonal 78 | Párbeszédablak 79 | Alapértelmezett 80 | Elérési út 81 | Alkönyvtár készítése a leolvasó számára 82 | Gazdagép 83 | Port 84 | URL 85 | Felhasználónév 86 | Jelszó 87 | Torrentkliens 88 | URL (csak No SSL) 89 | Elérési út helyettesítése (csak távoli) 90 | Bezár 91 | Beállítások megnyitása 92 | Torrentkliens Böngésző 93 | Trackerek telepítése 94 | Tracker beállítások 95 | Tárolóhely tisztítása 96 | Kereső választása 97 | Nem rendelkezel külső keresővel. Kérlek, telepíts egyet. 98 | 99 | -------------------------------------------------------------------------------- /resources/contenters/ThePirateBaySe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter plugin for XBMC 4 | Copyright (C) 2012 Vadim Skorba 5 | vadim.skorba@gmail.com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | import re 21 | 22 | import Content 23 | 24 | 25 | class ThePirateBaySe(Content.Content): 26 | # debug = log 27 | category_dict = { 28 | 'tvshows': ('TV Shows', '/browse/205', {'page': '/browse/208/%d', 'increase': 1, 'second_page': 1, 29 | 'sort': [{'name': 'by Seeders', 'url_after': '/0/7/0'}, 30 | {'name': 'by Date', 'url_after': '/0/3/0'}]}), 31 | 'tvshowshd': ('TV Shows [HD]', '/browse/208', {'page': '/browse/208/%d', 'increase': 1, 'second_page': 1, 32 | 'sort': [{'name': 'by Seeders', 'url_after': '/0/7/0'}, 33 | {'name': 'by Date', 'url_after': '/0/3/0'}]}), 34 | 'movies': ('Movies', '/browse/201', {'page': '/browse/208/%d', 'increase': 1, 'second_page': 1, 35 | 'sort': [{'name': 'by Seeders', 'url_after': '/0/7/0'}, 36 | {'name': 'by Date', 'url_after': '/0/3/0'}]}), 37 | 'movieshd': ('Movies [HD]', '/browse/207', {'page': '/browse/208/%d', 'increase': 1, 'second_page': 1, 38 | 'sort': [{'name': 'by Seeders', 'url_after': '/0/7/0'}, 39 | {'name': 'by Date', 'url_after': '/0/3/0'}]}), 40 | 'movies3D': ('Movies [3D]', '/browse/209', {'page': '/browse/209/%d', 'increase': 1, 'second_page': 1, 41 | 'sort': [{'name': 'by Seeders', 'url_after': '/0/7/0'}, 42 | {'name': 'by Date', 'url_after': '/0/3/0'}]}), 43 | 'movies bluray': ('Movies [Bluray]', '/search/bluray', {'page': '/search/bluray/%d', 'increase': 1, 'second_page': 1, 44 | 'sort': [{'name': 'by Seeders', 'url_after': '/0/7/207'}, 45 | {'name': 'by Date', 'url_after': '/0/3/207'}]}), 46 | 'heb_movies': ('סרטים מדובבים', '/search/Hebrew-dubbed/0/7/0'), 47 | } 48 | 49 | baseurl = "http://thepiratebay.ae" 50 | headers = [('User-Agent', 51 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ 52 | ' YaBrowser/14.10.2062.12061 Safari/537.36'), 53 | ('Referer', 'http://thepiratebay.ae/'), ('Accept-Encoding', 'gzip')] 54 | ''' 55 | Weight of source with this searcher provided. 56 | Will be multiplied on default weight. 57 | Default weight is seeds number 58 | ''' 59 | sourceWeight = 1 60 | 61 | def isTracker(self): 62 | return True 63 | 64 | def isSearcher(self): 65 | return False 66 | 67 | def isScrappable(self): 68 | return False 69 | 70 | def isInfoLink(self): 71 | return True 72 | 73 | def isPages(self): 74 | return True 75 | 76 | def isSort(self): 77 | return True 78 | 79 | def isSearchOption(self): 80 | return False 81 | 82 | def get_contentList(self, category, subcategory=None, apps_property=None): 83 | contentList = [] 84 | url = self.get_url(category, subcategory, apps_property) 85 | 86 | import requests 87 | response = requests.get(url).text 88 | 89 | if None != response and 0 < len(response): 90 | # print response 91 | if category: 92 | contentList = self.mode(response) 93 | # print str(contentList) 94 | return contentList 95 | 96 | def mode(self, response): 97 | contentList = [] 98 | self.debug = self.log 99 | num = 31 100 | self.debug(response) 101 | regex = '''

.+?''' 102 | regex_tr = r'.+?' 103 | for tr in re.compile(regex, re.DOTALL).findall(response): 104 | result = re.compile(regex_tr, re.DOTALL).findall(tr) 105 | self.debug(tr + ' -> ' + str(result)) 106 | if result: 107 | (title, link, date, size, seeds, leechers) = result[0] 108 | 109 | info = {} 110 | num = num - 1 111 | original_title = None 112 | year = 0 113 | img = '' 114 | size = size.replace(' ', ' ') 115 | date = self.stripHtml(date.replace(' ', ' ')) 116 | 117 | # info 118 | 119 | info['label'] = info['title'] = self.unescape(title) 120 | info['link'] = link 121 | self.log(info['link']) 122 | info['plot'] = info['title'] + '\r\n[I](%s) [S/L: %s/%s] [/I]\r\n%s' % (size, seeds, leechers, date) 123 | contentList.append(( 124 | int(int(self.sourceWeight) * (int(num))), 125 | original_title, title, int(year), img, info, 126 | )) 127 | return contentList 128 | -------------------------------------------------------------------------------- /resources/utorrent/dopal/debug.py: -------------------------------------------------------------------------------- 1 | # File: debug.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | ''' 18 | Contains functions and objects useful for debugging DOPAL. 19 | ''' 20 | 21 | 22 | class DebugObject(object): 23 | pass 24 | 25 | 26 | class LinkDebugObject(DebugObject): 27 | pass 28 | 29 | 30 | class ErrorLinkDebug(LinkDebugObject): 31 | def __init__(self, cgi_path, error): 32 | self.cgi_path = cgi_path 33 | self.error = error 34 | 35 | 36 | class OutgoingExchangeDebug(LinkDebugObject): 37 | def __init__(self, cgi_path, data_to_send): 38 | self.cgi_path = cgi_path 39 | self.data_to_send = data_to_send 40 | 41 | 42 | class ConnectionExchangeDebug(LinkDebugObject): 43 | def __init__(self, cgi_path, data_sent, data_received): 44 | self.cgi_path = cgi_path 45 | self.data_sent = data_sent 46 | self.data_received = data_received 47 | 48 | 49 | class ConnectionDebugObject(DebugObject): 50 | pass 51 | 52 | 53 | class MethodRequestDebug(ConnectionDebugObject): 54 | def __init__(self, object_id, request_method): 55 | self.object_id = object_id 56 | self.request_method = request_method 57 | 58 | 59 | class MethodResponseDebug(ConnectionDebugObject): 60 | def __init__(self, object_id, request_method, response): 61 | self.object_id = object_id 62 | self.request_method = request_method 63 | self.response = response 64 | 65 | 66 | def print_everything(debug_object): 67 | if not isinstance(debug_object, LinkDebugObject): 68 | return 69 | 70 | print 71 | print '---------------' 72 | print 73 | 74 | if isinstance(debug_object, OutgoingExchangeDebug): 75 | print 'Sending to "%s"' % debug_object.cgi_path 76 | print 77 | print debug_object.data_to_send 78 | 79 | elif isinstance(debug_object, ConnectionExchangeDebug): 80 | print 'Recieved from "%s"' % debug_object.cgi_path 81 | print 82 | print debug_object.data_received 83 | 84 | elif isinstance(debug_object, ErrorLinkDebug): 85 | error = debug_object.error 86 | print 'Error from "%s"' % debug_object.cgi_path 87 | print 88 | print '%s: %s' % (error.__class__.__name__, error) 89 | 90 | print 91 | print '---------------' 92 | print 93 | 94 | 95 | def print_everything_with_stack(debug_object): 96 | if isinstance(debug_object, OutgoingExchangeDebug): 97 | import traceback 98 | 99 | print 100 | print '---------------' 101 | print 102 | print 'Stack trace of request:' 103 | traceback.print_stack() 104 | print 105 | print '---------------' 106 | print 107 | print_everything(debug_object) 108 | 109 | 110 | def print_method(debug_object): 111 | from dopal.utils import make_short_object_id as _sid 112 | 113 | if isinstance(debug_object, MethodRequestDebug): 114 | print 115 | print '---------------' 116 | print ' Object:', debug_object.object_id, 117 | if debug_object.object_id is not None: 118 | print "[sid=%s]" % _sid(debug_object.object_id), 119 | print 120 | print ' Method:', debug_object.request_method 121 | print 122 | elif isinstance(debug_object, MethodResponseDebug): 123 | import dopal.core 124 | 125 | if isinstance(debug_object.response, dopal.core.ErrorResponse): 126 | print ' Response Type: ERROR' 127 | print ' Response Data:', debug_object.response.response_data 128 | elif isinstance(debug_object.response, dopal.core.AtomicResponse): 129 | print ' Response Type: VALUE' 130 | print ' Response Data:', debug_object.response.response_data 131 | elif isinstance(debug_object.response, dopal.core.NullResponse): 132 | print ' Response Type: NULL / EMPTY' 133 | print ' Response Data: None' 134 | elif isinstance(debug_object.response, dopal.core.StructuredResponse): 135 | print ' Response Type: STRUCTURE' 136 | print ' Response Data:', 137 | 138 | obj_id = debug_object.response.get_object_id() 139 | if obj_id is not None: 140 | print 'Object [id=%s, sid=%s]' % (obj_id, _sid(obj_id)) 141 | else: 142 | print 'Non-object value' 143 | print '---------------' 144 | print 145 | 146 | 147 | def print_method_with_stack(debug_object): 148 | if isinstance(debug_object, MethodResponseDebug): 149 | import traceback 150 | 151 | print 152 | print '---------------' 153 | print 154 | print 'Stack trace of request:' 155 | traceback.print_stack() 156 | print 157 | print '---------------' 158 | print 159 | print_method(debug_object) 160 | 161 | 162 | class DebugGrabber(object): 163 | debug_object = None 164 | 165 | def get_in(self): 166 | if self.debug_object is None: 167 | raise Exception, "not captured any data yet" 168 | return self.debug_object.data_sent 169 | 170 | def get_out(self): 171 | if self.debug_object is None: 172 | raise Exception, "not captured any data yet" 173 | return self.debug_object.data_received 174 | 175 | def __call__(self, debug_object): 176 | if isinstance(debug_object, ConnectionExchangeDebug): 177 | self.debug_object = debug_object 178 | -------------------------------------------------------------------------------- /resources/scrapers/cache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import time 5 | import pickle 6 | import threading 7 | import zipfile 8 | 9 | import xbmc 10 | import xbmcvfs 11 | import xbmcgui 12 | import Localization 13 | from net import HTTP 14 | 15 | try: 16 | from sqlite3 import dbapi2 as sqlite 17 | except: 18 | from pysqlite2 import dbapi2 as sqlite 19 | 20 | rtrCache_lock = threading.RLock() 21 | 22 | 23 | class Cache: 24 | def __init__(self, name, version, expire=0, size=0, step=100): 25 | self.name = name 26 | self.version = version 27 | self._connect() 28 | if expire: 29 | self.expire(expire) 30 | if size: 31 | self.size(size, step) 32 | 33 | def get(self, token, callback, *param): 34 | cur = self.db.cursor() 35 | cur.execute('select expire,data from cache where id=? limit 1', (token,)) 36 | row = cur.fetchone() 37 | cur.close() 38 | 39 | if row: 40 | if row[0] and row[0] < int(time.time()): 41 | pass 42 | else: 43 | try: 44 | obj = pickle.loads(row[1]) 45 | except: 46 | pass 47 | else: 48 | return obj 49 | 50 | response = callback(*param) 51 | 52 | if response[0]: 53 | obj = sqlite.Binary(pickle.dumps(response[1])) 54 | curtime = int(time.time()) 55 | cur = self.db.cursor() 56 | if isinstance(response[0], bool): 57 | cur.execute('replace into cache(id,addtime,expire,data) values(?,?,?,?)', (token, curtime, None, obj)) 58 | else: 59 | cur.execute('replace into cache(id,addtime,expire,data) values(?,?,?,?)', 60 | (token, curtime, curtime + response[0], obj)) 61 | self.db.commit() 62 | cur.close() 63 | 64 | return response[1] 65 | 66 | def expire(self, expire): 67 | # with rtrCache_lock: 68 | cur = self.db.cursor() 69 | cur.execute('delete from cache where addtime. 19 | ''' 20 | import Content 21 | from BeautifulSoup import BeautifulSoup 22 | 23 | 24 | class SWESUB(Content.Content): 25 | category_dict = { 26 | 'tvshows': ('TV Shows', '/senaste-tv-serier/', {'page': '/senaste-tv-serier/?page=%d', 27 | 'increase': 1, 'second_page': 2, }), 28 | 'movies': ('Movies', '/senaste-filmer/'), 29 | # , {'page': '/senaste-filmer/?page=%d', 'increase': 1, 'second_page': 2,}), 30 | 'genre': {'genre': 'by Genre', 31 | 'action': ('Action', '/action/', {'page': '/action/?page=%d', 'increase': 1, 'second_page': 2, }), 32 | 'adventure': ( 33 | 'Adventure', '/aventyr/', {'page': '/aventyr/?page=%d', 'increase': 1, 'second_page': 2, }), 34 | 'animation': ( 35 | 'Animation', '/animerat/', {'page': '/animerat/?page=%d', 'increase': 1, 'second_page': 2, }), 36 | 'comedy': ('Comedy', '/komedi/', {'page': '/komedi/?page=%d', 'increase': 1, 'second_page': 2, }), 37 | 'crime': ('Crime', '/kriminal/', {'page': '/kriminal/?page=%d', 'increase': 1, 'second_page': 2, }), 38 | 'documentary': ( 39 | 'Documentary', '/dokumentar/', {'page': '/dokumentar/?page=%d', 'increase': 1, 'second_page': 2, }), 40 | 'drama': ('Drama', '/drama/', {'page': '/drama/?page=%d', 'increase': 1, 'second_page': 2, }), 41 | 'family': ('Family', '/familj/', {'page': '/familj/?page=%d', 'increase': 1, 'second_page': 2, }), 42 | 'fantasy': ('Fantasy', '/fantasy/', {'page': '/fantasy/?page=%d', 'increase': 1, 'second_page': 2, }), 43 | 'horror': ('Horror', '/skrack/', {'page': '/skrack/?page=%d', 'increase': 1, 'second_page': 2, }), 44 | 'music': ('Music', '/dans/', {'page': '/dans/?page=%d', 'increase': 1, 'second_page': 2, }), 45 | 'musical': ('Musical', '/musikal/', {'page': '/musikal/?page=%d', 'increase': 1, 'second_page': 2, }), 46 | 'romance': ( 47 | 'Romance', '/romantik/', {'page': '/romantik/?page=%d', 'increase': 1, 'second_page': 2, }), 48 | 'sci_fi': ('Sci-Fi', '/sci-fi/', {'page': '/sci-fi/?page=%d', 'increase': 1, 'second_page': 2, }), 49 | 'sport': ('Sport', '/sport/', {'page': '/sport/?page=%d', 'increase': 1, 'second_page': 2, }), 50 | 'thriller': ( 51 | 'Thriller', '/thriller/', {'page': '/thriller/?page=%d', 'increase': 1, 'second_page': 2, }), 52 | 'war': ('War', '/krig/', {'page': '/krig/?page=%d', 'increase': 1, 'second_page': 2, }), 53 | 'western': ('Western', '/western/', {'page': '/western/?page=%d', 'increase': 1, 'second_page': 2, }), 54 | } 55 | } 56 | 57 | baseurl = "http://swesub.tv" 58 | headers = [('User-Agent', 59 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ 60 | ' YaBrowser/14.10.2062.12061 Safari/537.36'), 61 | ('Referer', 'http://swesub.tv/'), ('Accept-Encoding', 'gzip')] 62 | ''' 63 | Weight of source with this searcher provided. 64 | Will be multiplied on default weight. 65 | Default weight is seeds number 66 | ''' 67 | sourceWeight = 1 68 | 69 | def isLabel(self): 70 | return True 71 | 72 | def isScrappable(self): 73 | return False 74 | 75 | def isInfoLink(self): 76 | return True 77 | 78 | def isPages(self): 79 | return True 80 | 81 | def isSort(self): 82 | return False 83 | 84 | def isSearchOption(self): 85 | return False 86 | 87 | def get_contentList(self, category, subcategory=None, apps_property=None): 88 | contentList = [] 89 | url = self.get_url(category, subcategory, apps_property) 90 | 91 | response = self.makeRequest(url, headers=self.headers) 92 | response = response.decode('iso-8859-1') 93 | 94 | if None != response and 0 < len(response): 95 | if category: 96 | contentList = self.mode(response) 97 | # print str(contentList) 98 | return contentList 99 | 100 | def mode(self, response): 101 | contentList = [] 102 | num = 51 103 | Soup = BeautifulSoup(response) 104 | result = Soup.findAll('article', {'class': 'box'}) 105 | # print str(result) 106 | for article in result: 107 | # main 108 | info = {} 109 | num = num - 1 110 | original_title = None 111 | year = 0 112 | 113 | div = article.find('div', {'class': 'box-img'}) 114 | title = div.find('img').get('alt') 115 | img = div.find('img').get('src') 116 | link = div.find('a').get('href').replace(self.baseurl, '').replace('.html', '') 117 | 118 | # info 119 | 120 | info['label'] = info['title'] = self.unescape(title) 121 | info['link'] = self.baseurl + '/downloads' + link + '/' 122 | info['infolink'] = self.baseurl + link + '.html' 123 | 124 | info['plot'] = article.find('div', {'class': 'item-content'}).text 125 | 126 | contentList.append(( 127 | int(int(self.sourceWeight) * (int(num))), 128 | original_title, title, int(year), img, info, 129 | )) 130 | return contentList 131 | -------------------------------------------------------------------------------- /resources/language/English/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Interface Language 4 | Lock Folders View Style 5 | Off 6 | Save Files To Folder (not FAT32) 7 | Use magnet-links 8 | Keep downloaded files 9 | Keep seeding of downloaded files 10 | Upload speed limit MBits/sec (0 - unlimited) 11 | Download speed limit MBits/sec (0 - unlimited) 12 | Use only system libtorrent 13 | Predownload and play next episode 14 | Download metadata for Content Lists 15 | Debug (Developer Mode) 16 | Confluence (by slng) 17 | Transperency (by slng) 18 | Confluence (by DiMartino) 19 | Confluence (by RussakHH) 20 | Enable Search History 21 | python-libtorrent 22 | Ace Stream 23 | P2P Player 24 | Rest Settings in Programms - AceStream Client 25 | Search Timeout 26 | Short (10s) 27 | Normal (20s) 28 | Long (30s) 29 | Predownload subtitles from all folders 30 | Keep seeding after watching 31 | Ask to change storage before play 32 | Delete Russian stuff 33 | Return Russian stuff 34 | Action onClick for torrent 35 | Open Torrent File 36 | Open Context Menu 37 | Download via Torrent-client 38 | Download via python-libtorrent 39 | Sort search results by seeds 40 | Custom search option phrase 41 | Encryption 42 | Search Thread Number 43 | Delete files 44 | Save files 45 | Ask to save 46 | Torrent2HTTP (libtorrent via http) 47 | Proxy for Search 48 | Proxy for Content Lists 49 | SOCKS-proxy IP 50 | SOCKS-proxy Port 51 | None 52 | Anti-zapret 53 | Tor 54 | Immunicity 55 | Max. connections (0 - unlimited) 56 | Use random ports 57 | Port for incoming connections 58 | Pre-buffer amount before start playing (MB) 59 | Autoplay next episode (or ask) 60 | Device configuration 61 | Average/Good PC 62 | Below average PC/router 63 | Storage size at least for auto clear (GB) 64 | Pause video after start 65 | Sort search results 66 | by Seeds 67 | Do not sort 68 | by Name 69 | Do not add to Watched History if played more (%) 70 | Confluence (by safonov_ivan) 71 | Aeon Nox (by joyrider) 72 | pyrrent2http (python-libtorrent via http) 73 | Append size to file name 74 | Enable DHT 75 | Estuary (by DiMartino) 76 | Стиль информации о раздаче 77 | In the end 78 | In the beginning 79 | Second string 80 | Arctic: Zephyr (by xbmc00) 81 | Interface 82 | P2P Network 83 | Advanced 84 | Torrent-client 85 | Hentai (fine-tuning) 86 | Search Window Mode 87 | Enabled, hide old style 88 | Enabled, replace old style 89 | Enabled, optional 90 | Disabled 91 | Search Window Transparent Background 92 | Disable Notifications 93 | Save path 94 | Call dialog 95 | Default 96 | Path 97 | Create subdirectory for scrapper 98 | Host 99 | Port 100 | URL 101 | Login 102 | Password 103 | Torrent Client 104 | URL (only No SSL) 105 | Path replacement (remote only) 106 | Close 107 | Open Settings 108 | Torrent Client Browser 109 | Install Trackers 110 | Trackers Settings 111 | Clear Storage 112 | Choose searcher 113 | You don't have external searcher. Please install it first. 114 | 115 | -------------------------------------------------------------------------------- /resources/language/spanish/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Idioma de la interfaz 4 | Modo vista de carpetas 5 | No fijar 6 | Carpeta en la que guardar archivos (no FAT32) 7 | Utilizar enlaces magnet 8 | Almacenaje de archivos descargados 9 | Seguir compartiendo archivos descargados 10 | Máx. vel. de subida en Mbit/s (0 = ilimitada) 11 | Máx. vel. de descarga en Mbit/s (0 = ilimitada) 12 | Utilizar solo libtorrent 13 | Lanzar descarga de episodio siguiente 14 | Descargar metadatos para listas de contenido 15 | Depuración (Modo desarrollador) 16 | Confluence (de slng) 17 | Transperency (de slng) 18 | Confluence (de DiMartino) 19 | Confluence (de RussakHH) 20 | Habilitar el historial de búsquedas 21 | python-libtorrent 22 | Ace Stream 23 | Reproductor P2P 24 | Restablecer ajustes en Add-ons de programas -- AceStream client 25 | Duración de las búsquedas 26 | Breve (10 s) 27 | Normal (20 s) 28 | Larga (30 s) 29 | Predescargar los subtítulos de todas las carpetas 30 | Seguir compartiendo después de ver el vídeo 31 | Preguntar para cambiar de almacén antes de la descarga 32 | Eliminar material ruso 33 | Restituir material ruso 34 | Efecto de clic sobre torrent 35 | Abrir archivo torrent 36 | Abrir menú contextual 37 | Descargar vía cliente BitTorrent 38 | Descargar vía python-libtorrent 39 | Ordenar resultados por fuentes 40 | Términos para búsqueda personalizada 41 | Cifrado 42 | Número de hilos de búsqueda 43 | Eliminar archivos 44 | Guardar archivos 45 | Preguntar para guardar 46 | Torrent2HTTP (libtorrent vía HTTP) 47 | Proxy 48 | Ninguno 49 | Anti-zapret (antirestricción) 50 | Tor 51 | Inmunidad 52 | Núm. máx. de conexiones (0 = ilimitadas) 53 | Utilizar puertos aleatorios 54 | Puerto para conexiones entrantes 55 | Tamaño de precarga para empezar a reproducir (MB) 56 | Autorreproducir episodio siguiente (o preguntar) 57 | Configuración de dispositivo 58 | Promedio/Buen PC 59 | Por debajo de la media, PC/router 60 | Tamaño mínimo de almacenaje para autoborrado (GB) 61 | Diferir inicio del reproductor 62 | Ordenar resultados de la búsqueda 63 | Por fuentes 64 | No ordenar 65 | Por nombre 66 | Añadir al historial de vistos después del (%) 67 | Confluence (de safonov_ivan) 68 | Aeon Nox (de joyrider) 69 | pyrrent2http (python-libtorrent vía HTTP) 70 | Añadir tamaño a nombre de archivo 71 | Activar protocolo DHT 72 | Estuary (de DiMartino) 73 | Modo presentación de datos 74 | Al final 75 | Al principio 76 | En segunda línea 77 | Arctic: Zephyr (de xbmc00) 78 | Interfaz 79 | Red P2P 80 | Avanzado 81 | Cliente de BitTorrent 82 | Hentai (reajustes) 83 | Modo ventana de búsquedas 84 | Activado ocultar estilo anterior 85 | Activado remplazar estilo anterior 86 | Activado opcional 87 | Desactivado 88 | Fondo transparente de ventana de búsquedas 89 | Desactivar notificaciones 90 | Ruta para guardar 91 | Preguntar 92 | Por defecto 93 | Ruta 94 | Crear subdirectorio para scrapper 95 | Host 96 | Puerto 97 | URL 98 | Usuario 99 | Contraseña 100 | Cliente de BitTorrent 101 | URL (no SSL) 102 | Sustitución de ruta (Solo remoto) 103 | Cerrar 104 | Abrir "Ajustes" 105 | Navegador de cliente BitTorrent 106 | Instalar rastreadores 107 | Ajustes de rastreadores 108 | Vaciar almacén 109 | Seleccionar buscador 110 | No hay buscador externo. Instale uno primero. 111 | 112 | -------------------------------------------------------------------------------- /resources/language/Russian/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Язык интерфейса 4 | Удерживать стиль отображения 5 | Отключено 6 | Сохранять файлы в директорию (не FAT32) 7 | Использовать магнет-ссылки 8 | Хранить загруженные файлы 9 | Оставаться на раздаче скачанных файлов 10 | Ограничить скорость раздачи МБит/сек (0 - неограничено) 11 | Ограничить скорость закачки МБит/сек (0 - неограничено) 12 | Использовать только пользовательский libtorrent 13 | Скачать и запустить следующий эпизод 14 | Загружать мета-данные для Списков Медиа 15 | Дебаг (Разработчик) 16 | Confluence (от slng) 17 | Transperency (от slng) 18 | Confluence (от DiMartino) 19 | Confluence (от RussakHH) 20 | Включить Историю Поиска 21 | python-libtorrent 22 | Ace Stream 23 | P2P Проигрыватель 24 | Остальные настройки в "Программы - AceStream Client" 25 | Ожидание ответа сервера при Поиске 26 | Короткое (10с) 27 | Среднее (20с) 28 | Долгое (30с) 29 | Предзакачать и подключить субтитры 30 | Продолжать сидировать после просмотра 31 | Предлагать изменить место хранения 32 | Удалить русские трекеры 33 | Вернуть русские трекеры 34 | Действие при нажатии на торрент 35 | Открыть Торрент Файл 36 | Открыть Контекстное меню 37 | Скачать Торрент-клиентом 38 | Скачать python-libtorrent 39 | Сортировать поиск по количеству раздающих 40 | Дополнительная фраза в вариантах поиска 41 | Шифрование 42 | Количество параллельных потоков поиска 43 | Удалять файлы 44 | Сохранять файлы 45 | Спросить о сохранении 46 | Torrent2HTTP (libtorrent по http) 47 | Прокси для поиска 48 | Прокси для списков медиа 49 | IP SOCKS-прокси 50 | Порт SOCKS-прокси 51 | Не использовать 52 | Anti-zapret 53 | Tor 54 | Immunicity 55 | Макс. соединений (0 - безлимит) 56 | Использовать случайные порты 57 | Порт для входящих соединений 58 | Загружаемый объем файла до начала просмотра (МБ) 59 | Автоматически проиграть следующий эпизод (или предложить) 60 | Конфигурация устройства 61 | Нормальный ПК 62 | Плохой ПК/роутер 63 | Минимальный размер хранилища для очистки (ГБ) 64 | Ставить паузу при старте видео 65 | Сортировать результат поиска 66 | по Количеству раздающих 67 | Не сортировать 68 | по Имени 69 | Не добавлять в История Просмотров если больше (%) 70 | Confluence (от safonov_ivan) 71 | Aeon Nox (от joyrider) 72 | pyrrent2http (python-libtorrent по http) 73 | Добавлять размер к имени файла 74 | Включить DHT 75 | Estuary (от DiMartino) 76 | Стиль информации о раздаче 77 | В конце 78 | В начале 79 | Второй строкой 80 | Arctic: Zephyr (от xbmc00) 81 | Интерфейс 82 | P2P Сеть 83 | Дополнительные 84 | Торрент-клиент 85 | Hentai (тонкая настр.) 86 | Режим работы Окна Поиска 87 | Включен, убрать старый вид 88 | Включен, заменить старый вид 89 | Включен, как опция 90 | Отключен 91 | Прозрачность Окна Поиска 92 | Отключить Уведомления 93 | Директория для сохранения файлов 94 | Вызывать диалог 95 | Задать по умолчанию 96 | Путь к директории 97 | Создавать поддиректорию для скрапера 98 | Хост 99 | Порт 100 | URL 101 | Логин 102 | Пароль 103 | URL (только без SSL) 104 | Торрент-клиент 105 | Замена пути (remote only) 106 | Закрыть 107 | Открыть Настройки 108 | Браузер Торрент-клиента 109 | Установить Трекеры 110 | Настройки Трекеров 111 | Очистить хранилище 112 | Выберите трекер 113 | У вас нет внешних серчеров. Пожалуйста, сначало установите их. 114 | 115 | -------------------------------------------------------------------------------- /AceStream.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter v2 plugin for XBMC/Kodi 4 | Copyright (C) 2012-2015 Vadim Skorba v1 - DiMartino v2 5 | https://forums.tvaddons.ag/addon-releases/29224-torrenter-v2.html 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | 21 | import os 22 | import urllib2 23 | import urllib 24 | import hashlib 25 | import re 26 | import base64 27 | from StringIO import StringIO 28 | import zlib 29 | 30 | from functions import file_decode, file_encode 31 | from functions import magnet_alert, log, loadsw_onstop 32 | import xbmcvfs 33 | 34 | 35 | class AceStream: 36 | try: 37 | fpath = os.path.expanduser("~") 38 | pfile = os.path.join(fpath, 'AppData\Roaming\ACEStream\engine', 'acestream.port') 39 | gf = open(pfile, 'r') 40 | aceport = int(gf.read()) 41 | gf.close() 42 | log('[AceStream]: aceport - '+str(aceport)) 43 | except: 44 | aceport = 62062 45 | 46 | torrentFile = None 47 | magnetLink = None 48 | storageDirectory = '' 49 | torrentFilesDirectory = 'torrents' 50 | startPart = 0 51 | endPart = 0 52 | partOffset = 0 53 | torrentHandle = None 54 | session = None 55 | downloadThread = None 56 | threadComplete = False 57 | lt = None 58 | 59 | def __init__(self, storageDirectory='', torrentFile='', torrentFilesDirectory='torrents'): 60 | try: 61 | from ASCore import TSengine as tsengine 62 | 63 | log('Imported TSengine from ASCore') 64 | except Exception, e: 65 | log('Error importing TSengine from ASCore. Exception: ' + str(e)) 66 | return 67 | 68 | self.TSplayer = tsengine() 69 | del tsengine 70 | self.torrentFilesDirectory = torrentFilesDirectory 71 | self.storageDirectory = storageDirectory 72 | _path = os.path.join(self.storageDirectory, self.torrentFilesDirectory) + os.sep 73 | if re.match("^magnet\:.+$", torrentFile): 74 | torrentFile = self.magnetToTorrent(torrentFile) 75 | if not xbmcvfs.exists(_path): 76 | xbmcvfs.mkdirs(_path) 77 | if xbmcvfs.exists(torrentFile): 78 | self.torrentFile = torrentFile 79 | content = xbmcvfs.File(torrentFile, "rb").read() 80 | self.torrentFileInfo = self.TSplayer.load_torrent(base64.b64encode(content), 'RAW') 81 | 82 | def __exit__(self): 83 | self.TSplayer.end() 84 | loadsw_onstop() # Reload Search Window 85 | 86 | def play_url_ind(self, ind, label, icon): 87 | self.TSplayer.play_url_ind(int(ind), label, str(icon), '') 88 | 89 | def saveTorrent(self, torrentUrl): 90 | if re.match("^magnet\:.+$", torrentUrl): 91 | torrentFile = self.magnetToTorrent(torrentUrl) 92 | content = xbmcvfs.File(file_decode(torrentFile), "rb").read() 93 | else: 94 | torrentFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + self.md5( 95 | torrentUrl) + '.torrent' 96 | try: 97 | if not re.match("^[htps]+?://.+$|^://.+$", torrentUrl): 98 | log('xbmcvfs.File for %s' % torrentUrl) 99 | content = xbmcvfs.File(torrentUrl, "rb").read() 100 | else: 101 | log('request for %s' % torrentUrl) 102 | content = self.makeRequest(torrentUrl) 103 | 104 | localFile = xbmcvfs.File(torrentFile, "w+b") 105 | localFile.write(content) 106 | localFile.close() 107 | except Exception, e: 108 | log('Unable to save torrent file from "' + torrentUrl + '" to "' + torrentFile + 109 | '" in Torrent::saveTorrent' + '. Exception: ' + str(e)) 110 | return 111 | if xbmcvfs.exists(torrentFile): 112 | self.torrentFile = torrentFile 113 | self.torrentFileInfo = self.TSplayer.load_torrent(base64.b64encode(content), 'RAW') 114 | return self.torrentFile 115 | 116 | def makeRequest(self, torrentUrl): 117 | torrentUrl = re.sub('^://', 'http://', torrentUrl) 118 | x = re.search("://(.+?)/|://(.+?)$", torrentUrl) 119 | if x: 120 | baseurl = x.group(1) if x.group(1) else x.group(2) 121 | else: 122 | baseurl ='' 123 | 124 | headers = [('User-Agent', 125 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 YaBrowser/14.10.2062.12061 Safari/537.36'), 126 | ('Referer', 'http://%s/' % baseurl), ('Accept-encoding', 'gzip'), ] 127 | 128 | opener = urllib2.build_opener() 129 | opener.addheaders = headers 130 | result = opener.open(torrentUrl) 131 | if result.info().get('Content-Encoding') == 'gzip': 132 | buf = StringIO(result.read()) 133 | decomp = zlib.decompressobj(16 + zlib.MAX_WBITS) 134 | content = decomp.decompress(buf.getvalue()) 135 | else: 136 | content = result.read() 137 | return content 138 | 139 | def magnetToTorrent(self, magnet): 140 | try: 141 | from SkorbaLoader import SkorbaLoader 142 | torrent = SkorbaLoader(self.storageDirectory, magnet) 143 | torrent.magnetToTorrent(magnet) 144 | self.torrentFile = torrent.torrentFile 145 | log('[AceStream][magnetToTorrent]: self.torrentFile '+str(self.torrentFile)) 146 | return self.torrentFile 147 | except: 148 | magnet_alert() 149 | exit() 150 | 151 | def getFilePath(self, contentId=0): 152 | fileList = self.getContentList() 153 | for i in fileList: 154 | if i['ind'] == contentId: 155 | return os.path.join(file_encode(self.storageDirectory), i['title']) 156 | 157 | def getContentList(self): 158 | filelist = [] 159 | for k, v in self.TSplayer.files.iteritems(): 160 | stringdata = {"title": urllib.unquote_plus(k), "ind": int(v)} 161 | filelist.append(stringdata) 162 | return filelist 163 | 164 | def md5(self, string): 165 | hasher = hashlib.md5() 166 | try: 167 | hasher.update(string) 168 | except: 169 | hasher.update(string.encode('utf-8', 'ignore')) 170 | return hasher.hexdigest() 171 | -------------------------------------------------------------------------------- /resources/contenters/unused/CXZ.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Torrenter plugin for XBMC 4 | Copyright (C) 2012 Vadim Skorba 5 | vadim.skorba@gmail.com 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ''' 20 | 21 | import re 22 | 23 | import Content 24 | from BeautifulSoup import BeautifulSoup 25 | from datetime import date 26 | 27 | def make_category_dict(): 28 | category_dict = { 29 | 'movies': ('Movies', '/films/fl_foreign_hight/?'), 30 | 'rus_movies': ('Russian Movies', '/films/fl_our_hight/?'), 31 | 'tvshows': ('TV Shows', '/serials/fl_hight/?'), 32 | 'cartoons': ('Cartoons', '/cartoons/fl_hight/?'), 33 | 'anime': ('Anime', '/cartoons/cartoon_genre/anime/?'), 34 | 'hot': ('Most Recent', '/films/fl_hight/?'), 35 | 'top': ('Top 250 Movies', '/films/fl_hight/?sort=popularity&'), 36 | 'genre': {'genre': 'by Genre', 37 | 'action': ('Action', '/films/film_genre/bojevik/?'), 38 | 'adventure': ('Adventure', '/films/film_genre/priklucheniya/?'), 39 | 'biography': ('Biography', '/films/film_genre/biografiya/?'), 40 | 'comedy': ('Comedy', '/films/film_genre/komediya/?'), 41 | 'crime': ('Crime', '/films/film_genre/detektiv/?'), 42 | 'documentary': ('Documentary', '/films/film_genre/dokumentalnyj/?'), 43 | 'drama': ('Drama', '/films/film_genre/drama/?'), 44 | 'erotika': ('Adult', '/films/film_genre/erotika/?'), 45 | 'family': ('Family', '/films/film_genre/semejnyj/?'), 46 | 'fantasy': ('Fantasy', '/films/film_genre/fentezi/?'), 47 | 'film_noir': ('Film-Noir', '/films/film_genre/nuar/?'), 48 | 'history': ('History', '/films/film_genre/istoriya/?'), 49 | 'horror': ('Horror', '/films/film_genre/uzhasy/?'), 50 | 'kids': ('For Kids', '/films/film_genre/detskij/?'), 51 | 'musical': ('Musical', '/films/film_genre/muzikl/?'), 52 | 'mystery': ('Mystery', '/films/film_genre/mistika/?'), 53 | 'romance': ('Romance', '/films/film_genre/melodrama/?'), 54 | 'sci_fi': ('Sci-Fi', '/films/film_genre/fantastika/?'), 55 | 'short': ('Short', '/films/film_genre/korotkometrazhka/?'), 56 | 'thriller': ('Thriller', '/films/film_genre/triller/?'), 57 | 'war': ('War', '/films/film_genre/vojennyj/?'), 58 | 'western': ('Western', '/films/film_genre/vestern/?'), 59 | } 60 | } 61 | 62 | for category in category_dict.keys(): 63 | if isinstance(category_dict.get(category), dict): 64 | for subcategory in category_dict.get(category).keys(): 65 | if subcategory != category: 66 | x = category_dict[category][subcategory] 67 | category_dict[category][subcategory] = ( 68 | x[0], x[1] + 'view=list', {'page': x[1] + 'view=list&page=%d', 'increase': 1, 'second_page': 1}) 69 | if not isinstance(category_dict.get(category), dict): 70 | x = category_dict[category] 71 | category_dict[category] = ( 72 | x[0], x[1] + 'view=list', {'page': x[1] + 'view=list&page=%d', 'increase': 1, 'second_page': 1}) 73 | 74 | category_dict['year'] = {'year': 'by Year', } 75 | for y in range(date.today().year, 1970, -1): 76 | category_dict['year'][str(y)] = (str(y), '/films/year/%s/' % str(y), 77 | {'page': '/films/year/%s/' % str(y) + '?view=list&page=%d', 'increase': 1, 78 | 'second_page': 1}) 79 | 80 | return category_dict 81 | 82 | 83 | class CXZ(Content.Content): 84 | category_dict = make_category_dict() 85 | 86 | regex_list = [] 87 | 88 | baseurl = "http://cxz.to" 89 | headers = [('User-Agent', 90 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124' + \ 91 | ' YaBrowser/14.10.2062.12061 Safari/537.36'), 92 | ('Referer', baseurl), ('Accept-Encoding', 'gzip'), ('Accept-Language', 'ru,en;q=0.8')] 93 | ''' 94 | Weight of source with this searcher provided. 95 | Will be multiplied on default weight. 96 | Default weight is seeds number 97 | ''' 98 | sourceWeight = 2 99 | 100 | 101 | def isTracker(self): 102 | return False 103 | 104 | def isSearcher(self): 105 | return False 106 | 107 | def isInfoLink(self): 108 | return False 109 | 110 | def isPages(self): 111 | return True 112 | 113 | def isSearchOption(self): 114 | return True 115 | 116 | def isScrappable(self): 117 | return True 118 | 119 | def get_contentList(self, category, subcategory=None, apps_property=None): 120 | contentList = [] 121 | url = self.get_url(category, subcategory, apps_property) 122 | 123 | response = self.makeRequest(url, headers=self.headers) 124 | 125 | if None != response and 0 < len(response): 126 | #print response 127 | if category: 128 | contentList = self.mode(response) 129 | #print str(contentList) 130 | return contentList 131 | 132 | def mode(self, response): 133 | contentList = [] 134 | Soup = BeautifulSoup(response) 135 | result = Soup.findAll('div', {'class': 'b-poster-tile '}) 136 | num = 0 137 | for tr in result: 138 | #main 139 | info = {} 140 | year = 0 141 | num = num + 1 142 | title = tr.find('span', 'b-poster-tile__title-full').text.strip() 143 | originaltitle = None 144 | year = re.compile('(\d\d\d\d)').findall(tr.find('span', 'b-poster-tile__title-info-items').text)[0] 145 | link = tr.find('a', 'b-poster-tile__link').get('href') 146 | for i in ['/serials/', '/cartoonserials/', '/tvshow/']: 147 | if i in link: 148 | info['tvshowtitle'] = title 149 | break 150 | 151 | img = tr.find('img').get('src') 152 | img = img if img else '' 153 | 154 | #info 155 | 156 | contentList.append(( 157 | int(int(self.sourceWeight) * (251 - int(num))), 158 | originaltitle, title, int(year), img, info, 159 | )) 160 | #print result 161 | return contentList -------------------------------------------------------------------------------- /resources/utorrent/dopal/utils.py: -------------------------------------------------------------------------------- 1 | # File: utils.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | ''' 18 | General utility functions. 19 | ''' 20 | 21 | __pychecker__ = 'unusednames=metaclass_,attr_,no-unreachable' 22 | 23 | # Converts a binary string character into a hexidecimal equivalent. 24 | # 25 | # >>> byte_to_hex_form('N') 26 | # u'4E' 27 | def byte_to_hex_form(byte): 28 | # ord is used to convert the byte into it's numeric equivalent. 29 | # hex will turn the number into a string like "0x5" or "0xa3" 30 | # the slicing will chop off the "0x" prefix. 31 | # zfill will change any single digit values to have a leading 0 character. 32 | # upper will force all the A-F characters to be uppercase. 33 | return unicode(hex(ord(byte))[2:].zfill(2).upper()) 34 | 35 | 36 | # Converts a binary string into a hexidecimal equivalent. 37 | # 38 | # >>> string_to_hex_form('xK3-') 39 | # u'784B332D' 40 | def string_to_hex_form(chars): 41 | if not chars: 42 | raise ValueError, "cannot convert empty string" 43 | return ''.join([byte_to_hex_form(char) for char in chars]) 44 | 45 | 46 | # Converts a 2 character hexidecimal string into a binary character. 47 | # 48 | # >>> hex_form_to_byte(u'4E') 49 | # 'L' 50 | def hex_pair_to_byte(hex_pair): 51 | return chr(int(hex_pair.encode('utf-8'), 16)) 52 | 53 | 54 | # Converts a hexidecimal string (a string containing only characters valid 55 | # in use for displaying a hexidecimal number, i.e. 0123456789ABCDEF) into 56 | # a binary string. 57 | # 58 | # >>> hex_string_to_binary(u'784B332D') 59 | # 'xK3-' 60 | def hex_string_to_binary(hex_string): 61 | if len(hex_string) % 2: 62 | raise ValueError, "string given has odd-number of characters, must be even" 63 | if not hex_string: 64 | raise ValueError, "cannot convert empty string" 65 | return ''.join([hex_pair_to_byte(hex_string[i:i + 2]) for i in range(0, len(hex_string), 2)]) 66 | 67 | 68 | def make_short_object_id(object_id): 69 | # Due to the way Azureus generates object ID's (it tends to be a 70 | # long integer which is then incremented for each object it 71 | # generates), we don't bother rendering the entire object ID 72 | # as it would be too long (it can be as long as 20 characters). 73 | # 74 | # So instead, we only use the last 6 digits - turning the ID into 75 | # hexidecimal. This gives us a range of 16**6 = 16.7 million. So 76 | # Azureus would have to generate more than 16 million objects 77 | # before it generates an ID which has the same short ID form as 78 | # another object. 79 | # 80 | # We show the short ID as a way of easily seeing whether two 81 | # objects represent the same remote object or not. 82 | hex_id = hex(object_id) 83 | 84 | if hex_id[-1] == 'L': 85 | hex_short_id = hex_id[-7:-1] 86 | else: 87 | hex_short_id = hex_id[-6:] 88 | 89 | return hex_short_id 90 | 91 | 92 | def parse_azureus_version_string(ver_string): 93 | ver_bits = ver_string.split('_', 2) 94 | if len(ver_bits) == 1: 95 | major_ver, minor_ver = ver_string, None 96 | else: 97 | major_ver, minor_ver = ver_bits 98 | 99 | ver_segments = [int(bit) for bit in major_ver.split('.')] 100 | if minor_ver: 101 | if minor_ver[0].lower() == 'b': 102 | ver_segments.append('b') 103 | try: 104 | beta_ver = int(minor_ver[1:]) 105 | except ValueError: 106 | pass 107 | else: 108 | ver_segments.append(beta_ver) 109 | 110 | return tuple(ver_segments) 111 | 112 | # 113 | # I love this code. :) 114 | # 115 | # I might turn it into something more generic, and use it elsewhere.. 116 | # 117 | # Would be nicer if there was a better API for doing this, but given the amount 118 | # of hackery that I'm doing right now, I won't complain. :) 119 | # 120 | # What a lot of effort just to act as if these methods were defined in the 121 | # class itself. 122 | import new 123 | 124 | 125 | class MethodFactory(object): 126 | def __init__(self, method_object): 127 | _codeobj = method_object.func_code 128 | code_arguments = [ 129 | _codeobj.co_argcount, _codeobj.co_nlocals, _codeobj.co_stacksize, 130 | _codeobj.co_flags, _codeobj.co_code, _codeobj.co_consts, 131 | _codeobj.co_names, _codeobj.co_varnames, _codeobj.co_filename, 132 | _codeobj.co_name, _codeobj.co_firstlineno, _codeobj.co_lnotab, 133 | ] 134 | self.code_arguments = code_arguments 135 | 136 | def _build_function(self, name): 137 | code_args = self.code_arguments[:] 138 | code_args[9] = name 139 | # code_args[8] = 140 | codeobj = new.code(*code_args) 141 | return new.function(codeobj, {'__funcname__': name, '__builtins__': __builtins__}, name) 142 | 143 | def make_instance_method(self, name, instanceobj): 144 | method = self._build_function(name) 145 | return new.instancemethod(method, instanceobj, type(instanceobj)) 146 | 147 | def make_class_method(self, name, classobj): 148 | method = self._build_function(name) 149 | return new.instancemethod(method, None, classobj) 150 | 151 | 152 | def _not_implemented(self, *args, **kwargs): 153 | class_name = self.__class__.__name__ 154 | funcname = __funcname__ 155 | raise NotImplementedError, "%(class_name)s.%(funcname)s" % locals() 156 | 157 | 158 | not_implemented_factory = MethodFactory(_not_implemented) 159 | make_not_implemented_class_method = not_implemented_factory.make_class_method 160 | del _not_implemented, not_implemented_factory 161 | 162 | 163 | def handle_kwargs(kwargs, *required, **optional): 164 | result = {} 165 | result.update(optional) 166 | 167 | required_args = dict([(x, None) for x in required]) 168 | 169 | for kwarg_key, kwarg_value in kwargs.iteritems(): 170 | if optional.has_key(kwarg_key): 171 | pass 172 | else: 173 | try: 174 | required_args.pop(kwarg_key) 175 | except KeyError: 176 | raise TypeError, "unexpected keyword argument: %r" % kwarg_key 177 | 178 | result[kwarg_key] = kwarg_value 179 | 180 | if required_args: 181 | missing_key = required_args.popitem()[0] 182 | raise TypeError, "missing keyword argument: %r" % missing_key 183 | 184 | return result 185 | 186 | 187 | class Sentinel(object): 188 | def __init__(self, value): 189 | self.value = value 190 | 191 | def __str__(self): 192 | return str(self.value) 193 | 194 | def __repr__(self): 195 | return '' % (self.value, id(self)) 196 | -------------------------------------------------------------------------------- /resources/scrapers/kinopoisk/pageparser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Russian metadata plugin for Plex, which uses http://www.kinopoisk.ru/ to get the tag data. 4 | # Плагин для обновления информации о фильмах использующий КиноПоиск (http://www.kinopoisk.ru/). 5 | # Copyright (C) 2013 Yevgeny Nyden 6 | # 7 | # This program is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU General Public License 9 | # as published by the Free Software Foundation; either version 2 10 | # of the License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 | # 02110-1301, USA. 21 | # 22 | # @author zhenya (Yevgeny Nyden) 23 | # @version 1.52 24 | # @revision 148 25 | 26 | import re 27 | import urllib 28 | import operator 29 | 30 | import common 31 | import pluginsettings as S 32 | import translit 33 | 34 | 35 | 36 | 37 | 38 | # MATCHER_MOVIE_DURATION = re.compile('\s*(\d+).*?', re.UNICODE | re.DOTALL) 39 | # MATCHER_IMDB_RATING = re.compile('IMDb:\s*(\d+\.?\d*)\s*\(\s*([\s\d]+)\s*\)', re.UNICODE | re.DOTALL) 40 | # MATCHER_IMDB_RATING = re.compile('IMDb:\s*(\d+\.?\d*)\s?\((.*)\)', re.UNICODE) 41 | 42 | USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22' 43 | ''' 44 | MOVIE_THUMBNAIL_SMALL_WIDTH = 130 45 | MOVIE_THUMBNAIL_SMALL_HEIGHT = 168 46 | MOVIE_THUMBNAIL_BIG_WIDTH = 780 47 | MOVIE_THUMBNAIL_BIG_HEIGHT = 1024 48 | 49 | # Compiled regex matchers. 50 | MATCHER_WIDTH_FROM_STYLE = re.compile('.*width\s*:\s*(\d+)px.*', re.UNICODE) 51 | MATCHER_HEIGHT_FROM_STYLE = re.compile('.*height\s*:\s*(\d+)px.*', re.UNICODE) 52 | MATCHER_LEADING_NONALPHA = re.compile('^[\s\d\.\(\)]*', re.UNICODE | re.MULTILINE) 53 | 54 | 55 | # Русские месяца, пригодятся для определения дат. 56 | RU_MONTH = { 57 | u'января': '01', 58 | u'февраля': '02', 59 | u'марта': '03', 60 | u'апреля': '04', 61 | u'мая': '05', 62 | u'июня': '06', 63 | u'июля': '07', 64 | u'августа': '08', 65 | u'сентября': '09', 66 | u'октября': '10', 67 | u'ноября': '11', 68 | u'декабря': '12' 69 | } 70 | ''' 71 | 72 | 73 | class PageParser: 74 | def __init__(self, logger, httpUtils=common.HttpUtils(S.ENCODING_KINOPOISK_PAGE, USER_AGENT), isDebug=False): 75 | self.log = logger 76 | self.isDebug = isDebug 77 | self.httpUtils = httpUtils 78 | 79 | def fetchAndParseSearchResults(self, mediaName, mediaYear, mediaAltName=None): 80 | """ Searches for movie titles on KinoPoisk. 81 | @param mediaName Movie title parsed from a filename. 82 | @param mediaName Movie year parsed from a filename. 83 | @return Array of tuples: [kinoPoiskId, title, year, score] 84 | """ 85 | self.log.Info('Quering kinopoisk...') 86 | results = self.queryKinoPoisk(mediaName, mediaYear) 87 | 88 | # Check media name is all ASCII characters, and if it is, 89 | # issue another query to KinoPoisk using a translified media name; 90 | # lastly, merge the scored results. 91 | if common.isAsciiString(mediaName): 92 | translifiedMediaName = translit.detranslify(mediaName) 93 | moreResults = self.queryKinoPoisk(translifiedMediaName, mediaYear) 94 | resultsMap = dict() 95 | for result in results: 96 | resultsMap[result[0]] = result 97 | results = [] # Recreate and repopulate the results array removing duplicates. 98 | for result in moreResults: 99 | currId = result[0] 100 | if currId in resultsMap.keys(): 101 | origResult = resultsMap[currId] 102 | del resultsMap[currId] 103 | if result[3] >= origResult[3]: 104 | results.append(result) 105 | else: 106 | results.append(origResult) 107 | else: 108 | results.append(result) 109 | results.extend(resultsMap.values()) 110 | 111 | if mediaAltName: 112 | moreResults = self.queryKinoPoisk(mediaAltName, mediaYear) 113 | resultsMap = dict() 114 | for result in results: 115 | resultsMap[result[0]] = result 116 | results = [] # Recreate and repopulate the results array removing duplicates. 117 | for result in moreResults: 118 | currId = result[0] 119 | if currId in resultsMap.keys(): 120 | origResult = resultsMap[currId] 121 | del resultsMap[currId] 122 | if result[3] >= origResult[3]: 123 | results.append(result) 124 | else: 125 | results.append(origResult) 126 | else: 127 | results.append(result) 128 | results.extend(resultsMap.values()) 129 | 130 | # Sort all results based on their score. 131 | results.sort(key=operator.itemgetter(3)) 132 | results.reverse() 133 | if self.isDebug: 134 | self.log.Debug('Search produced %d results:' % len(results)) 135 | index = -1 136 | for result in results: 137 | index += 1 138 | self.log.Debug(' ... %d: id="%s", name="%s", year="%s", score="%d".' % 139 | (index, result[0], result[1], str(result[2]), result[3])) 140 | return results 141 | 142 | def queryKinoPoisk(self, mediaName, mediaYear): 143 | """ Ищет фильм на кинопоиске. 144 | Returns title results as they are returned (no sorting is done here!). 145 | """ 146 | results = [] 147 | try: 148 | encodedName = urllib.quote(mediaName.encode(S.ENCODING_KINOPOISK_PAGE)) 149 | except: 150 | encodedName = urllib.quote(mediaName.encode('utf-8')) 151 | page = self.httpUtils.requestAndParseHtmlPage(S.KINOPOISK_SEARCH_SIMPLE % encodedName) 152 | if page is None: 153 | self.log.Warn(' ### nothing was found on kinopoisk for media name "%s"' % mediaName) 154 | return results 155 | 156 | # Страница получена, берем с нее перечень всех названий фильмов. 157 | self.log.Debug('**** Finding %s (%s) got a KinoPoisk...' % (mediaName, mediaYear)) 158 | 159 | reobj = re.compile(r'(.+?)
(.+?)') 160 | result = reobj.findall(page) 161 | 162 | # Inspect query results titles and score them. 163 | self.log.Debug('found %d results (div info tags)' % len(result)) 164 | itemIndex = -1 165 | for itemKinoPoiskId, itemTitleitemYear, itemAltTitle in result: 166 | itemIndex = itemIndex + 1 167 | itemAltTitle = itemAltTitle.replace(' ', '') 168 | try: 169 | itemTitle, itemYear = re.compile('^(.+?), (\d\d\d\d)$').findall(itemTitleitemYear.strip())[0] 170 | except: 171 | itemYear = None 172 | itemTitle = itemTitleitemYear 173 | itemScore = common.scoreMediaTitleMatch(mediaName, mediaYear, itemTitle, itemAltTitle, itemYear, itemIndex) 174 | results.append([itemKinoPoiskId, itemTitle, itemYear, itemScore]) 175 | 176 | return results 177 | -------------------------------------------------------------------------------- /resources/scrapers/kinopoisk/HTTP.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Russian metadata plugin for Plex, which uses http://www.kinopoisk.ru/ to get the tag data. 4 | # Плагин для обновления информации о фильмах использующий КиноПоиск (http://www.kinopoisk.ru/). 5 | # Copyright (C) 2012 Yevgeny Nyden 6 | # 7 | # This program is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU General Public License 9 | # as published by the Free Software Foundation; either version 2 10 | # of the License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 | # 02110-1301, USA. 21 | # 22 | # @author zhenya (Yevgeny Nyden) 23 | # @revision 148 24 | 25 | import urllib 26 | import urllib2 27 | import base64 28 | import time 29 | 30 | import LOGGER as Log 31 | 32 | 33 | class HTTP: 34 | def __init__(self): 35 | pass 36 | 37 | 38 | def fetch(self, request, **kwargs): 39 | self.con, self.fd, self.progress, self.cookies, self.request = None, None, None, None, request 40 | 41 | if not isinstance(self.request, HTTPRequest): 42 | self.request = HTTPRequest(url=self.request, **kwargs) 43 | 44 | self.response = HTTPResponse(self.request) 45 | 46 | Log.Debug('XBMCup: HTTP: request: ' + str(self.request)) 47 | try: 48 | self._opener() 49 | self._fetch() 50 | except Exception, e: 51 | Log.Debug('XBMCup: HTTP: ' + str(e)) 52 | if isinstance(e, urllib2.HTTPError): 53 | self.response.code = e.code 54 | self.response.error = e 55 | else: 56 | self.response.code = 200 57 | 58 | if self.fd: 59 | self.fd.close() 60 | self.fd = None 61 | 62 | if self.con: 63 | self.con.close() 64 | self.con = None 65 | 66 | if self.progress: 67 | self.progress.close() 68 | self.progress = None 69 | 70 | self.response.time = time.time() - self.response.time 71 | 72 | return self.response 73 | 74 | 75 | def _opener(self): 76 | 77 | build = [urllib2.HTTPHandler()] 78 | 79 | if self.request.redirect: 80 | build.append(urllib2.HTTPRedirectHandler()) 81 | 82 | if self.request.proxy_host and self.request.proxy_port: 83 | build.append(urllib2.ProxyHandler( 84 | {self.request.proxy_protocol: self.request.proxy_host + ':' + str(self.request.proxy_port)})) 85 | 86 | if self.request.proxy_username: 87 | proxy_auth_handler = urllib2.ProxyBasicAuthHandler() 88 | proxy_auth_handler.add_password('realm', 'uri', self.request.proxy_username, 89 | self.request.proxy_password) 90 | build.append(proxy_auth_handler) 91 | 92 | urllib2.install_opener(urllib2.build_opener(*build)) 93 | 94 | 95 | def _fetch(self): 96 | params = {} if self.request.params is None else self.request.params 97 | 98 | if self.request.method == 'POST': 99 | if isinstance(params, dict) or isinstance(params, list): 100 | params = urllib.urlencode(params) 101 | req = urllib2.Request(self.request.url, params) 102 | else: 103 | req = urllib2.Request(self.request.url) 104 | 105 | for key, value in self.request.headers.iteritems(): 106 | req.add_header(key, value) 107 | 108 | if self.request.auth_username and self.request.auth_password: 109 | req.add_header('Authorization', 'Basic %s' % base64.encodestring( 110 | ':'.join([self.request.auth_username, self.request.auth_password])).strip()) 111 | 112 | # self.con = urllib2.urlopen(req, timeout=self.request.timeout) 113 | self.con = urllib2.urlopen(req) 114 | self.response.headers = self._headers(self.con.info()) 115 | 116 | self.response.body = self.con.read() 117 | 118 | if self.request.cookies: 119 | self.cookies.save(self.request.cookies) 120 | 121 | 122 | def _headers(self, raw): 123 | headers = {} 124 | for line in raw.headers: 125 | pair = line.split(':', 1) 126 | if len(pair) == 2: 127 | tag = pair[0].lower().strip() 128 | value = pair[1].strip() 129 | if tag and value: 130 | headers[tag] = value 131 | return headers 132 | 133 | 134 | def _progress(self, read, size, name): 135 | res = [] 136 | if size < 0: 137 | res.append(1) 138 | else: 139 | res.append(int(float(read) / (float(size) / 100.0))) 140 | if name: 141 | res.append(u'File: ' + name) 142 | if size != -1: 143 | res.append(u'Size: ' + self._human(size)) 144 | res.append(u'Load: ' + self._human(read)) 145 | return res 146 | 147 | def _human(self, size): 148 | human = None 149 | for h, f in (('KB', 1024), ('MB', 1024 * 1024), ('GB', 1024 * 1024 * 1024), ('TB', 1024 * 1024 * 1024 * 1024)): 150 | if size / f > 0: 151 | human = h 152 | factor = f 153 | else: 154 | break 155 | if human is None: 156 | return (u'%10.1f %s' % (size, u'byte')).replace(u'.0', u'') 157 | else: 158 | return u'%10.2f %s' % (float(size) / float(factor), human) 159 | 160 | 161 | class HTTPRequest: 162 | def __init__(self, url, method='GET', headers=None, cookies=None, params=None, upload=None, download=None, 163 | progress=False, auth_username=None, auth_password=None, proxy_protocol='http', proxy_host=None, 164 | proxy_port=None, proxy_username=None, proxy_password='', timeout=20.0, redirect=True, gzip=False): 165 | if headers is None: 166 | headers = {} 167 | 168 | self.url = url 169 | self.method = method 170 | self.headers = headers 171 | 172 | self.cookies = cookies 173 | 174 | self.params = params 175 | 176 | self.upload = upload 177 | self.download = download 178 | self.progress = progress 179 | 180 | self.auth_username = auth_username 181 | self.auth_password = auth_password 182 | 183 | self.proxy_protocol = proxy_protocol 184 | self.proxy_host = proxy_host 185 | self.proxy_port = proxy_port 186 | self.proxy_username = proxy_username 187 | self.proxy_password = proxy_password 188 | 189 | self.timeout = timeout 190 | 191 | self.redirect = redirect 192 | 193 | self.gzip = gzip 194 | 195 | def __repr__(self): 196 | return '%s(%s)' % (self.__class__.__name__, ','.join('%s=%r' % i for i in self.__dict__.iteritems())) 197 | 198 | 199 | class HTTPResponse: 200 | def __init__(self, request): 201 | self.request = request 202 | self.code = None 203 | self.headers = {} 204 | self.error = None 205 | self.body = None 206 | self.filename = None 207 | self.time = time.time() 208 | 209 | def __repr__(self): 210 | args = ','.join('%s=%r' % i for i in self.__dict__.iteritems() if i[0] != 'body') 211 | if self.body: 212 | args += ',body=' 213 | else: 214 | args += ',body=None' 215 | return '%s(%s)' % (self.__class__.__name__, args) 216 | -------------------------------------------------------------------------------- /resources/utorrent/dopal/xmlutils.py: -------------------------------------------------------------------------------- 1 | # File: xmlutils.py 2 | # Library: DOPAL - DO Python Azureus Library 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; version 2 of the License. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details ( see the COPYING file ). 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | 17 | ''' 18 | XML utility functions. 19 | ''' 20 | 21 | # Given an object which has the same interface as xml.dom.Node: 22 | # a) Join all concurrent text nodes together. 23 | # b) Strip all trailing and leading whitespace from each text node. 24 | # 25 | # This function will recursively process the tree structure given in the node 26 | # object. No value will be returned by this function, instead the given object 27 | # will be modified. 28 | def normalise_xml_structure(xml_node): 29 | # Concurrent text nodes should be joined together. 30 | xml_node.normalize() 31 | 32 | # Strip all text nodes which are empty of content (whitespace is not 33 | # content). 34 | from xml.dom import Node 35 | 36 | nodes_to_delete = [] 37 | 38 | for node in xml_node.childNodes: 39 | if node.nodeType == Node.TEXT_NODE: 40 | stripped_text = node.nodeValue.strip() 41 | if stripped_text: 42 | node.nodeValue = stripped_text 43 | else: 44 | nodes_to_delete.append(node) 45 | else: 46 | normalise_xml_structure(node) 47 | 48 | for node in nodes_to_delete: 49 | xml_node.removeChild(node) 50 | node.unlink() 51 | 52 | 53 | def get_text_content(node): 54 | from xml.dom import Node 55 | 56 | # Text content is stored directly in this node. 57 | if node.nodeType == Node.TEXT_NODE: 58 | return node.nodeValue 59 | 60 | # Otherwise, must be in a child node. 61 | #elif len(node.childNodes) == 1 and \ 62 | # node.firstChild.nodeType == Node.TEXT_NODE: 63 | # return node.firstChild.nodeValue 64 | 65 | # Sometimes happens for attributes with no real value. 66 | elif len(node.childNodes) == 0: 67 | return '' 68 | 69 | text_node = None 70 | err_text = None 71 | for child in node.childNodes: 72 | if child.nodeType == Node.TEXT_NODE: 73 | if text_node is None: 74 | text_node = child 75 | else: 76 | err_text = "contained multiple text nodes" 77 | break 78 | else: 79 | if text_node is None: 80 | if len(node.childNodes) != 1: 81 | err_text = "contained multiple nodes, but none were text" 82 | else: 83 | err_text = "did not contain a character string as its value" 84 | else: 85 | return text_node.nodeValue 86 | 87 | raise ValueError, ("the node %s " % node.nodeName) + err_text 88 | 89 | 90 | from xml.sax.saxutils import quoteattr, escape 91 | 92 | # This base class will be removed when XMLObject is removed. 93 | class _XMLObjectBase(object): 94 | def __init__(self, tag_name): 95 | self.tag_name = tag_name 96 | self.attributes = {} 97 | self.contents = [] 98 | 99 | def add_attribute(self, attribute_name, attribute_value): 100 | self.attributes[attribute_name] = attribute_value 101 | 102 | def add_content(self, content): 103 | self.contents.append(content) 104 | 105 | def to_string(self, out=None, indent=0): 106 | if out is None: 107 | # We use StringIO instead of cStringIO not to lose unicode strings. 108 | import StringIO 109 | 110 | out = StringIO.StringIO() 111 | return_as_string = True 112 | else: 113 | return_as_string = False 114 | 115 | indent_string = ' ' * indent 116 | out.write(indent_string) 117 | out.write('<') 118 | out.write(self.tag_name) 119 | for attr_name, attr_value in self.attributes.items(): 120 | out.write(' ') 121 | out.write(attr_name) 122 | out.write('=') 123 | out.write(quoteattr(attr_value)) 124 | 125 | # If we have no contents, we'll close the tag here. 126 | if not self.contents: 127 | out.write(' />\n') 128 | 129 | else: 130 | out.write('>') 131 | 132 | # If we have one piece of content, which is just a string, then 133 | # we'll put it on the same line as the opening tag is on. 134 | if len(self.contents) == 1 and not hasattr(self.contents[0], 'to_string'): 135 | out.write(escape(self.contents[0])) 136 | 137 | # Otherwise, we assume we have some more XML blocks to write out, 138 | # so we'll indent them and put them on newlines. 139 | elif self.contents: 140 | out.write('\n') 141 | for content in self.contents: 142 | content.to_string(out, indent + 2) 143 | out.write(indent_string) 144 | 145 | # Write out the closing tag (if we haven't written it already). 146 | if self.contents: 147 | out.write('\n') 150 | 151 | # If the invocation of this method was not passed a buffer to write 152 | # into, then we return the string representation. 153 | if return_as_string: 154 | return out.getvalue() 155 | 156 | return None 157 | 158 | 159 | class XMLObject(_XMLObjectBase): 160 | ''' 161 | B{Deprecated:} An object representing a block of XML. 162 | 163 | @attention: B{Deprecated:} This class does not provide any guarantees in 164 | the way that byte strings are handled. Use L{UXMLObject} instead. 165 | ''' 166 | 167 | def __init__(self, tag_name): 168 | from dopal.errors import DopalPendingDeprecationWarning 169 | 170 | import warnings 171 | 172 | warnings.warn("XMLObject is deprecated - use UXMLObject instead", DopalPendingDeprecationWarning) 173 | 174 | _XMLObjectBase.__init__(self, tag_name) 175 | 176 | 177 | class UXMLObject(_XMLObjectBase): 178 | ''' 179 | An object representing a block of XML. 180 | 181 | Any string which is added to this block (either through the L{add_content} 182 | or L{add_attribute} methods should be a unicode string, rather than a byte 183 | string. If it is a byte string, then it must be a string which contains 184 | text in the system's default encoding - attempting to add text encoding in 185 | other formats is not allowed. 186 | ''' 187 | 188 | def to_string(self, out=None, indent=0): 189 | result = _XMLObjectBase.to_string(self, out, indent) 190 | if result is None: 191 | return None 192 | return unicode(result) 193 | 194 | def encode(self, encoding='UTF-8'): 195 | return (('\n' % encoding) + self.to_string()).encode(encoding) 196 | 197 | def __unicode__(self): 198 | return self.to_string() 199 | 200 | 201 | def make_xml_ref_for_az_object(object_id): 202 | ''' 203 | Creates an XML block which represents a remote object in Azureus with the given object ID. 204 | 205 | @param object_id: The object ID to reference. 206 | @type object_id: int / long 207 | @return: A L{UXMLObject} instance. 208 | ''' 209 | object_id_block = UXMLObject('_object_id') 210 | object_id_block.add_content(str(object_id)) 211 | 212 | object_block = UXMLObject('OBJECT') 213 | object_block.add_content(object_id_block) 214 | return object_block 215 | -------------------------------------------------------------------------------- /resources/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 48 | 49 | 50 | 51 | 52 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /resources/scrapers/kinopoisk/translit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # -*- test-case-name: pytils.test.test_translit -*- 3 | # pytils - simple processing for russian strings 4 | # Copyright (C) 2006-2007 Yury Yurevich 5 | # 6 | # http://www.pyobject.ru/projects/pytils/ 7 | # 8 | # This program is free software; you can redistribute it and/or 9 | # modify it under the terms of the GNU General Public License 10 | # as published by the Free Software Foundation, version 2 11 | # of the License. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | """ 18 | Simple transliteration 19 | """ 20 | 21 | # __id__ = __revision__ = "$Id: translit.py 102 2007-07-12 12:33:36Z the.pythy $" 22 | # __url__ = "$URL: https://pythy.googlecode.com/svn/tags/pytils/0_2_2/pytils/translit.py $" 23 | 24 | import re 25 | 26 | TRANSTABLE = ( 27 | (u"'", u"'"), 28 | (u'"', u'"'), 29 | (u"‘", u"'"), 30 | (u"’", u"'"), 31 | (u"«", u'"'), 32 | (u"»", u'"'), 33 | (u"–", u"-"), 34 | (u"…", u"..."), 35 | (u"№", u"#"), 36 | ## верхний регистр 37 | # трехбуквенные замены 38 | (u"Щ", u"Sch"), 39 | # при замене русский->английский будет первая замена, 40 | # т.е. Sch 41 | # а вот если английский->русский, то вариант SCH и Sch -- 42 | # оба пройдут 43 | (u"Щ", u"SCH"), 44 | # двухбуквенные замены 45 | (u"Ё", u"Yo"), 46 | (u"Ё", u"YO"), 47 | (u"Ж", u"Zh"), 48 | (u"Ж", u"ZH"), 49 | (u"Ц", u"Ts"), 50 | (u"Ц", u"TS"), 51 | (u"Ч", u"Ch"), 52 | (u"Ч", u"CH"), 53 | (u"Ш", u"Sh"), 54 | (u"Ш", u"SH"), 55 | (u"Ы", u"Yi"), 56 | (u"Ы", u"YI"), 57 | (u"Ю", u"Yu"), 58 | (u"Ю", u"YU"), 59 | (u"Я", u"Ya"), 60 | (u"Я", u"YA"), 61 | (u"ИЙ", u"IY"), 62 | # однобуквенные замены 63 | (u"А", u"A"), 64 | (u"Б", u"B"), 65 | (u"В", u"V"), 66 | (u"Г", u"G"), 67 | (u"Д", u"D"), 68 | (u"Е", u"E"), 69 | (u"З", u"Z"), 70 | (u"И", u"I"), 71 | (u"Й", u"J"), 72 | (u"К", u"K"), 73 | (u"Л", u"L"), 74 | (u"М", u"M"), 75 | (u"Н", u"N"), 76 | (u"О", u"O"), 77 | (u"П", u"P"), 78 | (u"Р", u"R"), 79 | (u"С", u"S"), 80 | (u"Т", u"T"), 81 | (u"У", u"U"), 82 | (u"Ф", u"F"), 83 | (u"Х", u"H"), 84 | (u"Э", u"E"), 85 | (u"Ъ", u"`"), 86 | (u"Ы", u"Y"), 87 | (u"Ь", u"'"), 88 | ## нижний регистр 89 | # трехбуквенные замены 90 | (u"щ", u"sch"), 91 | # двухбуквенные замены 92 | (u"ё", u"yo"), 93 | (u"ж", u"zh"), 94 | (u"ц", u"ts"), 95 | (u"ч", u"ch"), 96 | (u"ш", u"sh"), 97 | (u"ы", u"yi"), 98 | (u"ю", u"yu"), 99 | (u"я", u"ya"), 100 | (u"я", u"ja"), 101 | (u"ий", u"iy"), 102 | # однобуквенные замены 103 | (u"а", u"a"), 104 | (u"б", u"b"), 105 | (u"в", u"v"), 106 | (u"г", u"g"), 107 | (u"д", u"d"), 108 | (u"е", u"e"), 109 | (u"з", u"z"), 110 | (u"и", u"i"), 111 | (u"й", u"j"), 112 | (u"к", u"k"), 113 | (u"л", u"l"), 114 | (u"м", u"m"), 115 | (u"н", u"n"), 116 | (u"о", u"o"), 117 | (u"п", u"p"), 118 | (u"р", u"r"), 119 | (u"с", u"s"), 120 | (u"т", u"t"), 121 | (u"у", u"u"), 122 | (u"ф", u"f"), 123 | (u"х", u"h"), 124 | (u"э", u"e"), 125 | (u"ъ", u"`"), 126 | (u"ь", u"'"), 127 | # для полноты английского алфавит (в slugify) 128 | # дополняем английскими буквами, которых 129 | # не в парах 130 | (u"c", u"c"), 131 | (u"q", u"q"), 132 | (u"y", u"y"), 133 | (u"x", u"x"), 134 | (u"w", u"w"), 135 | (u"1", u"1"), 136 | (u"2", u"2"), 137 | (u"3", u"3"), 138 | (u"4", u"4"), 139 | (u"5", u"5"), 140 | (u"6", u"6"), 141 | (u"7", u"7"), 142 | (u"8", u"8"), 143 | (u"9", u"9"), 144 | (u"0", u"0"), 145 | ) #: Translation table 146 | 147 | RU_ALPHABET = [x[0] for x in TRANSTABLE] #: Russian alphabet that we can translate 148 | EN_ALPHABET = [x[1] for x in TRANSTABLE] #: English alphabet that we can detransliterate 149 | ALPHABET = RU_ALPHABET + EN_ALPHABET #: Alphabet that we can (de)transliterate 150 | 151 | 152 | def translify(in_string): 153 | """ 154 | Translify russian text 155 | 156 | @param in_string: input string 157 | @type in_string: C{unicode} 158 | 159 | @return: transliterated string 160 | @rtype: C{str} 161 | 162 | @raise TypeError: when in_string is not C{unicode} 163 | @raise ValueError: when string doesn't transliterate completely 164 | """ 165 | if not isinstance(in_string, unicode): 166 | raise TypeError("Argument must be unicode, not %s" % type(in_string)) 167 | 168 | translit = in_string 169 | for symb_in, symb_out in TRANSTABLE: 170 | translit = translit.replace(symb_in, symb_out) 171 | 172 | try: 173 | translit = str(translit) 174 | except UnicodeEncodeError: 175 | raise ValueError("Unicode string doesn't transliterate completely, " + \ 176 | "is it russian?") 177 | 178 | return translit 179 | 180 | 181 | def detranslify(in_string): 182 | """ 183 | Detranslify 184 | 185 | @param in_string: input string 186 | @type in_string: C{basestring} 187 | 188 | @return: detransliterated string 189 | @rtype: C{str} 190 | 191 | @raise TypeError: when in_string neither C{str}, no C{unicode} 192 | @raise ValueError: if in_string is C{str}, but it isn't ascii 193 | """ 194 | if not isinstance(in_string, basestring): 195 | raise TypeError("Argument must be basestring, not %s" % type(in_string)) 196 | 197 | # в unicode 198 | try: 199 | russian = unicode(in_string) 200 | except UnicodeDecodeError: 201 | raise ValueError("We expects when in_string is str type," + \ 202 | "it is an ascii, but now it isn't. Use unicode " + \ 203 | "in this case.") 204 | 205 | for symb_out, symb_in in TRANSTABLE: 206 | russian = russian.replace(symb_in, symb_out) 207 | 208 | return russian 209 | 210 | 211 | def slugify(in_string): 212 | """ 213 | Prepare string for slug (i.e. URL or file/dir name) 214 | 215 | @param in_string: input string 216 | @type in_string: C{basestring} 217 | 218 | @return: slug-string 219 | @rtype: C{str} 220 | 221 | @raise TypeError: when in_string isn't C{unicode} or C{str} 222 | @raise ValueError: if in_string is C{str}, but it isn't ascii 223 | """ 224 | if not isinstance(in_string, basestring): 225 | raise TypeError("Argument must be basestring, not %s" % type(in_string)) 226 | try: 227 | u_in_string = unicode(in_string).lower() 228 | except UnicodeDecodeError: 229 | raise ValueError("We expects when in_string is str type," + \ 230 | "it is an ascii, but now it isn't. Use unicode " + \ 231 | "in this case.") 232 | # convert & to "and" 233 | u_in_string = re.sub('\&\;|\&', ' and ', u_in_string) 234 | # replace spaces by hyphen 235 | u_in_string = re.sub('[-\s]+', '-', u_in_string) 236 | # remove symbols that not in alphabet 237 | u_in_string = u''.join([symb for symb in u_in_string if symb in ALPHABET]) 238 | # translify it 239 | out_string = translify(u_in_string) 240 | # remove non-alpha 241 | return re.sub('[^\w\s-]', '', out_string).strip().lower() 242 | 243 | 244 | def dirify(in_string): 245 | """ 246 | Alias for L{slugify} 247 | """ 248 | slugify(in_string) 249 | 250 | 251 | def provide_unicode(stext, encoding, default=u"неизвестно"): 252 | """ 253 | Provide Unicode from text 254 | 255 | @param stext: text 256 | @type stext: C{str} 257 | 258 | @param encoding: encoding if input text 259 | @type encoding: C{str} 260 | 261 | @return: C{unicode} 262 | """ 263 | try: 264 | utext = str(stext).decode(encoding) 265 | except UnicodeDecodeError, err: 266 | utext = default % {'error': err, 'value': u""} 267 | return utext 268 | 269 | 270 | def provide_str(utext, encoding, default="unknown"): 271 | """ 272 | Provide text from Unicode 273 | 274 | @param utext: unicode text 275 | @type utext: C{unicode} 276 | 277 | @param encoding: encoding of output text 278 | @type encoding: C{str} 279 | 280 | @return: C{str} 281 | """ 282 | try: 283 | stext = unicode(utext).encode(encoding) 284 | except UnicodeEncodeError, err: 285 | stext = default % {'error': err, 'value': ""} 286 | return stext 287 | -------------------------------------------------------------------------------- /resources/scrapers/fuzzywuzzy/fuzz.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """ 4 | fuzz.py 5 | 6 | Copyright (c) 2011 Adam Cohen 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | """ 27 | from __future__ import unicode_literals 28 | 29 | try: 30 | from .StringMatcher import StringMatcher as SequenceMatcher 31 | except ImportError: 32 | from difflib import SequenceMatcher 33 | 34 | from . import utils 35 | 36 | 37 | ########################### 38 | # Basic Scoring Functions # 39 | ########################### 40 | 41 | 42 | def ratio(s1, s2): 43 | 44 | if s1 is None: 45 | raise TypeError("s1 is None") 46 | if s2 is None: 47 | raise TypeError("s2 is None") 48 | s1, s2 = utils.make_type_consistent(s1, s2) 49 | if len(s1) == 0 or len(s2) == 0: 50 | return 0 51 | 52 | m = SequenceMatcher(None, s1, s2) 53 | return utils.intr(100 * m.ratio()) 54 | 55 | 56 | # todo: skip duplicate indexes for a little more speed 57 | def partial_ratio(s1, s2): 58 | 59 | if s1 is None: 60 | raise TypeError("s1 is None") 61 | if s2 is None: 62 | raise TypeError("s2 is None") 63 | s1, s2 = utils.make_type_consistent(s1, s2) 64 | if len(s1) == 0 or len(s2) == 0: 65 | return 0 66 | 67 | if len(s1) <= len(s2): 68 | shorter = s1 69 | longer = s2 70 | else: 71 | shorter = s2 72 | longer = s1 73 | 74 | m = SequenceMatcher(None, shorter, longer) 75 | blocks = m.get_matching_blocks() 76 | 77 | # each block represents a sequence of matching characters in a string 78 | # of the form (idx_1, idx_2, len) 79 | # the best partial match will block align with at least one of those blocks 80 | # e.g. shorter = "abcd", longer = XXXbcdeEEE 81 | # block = (1,3,3) 82 | # best score === ratio("abcd", "Xbcd") 83 | scores = [] 84 | for block in blocks: 85 | long_start = block[1] - block[0] if (block[1] - block[0]) > 0 else 0 86 | long_end = long_start + len(shorter) 87 | long_substr = longer[long_start:long_end] 88 | 89 | m2 = SequenceMatcher(None, shorter, long_substr) 90 | r = m2.ratio() 91 | if r > .995: 92 | return 100 93 | else: 94 | scores.append(r) 95 | 96 | return int(100 * max(scores)) 97 | 98 | 99 | ############################## 100 | # Advanced Scoring Functions # 101 | ############################## 102 | 103 | # Sorted Token 104 | # find all alphanumeric tokens in the string 105 | # sort those tokens and take ratio of resulting joined strings 106 | # controls for unordered string elements 107 | def _token_sort(s1, s2, partial=True, force_ascii=True): 108 | 109 | if s1 is None: 110 | raise TypeError("s1 is None") 111 | if s2 is None: 112 | raise TypeError("s2 is None") 113 | 114 | # pull tokens 115 | tokens1 = utils.full_process(s1, force_ascii=force_ascii).split() 116 | tokens2 = utils.full_process(s2, force_ascii=force_ascii).split() 117 | 118 | # sort tokens and join 119 | sorted1 = " ".join(sorted(tokens1)) 120 | sorted2 = " ".join(sorted(tokens2)) 121 | 122 | sorted1 = sorted1.strip() 123 | sorted2 = sorted2.strip() 124 | 125 | if partial: 126 | return partial_ratio(sorted1, sorted2) 127 | else: 128 | return ratio(sorted1, sorted2) 129 | 130 | 131 | def token_sort_ratio(s1, s2, force_ascii=True): 132 | return _token_sort(s1, s2, partial=False, force_ascii=force_ascii) 133 | 134 | 135 | def partial_token_sort_ratio(s1, s2, force_ascii=True): 136 | return _token_sort(s1, s2, partial=True, force_ascii=force_ascii) 137 | 138 | 139 | # Token Set 140 | # find all alphanumeric tokens in each string...treat them as a set 141 | # construct two strings of the form 142 | # 143 | # take ratios of those two strings 144 | # controls for unordered partial matches 145 | def _token_set(s1, s2, partial=True, force_ascii=True): 146 | 147 | if s1 is None: 148 | raise TypeError("s1 is None") 149 | if s2 is None: 150 | raise TypeError("s2 is None") 151 | 152 | p1 = utils.full_process(s1, force_ascii=force_ascii) 153 | p2 = utils.full_process(s2, force_ascii=force_ascii) 154 | 155 | if not utils.validate_string(p1): 156 | return 0 157 | if not utils.validate_string(p2): 158 | return 0 159 | 160 | # pull tokens 161 | tokens1 = set(utils.full_process(p1).split()) 162 | tokens2 = set(utils.full_process(p2).split()) 163 | 164 | intersection = tokens1.intersection(tokens2) 165 | diff1to2 = tokens1.difference(tokens2) 166 | diff2to1 = tokens2.difference(tokens1) 167 | 168 | sorted_sect = " ".join(sorted(intersection)) 169 | sorted_1to2 = " ".join(sorted(diff1to2)) 170 | sorted_2to1 = " ".join(sorted(diff2to1)) 171 | 172 | combined_1to2 = sorted_sect + " " + sorted_1to2 173 | combined_2to1 = sorted_sect + " " + sorted_2to1 174 | 175 | # strip 176 | sorted_sect = sorted_sect.strip() 177 | combined_1to2 = combined_1to2.strip() 178 | combined_2to1 = combined_2to1.strip() 179 | 180 | if partial: 181 | ratio_func = partial_ratio 182 | else: 183 | ratio_func = ratio 184 | 185 | pairwise = [ 186 | ratio_func(sorted_sect, combined_1to2), 187 | ratio_func(sorted_sect, combined_2to1), 188 | ratio_func(combined_1to2, combined_2to1) 189 | ] 190 | return max(pairwise) 191 | 192 | 193 | def token_set_ratio(s1, s2, force_ascii=True): 194 | return _token_set(s1, s2, partial=False, force_ascii=force_ascii) 195 | 196 | 197 | def partial_token_set_ratio(s1, s2, force_ascii=True): 198 | return _token_set(s1, s2, partial=True, force_ascii=force_ascii) 199 | 200 | 201 | # TODO: numerics 202 | 203 | ################### 204 | # Combination API # 205 | ################### 206 | 207 | # q is for quick 208 | def QRatio(s1, s2, force_ascii=True): 209 | 210 | p1 = utils.full_process(s1, force_ascii=force_ascii) 211 | p2 = utils.full_process(s2, force_ascii=force_ascii) 212 | 213 | if not utils.validate_string(p1): 214 | return 0 215 | if not utils.validate_string(p2): 216 | return 0 217 | 218 | return ratio(p1, p2) 219 | 220 | 221 | def UQRatio(s1, s2): 222 | return QRatio(s1, s2, force_ascii=False) 223 | 224 | 225 | # w is for weighted 226 | def WRatio(s1, s2, force_ascii=True): 227 | 228 | p1 = utils.full_process(s1, force_ascii=force_ascii) 229 | p2 = utils.full_process(s2, force_ascii=force_ascii) 230 | 231 | if not utils.validate_string(p1): 232 | return 0 233 | if not utils.validate_string(p2): 234 | return 0 235 | 236 | # should we look at partials? 237 | try_partial = True 238 | unbase_scale = .95 239 | partial_scale = .90 240 | 241 | base = ratio(p1, p2) 242 | len_ratio = float(max(len(p1), len(p2))) / min(len(p1), len(p2)) 243 | 244 | # if strings are similar length, don't use partials 245 | if len_ratio < 1.5: 246 | try_partial = False 247 | 248 | # if one string is much much shorter than the other 249 | if len_ratio > 8: 250 | partial_scale = .6 251 | 252 | if try_partial: 253 | partial = partial_ratio(p1, p2) * partial_scale 254 | ptsor = partial_token_sort_ratio(p1, p2, force_ascii=force_ascii) \ 255 | * unbase_scale * partial_scale 256 | ptser = partial_token_set_ratio(p1, p2, force_ascii=force_ascii) \ 257 | * unbase_scale * partial_scale 258 | 259 | return int(max(base, partial, ptsor, ptser)) 260 | else: 261 | tsor = token_sort_ratio(p1, p2, force_ascii=force_ascii) * unbase_scale 262 | tser = token_set_ratio(p1, p2, force_ascii=force_ascii) * unbase_scale 263 | 264 | return int(max(base, tsor, tser)) 265 | 266 | 267 | def UWRatio(s1, s2): 268 | return WRatio(s1, s2, force_ascii=False) 269 | --------------------------------------------------------------------------------
(\d+?)(\d+?)