why?
pull/1/head
DiMartinoX 2015-01-09 14:11:21 +03:00
commit 6fef9834fe
396 changed files with 49459 additions and 0 deletions

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

5
.idea/misc.xml 100644
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.7 (D:/Python27/python.exe)" project-jdk-type="Python SDK" />
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/plugin.video.torrenter.iml" filepath="$PROJECT_DIR$/.idea/plugin.video.torrenter.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

7
.idea/vcs.xml 100644
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

380
.idea/workspace.xml 100644
View File

@ -0,0 +1,380 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="56e91349-2444-4553-bc78-90c2bab49e9c" name="Default" comment="">
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/AddonWindow/ContentPanel.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton-focus.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/AddonWindow/DialogCloseButton.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/EZTV.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/ExtraTorrent.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Button/KeyboardKey.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Button/KeyboardKeyNF.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/KickAssSo.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/KinoZalTV.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/List/MenuItemFO.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/RadioButton/MenuItemFO.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/List/MenuItemNF.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/RadioButton/MenuItemNF.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/Nyaa.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/OldPirateBay3.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/OpenSharing.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/RiperAM.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/AddonWindow/SKINDEFAULT.jpg" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/TFileME.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Edit/black-back2.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/images/black.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Edit/button-focus.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/clear.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/AddonWindow/dialogheader.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/fav.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/history2.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icon.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/list.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/magnet.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/media.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/nnm-club.ru.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Slider/osd_slider_bg.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Slider/osd_slider_bg_2.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Slider/osd_slider_nib.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/Slider/osd_slider_nibNF.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/RadioButton/radiobutton-focus.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/textures/default/RadioButton/radiobutton-nofocus.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/rutor.org.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/rutracker.org.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/search.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/settings.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/icons/thepiratebay.se.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/torrent-client.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/torrentPlayer.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/unfav.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/icons/video.png" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/AceStream.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/BeautifulSoup.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/contenters/CXZ.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Content.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Core.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Downloader.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/contenters/EZTV.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/EZTV.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/ExtraTorrent.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/contenters/FastTorrent.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisk/HTTP.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/contenters/IMDB.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/KickAssSo.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/contenters/KinoPoisk.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/unused/KinoZalTV.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisk/LOGGER.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Libtorrent.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Localization.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/NNMClubRu.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/Nyaa.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/OldPirateBay.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/OpenSharing.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Player.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Proxier.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/README.txt" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Rates.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/contenters/RiperAM.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/RiperAM.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/RuTorOrg.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/RuTrackerOrg.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/SearcherABC.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/fuzzywuzzy/StringMatcher.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/TFileME.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/searchers/ThePirateBaySe.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/fuzzywuzzy/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisk/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/contrib/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/packages/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/__init__.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/_collections.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/adapters.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/addon.xml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/pyxbmct/addonwindow.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/api.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/auth.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/aztypes.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/big5freq.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/big5prober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/cacert.pem" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/cache.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/cal.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/certs.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/changelog.txt" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/chardetect.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/chardistribution.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/charsetgroupprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/charsetprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/class_defs.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/classes.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/codingstatemachine.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisk/common.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/compat.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/compat.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/connection.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/connection.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/connectionpool.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/constants.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/controlcenter.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/convert.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/cookies.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/core.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/cp949prober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/debug.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/default.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/errors.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/escprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/escsm.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/eucjpprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/euckrfreq.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/euckrprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/euctwfreq.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/euctwprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/exceptions.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/exceptions.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/fields.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/filepost.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/functions.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/fuzzywuzzy/fuzz.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/gb2312freq.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/gb2312prober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/hebrewprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/hooks.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/html.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/interact.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/jisfreq.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/jpcntx.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisks.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/langbulgarianmodel.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/langcyrillicmodel.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/langgreekmodel.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/langhebrewmodel.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/langhungarianmodel.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/langthaimodel.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/latin1prober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/logutils.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/main.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/mbcharsetprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/mbcsgroupprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/mbcssm.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/models.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/net.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/net.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/contrib/ntlmpool.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/obj_impl.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/objects.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/packages/ordered_dict.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisk/pageparser.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/persistency.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisk/pluginsettings.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/poolmanager.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/fuzzywuzzy/process.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/contrib/pyopenssl.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/request.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/request.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/response.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/response.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/retry.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/sbcharsetprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/sbcsgroupprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/scrapers.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/scripting.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/sessions.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/settings.xml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/packages/six.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/sjisprober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/ssl_.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/status_codes.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/fuzzywuzzy/string_processing.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/language/English/strings.xml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/language/Russian/strings.xml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/structures.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/timeout.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/tmdb.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/tmdbs.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/kinopoisk/translit.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/tvdb.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/universaldetector.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/urllib3/util/url.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/packages/chardet/utf8prober.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/fuzzywuzzy/utils.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/scrapers/requests/utils.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/utils.py" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/resources/utorrent/dopal/xmlutils.py" />
</list>
<ignored path="plugin.video.torrenter.iws" />
<ignored path=".idea/workspace.xml" />
<ignored path=".idea/modules.xml" />
<ignored path=".idea/vcs.xml" />
<ignored path=".idea/encodings.xml" />
<ignored path=".idea/misc.xml" />
<ignored path=".idea/plugin.video.torrenter.iml" />
<ignored path=".idea/scopes/scope_settings.xml" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value="" />
</component>
<component name="DaemonCodeAnalyzer">
<disable_hints />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
<component name="FavoritesManager">
<favorites_list name="plugin.video.torrenter" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectFrameBounds">
<option name="x" value="-8" />
<option name="y" value="-8" />
<option name="width" value="1936" />
<option name="height" value="1056" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectReloadState">
<option name="STATE" value="0" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="" version="1">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="plugin.video.torrenter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="plugin.video.torrenter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="plugin.video.torrenter" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
</component>
<component name="RunManager">
<list size="0" />
</component>
<component name="ShelveChangesManager" show_recycled="false" />
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="56e91349-2444-4553-bc78-90c2bab49e9c" name="Default" comment="" />
<created>1420801533300</created>
<updated>1420801533300</updated>
</task>
<servers />
</component>
<component name="TodoView" selected-index="0">
<todo-panel id="selected-file">
<are-packages-shown value="false" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="false" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="false" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="false" />
</todo-panel>
<todo-panel id="default-changelist">
<are-packages-shown value="false" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="false" />
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="-8" y="-8" width="1936" height="1056" extended-state="6" />
<editor active="false" />
<layout>
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32925472" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Terminal" active="true" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.32925472" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32925472" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.24973656" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32925472" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
</layout>
</component>
<component name="Vcs.Log.UiProperties">
<option name="RECENTLY_FILTERED_USER_GROUPS">
<collection />
</option>
<option name="RECENTLY_FILTERED_BRANCH_GROUPS">
<collection />
</option>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="VcsManagerConfiguration">
<option name="myTodoPanelSettings">
<TodoPanelSettings />
</option>
</component>
<component name="XDebuggerManager">
<breakpoint-manager />
</component>
</project>

152
AceStream.py 100644
View File

@ -0,0 +1,152 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import time
import sys
import os
import urllib2
import urllib
import hashlib
import re
import base64
from StringIO import StringIO
import gzip
from functions import file_decode, file_encode
from functions import magnet_alert
import xbmcvfs
class AceStream:
try:
fpath = os.path.expanduser("~")
pfile = os.path.join(fpath, 'AppData\Roaming\ACEStream\engine', 'acestream.port')
gf = open(pfile, 'r')
aceport = int(gf.read())
gf.close()
print aceport
except:
aceport = 62062
torrentFile = None
magnetLink = None
storageDirectory = ''
torrentFilesDirectory = 'torrents'
startPart = 0
endPart = 0
partOffset = 0
torrentHandle = None
session = None
downloadThread = None
threadComplete = False
lt = None
def __init__(self, storageDirectory='', torrentFile='', torrentFilesDirectory='torrents'):
try:
from ASCore import TSengine as tsengine
print 'Imported TSengine from ASCore'
except Exception, e:
print 'Error importing TSengine from ASCore. Exception: ' + str(e)
return
self.TSplayer = tsengine()
del tsengine
self.torrentFilesDirectory = torrentFilesDirectory
self.storageDirectory = storageDirectory
_path=os.path.join(self.storageDirectory, self.torrentFilesDirectory)+os.sep
if not xbmcvfs.exists(_path):
xbmcvfs.mkdirs(_path)
if xbmcvfs.exists(torrentFile):
self.torrentFile = torrentFile
content = xbmcvfs.File(torrentFile, "rb").read()
self.torrentFileInfo = self.TSplayer.load_torrent(base64.b64encode(content), 'RAW')
elif re.match("^magnet\:.+$", torrentFile):
magnet_alert()
exit()
def __exit__(self):
self.TSplayer.end()
def play_url_ind(self, ind, label, icon):
self.TSplayer.play_url_ind(int(ind), label, str(icon), '')
def saveTorrent(self, torrentUrl):
if re.match("^magnet\:.+$", torrentUrl):
magnet_alert()
exit()
else:
torrentFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + self.md5(
torrentUrl) + '.torrent'
try:
if xbmcvfs.exists(file_decode(torrentUrl)):
content = xbmcvfs.File(file_decode(torrentUrl), "rb").read()
else:
request = urllib2.Request(torrentUrl)
request.add_header('Referer', torrentUrl)
request.add_header('Accept-encoding', 'gzip')
result = urllib2.urlopen(request)
if result.info().get('Content-Encoding') == 'gzip':
buf = StringIO(result.read())
f = gzip.GzipFile(fileobj=buf)
content = f.read()
else:
content = result.read()
localFile = xbmcvfs.File(torrentFile, "w+b")
localFile.write(content)
localFile.close()
except Exception, e:
print 'Unable to save torrent file from "' + torrentUrl + '" to "' + torrentFile + '" in AceStream::saveTorrent' + '. Exception: ' + str(e)
return
if xbmcvfs.exists(torrentFile):
self.torrentFile = torrentFile
self.torrentFileInfo = self.TSplayer.load_torrent(base64.b64encode(content), 'RAW')
return self.torrentFile
def getMagnetInfo(self):
magnet_alert()
exit()
def magnetToTorrent(self, magnet):
magnet_alert()
exit()
def getFilePath(self, contentId=0):
fileList = self.getContentList()
for i in fileList:
if i['ind'] == contentId:
return os.path.join(file_encode(self.storageDirectory),i['title'])
def getContentList(self):
filelist = []
for k, v in self.TSplayer.files.iteritems():
stringdata = {"title": urllib.unquote_plus(k), "ind": int(v)}
filelist.append(stringdata)
return filelist
def md5(self, string):
hasher = hashlib.md5()
try:
hasher.update(string)
except:
hasher.update(string.encode('utf-8', 'ignore'))
return hasher.hexdigest()

BIN
AceStream.pyo 100644

Binary file not shown.

2049
BeautifulSoup.py 100644

File diff suppressed because it is too large Load Diff

BIN
BeautifulSoup.pyc 100644

Binary file not shown.

BIN
BeautifulSoup.pyo 100644

Binary file not shown.

233
Content.py 100644
View File

@ -0,0 +1,233 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for Kodi
Copyright (C) 2012 DiMartino
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import abc
import urllib
import urllib2
import cookielib
import re
from StringIO import StringIO
import gzip
import HTMLParser
import Localization
class Content:
__metaclass__ = abc.ABCMeta
searchIcon = '/icons/video.png'
sourceWeight = 1
cookieJar = None
def isLabel(self):
return 'Should search on ruhunt?'
def isPages(self):
return False
def isScrappable(self):
return True
def isInfoLink(self):
return False
def isSearchOption(self):
return True
category_dict = {
'sites': ('[B]by Site[/B]',),
'search': ('[B]Search[/B]',),
'movies': ('Forieng Movies',),
'rus_movies': ('Russian Movies',),
'tvshows': ('TV Shows',),
'cartoons': ('Cartoons',),
'hot': ('Hot & New',),
'top': ('Top All Time',),
'anime': ('Anime',),
'year': {'year': 'by Year', },
'genre': {'genre': 'by Genre',
'action': ('Action',),
'comedy': ('Comedy',),
'documentary': ('Documentary',),
'drama': ('Drama',),
'fantasy': ('Fantasy',),
'horror': ('Horror',),
'romance': ('Romance',),
'thriller': ('Thriller',),
}
}
for y in range(2015, 1970, -1):
category_dict['year'][str(y)] = (str(y), '/top/y/%s/' % str(y))
def get_contentList(self, category, subcategory=None, page=None):
'''
Retrieve keyword from the input and return a list of tuples:
filesList.append((
int(weight),
int(seeds),
str(title),
str(link),
str(image),
))
'''
return
def has_category(self, category, subcategory=None):
has_category = False
if not subcategory or subcategory == True:
if category in self.category_dict.keys():
has_category = True
else:
if category in self.category_dict:
cat_con = self.category_dict[category]
if isinstance(cat_con, dict):
if subcategory in cat_con.keys():
has_category = True
return has_category
def get_url(self, category, subcategory, page, baseurl):
if not subcategory or subcategory == True or category == 'search':
get = self.category_dict[category]
else:
get = self.category_dict[category][subcategory]
if category == 'search': get = (get[0], get[1] % urllib.quote_plus(subcategory.encode('utf-8')))
if not page or page == 1:
url = baseurl + get[1]
else:
property = self.get_property(category, subcategory)
page_url = property['page'] % (property['second_page'] + ((page - 2) * property['increase']))
url = baseurl + str(page_url)
return url
def get_property(self, category, subcategory=None):
has_property = False
property = {}
if not subcategory or subcategory == True:
if category in self.category_dict.keys():
try:
property = self.category_dict[category][2]
if isinstance(property, dict):
has_property = True
except:
pass
else:
if category in self.category_dict:
cat_con = self.category_dict[category]
if isinstance(cat_con, dict):
if subcategory in cat_con.keys():
try:
property = cat_con[subcategory][2]
if isinstance(property, dict):
has_property = True
except:
pass
if has_property:
if category == 'search': property['page'] = property['page'] % urllib.quote_plus(
subcategory.encode('utf-8'))
return property
def makeRequest(self, url, data={}, headers=[]):
self.cookieJar = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar))
opener.addheaders = headers
if 0 < len(data):
encodedData = urllib.urlencode(data)
else:
encodedData = None
response = opener.open(url, encodedData)
if response.info().get('Content-Encoding') == 'gzip':
buf = StringIO(response.read())
f = gzip.GzipFile(fileobj=buf)
response = f.read()
else:
response = response.read()
return response
htmlCodes = (
('&', '&amp;'),
('<', '&lt;'),
('>', '&gt;'),
('"', '&quot;'),
("'", '&#39;'),
(' ', '&nbsp;',),
('"', '&laquo;', ),
('"', '&raquo;', ),
('·', '&#183;',),
('e', '&#233;',),
('e', '&#232;',),
('&', '&#38;',),
('u', '&#249;',),
('u', '&#250;',),
('o', '&#244;',),
('u', '&#251;'),
('-', '&ndash;'),
)
stripPairs = (
('<p>', '\n'),
('<li>', '\n'),
('<br>', '\n'),
('<.+?>', ' '),
('</.+?>', ' '),
( '&nbsp;', ' ',),
('&laquo;', '"',),
('&raquo;', '"', ),
('&ndash;', '-'),
)
def unescape(self, string):
pars = HTMLParser.HTMLParser()
return pars.unescape(string)
#for (symbol, code) in self.htmlCodes:
# try:
# string = re.sub(code, symbol, string)
# except:
# pass
#return string
def stripHtml(self, string):
for (html, replacement) in self.stripPairs:
string = re.sub(html, replacement, string)
return string
def translate(self, category, subcategory=None):
if not subcategory:
if isinstance(self.category_dict.get(category), dict):
return self.localize(self.category_dict.get(category).get(category))
else:
return self.localize(self.category_dict.get(category)[0])
else:
return self.localize(self.category_dict.get(category).get(subcategory)[0])
def localize(self, string):
if string:
try:
return Localization.localize(string)
except:
return string
else:
return 'Empty string'
#print str(Content().has_category('x'))

BIN
Content.pyc 100644

Binary file not shown.

BIN
Content.pyo 100644

Binary file not shown.

1579
Core.py 100644

File diff suppressed because it is too large Load Diff

BIN
Core.pyo 100644

Binary file not shown.

132
Downloader.py 100644
View File

@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import hashlib, sys
import Libtorrent
import AceStream
class Torrent():
__settings__ = sys.modules["__main__"].__settings__
def __init__(self, storageDirectory='', torrentFile='', torrentFilesDirectory='torrents'):
self.get_torrent_client()
if self.player == 'libtorrent':
self.player = Libtorrent.Libtorrent(storageDirectory, torrentFile, torrentFilesDirectory)
elif self.player == 'acestream':
self.player = AceStream.AceStream(storageDirectory, torrentFile, torrentFilesDirectory)
def __exit__(self):
self.player.__exit__()
def get_torrent_client(self):
player = self.__settings__.getSetting("torrent_player")
if player == '0':
self.player = 'libtorrent'
elif player == '1':
self.player = 'acestream'
def play_url_ind(self, ind, label, icon):
return self.player.play_url_ind(int(ind), label, str(icon))
def saveTorrent(self, torrentUrl):
return self.player.saveTorrent(torrentUrl)
def getMagnetInfo(self):
return self.player.getMagnetInfo()
def magnetToTorrent(self, magnet):
return self.player.magnetToTorrent(magnet)
def getUploadRate(self):
return self.player.getUploadRate()
def getDownloadRate(self):
return self.player.getDownloadRate()
def getPeers(self):
return self.player.getPeers()
def getSeeds(self):
return self.player.getMagnetInfo()
def getFileSize(self, contentId=0):
return self.player.getFileSize(contentId)
def getFilePath(self, contentId=0):
return self.player.getFilePath(contentId)
def getContentList(self):
#print str(self.player.getContentList())
return self.player.getContentList()
def setUploadLimit(self, bytesPerSecond):
return self.player.setUploadLimit(bytesPerSecond)
def setDownloadLimit(self, bytesPerSecond):
return self.player.setDownloadLimit(bytesPerSecond)
def stopSession(self):
return self.player.stopSession()
def md5(self, string):
hasher = hashlib.md5()
try:
hasher.update(string)
except:
hasher.update(string.encode('utf-8', 'ignore'))
return hasher.hexdigest()
def downloadProcess(self, contentId):
pass
def initSession(self):
return self.player.initSession()
def startSession(self):
return self.player.startSession()
def continueSession(self, contentId=0, Offset=155, seeding=True):
return self.player.continueSession(contentId, Offset, seeding)
def addToSeeding(self):
return self.player.addToSeeding()
def fetchParts(self):
return self.player.fetchParts()
def checkThread(self):
return self.player.checkThread()
def _makedirs(self, _path):
return self.player._makedirs(_path)
def debug(self):
return self.player.debug()
def dump(self, obj):
for attr in dir(obj):
try:
print "'%s':'%s'," % (attr, getattr(obj, attr))
except:
pass

BIN
Downloader.pyo 100644

Binary file not shown.

399
Libtorrent.py 100644
View File

@ -0,0 +1,399 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
#import time
import thread
import os
import urllib2
import hashlib
import re
import sys
import platform
from StringIO import StringIO
import gzip
from functions import file_decode, file_encode, isSubtitle
import xbmc
import xbmcgui
import xbmcvfs
import Localization
class Libtorrent:
torrentFile = None
magnetLink = None
storageDirectory = ''
torrentFilesDirectory = 'torrents'
startPart = 0
endPart = 0
partOffset = 0
torrentHandle = None
session = None
downloadThread = None
threadComplete = False
threadSeeding = False
seedingHandle = None
lt = None
def __init__(self, storageDirectory='', torrentFile='', torrentFilesDirectory='torrents'):
try:
import libtorrent
print 'Imported libtorrent v' + libtorrent.version + ' from system'
except Exception, e:
print 'Error importing from system. Exception: ' + str(e)
if platform.system() != 'Windows':
if sys.maxsize > 2 ** 32:
system = 'linux_x86_64'
else:
system = 'linux_x86'
else:
system = 'windows'
dirname = os.path.join(xbmc.translatePath('special://home'), 'addons', 'script.module.libtorrent',
'python_libtorrent', system)
sys.path.insert(0, dirname)
try:
import libtorrent
print 'Imported libtorrent v' + libtorrent.version + ' from python_libtorrent.' + system
except Exception, e:
print 'Error importing python_libtorrent.' + system + '. Exception: ' + str(e)
pass
self.lt = libtorrent
del libtorrent
self.torrentFilesDirectory = torrentFilesDirectory
self.storageDirectory = storageDirectory
_path=os.path.join(self.storageDirectory, self.torrentFilesDirectory)+os.sep
if not xbmcvfs.exists(_path):
xbmcvfs.mkdirs(_path)
if xbmcvfs.exists(torrentFile):
self.torrentFile = torrentFile
self.torrentFileInfo = self.lt.torrent_info(file_decode(self.torrentFile))
elif re.match("^magnet\:.+$", torrentFile):
self.magnetLink = torrentFile
def saveTorrent(self, torrentUrl):
if re.match("^magnet\:.+$", torrentUrl):
self.magnetLink = torrentUrl
self.magnetToTorrent(torrentUrl)
return self.torrentFile
else:
torrentFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + self.md5(
torrentUrl) + '.torrent'
try:
if not re.match("^http\:.+$", torrentUrl):
content = xbmcvfs.File(file_decode(torrentUrl), "rb").read()
else:
request = urllib2.Request(torrentUrl)
request.add_header('Referer', torrentUrl)
request.add_header('Accept-encoding', 'gzip')
result = urllib2.urlopen(request)
if result.info().get('Content-Encoding') == 'gzip':
buf = StringIO(result.read())
f = gzip.GzipFile(fileobj=buf)
content = f.read()
else:
content = result.read()
localFile = xbmcvfs.File(torrentFile, "w+b")
localFile.write(content)
localFile.close()
except Exception, e:
print 'Unable to save torrent file from "' + torrentUrl + '" to "' + torrentFile + '" in Torrent::saveTorrent' + '. Exception: ' + str(e)
return
if xbmcvfs.exists(torrentFile):
try:
self.torrentFileInfo = self.lt.torrent_info(file_decode(torrentFile))
except Exception, e:
print 'Exception: ' + str(e)
xbmcvfs.delete(torrentFile)
return
baseName = file_encode(os.path.basename(self.getFilePath()))
newFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + baseName + '.' + self.md5(
torrentUrl) + '.torrent'
xbmcvfs.delete(newFile)
if not xbmcvfs.exists(newFile):
try:
xbmcvfs.rename(torrentFile, newFile)
except Exception, e:
print 'Unable to rename torrent file from "' + torrentFile + '" to "' + newFile + '" in Torrent::renameTorrent'+ '. Exception: ' + str(e)
return
self.torrentFile = newFile
if not self.torrentFileInfo:
self.torrentFileInfo = self.lt.torrent_info(file_decode(self.torrentFile))
return self.torrentFile
def getMagnetInfo(self):
magnetSettings = {
'save_path': self.storageDirectory,
'storage_mode': self.lt.storage_mode_t(0),
'paused': True,
'auto_managed': True,
'duplicate_is_error': True
}
progressBar = xbmcgui.DialogProgress()
progressBar.create(Localization.localize('Please Wait'), Localization.localize('Magnet-link is converting.'))
self.torrentHandle = self.lt.add_magnet_uri(self.session, self.magnetLink, magnetSettings)
iterator = 0
while not self.torrentHandle.has_metadata() and iterator != 100:
progressBar.update(iterator)
iterator += 1
if progressBar.iscanceled():
progressBar.update(0)
progressBar.close()
return
xbmc.sleep(500)
progressBar.update(0)
progressBar.close()
if self.torrentHandle.has_metadata():
return self.torrentHandle.get_torrent_info()
def magnetToTorrent(self, magnet):
self.magnetLink = magnet
self.initSession()
torrentInfo = self.getMagnetInfo()
try:
torrentFile = self.lt.create_torrent(torrentInfo)
baseName = file_encode(os.path.basename(self.storageDirectory + os.sep + torrentInfo.files()[0].path))
self.torrentFile = self.storageDirectory + os.sep + self.torrentFilesDirectory + os.sep + baseName + '.torrent'
torentFileHandler = xbmcvfs.File(self.torrentFile, "w+b")
torentFileHandler.write(self.lt.bencode(torrentFile.generate()))
torentFileHandler.close()
self.torrentFileInfo = self.lt.torrent_info(file_decode(self.torrentFile))
except:
xbmc.executebuiltin("Notification(%s, %s, 7500)" % (Localization.localize('Error'), Localization.localize(
'Can\'t download torrent, probably no seeds available.')))
self.torrentFileInfo = torrentInfo
def getUploadRate(self):
if None == self.torrentHandle:
return 0
else:
return self.torrentHandle.status().upload_payload_rate
def getDownloadRate(self):
if None == self.torrentHandle:
return 0
else:
return self.torrentHandle.status().download_payload_rate
def getPeers(self):
if None == self.torrentHandle:
return 0
else:
return self.torrentHandle.status().num_peers
def getSeeds(self):
if None == self.torrentHandle:
return 0
else:
return self.torrentHandle.status().num_seeds
def getFileSize(self, contentId=0):
return self.getContentList()[contentId]['size']
def getFilePath(self, contentId=0):
return os.path.join(self.storageDirectory,self.getContentList()[contentId]['title'])#.decode('utf8')
def getContentList(self):
filelist = []
for contentId, contentFile in enumerate(self.torrentFileInfo.files()):
stringdata = {"title": contentFile.path, "size": contentFile.size, "ind": int(contentId),
'offset': contentFile.offset}
filelist.append(stringdata)
return filelist
def getSubsIds(self, filename):
subs=[]
for i in self.getContentList():
if isSubtitle(filename, i['title']):
subs.append((i['ind'], i['title']))
return subs
def setUploadLimit(self, bytesPerSecond):
self.session.set_upload_rate_limit(int(bytesPerSecond))
def setDownloadLimit(self, bytesPerSecond):
self.session.set_download_rate_limit(int(bytesPerSecond))
def md5(self, string):
hasher = hashlib.md5()
try:
hasher.update(string)
except:
hasher.update(string.encode('utf-8', 'ignore'))
return hasher.hexdigest()
def downloadProcess(self, contentId):
pass
#for part in range(self.startPart, self.endPart + 1):
# print 'getPiece'+str(part)
# self.getPiece(part)
# time.sleep(0.5)
# self.checkThread()
#self.threadComplete = True
def initSession(self):
try:
self.session.remove_torrent(self.torrentHandle)
except:
pass
self.session = self.lt.session()
self.session.start_dht()
self.session.add_dht_router("router.bittorrent.com", 6881)
self.session.add_dht_router("router.utorrent.com", 6881)
self.session.add_dht_router("router.bitcomet.com", 6881)
self.session.listen_on(6881, 6891)
self.session.set_alert_mask(self.lt.alert.category_t.storage_notification)
def startSession(self):
self.initSession()
if None == self.magnetLink:
self.torrentHandle = self.session.add_torrent({'ti': self.torrentFileInfo,
'save_path': self.storageDirectory,
'flags': 0x300,
#'storage_mode': self.lt.storage_mode_t.storage_mode_allocate,
})
else:
self.torrentFileInfo = self.getMagnetInfo()
self.torrentHandle.set_sequential_download(True)
self.stopSession()
def stopSession(self):
for i in range(self.torrentFileInfo.num_pieces()):
self.torrentHandle.piece_priority(i, 0)
def continueSession(self, contentId=0, Offset=0, seeding=False):
self.piece_length = self.torrentFileInfo.piece_length()
selectedFileInfo = self.getContentList()[contentId]
if not Offset:
Offset = selectedFileInfo['size'] / (1024 * 1024)
self.partOffset = (Offset * 1024 * 1024 / self.piece_length) + 1
#print 'partOffset ' + str(self.partOffset)+str(' ')
self.startPart = selectedFileInfo['offset'] / self.piece_length
self.endPart = int((selectedFileInfo['offset'] + selectedFileInfo['size']) / self.piece_length)
#print 'part ' + str(self.startPart)+ str(' ')+ str(self.endPart)
for i in range(self.startPart, self.startPart + self.partOffset):
if i <= self.endPart:
self.torrentHandle.piece_priority(i, 7)
#print str(i)
self.torrentHandle.piece_priority(self.endPart - 1, 7)
self.torrentHandle.piece_priority(self.endPart, 7)
#thread.start_new_thread(self.checkProcess, ())
#thread.start_new_thread(self.downloadProcess, (contentId,))
if seeding:# and None == self.magnetLink:
thread.start_new_thread(self.addToSeeding, (contentId,))
def addToSeeding(self, contentId):
print 'addToSeeding!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1'
if self.torrentHandle:
print 'addToSeeding torrentHandle OK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1'
info = self.torrentHandle.get_torrent_info()
fileSettings = {
'ti': info,
'save_path': self.storageDirectory,
'flags': 0x300
}
self.seedingHandle= self.session.add_torrent(fileSettings)
piece_length = info.piece_length()
filelist=[]
for contentId, contentFile in enumerate(info.files()):
stringdata = {"title": contentFile.path, "size": contentFile.size, "ind": int(contentId),
'offset': contentFile.offset}
filelist.append(stringdata)
selectedFileInfo = filelist[contentId]
Offset = selectedFileInfo['size'] / (1024 * 1024)
partOffset = (Offset * 1024 * 1024 / piece_length) + 1
#print 'partOffset ' + str(self.partOffset)+str(' ')
startPart = selectedFileInfo['offset'] / piece_length
endPart = int((selectedFileInfo['offset'] + selectedFileInfo['size']) / piece_length)
#print 'part ' + str(self.startPart)+ str(' ')+ str(self.endPart)
for i in range(startPart, startPart + partOffset):
if i <= endPart:
self.seedingHandle.piece_priority(i, 7)
#print str(i)
self.seedingHandle.piece_priority(endPart - 1, 7)
self.seedingHandle.piece_priority(endPart, 7)
while self.seedingHandle:
xbmc.sleep(5000)
self.debug(seeding=True)
def fetchParts(self):
priorities = self.torrentHandle.piece_priorities()
status = self.torrentHandle.status()
if len(status.pieces) == 0:
return
if priorities[self.startPart] == 0:
self.torrentHandle.piece_priority(self.startPart, 2)
for part in range(self.startPart, self.endPart + 1):
if priorities[part] == 0:
self.torrentHandle.piece_priority(part, 1)
def checkThread(self):
if self.threadComplete == True:
print 'checkThread KIIIIIIIIIIILLLLLLLLLLLLLLL'
self.session.remove_torrent(self.torrentHandle)
if self.threadSeeding == True and self.seedingHandle:
print 'Seeding KIIIIIIIIIIILLLLLLLLLLLLLLL'
self.session.remove_torrent(self.seedingHandle)
thread.exit()
def debug(self, seeding=False):
try:
#print str(self.getFilePath(0))
if seeding:
s = self.seedingHandle.status()
else:
s = self.torrentHandle.status()
#get_settings=self.torrentHandle.status
#print s.num_pieces
#priorities = self.torrentHandle.piece_priorities()
#self.dump(priorities)
#print str('anonymous_mode '+str(get_settings['anonymous_mode']))
state_str = ['queued', 'checking', 'downloading metadata',
'downloading', 'finished', 'seeding', 'allocating']
print '[%s;%s] %.2f%% complete (down: %.1f kb/s up: %.1f kB/s peers: %d) %s' % \
(self.lt.version, str(seeding), s.progress * 100, s.download_rate / 1000,
s.upload_rate / 1000,
s.num_peers, state_str[s.state])
i = 0
#for t in s.pieces:
# if t: i=i+1
#print str(self.session.pop_alert())
#print str(s.pieces[self.startPart:self.endPart])
#print 'True pieces: %d' % i
#print s.current_tracker
#print str(s.pieces)
except:
print 'debug error'
pass
def dump(self, obj):
for attr in dir(obj):
try:
print "'%s':'%s'," % (attr, getattr(obj, attr))
except:
pass

BIN
Libtorrent.pyo 100644

Binary file not shown.

207
Localization.py 100644
View File

@ -0,0 +1,207 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
try:
import xbmcaddon
__settings__ = xbmcaddon.Addon(id='plugin.video.torrenter')
language = ('en', 'ru')[int(__settings__.getSetting("language"))]
except:
language = 'ru'
dictionary = {
'ru': {
'Seeds searching.': 'Идёт поиск сидов.',
'Please Wait': 'Подождите',
'Information': 'Информация',
'Torrent downloading is stopped.': 'Загрузка торрента прекращена.',
'Search': 'Поиск',
'Seeds': 'Сиды',
'Peers': 'Пиры',
'Materials are loading now.': 'Идёт загрузка материалов.',
'Search Phrase': 'Фраза для поиска',
'Magnet-link is converting.': 'Идёт преобразование magnet-ссылки.',
'Error': 'Ошибка',
'Your library out of date and can\'t save magnet-links.': 'Ваша библиотека устарела и не может сохранять магнет-ссылки.',
'Bookmarks': 'Закладки',
'Logout': 'Выход',
'Login': 'Вход',
'Recent Materials': 'Свежие Материалы ',
'Register': 'Регистрация',
'Bookmark': 'Закладка',
'Item successfully added to Bookmarks': 'Элемент удачно добавлен в закладки',
'Item successfully removed from Bookmarks': 'Элемент удачно удалён из закладок',
'Bookmark not added': 'Закладка не добавлена',
'Bookmark not removed': 'Закладка не удалена',
'Add To Bookmarks': 'Добавить в закладки',
'Remove From Bookmarks': 'Удалить из Закладок',
'Auth': 'Авторизация',
'Already logged in': 'Пользователь уже в системе',
'Input Email (for password recovery):': 'Введите E-mail (для восстановления пароля):',
'Input Email:': 'Введите E-mail:',
'Input Password (6+ symbols):': 'Введите пароль (6+ символов):',
'Input Password:': 'Введите пароль:',
'Login successfull': 'Вход выполнен успешно',
'Login failed': 'Вход не выполнен',
'User not logged in': 'Пользователь не в системе',
'User successfully logged out': 'Пользователь успешно покинул систему',
'Preloaded: ': 'Предзагружено: ',
'Do you want to STOP torrent downloading and seeding?': 'Вы хотите остановить загрузку и раздачу торрента?',
'Torrent Downloading': 'Загрузка торрента',
'Auth expired, please relogin': 'Авторизация истекла, пожалуйста войдите снова',
'Storage': 'Хранилище',
'Storage was cleared': 'Хранилище очищено',
'Clear Storage': 'Очистить хранилище',
'Popular': 'Популярное',
'Views': 'Просмотров',
'Uploading': 'Раздача',
'Download': 'Скачать',
'Input symbols from CAPTCHA image:': 'Введите символы с картинки CAPTCHA:',
'Please, rate watched video:': 'Пожалуйста, оцените просмотренное видео:',
'Bad': 'Плохо',
'So-So': 'Такое...',
'Good': 'Отлично',
'Ratings': 'Оценки',
'Rating': 'Оценка',
'Retry': 'Повторная попытка',
'%ds has left': 'Осталось %d попыток',
'File failed to play! Do you want to RETRY and buffer more?': 'Ошибка проигрывания файла! Хотите предзагрузить больше и повторить?',
'High Priority All Files': 'Высокий Приоритет Всем Файлам',
'Skip All Files': 'Пропустить Все Файлы',
'Start': 'Пуск',
'Stop': 'Стоп',
'High Priority': 'Высокий Приоритет',
'Skip File': 'Пропустить Файл',
'Remove': 'Удалить',
'Remove with files': 'Удалить с файлами',
'Play File': 'Проиграть файл',
'Start All Files': 'Пуск Всем Файлам',
'Stop All Files': 'Стоп Всем Файлам',
'Torrent-client Browser': 'Браузер Торрент-клиента',
'Remote Torrent-client': 'Удаленный торрент-клиент',
'You didn\'t set up replacement path in setting.': 'Вы не настроили замены путей.',
'For example /media/dl_torr/ to smb://SERVER/dl_torr/. Setup now?': 'Например /media/dl_torr/ на smb://SERVER/dl_torr/. Настроить?',
'Manual Torrent-client Path Edit': 'Вручную изменить папку торрент-клиента по-умолчанию',
'Choose .torrent in video library': 'Выберите .torrent в видеобиблиотеке',
'.torrent Player': '.torrent Проигрыватель',
'Choose directory:': 'Выберите папку:',
'Starting download next episode!': 'Начинаю скачку следующего эпизода!',
'Choose in torrent-client:': 'Выберите раздачу:',
'Search Control Window': 'Окно Управления Поиском',
'Magnet-link (magnet:...)': 'Magnet-ссылка (magnet:...)',
'Not a magnet-link!': 'Не является magnet-ссылкой',
'Magnet-link Player': 'Проигрыватель Magnet-Ссылок',
'UNKNOWN STATUS': 'Неизвестное состояние',
'Checking preloaded files...': 'Проверка файлов...',
'Waiting for website response...': 'Ожидание ответа сайта...',
'Search and cache information for:': 'Поиск и кэширование информации для:',
'Open Torrent': 'Открыть Список файлов',
'Torrent list is empty.': 'Список раздач пуст.',
'Content Lists': 'Списки Медиа',
'Canceled by User': 'Отменено пользователем',
'Do you want to search and cache full metadata + arts?': 'Хотите автоматически получать мета-данные и арты?',
'This vastly decreases load speed, but you will be asked to download premade bases!': 'Это существенно снижает скорость загрузки, но Вам предложат скачать готовые базы!',
'Do you want to preload full metadata?': 'Хотите готовую загрузить базу данных?',
'It is highly recommended!': 'Настоятельно рекомендовано согласиться!',
'TV Shows': 'Сериалы',
'Cartoons': 'Мультфильмы',
'Anime': 'Аниме',
'Hot & New': 'Горячие Новинки',
'Top 250 Movies': 'Лучшие 250 фильмов',
'Top All Time': 'Лучшее за ВСЕ ВРЕМЯ',
'by Genre': 'по Жанру',
'by Year': 'по Году',
'Action': 'Боевики',
'Adventure': 'Приключения',
'Animation': 'Анимация',
'Biography': 'Биография',
'Comedy': 'Комедии',
'Crime': 'Детектив',
'Documentary': 'Документальное',
'Drama': 'Драмы',
'Family': 'Семейное',
'Fantasy': 'Фэнтази',
'Film-Noir': 'Нуар',
'History': 'Историчекие',
'Horror': 'Ужасы',
'Music': 'Музыкальные',
'Musical': 'Мьюзиклы',
'Mystery': 'Мистика',
'Romance': 'Мелодрамы',
'Sci-Fi': 'Фантастика',
'Short': 'Короткометражки',
'Sport': 'Спортивные',
'Thriller': 'Триллеры',
'War': 'Военные',
'Western': 'Вестерны',
'[B]by Site[/B]': '[B]по Сайту[/B]',
'Movies': 'Фильмы',
'Cartoons Series': 'Мультсериалы',
'Cartoons Short': 'Мультфильмы (короткометражки)',
'Male': 'Мужские',
'Female': 'Женские',
'Russia & USSR': 'Россия + СССР',
'Next Page': 'Следующая Страница',
'Previous Page': 'Предыдущая Страница',
'Russian Movies': 'Отечественные Фильмы',
'Forieng Movies': 'Зарубежные Фильмы',
'Anime Film': 'Полнометражное Аниме',
'Anime Series': 'Аниме Сериалы',
'Can\'t download torrent, probably no seeds available.': 'Не могу скачать торрент, скорее всего нет доступных сидов.',
'Personal List': 'Личный Список',
'Add to %s': 'Добавить в %s',
'Delete from %s': 'Удалить из %s',
'Added!': 'Добавлено',
'Deleted!': 'Удалено!',
'Search History': 'История Поиска',
'Favourites': 'Избранное',
'Favourites SH': 'Избранное ИП',
'Clear Search History': 'Очистить Историю Поиска',
'Clear!': 'Очищено!',
'kb/s': 'Кб/с',
'Queued': 'В очереди',
'Checking': 'Проверка',
'Downloading metadata': 'Скачивание мета-данных',
'Downloading': 'Скачивание',
'Finished': 'Окончено',
'Seeding': 'Раздача (сидирование)',
'Allocating': 'Allocating',
'Allocating file & Checking resume': 'Allocating file & Checking resume',
'For Kids': 'Детское',
'Adult': 'Эротика',
'Does not support magnet links!': 'Не поддерживает магнит-ссылки!',
'Reset All Cache DBs': 'Сбросить Базы Данных',
'[B]Search[/B]': '[B]Поиск[/B]',
'You can always restart this by deleting DBs via Context Menu': 'Вы всегда можете перезапустить этот процесс через Контекстное Меню',
'Your preloaded databases are outdated!': 'Ваши предзакаченные базы метаданных устарели!',
'Do you want to download new ones right now?': 'Хотите прямо сейчас скачать новые?',
'Individual Tracker Options':'Выбор Трекеров',
'Downloading and copy subtitles. Please wait.':'Скачиваю и копирую субтитры. Пожалуйста подождите.',
'International Check - First Run':'International Check - Первый запуск',
'Delete Russian stuff?':'Удалить русские трекеры?'
}
}
def localize(text):
try:
return dictionary[language][text]
except:
return text

BIN
Localization.pyc 100644

Binary file not shown.

BIN
Localization.pyo 100644

Binary file not shown.

377
Player.py 100644
View File

@ -0,0 +1,377 @@
# -*- coding: utf-8 -*-
import os
import urllib
import json
import tempfile
import sys
from contextlib import contextmanager, closing, nested
import xbmc
import xbmcgui
import Downloader
import xbmcgui
import xbmcvfs
import Localization
from functions import calculate, showMessage, clearStorage, tempdir
ROOT = sys.modules["__main__"].__root__
RESOURCES_PATH = os.path.join(ROOT, 'resources')
TORRENT2HTTP_TIMEOUT = 20
TORRENT2HTTP_POLL = 1000
PLAYING_EVENT_INTERVAL = 60
MIN_COMPLETED_PIECES = 0.5
WINDOW_FULLSCREEN_VIDEO = 12005
XBFONT_LEFT = 0x00000000
XBFONT_RIGHT = 0x00000001
XBFONT_CENTER_X = 0x00000002
XBFONT_CENTER_Y = 0x00000004
XBFONT_TRUNCATED = 0x00000008
XBFONT_JUSTIFY = 0x00000010
STATE_STRS = [
'Queued',
'Checking',
'Downloading metadata',
'Downloading',
'Finished',
'Seeding',
'Allocating',
'Allocating file & Checking resume'
]
VIEWPORT_WIDTH = 1920.0
VIEWPORT_HEIGHT = 1088.0
OVERLAY_WIDTH = int(VIEWPORT_WIDTH * 0.7) # 70% size
OVERLAY_HEIGHT = 150
ENCRYPTION_SETTINGS = {
"Forced": 0,
"Enabled": 1,
"Disabled": 2,
}
class OverlayText(object):
def __init__(self, w, h, *args, **kwargs):
self.window = xbmcgui.Window(WINDOW_FULLSCREEN_VIDEO)
viewport_w, viewport_h = self._get_skin_resolution()
# Adjust size based on viewport, we are using 1080p coordinates
w = int(w * viewport_w / VIEWPORT_WIDTH)
h = int(h * viewport_h / VIEWPORT_HEIGHT)
x = (viewport_w - w) / 2
y = (viewport_h - h) / 2
self._shown = False
self._text = ""
self._label = xbmcgui.ControlLabel(x, y, w, h, self._text, *args, **kwargs)
self._background = xbmcgui.ControlImage(x, y, w, h, os.path.join(RESOURCES_PATH, "images", "black.png"))
self._background.setColorDiffuse("0xD0000000")
def show(self):
if not self._shown:
self.window.addControls([self._background, self._label])
self._shown = True
self._background.setColorDiffuse("0xD0000000")
def hide(self):
if self._shown:
self._shown = False
self.window.removeControls([self._background, self._label])
self._background.setColorDiffuse("0xFF000000")
def close(self):
self.hide()
@property
def text(self):
return self._text
@text.setter
def text(self, text):
self._text = text
if self._shown:
self._label.setLabel(self._text)
# This is so hackish it hurts.
def _get_skin_resolution(self):
import xml.etree.ElementTree as ET
skin_path = xbmc.translatePath("special://skin/")
tree = ET.parse(os.path.join(skin_path, "addon.xml"))
res = tree.findall("./extension/res")[0]
return int(res.attrib["width"]), int(res.attrib["height"])
class TorrentPlayer(xbmc.Player):
__plugin__ = sys.modules["__main__"].__plugin__
__settings__ = sys.modules["__main__"].__settings__
ROOT = sys.modules["__main__"].__root__ #.decode('utf-8').encode(sys.getfilesystemencoding())
userStorageDirectory = __settings__.getSetting("storage")
USERAGENT = "Mozilla/5.0 (Windows NT 6.1; rv:5.0) Gecko/20100101 Firefox/5.0"
torrentFilesDirectory = 'torrents'
debug = __settings__.getSetting('debug') == 'true'
subs_dl = __settings__.getSetting('subs_dl') == 'true'
seeding = __settings__.getSetting('keep_seeding') == 'true'
def __init__(self, torrentUrl, params={}):
if 0 == len(self.userStorageDirectory):
try:
temp_dir = tempfile.gettempdir()
except:
temp_dir = tempdir()
self.userStorageDirectory = temp_dir + os.path.sep + 'Torrenter'
else:
self.userStorageDirectory = self.userStorageDirectory + 'Torrenter'
xbmc.Player.__init__(self)
print ("[TorrentPlayer] Initalized")
self.params = params
self.get = self.params.get
self.contentId = int(self.get("url"))
try:
self.ids_video = urllib.unquote_plus(self.get("url2")).split(',')
#print str(self.ids_video)
except:
self.ids_video = None
self.torrent = Downloader.Torrent(self.userStorageDirectory, torrentUrl, self.torrentFilesDirectory).player
self.init()
self.setup_torrent()
if self.buffer():
while True:
if self.setup_play():
#print '************************************* GOING LOOP'
self.torrent.continueSession(self.contentId, seeding=self.seeding)
self.loop()
else:
break
#print '************************************* GO NEXT?'
if self.next_dl and self.next_dling and self.next_contentId and self.iterator == 100:
self.contentId = self.next_contentId
continue
#print '************************************* NO! break'
break
self.torrent.stopSession()
self.torrent.threadComplete = True
self.torrent.checkThread()
if 'false' == self.__settings__.getSetting("keep_files"):
clearStorage(self.userStorageDirectory)
else:
showMessage(Localization.localize('Information'),
Localization.localize('Torrent downloading is stopped.'), forced=True)
def init(self):
self.next_dl = True if self.__settings__.getSetting('next_dl') == 'true' else False
self.next_contentId = None
self.display_name = ''
self.downloadedSize = 0
self.dialog = xbmcgui.Dialog()
self.on_playback_started = []
self.on_playback_resumed = []
self.on_playback_paused = []
self.on_playback_stopped = []
def setup_torrent(self):
self.torrent.startSession()
if 0 < int(self.__settings__.getSetting("upload_limit")):
self.torrent.setUploadLimit(int(self.__settings__.getSetting("upload_limit")) * 1000000 / 8) #MBits/second
if 0 < int(self.__settings__.getSetting("download_limit")):
self.torrent.setDownloadLimit(
int(self.__settings__.getSetting("download_limit")) * 1000000 / 8) #MBits/second
self.torrent.status = False
self.fullSize = self.torrent.getFileSize(self.contentId)
Offset = calculate(self.fullSize)
#print 'Offset: '+str(Offset)
self.torrent.threadSeeding = self.seeding
self.torrent.continueSession(self.contentId, Offset=Offset, seeding=self.seeding)
def buffer(self):
iterator = 0
progressBar = xbmcgui.DialogProgress()
progressBar.create(Localization.localize('Please Wait') + str(' [%s]' % str(self.torrent.lt.version)),
Localization.localize('Seeds searching.'))
if self.subs_dl:
subs=self.torrent.getSubsIds(os.path.basename(self.torrent.getFilePath(self.contentId)))
if len(subs)>0:
for ind, title in subs:
self.torrent.continueSession(ind)
num_pieces=int(self.torrent.torrentFileInfo.num_pieces())
while iterator < 100:
xbmc.sleep(1000)
self.torrent.debug()
downloadedSize = self.torrent.torrentHandle.file_progress()[self.contentId]
status = self.torrent.torrentHandle.status()
iterator = int(status.progress * 100)
if status.state == 0 or (status.progress == 0 and status.num_pieces > 0):
iterator = int(status.num_pieces*100/num_pieces)
if iterator > 99: iterator = 99
progressBar.update(iterator, Localization.localize('Checking preloaded files...'), ' ', ' ')
elif status.state == 3:
dialogText = Localization.localize('Preloaded: ') + str(downloadedSize / 1024 / 1024) + ' MB / ' + str(
self.fullSize / 1024 / 1024) + ' MB'
peersText = ' [%s: %s; %s: %s]' % (
Localization.localize('Seeds'), str(self.torrent.getSeeds()), Localization.localize('Peers'),
str(self.torrent.getPeers()),)
speedsText = '%s: %s Mbit/s; %s: %s Mbit/s' % (
Localization.localize('Downloading'), str(self.torrent.getDownloadRate() * 8 / 1000000),
Localization.localize('Uploading'), str(self.torrent.getUploadRate() * 8 / 1000000))
progressBar.update(iterator, Localization.localize('Seeds searching.') + peersText, dialogText,
speedsText)
else:
progressBar.update(iterator, Localization.localize('UNKNOWN STATUS'), ' ', ' ')
if progressBar.iscanceled():
progressBar.update(0)
progressBar.close()
self.torrent.threadComplete = True
self.torrent.checkThread()
return
progressBar.update(0)
progressBar.close()
return True
def setup_subs(self, label, path):
iterator=0
subs=self.torrent.getSubsIds(label)
#print str(subs)
if len(subs)>0:
showMessage(Localization.localize('Information'),
Localization.localize('Downloading and copy subtitles. Please wait.'), forced=True)
for ind, title in subs:
self.torrent.continueSession(ind)
while iterator < 100:
xbmc.sleep(1000)
self.torrent.debug()
status = self.torrent.torrentHandle.status()
iterator = int(status.progress * 100)
xbmc.sleep(2000)
for ind, title in subs:
newFileName=os.path.join(self.userStorageDirectory,os.path.dirname(path),os.path.basename(title))
if xbmcvfs.exists(newFileName):
newFileName=None
for i in range(1,9):
temp=os.path.basename(title)
ext=temp.split('.')[-1]
temp = temp[:len(temp) - len(temp.split('.')[-1]) - 1]+'.'+str(i)+'.'+ext
if not xbmcvfs.exists(os.path.join(self.userStorageDirectory,os.path.dirname(path),temp)):
newFileName=os.path.join(self.userStorageDirectory,os.path.dirname(path),temp)
break
if newFileName:
#print str((os.path.join(self.userStorageDirectory,title),newFileName))
xbmcvfs.copy(os.path.join(self.userStorageDirectory,title),newFileName)
def setup_play(self):
self.next_dling = False
self.iterator=0
path = self.torrent.getFilePath(self.contentId)
label = os.path.basename(path)
listitem = xbmcgui.ListItem(label, path=path)
if self.subs_dl:
self.setup_subs(label, path)
if self.next_dl and self.ids_video:
next_contentId_index = self.ids_video.index(str(self.contentId)) + 1
if len(self.ids_video) > next_contentId_index:
self.next_contentId = int(self.ids_video[next_contentId_index])
else:
self.next_contentId = False
if not self.ids_video:
seasonId = self.get("seasonId")
episodeId = self.get("episodeId")
title = self.get("title")
try:
label = urllib.unquote_plus(self.get("label"))
print 'ok'
except:
print 'except'
if seasonId and episodeId and label and title:
listitem = xbmcgui.ListItem(label, path=path)
listitem.setInfo(type='video', infoLabels={'title': label,
'episode': int(episodeId),
'season': int(seasonId),
'tvshowtitle': urllib.unquote_plus(title)})
thumbnail = self.get("thumbnail")
if thumbnail:
listitem.setThumbnailImage(urllib.unquote_plus(thumbnail))
self.display_name = label
#мегакостыль!
rpc = ({'jsonrpc': '2.0', 'method': 'Files.GetDirectory', 'params': {
'media': 'video', 'directory': os.path.dirname(path)}, 'id': 0})
data = json.dumps(rpc)
request = xbmc.executeJSONRPC(data)
response = json.loads(request)
xbmc.sleep(300)
if response:
xbmc.Player().play(path, listitem)
xbmc.sleep(3000)#very important, do not edit this, podavan
return True
def onPlayBackStarted(self):
for f in self.on_playback_started:
f()
print(str(("video", "play", self.display_name)))
def onPlayBackResumed(self):
for f in self.on_playback_resumed:
f()
self.onPlayBackStarted()
def onPlayBackPaused(self):
for f in self.on_playback_paused:
f()
print(str(("video", "pause", self.display_name)))
def onPlayBackStopped(self):
for f in self.on_playback_stopped:
f()
print(str(("video", "stop", self.display_name)))
@contextmanager
def attach(self, callback, *events):
for event in events:
event.append(callback)
yield
for event in events:
event.remove(callback)
def loop(self):
with closing(
OverlayText(w=OVERLAY_WIDTH, h=OVERLAY_HEIGHT, alignment=XBFONT_CENTER_X | XBFONT_CENTER_Y)) as overlay:
with nested(self.attach(overlay.show, self.on_playback_paused),
self.attach(overlay.hide, self.on_playback_resumed, self.on_playback_stopped)):
while not xbmc.abortRequested and self.isPlaying():
self.torrent.checkThread()
self.torrent.debug()
status = self.torrent.torrentHandle.status()
overlay.text = "\n".join(self._get_status_lines(status))
#downloadedSize = torrent.torrentHandle.file_progress()[contentId]
self.iterator = int(status.progress * 100)
if self.iterator == 100 and not self.next_dling and self.next_contentId:
showMessage(Localization.localize('Torrent Downloading'),
Localization.localize('Starting download next episode!'), forced=True)
self.torrent.stopSession()
xbmc.sleep(1000)
self.torrent.threadSeeding = self.seeding
self.torrent.continueSession(self.next_contentId, seeding=self.seeding)
path = self.torrent.getFilePath(self.next_contentId)
self.display_name = os.path.basename(path)
self.next_dling = True
xbmc.sleep(1000)
def _get_status_lines(self, s):
return [
self.display_name.decode('utf-8'),
"%.2f%% %s" % (s.progress * 100, Localization.localize(STATE_STRS[s.state]).decode('utf-8')),
"D:%.2f%s U:%.2f%s S:%d P:%d" % (s.download_rate / 1000, Localization.localize('kb/s').decode('utf-8'),
s.upload_rate / 1000, Localization.localize('kb/s').decode('utf-8'),
s.num_seeds, s.num_peers)
]

BIN
Player.pyo 100644

Binary file not shown.

80
Proxier.py 100644
View File

@ -0,0 +1,80 @@
import socket
import thread
import re
import os
class Proxier:
HOST = '127.0.0.1'
PORT = 51515
PATH = ''
CHUNK_SIZE = 1024
seekBytes = 0
buffering = 0
def server(self, path):
self.PATH = path
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.settimeout(600)
s.bind((self.HOST, self.PORT))
s.listen(1)
while True:
conn, addr = s.accept()
conn.setblocking(1)
data = conn.recv(1024)
range = re.compile('Range: bytes=(\d+)-(\d*)')
size = os.path.getsize(self.PATH)
byte1, byte2 = 0, None
if range.search(data):
g = range.search(data).groups()
if g[0]: byte1 = int(g[0])
if g[1]: byte2 = int(g[1])
length = size - byte1
if byte2 is not None:
length = byte2 - byte1
thread.start_new_thread(self.streamer, (conn, byte1, byte2, length, size, ))
def streamer(self, conn, byte1, byte2, length, size):
send = ''
if byte1 > 0 or byte2 != None:
send = send + "HTTP/1.1 206 Partial Content"
send = send + "\r\n"
send = send + "Content-Range: %s-%s/%s" % (str(byte1), str(byte1 + length - 1), str(size))
send = send + "\r\n"
self.seekBytes = byte1
else:
send = send + "HTTP/1.1 200 OK"
send = send + "\r\n"
send = send + "Content-Type: video/mp4"
send = send + "\r\n"
send = send + "Content-Length: " + str(length)
send = send + "\r\n"
send = send + "Accept-Ranges: bytes"
send = send + "\r\n"
send = send + "Connection: close"
send = send + "\r\n"
send = send + "\r\n"
conn.send(send)
fp = open(self.PATH, "rb")
fp.seek(byte1)
sent = 0
while length > sent:
chunk = fp.read(self.CHUNK_SIZE)
if chunk:
self.buffering = 0
try:
sent = sent + conn.send(chunk)
except socket.error, e:
conn.close()
break
else:
self.buffering = self.buffering + 1
time.sleep(1)
if self.buffering > 60:
break
fp.close()
conn.close()

57
README.txt 100644
View File

@ -0,0 +1,57 @@
Official library`s website is http://www.rasterbar.com/products/libtorrent/
Plugin requires python binding
--- INSTALLATION ---
1. Windows
1.1 Download from 'http://code.google.com/p/libtorrent/downloads/list'
last version of installer for windows, at the moment
it is 'python-libtorrent-0.16.0.win32-py2.6.msi'
1.2 Run installer and point as an installation directory - directory of
python for xbmc. Usually it is 'C:\Program Files (x86)\XBMC\system\python'
1.3 Install addon and enjoy
2. Linux
2.1 Run at console 'sudo apt-get install python-libtorrent'
2.2 Install addon and enjoy
or you could compile it:
sudo apt-get install libboost-dev libboost-python-dev libboost-system-dev g++ libssl openssl autotool automake subversion
svn co https://libtorrent.svn.sourceforge.net/svnroot/libtorrent/trunk/ lt/
cd lt/
./autotool.sh
./configure
make
sudo make install
sudo ldconfig
________________________________________________________________________________________________________
Вебсайт библиотеки http://www.rasterbar.com/products/libtorrent/
Для работы плагина нужен её билд под python
--- ИНСТАЛЯЦИЯ ---
1. Windows
1.1 Скачиваем отсюда 'http://code.google.com/p/libtorrent/downloads/list'
последнюю версию инсталлера библиотеки для windows, на момент написания
это была версия 'python-libtorrent-0.16.0.win32-py2.6.msi'
1.2 После запуска инсталлера нужно указать в качестве директории установки -
директорию python для xbmc. Обычно это 'C:\Program Files (x86)\XBMC\system\python'
1.3 Устанавливаем аддон в XBMC и пользуемся
2. Linux
2.1 Выполняем в терминале sudo apt-get install python-libtorrent
2.2 Устанавливаем аддон в XBMC и пользуемся
или компилируем:
sudo apt-get install libboost-dev libboost-python-dev libboost-system-dev g++ libssl openssl autotool automake subversion
svn co https://libtorrent.svn.sourceforge.net/svnroot/libtorrent/trunk/ lt/
cd lt/
./autotool.sh
./configure
make
sudo make install
sudo ldconfig

66
Rates.py 100644
View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import sys
import xbmcgui
import Localization
class Rates(xbmcgui.Window):
buttonWidth = 250
buttonHeight = 250
width = 1280
height = 720
rate = 0
def __init__(self, *kargs):
noFocus = sys.modules["__main__"].__root__ + '/icons/bookmarks.png'
focus = sys.modules["__main__"].__root__ + '/icons/add_bookmark.png'
self.addControl(xbmcgui.ControlLabel(0, self.height / 4, self.width, 100,
Localization.localize('Please, rate watched video:'), alignment=6))
self.bad = xbmcgui.ControlButton(self.buttonWidth / 2, self.height / 2, self.buttonWidth, self.buttonHeight,
Localization.localize('Bad'), alignment=6, textColor='0xFFCC3333',
focusedColor='0xFFFF0000', focusTexture=focus, noFocusTexture=noFocus,
shadowColor='0xFF000000')
self.normal = xbmcgui.ControlButton(self.width / 2 - self.buttonWidth / 2, self.height / 2, self.buttonWidth,
self.buttonHeight, Localization.localize('So-So'), alignment=6,
textColor='0xFFCCCC33', focusedColor='0xFFFFFF00', focusTexture=focus,
noFocusTexture=noFocus, shadowColor='0xFF000000')
self.good = xbmcgui.ControlButton(self.width - self.buttonWidth * 3 / 2, self.height / 2, self.buttonWidth,
self.buttonHeight, Localization.localize('Good'), alignment=6,
textColor='0xFF33CC33', focusedColor='0xFF00FF00', focusTexture=focus,
noFocusTexture=noFocus, shadowColor='0xFF000000')
self.addControl(self.bad)
self.addControl(self.normal)
self.addControl(self.good)
self.bad.setNavigation(self.bad, self.bad, self.bad, self.normal)
self.normal.setNavigation(self.normal, self.normal, self.bad, self.good)
self.good.setNavigation(self.good, self.good, self.normal, self.good)
self.setFocus(self.normal)
def onControl(self, control):
if control == self.bad:
self.rate = -1
if control == self.normal:
self.rate = 0
if control == self.good:
self.rate = 1
self.close()

216
SearcherABC.py 100644
View File

@ -0,0 +1,216 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import abc
import urllib
import urllib2
import cookielib
import re
import tempfile
import hashlib
import os
from StringIO import StringIO
import gzip
import socket
import xml.etree.ElementTree as ET
import sys
import xbmcgui
import xbmc
import Localization
class SearcherABC:
__metaclass__ = abc.ABCMeta
searchIcon = '/icons/video.png'
sourceWeight = 1
cookieJar = None
timeout_multi=int(sys.modules["__main__"].__settings__.getSetting("timeout"))
socket.setdefaulttimeout(10+(10*int(timeout_multi)))
@abc.abstractmethod
def search(self, keyword):
'''
Retrieve keyword from the input and return a list of tuples:
filesList.append((
int(weight),
int(seeds),
str(title),
str(link),
str(image),
))
'''
return
@abc.abstractproperty
def isMagnetLinkSource(self):
return 'Should never see this'
def getTorrentFile(self, url):
return url
def sizeConvert(self, sizeBytes):
if long(sizeBytes) >= 1024 * 1024 * 1024:
size = str(long(sizeBytes) / (1024 * 1024 * 1024)) + 'GB'
elif long(sizeBytes) >= 1024 * 1024:
size = str(long(sizeBytes) / (1024 * 1024)) + 'MB'
elif sizeBytes >= 1024:
size = str(long(sizeBytes) / 1024) + 'KB'
else:
size = str(long(sizeBytes)) + 'B'
return size
def check_login(self, response=None):
return True
def login(self):
return True
def load_cookie(self):
cookie=os.path.join(self.tempdir(),'cookie.txt')
self.cookieJar = cookielib.MozillaCookieJar(cookie)
if os.path.exists(cookie): self.cookieJar.load(ignore_discard=True)
def makeRequest(self, url, data={}, headers={}):
self.load_cookie()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookieJar))
opener.addheaders = headers
if 0 < len(data):
encodedData = urllib.urlencode(data)
else:
encodedData = None
response = opener.open(url, encodedData)
#self.cookieJar.extract_cookies(response, urllib2)
if response.info().get('Content-Encoding') == 'gzip':
buf = StringIO(response.read())
f = gzip.GzipFile(fileobj=buf)
response = f.read()
else:
response = response.read()
return response
def askCaptcha(self, url):
temp_dir = tempfile.gettempdir()
if isinstance(temp_dir, list): temp_dir = temp_dir[0]
urllib.URLopener().retrieve(url, temp_dir + '/captcha.png')
window = xbmcgui.Window(xbmcgui.getCurrentWindowId())
temp_dir = tempfile.gettempdir()
if isinstance(temp_dir, list): temp_dir = temp_dir[0]
image = xbmcgui.ControlImage(460, 20, 360, 160, temp_dir + '/captcha.png')
window.addControl(image)
keyboardCaptcha = xbmc.Keyboard('', Localization.localize('Input symbols from CAPTCHA image:'))
keyboardCaptcha.doModal()
captchaText = keyboardCaptcha.getText()
window.removeControl(image)
if not captchaText:
return False
else:
return captchaText
htmlCodes = (
('&', '&amp;'),
('<', '&lt;'),
('>', '&gt;'),
('"', '&quot;'),
("'", '&#39;'),
("&", '&#38;'),)
stripPairs = (
('<p>', '\n'),
('<li>', '\n'),
('<br>', '\n'),
('<.+?>', ' '),
('</.+?>', ' '),
('&nbsp;', ' '),
('&laquo;', '"'),
('&raquo;', '"'),
)
def unescape(self, string):
for (symbol, code) in self.htmlCodes:
string = re.sub(code, symbol, string)
return string
def stripHtml(self, string):
for (html, replacement) in self.stripPairs:
string = re.sub(html, replacement, string)
return string
def md5(self, string):
hasher = hashlib.md5()
hasher.update(string)
return hasher.hexdigest()
def tempdir(self):
dirname = xbmc.translatePath('special://temp')
for subdir in ('xbmcup', 'plugin.video.torrenter'):
dirname = os.path.join(dirname, subdir)
if not os.path.exists(dirname):
os.mkdir(dirname)
return dirname
def getByLabel(self, label):
clean_label = self.clean(label)
url = 'http://ruhunt.org/feed?q=%s' % urllib.quote_plus(clean_label)
response = self.makeRequest(url)
if None != response and 0 < len(response):
#print response
try:
dat = ET.fromstring(response)
url = dat.findall('channel')[0].findall('item')[0].find('link').text
#print str(url)
response = self.makeRequest(url)
if None != response and 0 < len(response):
#print response
magnet = re.compile('<a href="(magnet.+?)">', re.DOTALL | re.MULTILINE).findall(response)[0]
return magnet
except:
return
def timeout(self, add_seconds=0):
seconds=10+(10*int(self.timeout_multi))+int(add_seconds)
socket.setdefaulttimeout(int(seconds))
def clean(self, string):
specials = ['/', '\\', '-', '[', ']', '(', ')', ',']
for symbol in specials:
string = string.replace(symbol, ' ')
if len(string) > 120:
string = string[:120]
last_piece = string.split(' ')[-1]
string = string[:120 - len(last_piece)].strip()
return string
def saveTorrentFile(self, url, content):
try:
temp_dir = tempfile.gettempdir()
except:
temp_dir = self.tempdir()
localFileName = temp_dir + os.path.sep + self.md5(url)
localFile = open(localFileName, 'wb+')
localFile.write(content)
localFile.close()
return localFileName

BIN
SearcherABC.pyo 100644

Binary file not shown.

24
addon.xml 100644
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.torrenter" name="Torrenter" version="2.0.8" provider-name="vadim.skorba, DiMartino">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.libtorrent"/>
<import addon="script.module.torrent.ts"/>
</requires>
<extension point="xbmc.python.pluginsource" provides="video" library="default.py">
<provides>video</provides>
</extension>
<extension point="xbmc.addon.metadata">
<platform>all</platform>
<summary lang='en'>Plugin helps you to watch videos from p2p torrent-networks, without full predownload.
</summary>
<description lang='en'>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, Transmisson or Vuse.
</description>
<disclaimer lang='en'>GNU GPLv3 http://www.gnu.org/licenses/</disclaimer>
<summary lang='ru'>Плагин позволяет просматривать видео из пиринговых торрент-сетей, не дожидаясь полного скачивания.
</summary>
<description lang='ru'>Плагин позволяет просматривать видео из пиринговых торрент-сетей, не дожидаясь полного скачивания. Использует внутренюю библиотеку python-libtorrent или Ace Stream. Так же плагин может добавлять, проигрывать и управлять скачками в uTorrent, Transmisson и Vuse.
</description>
<disclaimer lang='ru'>GNU GPLv3 http://www.gnu.org/licenses/</disclaimer>
</extension>
</addon>

12
cal.py 100644
View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
import sys
import os
ROOT = os.path.dirname(sys.modules["__main__"].sys.argv[0])
searcherObject = {}
searcher = 'IMDB'
if ROOT + os.sep + 'resources' + os.sep + 'contenters' not in sys.path:
sys.path.insert(0, ROOT + os.sep + 'resources' + os.sep + 'contenters')
searcherObject[searcher] = getattr(__import__(searcher), searcher)()
#print str(searcherObject[searcher].get_contentList(category='search', subcategory='Hobbit'))

40
changelog.txt 100644
View File

@ -0,0 +1,40 @@
[B]Version 2.0.7[/B]
[+] Поиск: Добавлено несколько зарубежных трекеров
[+] Добавлена поддержка интернациональности, придется пару раз кликнуть нет
[B]Version 2.0.6[/B]
[+] Проигрыватель: Автоматическое подключение внешних субтитров
[+] Проигрыватель: Исправлено проигрывание следующего эпизода
[B]Version 2.0.5[/B]
[+] История Поиска: Избранным запросам можно отдельно указывать трекеры для поиска
[+] Оптимизирована работа с трекерами требующими авторизацию
[+] Поиск: Исправлен NNM-club, Добавлен TFileMe (возможны проблемы из-за рейтинга)
[+] Поиск: Написан KinoZalTV, но если никто не поделится для всех аккаунтом, то в общий поиск не добавлю. Однако он есть в папке \resources\searchers\unused, если переместить в \resources\searchers и изменить в коде логин и пароль, то он заработает.
[+] Исправлена работа с кириллическими путями
[+] Некоторые настройки Ace Stream перенесены в плагин
[B]Version 2.0.4[/B]
[+] Общая оптимизация
[+] Поиск: Исправлен RuTorOrg, NNM-club
[+] Списки Медиа: Улучшено распознавание метаданных
[+] Списки Медиа: Организована замена устаревших баз
[+] Списки Медиа: Новая функция - Поиск
[+] Списки Медиа: В некоторых категориях КиноПоиска добавлены страницы
[+] Добавлен вариант поиска на случай неправильного распознавания
[B]Version 2.0.3[/B]
[+] Добавлен CXZ.TO в качестве источника Списков Медиа
[+] Добавлена поддержка Ace Stream от [B]nuismons[/B]
[+] Rutor, OpenSharing, OldPirateBay переведены на .torrent с магнит-ссылок
[B]Version 2.0.2[/B]
[+] Отображение статуса загрузки при паузе
[B]Version 2.0.1[/B]
[+] Увеличен буфер для файлов более 1 ГБ
[+] Добавлен серчер OldPirateBay
[+] История поиска: Изменен порядок вывода, добавлено отключение и очищение
[B]Version 2.0.0[/B]
[+] Релиз

385
controlcenter.py 100644
View File

@ -0,0 +1,385 @@
# -*- coding: utf-8 -*-
import xbmcaddon
import xbmc, sys
from functions import getParameters, HistoryDB
from resources.pyxbmct.addonwindow import *
from functions import Searchers
__settings__ = xbmcaddon.Addon(id='plugin.video.torrenter')
__language__ = __settings__.getLocalizedString
__version__ = __settings__.getAddonInfo('version')
__plugin__ = __settings__.getAddonInfo('name') + " v." + __version__
__root__ = __settings__.getAddonInfo('path')
HistoryDB_name, HistoryDB_ver = 'history', 1.1
print 'SYS ARGV: ' + str(sys.argv)
if len(sys.argv)>1:
params = getParameters(sys.argv[1])
else:
params={}
class MyAddon(AddonDialogWindow):
def __init__(self, title=''):
super(MyAddon, self).__init__(title)
self.setGeometry(700, 450, 9, 4)
self.set_info_controls()
self.set_active_controls()
self.set_navigation()
# Connect a key action (Backspace) to close the window.
self.connect(ACTION_NAV_BACK, self.close)
def set_info_controls(self):
# Demo for PyXBMCt UI controls.
no_int_label = Label('Information output', alignment=ALIGN_CENTER)
self.placeControl(no_int_label, 0, 0, 1, 2)
#
label_label = Label('Label')
self.placeControl(label_label, 1, 0)
# Label
self.label = Label('Simple label')
self.placeControl(self.label, 1, 1)
#
fadelabel_label = Label('FadeLabel')
self.placeControl(fadelabel_label, 2, 0)
# FadeLabel
self.fade_label = FadeLabel()
self.placeControl(self.fade_label, 2, 1)
self.fade_label.addLabel('Very long string can be here.')
#
textbox_label = Label('TextBox')
self.placeControl(textbox_label, 3, 0)
# TextBox
self.textbox = TextBox()
self.placeControl(self.textbox, 3, 1, 2, 1)
self.textbox.setText('Text box.\nIt can contain several lines.')
#
image_label = Label('Image')
self.placeControl(image_label, 5, 0)
def set_active_controls(self):
int_label = Label('Interactive Controls', alignment=ALIGN_CENTER)
self.placeControl(int_label, 0, 2, 1, 2)
#
radiobutton_label = Label('RadioButton')
self.placeControl(radiobutton_label, 1, 2)
# RadioButton
self.radiobutton = RadioButton('Off')
self.placeControl(self.radiobutton, 1, 3)
self.connect(self.radiobutton, self.radio_update)
#
edit_label = Label('Edit')
self.placeControl(edit_label, 2, 2)
# Edit
self.edit = Edit('Edit')
self.placeControl(self.edit, 2, 3)
# Additional properties must be changed after (!) displaying a control.
self.edit.setText('Enter text here')
#
list_label = Label('List')
self.placeControl(list_label, 3, 2)
#
self.list_item_label = Label('', textColor='0xFF808080')
self.placeControl(self.list_item_label, 4, 2)
# List
self.list = List()
self.placeControl(self.list, 3, 3, 3, 1)
# Add items to the list
items = ['Item %s' % i for i in range(1, 8)]
self.list.addItems(items)
# Connect the list to a function to display which list item is selected.
self.connect(self.list, lambda: xbmc.executebuiltin('Notification(Note!,%s selected.)' %
self.list.getListItem(
self.list.getSelectedPosition()).getLabel()))
# Connect key and mouse events for list navigation feedback.
self.connectEventList(
[ACTION_MOVE_DOWN, ACTION_MOVE_UP, ACTION_MOUSE_WHEEL_DOWN, ACTION_MOUSE_WHEEL_UP, ACTION_MOUSE_MOVE],
self.list_update)
# Slider value label
SLIDER_INIT_VALUE = 25.0
self.slider_value = Label(str(SLIDER_INIT_VALUE), alignment=ALIGN_CENTER)
self.placeControl(self.slider_value, 6, 3)
#
slider_caption = Label('Slider')
self.placeControl(slider_caption, 7, 2)
# Slider
self.slider = Slider()
self.placeControl(self.slider, 7, 3, pad_y=10)
self.slider.setPercent(SLIDER_INIT_VALUE)
# Connect key and mouse events for slider update feedback.
self.connectEventList([ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT, ACTION_MOUSE_DRAG], self.slider_update)
#
button_label = Label('Button')
self.placeControl(button_label, 8, 2)
# Button
self.button = Button('Close')
self.placeControl(self.button, 8, 3)
# Connect control to close the window.
self.connect(self.button, self.close)
def set_navigation(self):
# Set navigation between controls
self.button.controlUp(self.slider)
self.button.controlDown(self.radiobutton)
self.radiobutton.controlUp(self.button)
self.radiobutton.controlDown(self.edit)
self.edit.controlUp(self.radiobutton)
self.edit.controlDown(self.list)
self.list.controlUp(self.edit)
self.list.controlDown(self.slider)
self.slider.controlUp(self.list)
self.slider.controlDown(self.button)
# Set initial focus
self.setFocus(self.radiobutton)
def slider_update(self):
# Update slider value label when the slider nib moves
try:
if self.getFocus() == self.slider:
self.slider_value.setLabel('%.1f' % self.slider.getPercent())
except (RuntimeError, SystemError):
pass
def radio_update(self):
# Update radiobutton caption on toggle
if self.radiobutton.isSelected():
self.radiobutton.setLabel('On')
else:
self.radiobutton.setLabel('Off')
def list_update(self):
# Update list_item label when navigating through the list.
try:
if self.getFocus() == self.list:
self.list_item_label.setLabel(self.list.getListItem(self.list.getSelectedPosition()).getLabel())
else:
self.list_item_label.setLabel('')
except (RuntimeError, SystemError):
pass
def setAnimation(self, control):
# Set fade animation for all add-on window controls
control.setAnimations([('WindowOpen', 'effect=fade start=0 end=100 time=500',),
('WindowClose', 'effect=fade start=100 end=0 time=500',)])
class ControlCenter(AddonDialogWindow):
def __init__(self, title, addtime=None):
super(ControlCenter, self).__init__(title)
self.dic = Searchers().dic()
self.db=None
self.addtime=None
self.keys = self.dic.keys()
if addtime:
self.addtime=addtime
self.db = HistoryDB(HistoryDB_name, HistoryDB_ver)
providers = self.db.get_providers(addtime)
if not providers:
self.db.set_providers(addtime, self.dic)
else:
for searcher in self.keys:
self.dic[searcher]=False
for searcher in providers:
try:
if searcher in self.keys:
self.dic[searcher]=True
except:
pass
self.keys = self.dic.keys()
self.placed, self.button_columns, self.last_column_row = self.place()
self.setGeometry(700, 150 + 50 * self.button_columns, 3 + self.button_columns, 3)
self.set_info_controls()
self.set_active_controls()
self.set_navigation()
# Connect a key action (Backspace) to close the window.
self.connect(ACTION_NAV_BACK, self.close)
def place(self):
placed = {}
i, j = -1, 0
for item in self.keys:
if i == 2:
i = 0
j += 1
else:
i += 1
placed[item] = (j, i)
# print item+str((j, i))
return placed, j, i
def set_info_controls(self):
# Demo for PyXBMCt UI controls.
# no_int_label = Label(__language__(30146), alignment=ALIGN_CENTER)
#self.placeControl(no_int_label, 0, 0, 1, 3)
#
#label_timeout = Label(__language__(30410))
#self.placeControl(label_timeout, 1, 0)
# Label
#self.label = Label(__language__(30545) % TimeOut().timeout())
#self.placeControl(self.label, 1, 1)
#
#label_watched = Label(__language__(30414) % (WatchedDB().count()))
#self.placeControl(label_watched, 2, 0)
pass
def set_active_controls(self):
# RadioButton
self.radiobutton = {}
self.radiobutton_top, self.radiobutton_bottom = [None, None, None], [None, None, None]
for searcher in self.keys:
place = self.placed[searcher]
self.radiobutton[searcher] = RadioButton(searcher)
self.placeControl(self.radiobutton[searcher], place[0], place[1])
self.radiobutton[searcher].setSelected(self.dic[searcher])
self.connect(self.radiobutton[searcher], self.radio_update)
if place[0] == 0:
self.radiobutton_top[place[1]] = self.radiobutton[searcher]
self.radiobutton_bottom[place[1]] = self.radiobutton[searcher]
# Button
self.button_openset = Button(__language__(30413))
self.placeControl(self.button_openset, 2 + self.button_columns, 0)
# Connect control to close the window.
self.connect(self.button_openset, self.openSettings)
# Button
self.button_utorrent = Button(__language__(30414))
self.placeControl(self.button_utorrent, 2 + self.button_columns, 1)
# Connect control to close the window.
self.connect(self.button_utorrent, self.openUtorrent)
# Button
self.button_close = Button(__language__(30412))
self.placeControl(self.button_close, 2 + self.button_columns, 2)
# Connect control to close the window.
self.connect(self.button_close, self.close)
def set_navigation(self):
# Set navigation between controls
placed_values = self.placed.values()
placed_keys = self.placed.keys()
for searcher in placed_keys:
buttons = [self.button_openset, self.button_utorrent, self.button_close]
place = self.placed[searcher]
if place[0] == 0:
self.radiobutton[searcher].controlUp(buttons[place[1]])
else:
ser = placed_keys[placed_values.index((place[0] - 1, place[1]))]
self.radiobutton[searcher].controlUp(self.radiobutton[ser])
# self.button_columns, self.last_column_row
if place[1] == 0 and place[0] == self.button_columns:
if self.last_column_row > 0:
ser = placed_keys[placed_values.index((place[0], self.last_column_row))]
else:
ser = placed_keys[placed_values.index((place[0] - 1, 2))]
elif place[1] == 0:
ser = placed_keys[placed_values.index((place[0], 2))]
else:
ser = placed_keys[placed_values.index((place[0], place[1] - 1))]
self.radiobutton[searcher].controlLeft(self.radiobutton[ser])
#print str((self.button_columns, self.last_column_row))
#print searcher
if place == (self.button_columns, self.last_column_row) and self.last_column_row < 2:
ser = placed_keys[placed_values.index((place[0] - 1, place[1] + 1))]
elif place[1] == 2:
ser = placed_keys[placed_values.index((place[0], 0))]
else:
ser = placed_keys[placed_values.index((place[0], place[1] + 1))]
self.radiobutton[searcher].controlRight(self.radiobutton[ser])
if place[0] == self.button_columns - 1 and place[1] > self.last_column_row or \
place[0] == self.button_columns:
self.radiobutton[searcher].controlDown(buttons[place[1]])
else:
ser = placed_keys[placed_values.index((place[0] + 1, place[1]))]
self.radiobutton[searcher].controlDown(self.radiobutton[ser])
self.button_openset.controlUp(self.radiobutton_bottom[0])
self.button_openset.controlDown(self.radiobutton_top[0])
self.button_openset.controlLeft(self.button_close)
self.button_openset.controlRight(self.button_utorrent)
self.button_utorrent.controlUp(self.radiobutton_bottom[1])
self.button_utorrent.controlDown(self.radiobutton_top[1])
self.button_utorrent.controlLeft(self.button_openset)
self.button_utorrent.controlRight(self.button_close)
self.button_close.controlUp(self.radiobutton_bottom[2])
self.button_close.controlDown(self.radiobutton_top[2])
self.button_close.controlLeft(self.button_utorrent)
self.button_close.controlRight(self.button_openset)
# Set initial focus
self.setFocus(self.button_close)
def openSettings(self):
__settings__.openSettings()
def openUtorrent(self):
xbmc.executebuiltin('ActivateWindow(Videos,plugin://plugin.video.torrenter/?action=uTorrentBrowser)')
self.close()
def slider_update(self):
# Update slider value label when the slider nib moves
try:
if self.getFocus() == self.slider:
self.slider_value.setLabel('%.1f' % self.slider.getPercent())
except (RuntimeError, SystemError):
pass
def radio_update(self):
# Update radiobutton caption on toggle
index = self.radiobutton.values().index(self.getFocus())
dic = Searchers().dic()
searcher = self.radiobutton.keys()[index]
if self.addtime:
self.db.change_providers(self.addtime, searcher)
else:
Searchers().setBoolSetting(searcher, not dic[searcher])
def list_update(self):
# Update list_item label when navigating through the list.
try:
if self.getFocus() == self.list:
self.list_item_label.setLabel(self.list.getListItem(self.list.getSelectedPosition()).getLabel())
else:
self.list_item_label.setLabel('')
except (RuntimeError, SystemError):
pass
def setAnimation(self, control):
# Set fade animation for all add-on window controls
control.setAnimations([('WindowOpen', 'effect=fade start=0 end=100 time=500',),
('WindowClose', 'effect=fade start=100 end=0 time=500',)])
def main():
title='Global Torrenter Control Center'
addtime=None
if params.get('title'):
title=str(params.get('title'))
addtime=str(params.get('addtime'))
window = ControlCenter(title, addtime)
window.doModal()
if __name__ == '__main__':
try:
main()
except Exception, e:
import xbmc
import traceback
map(xbmc.log, traceback.format_exc().split("\n"))
raise

40
default.py 100644
View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import sys
import xbmcaddon
__settings__ = xbmcaddon.Addon(id='plugin.video.torrenter')
__version__ = __settings__.getAddonInfo('version')
__plugin__ = __settings__.getAddonInfo('name') + " v." + __version__
__root__ = __settings__.getAddonInfo('path')
if (__name__ == "__main__" ):
print __plugin__
import Core
core = Core.Core()
if (not sys.argv[2]):
core.sectionMenu()
else:
params = core.getParameters(sys.argv[2])
core.executeAction(params)

1586
functions.py 100644

File diff suppressed because it is too large Load Diff

BIN
functions.pyc 100644

Binary file not shown.

BIN
functions.pyo 100644

Binary file not shown.

BIN
icon.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
icons/clear.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icons/fav.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
icons/history2.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
icons/list.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
icons/magnet.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
icons/media.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
icons/search.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
icons/settings.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
icons/unfav.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
icons/video.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,191 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import re
import Content
from BeautifulSoup import BeautifulSoup
def make_category_dict():
category_dict = {
'movies': ('Forieng Movies', '/films/fl_foreign_hight/?'),
'rus_movies': ('Russian Movies', '/films/fl_our_hight/?'),
'tvshows': ('TV Shows', '/serials/fl_hight/?'),
'cartoons': ('Cartoons', '/cartoons/fl_hight/?'),
'anime': ('Anime', '/cartoons/cartoon_genre/anime/?'),
'hot': ('Hot & New', '/films/fl_hight/?'),
'top': ('Top 250 Movies', '/films/fl_hight/?sort=popularity&'),
'genre': {'genre': 'by Genre',
'action': ('Action', '/films/film_genre/bojevik/?'),
'adventure': ('Adventure', '/films/film_genre/priklucheniya/?'),
'biography': ('Biography', '/films/film_genre/biografiya/?'),
'comedy': ('Comedy', '/films/film_genre/komediya/?'),
'crime': ('Crime', '/films/film_genre/detektiv/?'),
'documentary': ('Documentary', '/films/film_genre/dokumentalnyj/?'),
'drama': ('Drama', '/films/film_genre/drama/?'),
'erotika': ('Adult', '/films/film_genre/erotika/?'),
'family': ('Family', '/films/film_genre/semejnyj/?'),
'fantasy': ('Fantasy', '/films/film_genre/fentezi/?'),
'film_noir': ('Film-Noir', '/films/film_genre/nuar/?'),
'history': ('History', '/films/film_genre/istoriya/?'),
'horror': ('Horror', '/films/film_genre/uzhasy/?'),
'kids': ('For Kids', '/films/film_genre/detskij/?'),
'musical': ('Musical', '/films/film_genre/muzikl/?'),
'mystery': ('Mystery', '/films/film_genre/mistika/?'),
'romance': ('Romance', '/films/film_genre/melodrama/?'),
'sci_fi': ('Sci-Fi', '/films/film_genre/fantastika/?'),
'short': ('Short', '/films/film_genre/korotkometrazhka/?'),
'thriller': ('Thriller', '/films/film_genre/triller/?'),
'war': ('War', '/films/film_genre/vojennyj/?'),
'western': ('Western', '/films/film_genre/vestern/?'),
}
}
for category in category_dict.keys():
if isinstance(category_dict.get(category), dict):
for subcategory in category_dict.get(category).keys():
if subcategory != category:
x = category_dict[category][subcategory]
category_dict[category][subcategory] = (
x[0], x[1] + 'view=list', {'page': x[1] + 'view=list&page=%d', 'increase': 1, 'second_page': 1})
if not isinstance(category_dict.get(category), dict):
x = category_dict[category]
category_dict[category] = (
x[0], x[1] + 'view=list', {'page': x[1] + 'view=list&page=%d', 'increase': 1, 'second_page': 1})
category_dict['year'] = {'year': 'by Year', }
for y in range(2015, 1970, -1):
category_dict['year'][str(y)] = (str(y), '/films/year/%s/' % str(y),
{'page': '/films/year/%s/' % str(y) + '?view=list&page=%d', 'increase': 1,
'second_page': 1})
return category_dict
class CXZ(Content.Content):
category_dict = make_category_dict()
regex_list = []
baseurl = "http://cxz.to"
headers = [('User-Agent',
'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'),
('Referer', baseurl), ('Accept-Encoding', 'gzip'), ('Accept-Language', 'ru,en;q=0.8')]
'''
Weight of source with this searcher provided.
Will be multiplied on default weight.
Default weight is seeds number
'''
sourceWeight = 2
def isLabel(self):
return False
def isPages(self):
return True
def isSearchOption(self):
return True
def isScrappable(self):
return True
def get_contentList(self, category, subcategory=None, page=None):
contentList = []
url = self.get_url(category, subcategory, page, self.baseurl)
response = self.makeRequest(url, headers=self.headers)
if None != response and 0 < len(response):
#print response
if category:
contentList = self.mode(response)
#print str(contentList)
return contentList
def mode(self, response):
contentList = []
Soup = BeautifulSoup(response)
result = Soup.findAll('div', {'class': 'b-poster-tile '})
num = 0
for tr in result:
#main
info = {}
year = 0
num = num + 1
title = tr.find('span', 'b-poster-tile__title-full').text.strip()
originaltitle = None
year = re.compile('(\d\d\d\d)').findall(tr.find('span', 'b-poster-tile__title-info-items').text)[0]
link = tr.find('a', 'b-poster-tile__link').get('href')
for i in ['/serials/', '/cartoonserials/', '/tvshow/']:
if i in link:
info['tvshowtitle'] = title
break
img = tr.find('img').get('src')
img = img if img else ''
#info
contentList.append((
int(int(self.sourceWeight) * (251 - int(num))),
originaltitle, title, int(year), img, info,
))
#print result
return contentList
'''
- Video Values:
- genre : string (Comedy)
- year : integer (2009)
- episode : integer (4)
- season : integer (1)
- top250 : integer (192)
- rating : float (6.4) - range is 0..10
- cast : list (Michal C. Hall)
- castandrole : list (Michael C. Hall|Dexter)
- director : string (Dagur Kari)
- mpaa : string (PG-13)
- plot : string (Long Description)
- plotoutline : string (Short Description)
- title : string (Big Fan)
- originaltitle : string (Big Fan)
- sorttitle : string (Big Fan)
- duration : string (3:18)
- studio : string (Warner Bros.)
- tagline : string (An awesome movie) - short description of movie
- writer : string (Robert D. Siegel)
- tvshowtitle : string (Heroes)
- premiered : string (2005-03-04)
- status : string (Continuing) - status of a TVshow
- code : string (tt0110293) - IMDb code
- aired : string (2008-12-07)
- credits : string (Andy Kaufman) - writing credits
- lastplayed : string (Y-m-d h:m:s = 2009-04-05 23:16:04)
- album : string (The Joshua Tree)
- artist : list (['U2'])
- votes : string (12345 votes)
- trailer : string (/home/user/trailer.avi)
- dateadded : string (Y-m-d h:m:s = 2009-04-05 23:16:04)
'''

Binary file not shown.

View File

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import Content, re
class EZTV(Content.Content):
category_dict = {
'hot': ('Hot & New', '/', {'page': '/page_%d', 'increase': 1, 'second_page': 1}),
}
baseurl = "https://eztv.it"
headers = [('User-Agent',
'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'),
('Referer', 'https://eztv.it/'), ('Accept-Encoding', 'gzip')]
'''
Weight of source with this searcher provided.
Will be multiplied on default weight.
Default weight is seeds number
'''
sourceWeight = 1
def isLabel(self):
return True
def isScrappable(self):
return False
def isInfoLink(self):
return True
def isPages(self):
return True
def isSearchOption(self):
return False
def get_contentList(self, category, subcategory=None, page=None):
contentList = []
url = self.get_url(category, subcategory, page, self.baseurl)
response = self.makeRequest(url, headers=self.headers)
if None != response and 0 < len(response):
#print response
if category in ['hot']:
contentList = self.mode(response)
#print str(contentList)
return contentList
def mode(self, response):
contentList = []
#print str(result)
num = 51
result = re.compile(
r'''class="epinfo">(.+?)</a>.+?<a href="(magnet.+?)"''',
re.DOTALL).findall(response)
for title, link in result:
#main
info = {}
num = num - 1
original_title = None
year = 0
img = ''
#info
info['label'] = info['title'] = title
info['link'] = link
contentList.append((
int(int(self.sourceWeight) * (int(num))),
original_title, title, int(year), img, info,
))
return contentList

Binary file not shown.

View File

@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import re
import Content
from BeautifulSoup import BeautifulSoup
class FastTorrent(Content.Content):
category_dict = {
#'movies':('Movies', '/popular/'),
'tvshows': (
'TV Shows', '/last-tv-torrent/', {'page': '/last-tv-torrent/%d.html', 'increase': 1, 'second_page': 2}),
'cartoons': ('Cartoons', '/last-multfilm-torrent/',
{'page': '/last-multfilm-torrent/%d.html', 'increase': 1, 'second_page': 2}),
'anime': ('Anime', '/anime/multfilm/', {'page': '/anime/multfilm/%d.html', 'increase': 1, 'second_page': 2}),
'hot': ('Hot & New', '/new-films/', {'page': '/new-films/%d.html', 'increase': 1, 'second_page': 2}),
'genre': {'genre': 'by Genre',
'amime_series': ('Anime Series', '/anime-serialy/multfilm/',
{'page': '/anime-serialy/multfilm/%d.html', 'increase': 1, 'second_page': 2}),
}
#'top':('Top 250 Movies', '/top/'),
}
baseurl = "http://www.fast-torrent.ru"
headers = [('User-Agent',
'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'),
('Referer', baseurl), ('Accept-Encoding', 'gzip'), ('Accept-Language', 'ru,en;q=0.8')]
'''
Weight of source with this searcher provided.
Will be multiplied on default weight.
Default weight is seeds number
'''
sourceWeight = 1
def isLabel(self):
return False
def isScrappable(self):
return True
def isSearchOption(self):
return True
def isInfoLink(self):
return False
def isPages(self):
return True
def get_contentList(self, category, subcategory=None, page=None):
contentList = []
if not subcategory or subcategory == True:
get = self.category_dict[category]
else:
get = self.category_dict[category][subcategory]
if not page or page == 1:
url = self.baseurl + get[1]
else:
property = self.get_property(category, subcategory)
page_url = property['page'] % (property['second_page'] + ((page - 2) * property['increase']))
url = self.baseurl + str(page_url)
response = self.makeRequest(url, headers=self.headers)
if None != response and 0 < len(response):
#print response
if category: # in ['hot']:
contentList = self.popmode(response)
#print str(contentList)
return contentList
def popmode(self, response):
contentList = []
Soup = BeautifulSoup(response.decode('utf-8'))
result = Soup.findAll('div', {'class': 'film-wrap'})
num = 16
for tr in result:
#main
info = {}
num = num - 1
original_title = None
year = 0
h2 = tr.find('h2')
#print str(h2)
label = h2.find('span', {'itemprop': 'name'})
if label:
title = label.text
year = re.compile('\((\d\d\d\d)\)').findall(h2.text)
if year:
year = year[0]
original_title = h2.find('span', {'itemprop': 'alternativeHeadline'})
if original_title:
original_title = original_title.text
else:
try:
title, year, original_title = \
re.compile(u'<h2>(.+?) \((\d\d\d\d)\) <br.*?>\((.+?)\)<', re.DOTALL | re.I).findall(unicode(h2))[0]
except:
try:
title, year = re.compile(u'<h2>(.+?) \((\d\d\d\d)\)<', re.DOTALL | re.I).findall(unicode(h2))[0]
except:
pass
a = tr.find('div', 'film-image').find('a')
link = a.get('href')
img = a.get('style')
if img:
img = img.replace('background: url(', '').rstrip(')')
#info
info['label'] = title
info['link'] = link
info['title'] = title
genre = tr.find('div', 'film-genre').text
tv = [u'Зарубежный сериал', u'Русский сериал', u'Аниме сериалы', u'Мультсериалы']
for i in tv:
if re.search(i, genre):
info['tvshowtitle'] = title
info['year'] = int(year)
contentList.append((
int(int(self.sourceWeight) * (int(num))),
original_title, title, int(year), img, info,
))
return contentList

Binary file not shown.

View File

@ -0,0 +1,248 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import re
import HTMLParser
import Content
from BeautifulSoup import BeautifulSoup
class IMDB(Content.Content):
category_dict = {
'movies': ('Forieng Movies', '/search/title?languages=en|1&title_type=feature&sort=moviemeter,asc'),
'rus_movies': ('Russian Movies', '/search/title?languages=ru|1&title_type=feature&sort=moviemeter,asc'),
'tvshows': ('TV Shows', '/search/title?count=100&title_type=tv_series,mini_series&ref_=gnr_tv_mp'),
'cartoons': ('Cartoons', '/search/title?genres=animation&title_type=feature&sort=moviemeter,asc'),
'anime': ('Anime',
'/search/title?count=100&genres=animation&keywords=anime&num_votes=1000,&explore=title_type&ref_=gnr_kw_an'),
'hot': ('Hot & New', '/search/title?count=100&title_type=feature%2Ctv_series%2Ctv_movie&ref_=nv_ch_mm_1'),
'top': ('Top 250 Movies', '/chart/top/'),
'search': ('[B]Search[/B]', '/find?q=%s&s=tt&ttype=ft'),
'year': {'year': 'by Year', },
'genre': {'genre': 'by Genre',
'action': ('Action', '/genre/action'),
'adventure': ('Adventure', '/genre/adventure'),
'animation': ('Animation', '/genre/animation'),
'biography': ('Biography', '/genre/biography'),
'comedy': ('Comedy', '/genre/comedy'),
'crime': ('Crime', '/genre/crime'),
'documentary': ('Documentary', '/genre/documentary'),
'drama': ('Drama', '/genre/drama'),
'family': ('Family', '/genre/family'),
'fantasy': ('Fantasy', '/genre/fantasy'),
'film_noir': ('Film-Noir', '/genre/film_noir'),
'history': ('History', '/genre/history'),
'horror': ('Horror', '/genre/horror'),
'music': ('Music', '/genre/music'),
'musical': ('Musical', '/genre/musical'),
'mystery': ('Mystery', '/genre/mystery'),
'romance': ('Romance', '/genre/romance'),
'sci_fi': ('Sci-Fi', '/genre/sci_fi'),
'short': ('Short', '/genre/short'),
'sport': ('Sport', '/genre/sport'),
'thriller': ('Thriller', '/genre/thriller'),
'war': ('War', '/genre/war'),
'western': ('Western', '/genre/western'),
}
}
for y in range(2015, 1970, -1):
category_dict['year'][str(y)] = (str(y), '/year/%s/' % str(y))
regex_list = []
baseurl = "http://imdb.com"
headers = [('User-Agent',
'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'),
('Referer', baseurl), ('Accept-Encoding', 'gzip'), ('Accept-Language', 'ru,en;q=0.8')]
'''
Weight of source with this searcher provided.
Will be multiplied on default weight.
Default weight is seeds number
'''
sourceWeight = 2
def isLabel(self):
return False
def isPages(self):
return False
def isSearchOption(self):
return True
def isScrappable(self):
return True
def get_contentList(self, category, subcategory=None, page=None):
contentList = []
url = self.get_url(category, subcategory, page, self.baseurl)
response = self.makeRequest(url, headers=self.headers)
if None != response and 0 < len(response):
#print response
if category in ['top']:
contentList = self.topmode(response)
elif category == 'search':
contentList = self.searchmode(response)
else: #if category in ['genre']:
contentList = self.genremode(response)
#print str(contentList)
return contentList
def searchmode(self, response):
contentList = []
pars = HTMLParser.HTMLParser()
Soup = BeautifulSoup(response)
result = Soup.findAll('tr', {'class': ['findResult odd', 'findResult even']})
num = 250
for tr in result:
#main
info = {}
year = 0
num = num - 1
title = pars.unescape(tr.findAll('a')[1].text)
tdtitle = tr.find('td', 'result_text')
#print str(tdtitle.text.encode('utf-8'))
originaltitle = tr.find('i')
if originaltitle:
originaltitle = originaltitle.text
try:
year = re.compile('\((\d\d\d\d)\)').findall(tdtitle.text)[0]
except:
try:
year = re.compile('(\d\d\d\d)').findall(tdtitle.text)[0]
except:
pass
info['tvshowtitle'] = title
img = self.biggerImg(tr.find('img').get('src'))
contentList.append((
int(int(self.sourceWeight) * (251 - int(num))),
originaltitle, title, int(year), img, info,
))
#print result
return contentList
def genremode(self, response):
contentList = []
pars = HTMLParser.HTMLParser()
Soup = BeautifulSoup(response)
result = Soup.findAll('tr', {'class': ['odd detailed', 'even detailed']})
for tr in result:
#main
info = {}
year = 0
tdtitle = tr.find('td', 'title')
num = tr.find('td', 'number').text.rstrip('.')
originaltitle = None
title = pars.unescape(tdtitle.find('a').text)
try:
year = re.compile('\((\d\d\d\d)\)').findall(tdtitle.find('span', 'year_type').text)[0]
except:
try:
year = re.compile('(\d\d\d\d)').findall(tdtitle.find('span', 'year_type').text)[0]
except:
pass
info['tvshowtitle'] = title
img = self.biggerImg(tr.find('td', 'image').find('img').get('src'))
#info
info['code'] = tr.find('span', 'wlb_wrapper').get('data-tconst')
contentList.append((
int(int(self.sourceWeight) * (251 - int(num))),
originaltitle, title, int(year), img, info,
))
#print result
return contentList
def biggerImg(self, img):
if img and '._' in img:
img = img.split('._')[0] + '._V1_SY_CR1,0,,_AL_.jpg'
return img
def topmode(self, response):
contentList = []
Soup = BeautifulSoup(response)
result = Soup.findAll('tr', {'class': ['odd', 'even']})
for tr in result:
#main
tdtitle = tr.find('td', 'titleColumn')
num = tdtitle.find('span', {'name': 'ir'}).text.rstrip('.')
originaltitle = None
title = tdtitle.find('a').text
year = tdtitle.find('span', {'name': 'rd'}).text.rstrip(')').lstrip('(')
tdposter = tr.find('td', 'posterColumn')
img = self.biggerImg(tdposter.find('img').get('src'))
#info
info = {}
info['title'] = title
info['year'] = int(year)
info['code'] = tr.find('div', 'wlb_ribbon').get('data-tconst')
contentList.append((
int(int(self.sourceWeight) * (251 - int(num))),
originaltitle, title, int(year), img, info,
))
#print result
return contentList
'''
- Video Values:
- genre : string (Comedy)
- year : integer (2009)
- episode : integer (4)
- season : integer (1)
- top250 : integer (192)
- rating : float (6.4) - range is 0..10
- cast : list (Michal C. Hall)
- castandrole : list (Michael C. Hall|Dexter)
- director : string (Dagur Kari)
- mpaa : string (PG-13)
- plot : string (Long Description)
- plotoutline : string (Short Description)
- title : string (Big Fan)
- originaltitle : string (Big Fan)
- sorttitle : string (Big Fan)
- duration : string (3:18)
- studio : string (Warner Bros.)
- tagline : string (An awesome movie) - short description of movie
- writer : string (Robert D. Siegel)
- tvshowtitle : string (Heroes)
- premiered : string (2005-03-04)
- status : string (Continuing) - status of a TVshow
- code : string (tt0110293) - IMDb code
- aired : string (2008-12-07)
- credits : string (Andy Kaufman) - writing credits
- lastplayed : string (Y-m-d h:m:s = 2009-04-05 23:16:04)
- album : string (The Joshua Tree)
- artist : list (['U2'])
- votes : string (12345 votes)
- trailer : string (/home/user/trailer.avi)
- dateadded : string (Y-m-d h:m:s = 2009-04-05 23:16:04)
'''

Binary file not shown.

View File

@ -0,0 +1,310 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import re
import socket
import Content
from BeautifulSoup import BeautifulSoup
class KinoPoisk(Content.Content):
category_dict = {
'tvshows': ('TV Shows', '/top/serial/list/'),
'cartoons': ('Cartoons', '/top/id_genre/14/'),
'search': ('[B]Search[/B]', '/s/type/film/list/1/find/%s/'),
'movies': ('Forieng Movies', '/s/type/film/list/1/m_act[country]/1/m_act[type]/film/'),
'rus_movies': ('Russian Movies', '/s/type/film/list/1/m_act[country]/2/m_act[type]/film/'),
'anime': ('Anime', '/s/type/film/list/1/order/rating/m_act[genre][0]/1750/',),
'hot': ('Hot & New', '/popular/'),
'top': ('Top 250 Movies', '/top/'),
'genre': {'genre': 'by Genre',
'russia': ('Russia & USSR', '/top/rus/list/'),
'biography': ('Biography', '/s/type/film/list/1/m_act[genre][0]/22/'),
'action': ('Action', '/top/id_genre/3/'),
'thriller': ('Thriller', '/top/id_genre/4/'),
'comedy': ('Comedy', '/top/id_genre/6/'),
'drama': ('Drama', '/top/id_genre/8/'),
'romance': ('Romance', '/top/id_genre/7/'),
'horror': ('Horror', '/top/id_genre/1/'),
'sci_fi': ('Sci-Fi', '/top/id_genre/2/'),
'documentary': ('Documentary', '/top/id_genre/12/'),
'cartoonseries': ('Cartoons Series', '/top/mult_serial/list/'),
'cartoonshort': ('Cartoons Short', '/top/short_mult/list/'),
'short': ('Short', '/top/short/list/'),
'male': ('Male', '/top/sex/male/'),
'female': ('Female', '/top/sex/female/'),
}
}
for category in category_dict.keys():
if isinstance(category_dict.get(category), dict):
for subcategory in category_dict.get(category).keys():
if subcategory != category:
x = category_dict[category][subcategory]
if x[1].startswith('/s/type/film/list/'):
category_dict[category][subcategory] = (x[0], x[1] + 'perpage/25/',
{'page': x[1] + 'perpage/25/page/%d/', 'increase': 1,
'second_page': 2})
if not isinstance(category_dict.get(category), dict):
x = category_dict[category]
if x[1].startswith('/s/type/film/list/'):
category_dict[category] = (
x[0], x[1] + 'perpage/25/', {'page': x[1] + 'perpage/25/page/%d/', 'increase': 1, 'second_page': 2})
category_dict['year'] = {'year': 'by Year', }
for y in range(2015, 1970, -1):
category_dict['year'][str(y)] = (str(y), '/s/type/film/list/1/m_act[year]/%s/' % str(y) + 'perpage/25/',
{'page': '/s/type/film/list/1/m_act[year]/%s/' % str(y) + 'perpage/25/page/%d/',
'increase': 1, 'second_page': 2})
baseurl = "http://www.kinopoisk.ru"
headers = [('User-Agent',
'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'),
('Referer', baseurl), ('Accept-Encoding', 'gzip'), ('Accept-Language', 'ru,en;q=0.8')]
'''
Weight of source with this searcher provided.
Will be multiplied on default weight.
Default weight is seeds number
'''
sourceWeight = 2
def isLabel(self):
return False
def isPages(self):
return True
def isSearchOption(self):
return True
def isScrappable(self):
return True
def get_contentList(self, category, subcategory=None, page=None):
socket.setdefaulttimeout(15)
contentList = []
url = self.get_url(category, subcategory, page, self.baseurl)
#print url
response = self.makeRequest(url, headers=self.headers)
if None != response and 0 < len(response):
#print response
if category in ['hot']:
contentList = self.popmode(response)
elif url.startswith(self.baseurl + '/s/type/film/list/'):
contentList = self.infomode(response)
else:
contentList = self.topmode(response)
#print str(contentList)
return contentList
def stripTtl(self, title):
bad_end = [u'\(ТВ\)', u'\(сериал\)', u'\(видео\)']
for code in bad_end:
title = re.sub(u' ' + code + '$', '', title)
return title
def popmode(self, response):
contentList = []
Soup = BeautifulSoup(response)
result = Soup.find('div', 'stat').findAll('div', 'el')
#print str(result)
for tr in result:
#main
a = tr.findAll('a')
num = a[0].text
#print num
info = {}
year = 0
img = ''
originaltitle = tr.find('i')
if originaltitle:
originaltitle = self.stripHtml(self.unescape(originaltitle.text))
title = a[1].text
link = a[1].get('href')
if link:
id = re.compile('/film/(\d+)/').findall(link)
if id:
img = self.id2img(id[0])
try:
title, year = re.compile('(.+?) \((\d\d\d\d)\)', re.DOTALL).findall(a[1].text)[0]
except:
pass
if not year:
try:
title, year = re.compile('(.+?) \(.*(\d\d\d\d)').findall(a[1].text)[0]
info['tvshowtitle'] = title
except:
pass
title = self.stripHtml(self.stripTtl(title))
#info
info['title'] = title
info['year'] = int(year)
contentList.append((
int(int(self.sourceWeight) * (201 - int(num))),
originaltitle, title, int(year), img, info,
))
return contentList
def topmode(self, response):
contentList = []
Soup = BeautifulSoup(response)
result = Soup.find('table', {'cellpadding': '3'}).findAll('tr')[2:]
#print str(result)
for tr in result:
#main
td = tr.findAll('td')
year = 0
info = {}
img = ''
title, originaltitle = None, None
num = td[0].text.rstrip('.')
originaltitle = tr.find('span', 'text-grey')
if originaltitle:
originaltitle = self.stripHtml(self.unescape(originaltitle.text))
a_all = tr.find('a', {'class': 'all'})
if a_all:
link = a_all.get('href')
if link:
id = re.compile('/film/(\d+)/').findall(link)
if id:
img = self.id2img(id[0])
year = re.compile('(.+) \((\d\d\d\d)\)').findall(a_all.text)
if not year:
try:
title, year = re.compile('(.+) \(.*(\d\d\d\d)').findall(a_all.text)[0]
info['tvshowtitle'] = title
except:
title = a_all.text
else:
title, year = year[0]
title = self.stripHtml(self.stripTtl(title))
#info
if originaltitle and not title:
title = originaltitle
originaltitle = None
if title:
info['title'] = title
info['year'] = int(year)
contentList.append((
int(int(self.sourceWeight) * (251 - int(num))),
originaltitle, title, int(year), img, info,
))
return contentList
def infomode(self, response):
contentList = []
Soup = BeautifulSoup(response)
result = Soup.findAll('div', 'info')
#print str(result)
num = 0
for div in result:
#main
info = {}
img = ''
name = div.find('p', 'name')
title = name.find('a').text
link = name.find('a').get('href')
if link:
id = re.compile('/film/(\d+)/').findall(link)
if id:
img = self.id2img(id[0])
year = name.find('span', 'year') if name.find('span', 'year') else 0
if year:
year=year.text
ysplit = year.split(' ')
if len(ysplit) > 1: year = ysplit[0]
title = self.stripHtml(self.unescape(title))
tvshowtitle = re.compile(u'(.+?) \((.+?)\)$').findall(title)
if tvshowtitle and tvshowtitle[0][1] in [u'сериал']:
title = tvshowtitle[0][0]
info['tvshowtitle'] = title
num = num + 1
originaltitle = div.find('span', 'gray')
if originaltitle:
originaltitle = re.match('(.+?), \d', originaltitle.text)
if originaltitle:
originaltitle = self.stripHtml(self.unescape(originaltitle.group(1)))
title = self.stripTtl(title)
#info
info['title'] = title
info['year'] = int(year)
contentList.append((
int(int(self.sourceWeight) * (100 - int(num))),
originaltitle, title, int(year), img, info,
))
return contentList
def id2img(self, id):
if id:
return "http://st.kp.yandex.net/images/film_iphone/iphone360_%s.jpg" % (str(id))
else:
return ''
'''
- Video Values:
- genre : string (Comedy)
- year : integer (2009)
- episode : integer (4)
- season : integer (1)
- top250 : integer (192)
- tracknumber : integer (3)
- rating : float (6.4) - range is 0..10
- watched : depreciated - use playcount instead
- playcount : integer (2) - number of times this item has been played
- overlay : integer (2) - range is 0..8. See GUIListItem.h for values
- cast : list (Michal C. Hall)
- castandrole : list (Michael C. Hall|Dexter)
- director : string (Dagur Kari)
- mpaa : string (PG-13)
- plot : string (Long Description)
- plotoutline : string (Short Description)
- title : string (Big Fan)
- originaltitle : string (Big Fan)
- sorttitle : string (Big Fan)
- duration : string (3:18)
- studio : string (Warner Bros.)
- tagline : string (An awesome movie) - short description of movie
- writer : string (Robert D. Siegel)
- tvshowtitle : string (Heroes)
- premiered : string (2005-03-04)
- status : string (Continuing) - status of a TVshow
- code : string (tt0110293) - IMDb code
- aired : string (2008-12-07)
- credits : string (Andy Kaufman) - writing credits
- lastplayed : string (Y-m-d h:m:s = 2009-04-05 23:16:04)
- album : string (The Joshua Tree)
- artist : list (['U2'])
- votes : string (12345 votes)
- trailer : string (/home/user/trailer.avi)
- dateadded : string (Y-m-d h:m:s = 2009-04-05 23:16:04)
'''

Binary file not shown.

View File

@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
'''
Torrenter plugin for XBMC
Copyright (C) 2012 Vadim Skorba
vadim.skorba@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import Content
from BeautifulSoup import BeautifulSoup
class RiperAM(Content.Content):
category_dict = {
#'movies':('Movies', '/popular/'),
#'tvshows':('TV Shows', '/top/serial/list/'),
#'cartoons':('Cartoons', '/top/id_genre/14/'),
#'anime':('Anime', '/search/title?count=100&genres=animation&keywords=anime&num_votes=1000,&explore=title_type&ref_=gnr_kw_an'),
'hot': ('Hot & New', '/', {'page': '/portal.php?tp=%d', 'increase': 30, 'second_page': 30}),
#'top':('Top 250 Movies', '/top/'),
}
baseurl = "http://www.riper.am"
headers = [('User-Agent',
'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'),
('Referer', 'http://www.riper.am/'), ('Accept-Encoding', 'gzip,deflate,sdch'),
('Accept-Language', 'ru,en;q=0.8')]
'''
Weight of source with this searcher provided.
Will be multiplied on default weight.
Default weight is seeds number
'''
sourceWeight = 1
def isLabel(self):
return True
def isScrappable(self):
return False
def isInfoLink(self):
return True
def isPages(self):
return True
def isSearchOption(self):
return False
def get_contentList(self, category, subcategory=None, page=None):
contentList = []
url = self.get_url(category, subcategory, page, self.baseurl)
response = self.makeRequest(url, headers=self.headers)
if None != response and 0 < len(response):
#print response
if category in ['hot']:
contentList = self.popmode(response)
#print str(contentList)
return contentList
def popmode(self, response):
contentList = []
Soup = BeautifulSoup(response)
result = Soup.findAll('table', 'postbody postbody_portal')
#print str(result)
num = 31
bad_forum = [u'Безопасность', u'Книги и журналы', u'Action & Shooter', u'RPG/MMORPG']
for tr in result:
#main
info = {}
forum = tr.find('div', {'style': 'height:20px;overflow:hidden;'}).find('a').text
if forum and forum in bad_forum:
continue
link = tr.find('div', {'style': 'width:200px;overflow:hidden;'}).find('a').get('href')
num = num - 1
label = tr.find('strong').text
original_title = None
year = 0
title = self.unescape(label)
img = tr.findAll('a')[0].find('img').get('src')
if img:
img = img.replace('.webp', '.jpg')
#info
info['label'] = label
info['link'] = link
info['title'] = title
info['year'] = int(year)
contentList.append((
int(int(self.sourceWeight) * (int(num))),
original_title, title, int(year), img, info,
))
return contentList

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<strings>
<string id="30001">Interface Language</string>
<string id="30002">Lock Folders View Style</string>
<string id="30003">Off</string>
<string id="30004">Save Files To Folder</string>
<string id="30007">Use magnet-links</string>
<string id="30008">Keep downloaded files</string>
<string id="30009">Keep seeding of downloaded files</string>
<string id="30010">Upload speed limit MBits/sec (0 - unlimited)</string>
<string id="30011">Download speed limit MBits/sec (0 - unlimited)</string>
<string id="30012">Use only system libtorrent</string>
<string id="30013">Predownload and play next episode</string>
<string id="30014">Download metadata for Content Lists</string>
<string id="30015">Debug (Developer Mode)</string>
<string id="30016">Confluence (by slng)</string>
<string id="30017">Transperency (by slng)</string>
<string id="30018">Confluence (by DiMartino)</string>
<string id="30019">Confluence (by RussakHH)</string>
<string id="30020">Enable Search History</string>
<string id="30021">Python-Libtorrent (recommended)</string>
<string id="30022">Ace Stream (no magnets)</string>
<string id="30023">P2P Player</string>
<string id="30024">Rest Settings in Programms - AceStream Client</string>
<string id="30025">Search Timeout</string>
<string id="30026">Short (10s)</string>
<string id="30027">Normal (20s)</string>
<string id="30028">Long (30s)</string>
<string id="30029">Predownload subtitles from all folders</string>
<string id="30030">Keep seeding until Kodi restart</string>
<string id="30101">Interface</string>
<string id="30102">P2P Network</string>
<string id="50301">Save path</string>
<string id="50302">Call dialog</string>
<string id="50303">Default</string>
<string id="50304">Path</string>
<string id="50305">Create subdirectory for scrapper</string>
<string id="50312">Host</string>
<string id="50313">Port</string>
<string id="50314">URL</string>
<string id="50315">Login</string>
<string id="50316">Password</string>
<string id="50311">Torrent Client</string>
<string id="30426">Path replacement (remote only)</string>
<string id="30412">Close</string>
<string id="30413">Open Settings</string>
<string id="30414">Torrent Client Browser</string>
</strings>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<strings>
<string id="30001">Язык интерфейса</string>
<string id="30002">Удерживать стиль отображения</string>
<string id="30003">Отключено</string>
<string id="30004">Сохранять файлы в директорию</string>
<string id="30007">Использовать магнет-ссылки</string>
<string id="30008">Хранить загруженные файлы</string>
<string id="30009">Оставаться на раздаче скачанных файлов</string>
<string id="30010">Ограничить скорость раздачи МБит/сек (0 - неограничено)</string>
<string id="30011">Ограничить скорость закачки МБит/сек (0 - неограничено)</string>
<string id="30012">Использовать только пользовательский libtorrent</string>
<string id="30013">Скачать и запустить следующий эпизод</string>
<string id="30014">Загружать мета-данные для Списков Медиа</string>
<string id="30015">Дебаг (Разработчик)</string>
<string id="30016">Confluence (от slng)</string>
<string id="30017">Transperency (от slng)</string>
<string id="30018">Confluence (от DiMartino)</string>
<string id="30019">Confluence (от RussakHH)</string>
<string id="30020">Включить Историю Поиска</string>
<string id="30021">Python-Libtorrent (предпочтительно)</string>
<string id="30022">Ace Stream (без магнит-ссылок)</string>
<string id="30023">P2P Проигрыватель</string>
<string id="30024">Остальные настройки в "Программы - AceStream Client"</string>
<string id="30025">Ожидание ответа сервера при Поиске</string>
<string id="30026">Короткое (10с)</string>
<string id="30027">Среднее (20с)</string>
<string id="30028">Долгое (30с)</string>
<string id="30029">Предзакачать и подключить субтитры</string>
<string id="30030">Сидировать до полного выключения Kodi</string>
<string id="30101">Интерфейс</string>
<string id="30102">P2P Сеть</string>
<string id="50301">Директория для сохранения файлов</string>
<string id="50302">Вызывать диалог</string>
<string id="50303">Задать по умолчанию</string>
<string id="50304">Путь к директории</string>
<string id="50305">Создавать поддиректорию для скрапера</string>
<string id="50312">Хост</string>
<string id="50313">Порт</string>
<string id="50314">URL</string>
<string id="50315">Логин</string>
<string id="50316">Пароль</string>
<string id="50311">Торрент-клиент</string>
<string id="30426">Замена пути (remote only)</string>
<string id="30412">Закрыть</string>
<string id="30413">Открыть Настройки</string>
<string id="30414">Браузер Торрент-клиента</string>
<string id="30146">Статус Плагина</string>
</strings>

View File

Binary file not shown.

View File

@ -0,0 +1,774 @@
# -*- coding: utf-8 -*-
# This is a "local" version of PyXBMCt to be used in standalone addons.
#
# PyXBMCt is a mini-framework for creating XBMC Python addons with arbitrary UI
# made of controls - decendants of xbmcgui.Control class.
# The framework uses image textures from XBMC Confluence skin.
#
# Licence: GPL v.3 http://www.gnu.org/licenses/gpl.html
#
## @package addonwindow
# PyXBMCt framework module
import os
import xbmc, xbmcgui, xbmcaddon
#_addon = xbmcaddon.Addon()
_images = os.path.join(os.path.dirname(__file__), 'textures', 'default')
# Text alighnment constants. Mixed variants are obtained by bit OR (|)
ALIGN_LEFT = 0
ALIGN_RIGHT = 1
ALIGN_CENTER_X = 2
ALIGN_CENTER_Y = 4
ALIGN_CENTER = 6
ALIGN_TRUNCATED = 8
ALIGN_JUSTIFY = 10
# XBMC key action codes.
# More codes at https://github.com/xbmc/xbmc/blob/master/xbmc/guilib/Key.h
## ESC action
ACTION_PREVIOUS_MENU = 10
## Backspace action
ACTION_NAV_BACK = 92
## Left arrow key
ACTION_MOVE_LEFT = 1
## Right arrow key
ACTION_MOVE_RIGHT = 2
## Up arrow key
ACTION_MOVE_UP = 3
## Down arrow key
ACTION_MOVE_DOWN = 4
## Mouse wheel up
ACTION_MOUSE_WHEEL_UP = 104
## Mouse wheel down
ACTION_MOUSE_WHEEL_DOWN = 105
## Mouse drag
ACTION_MOUSE_DRAG = 106
## Mouse move
ACTION_MOUSE_MOVE = 107
def _set_textures(textures={}, kwargs={}):
"""Set texture arguments for controls."""
for texture in textures.keys():
try:
kwargs[texture]
except KeyError:
kwargs[texture] = textures[texture]
class AddonWindowError(Exception):
"""Custom exception."""
pass
class Label(xbmcgui.ControlLabel):
"""ControlLabel class.
Parameters:
label: string or unicode - text string.
font: string - font used for label text. (e.g. 'font13')
textColor: hexstring - color of enabled label's label. (e.g. '0xFFFFFFFF')
disabledColor: hexstring - color of disabled label's label. (e.g. '0xFFFF3300')
alignment: integer - alignment of label - *Note, see xbfont.h
hasPath: bool - True=stores a path / False=no path.
angle: integer - angle of control. (+ rotates CCW, - rotates CW)"
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.label = Label('Status', angle=45)
"""
def __new__(cls, *args, **kwargs):
return super(Label, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class FadeLabel(xbmcgui.ControlFadeLabel):
"""Control that scrolls label text.
Parameters:
font: string - font used for label text. (e.g. 'font13')
textColor: hexstring - color of fadelabel's labels. (e.g. '0xFFFFFFFF')
_alignment: integer - alignment of label - *Note, see xbfont.h
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.fadelabel = FadeLabel(textColor='0xFFFFFFFF')
"""
def __new__(cls, *args, **kwargs):
return super(FadeLabel, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class TextBox(xbmcgui.ControlTextBox):
"""ControlTextBox class.
Parameters:
font: string - font used for text. (e.g. 'font13')
textColor: hexstring - color of textbox's text. (e.g. '0xFFFFFFFF')
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.textbox = TextBox(textColor='0xFFFFFFFF')
"""
def __new__(cls, *args, **kwargs):
return super(TextBox, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class Image(xbmcgui.ControlImage):
"""ControlImage class.
Parameters:
filename: string - image filename.
colorKey: hexString - (example, '0xFFFF3300')
aspectRatio: integer - (values 0 = stretch (default), 1 = scale up (crops), 2 = scale down (black bars)
colorDiffuse: hexString - (example, '0xC0FF0000' (red tint)).
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.image = Image('d:\images\picture.jpg', aspectRatio=2)
"""
def __new__(cls, *args, **kwargs):
return super(Image, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class Button(xbmcgui.ControlButton):
"""ControlButton class.
Parameters:
label: string or unicode - text string.
focusTexture: string - filename for focus texture.
noFocusTexture: string - filename for no focus texture.
textOffsetX: integer - x offset of label.
textOffsetY: integer - y offset of label.
alignment: integer - alignment of label - *Note, see xbfont.h
font: string - font used for label text. (e.g. 'font13')
textColor: hexstring - color of enabled button's label. (e.g. '0xFFFFFFFF')
disabledColor: hexstring - color of disabled button's label. (e.g. '0xFFFF3300')
angle: integer - angle of control. (+ rotates CCW, - rotates CW)
shadowColor: hexstring - color of button's label's shadow. (e.g. '0xFF000000')
focusedColor: hexstring - color of focused button's label. (e.g. '0xFF00FFFF')
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.button = Button('Status', font='font14')
"""
def __new__(cls, *args, **kwargs):
textures = {'focusTexture': os.path.join(_images, 'Button', 'KeyboardKey.png'),
'noFocusTexture': os.path.join(_images, 'Button', 'KeyboardKeyNF.png')}
_set_textures(textures, kwargs)
try:
kwargs['alignment']
except KeyError:
kwargs['alignment'] = ALIGN_CENTER
return super(Button, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class RadioButton(xbmcgui.ControlRadioButton):
"""ControlRadioButton class.
Parameters:
label: string or unicode - text string.
focusTexture: string - filename for focus texture.
noFocusTexture: string - filename for no focus texture.
textOffsetX: integer - x offset of label.
textOffsetY: integer - y offset of label.
_alignment: integer - alignment of label - *Note, see xbfont.h
font: string - font used for label text. (e.g. 'font13')
textColor: hexstring - color of enabled radio button's label. (e.g. '0xFFFFFFFF')
disabledColor: hexstring - color of disabled radio button's label. (e.g. '0xFFFF3300')
angle: integer - angle of control. (+ rotates CCW, - rotates CW)
shadowColor: hexstring - color of radio button's label's shadow. (e.g. '0xFF000000')
focusedColor: hexstring - color of focused radio button's label. (e.g. '0xFF00FFFF')
focusOnTexture: string - filename for radio focused/checked texture.
noFocusOnTexture: string - filename for radio not focused/checked texture.
focusOffTexture: string - filename for radio focused/unchecked texture.
noFocusOffTexture: string - filename for radio not focused/unchecked texture.
Note: To customize RadioButton all 4 abovementioned textures need to be provided.
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.radiobutton = RadioButton('Status', font='font14')
"""
def __new__(cls, *args, **kwargs):
if int(xbmc.getInfoLabel('System.BuildVersion')[:2]) >= 13:
textures = {'focusTexture': os.path.join(_images, 'RadioButton', 'MenuItemFO.png'),
'noFocusTexture': os.path.join(_images, 'RadioButton', 'MenuItemNF.png'),
'focusOnTexture': os.path.join(_images, 'RadioButton', 'radiobutton-focus.png'),
'noFocusOnTexture': os.path.join(_images, 'RadioButton', 'radiobutton-focus.png'),
'focusOffTexture': os.path.join(_images, 'RadioButton', 'radiobutton-nofocus.png'),
'noFocusOffTexture': os.path.join(_images, 'RadioButton', 'radiobutton-nofocus.png')}
else: # This is for compatibility with Frodo and earlier versions.
textures = {'focusTexture': os.path.join(_images, 'RadioButton', 'MenuItemFO.png'),
'noFocusTexture': os.path.join(_images, 'RadioButton', 'MenuItemNF.png'),
'TextureRadioFocus': os.path.join(_images, 'RadioButton', 'radiobutton-focus.png'),
'TextureRadioNoFocus': os.path.join(_images, 'RadioButton', 'radiobutton-nofocus.png')}
_set_textures(textures, kwargs)
return super(RadioButton, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class Edit(xbmcgui.ControlEdit):
"""
ControlEdit class.
Edit(label[, font, textColor, disabledColor, alignment, focusTexture, noFocusTexture])
Parameters:
label : string or unicode - text string.
font : [opt] string - font used for label text. (e.g. 'font13')
textColor : [opt] hexstring - color of enabled label's label. (e.g. '0xFFFFFFFF')
disabledColor : [opt] hexstring - color of disabled label's label. (e.g. '0xFFFF3300')
_alignment : [opt] integer - alignment of label - *Note, see xbfont.h
focusTexture : [opt] string - filename for focus texture.
noFocusTexture : [opt] string - filename for no focus texture.
isPassword : [opt] bool - if true, mask text value.
*Note, You can use the above as keywords for arguments and skip certain optional arguments.
Once you use a keyword, all following arguments require the keyword.
After you create the control, you need to add it to the window with palceControl().
example:
- self.edit = Edit('Status')
"""
def __new__(cls, *args, **kwargs):
textures = {'focusTexture': os.path.join(_images, 'Edit', 'button-focus.png'),
'noFocusTexture': os.path.join(_images, 'Edit', 'black-back2.png')}
_set_textures(textures, kwargs)
return super(Edit, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class List(xbmcgui.ControlList):
"""ControlList class.
Parameters:
font: string - font used for items label. (e.g. 'font13')
textColor: hexstring - color of items label. (e.g. '0xFFFFFFFF')
buttonTexture: string - filename for no focus texture.
buttonFocusTexture: string - filename for focus texture.
selectedColor: integer - x offset of label.
_imageWidth: integer - width of items icon or thumbnail.
_imageHeight: integer - height of items icon or thumbnail.
_itemTextXOffset: integer - x offset of items label.
_itemTextYOffset: integer - y offset of items label.
_itemHeight: integer - height of items.
_space: integer - space between items.
_alignmentY: integer - Y-axis alignment of items label - *Note, see xbfont.h
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.cList = List('font14', space=5)
"""
def __new__(cls, *args, **kwargs):
textures = {'buttonTexture': os.path.join(_images, 'List', 'MenuItemNF.png'),
'buttonFocusTexture': os.path.join(_images, 'List', 'MenuItemFO.png')}
_set_textures(textures, kwargs)
return super(List, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class Slider(xbmcgui.ControlSlider):
"""ControlSlider class.
Parameters:
textureback: string - image filename.
texture: string - image filename.
texturefocus: string - image filename.
Note:
After you create the control, you need to add it to the window with placeControl().
Example:
self.slider = Slider()
"""
def __new__(cls, *args, **kwargs):
textures = {'textureback': os.path.join(_images, 'Slider', 'osd_slider_bg.png'),
'texture': os.path.join(_images, 'Slider', 'osd_slider_nibNF.png'),
'texturefocus': os.path.join(_images, 'Slider', 'osd_slider_nib.png')}
_set_textures(textures, kwargs)
return super(Slider, cls).__new__(cls, -10, -10, 1, 1, *args, **kwargs)
class _AbstractWindow(object):
"""
Top-level control window.
The control windows serves as a parent widget for other XBMC UI controls
much like Tkinter.Tk or PyQt QWidget class.
This is an abstract class which is not supposed to be instantiated directly
and will raise exeptions.
This class is a basic "skeleton" for a control window.
"""
def __init__(self):
"""Constructor method."""
self.actions_connected = []
self.controls_connected = []
def setGeometry(self, width_, height_, rows_, columns_, pos_x=-1, pos_y=-1):
"""
Set width, height, Grid layout, and coordinates (optional) for a new control window.
Parameters:
width_, height_: widgh and height of the created window.
rows_, columns_: rows and colums of the Grid layout to place controls on.
pos_x, pos_y (optional): coordinates of the top left corner of the window.
If pos_x and pos_y are not privided, the window will be placed
at the center of the screen.
Example:
self.setGeometry(400, 500, 5, 4)
"""
self.width = width_
self.height = height_
self.rows = rows_
self.columns = columns_
if pos_x > 0 and pos_y > 0:
self.x = pos_x
self.y = pos_y
else:
self.x = 640 - self.width/2
self.y = 360 - self.height/2
self.setGrid()
def setGrid(self):
"""
Set window grid layout of rows * columns.
This is a helper method not to be called directly.
"""
self.grid_x = self.x
self.grid_y = self.y
self.tile_width = self.width/self.columns
self.tile_height = self.height/self.rows
def placeControl(self, control, row, column, rowspan=1, columnspan=1, pad_x=5, pad_y=5):
"""
Place a control within the window grid layout.
pad_x, pad_y: horisontal and vertical padding for control's
size and aspect adjustments. Negative values can be used
to make a control overlap with grid cells next to it, if necessary.
Raises AddonWindowError if a grid has not yet been set.
Example:
self.placeControl(self.label, 0, 1)
"""
try:
control_x = (self.grid_x + self.tile_width * column) + pad_x
control_y = (self.grid_y + self.tile_height * row) + pad_y
control_width = self.tile_width * columnspan - 2 * pad_x
control_height = self.tile_height * rowspan - 2 * pad_y
except AttributeError:
raise AddonWindowError('Window geometry is not defined! Call setGeometry first.')
control.setPosition(control_x, control_y)
control.setWidth(control_width)
control.setHeight(control_height)
self.addControl(control)
self.setAnimation(control)
def getX(self):
"""Get X coordinate of the top-left corner of the window."""
try:
return self.x
except AttributeError:
raise AddonWindowError('Window geometry is not defined! Call setGeometry first.')
def getY(self):
"""Get Y coordinate of the top-left corner of the window."""
try:
return self.y
except AttributeError:
raise AddonWindowError('Window geometry is not defined! Call setGeometry first.')
def getWindowWidth(self):
"""Get window width."""
try:
return self.width
except AttributeError:
raise AddonWindowError('Window geometry is not defined! Call setGeometry first.')
def getWindowHeight(self):
"""Get window height."""
try:
return self.height
except AttributeError:
raise AddonWindowError('Window geometry is not defined! Call setGeometry first.')
def getRows(self):
"""
Get grid rows count.
Raises AddonWindowError if a grid has not yet been set.
"""
try:
return self.rows
except AttributeError:
raise AddonWindowError('Grid layot is not set! Call setGeometry first.')
def getColumns(self):
"""
Get grid columns count.
Raises AddonWindowError if a grid has not yet been set.
"""
try:
return self.columns
except AttributeError:
raise AddonWindowError('Grid layout is not set! Call setGeometry first.')
def connect(self, event, function):
"""
Connect an event to a function.
An event can be an inctance of a Control object or an integer key action code.
Several basic key action codes are provided by PyXBMCT. More action codes can be found at
https://github.com/xbmc/xbmc/blob/master/xbmc/guilib/Key.h
You can connect the following Controls: Button, RadioButton and List. Other Controls do not
generate any control events when activated so their connections won't work.
To catch Slider events you need to connect the following key actions:
ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT and ACTION_MOUSE_DRAG, and do a check
whether the Slider is focused.
"function" parameter is a function or a method to be executed. Note that you must provide
a function object [without brackets ()], not a function call!
lambda can be used as a function to call another function or method with parameters.
Examples:
self.connect(self.exit_button, self.close)
or
self.connect(ACTION_NAV_BACK, self.close)
"""
try:
self.disconnect(event)
except AddonWindowError:
if type(event) == int:
self.actions_connected.append([event, function])
else:
self.controls_connected.append([event, function])
def connectEventList(self, events, function):
"""
Connect a list of controls/action codes to a function.
See connect docstring for more info.
"""
[self.connect(event, function) for event in events]
def disconnect(self, event):
"""
Disconnect an event from a function.
An event can be an inctance of a Control object or an integer key action code
which has previously been connected to a function or a method.
Raises AddonWindowError if an event is not connected to any function.
Examples:
self.disconnect(self.exit_button)
or
self.disconnect(ACTION_NAV_BACK)
"""
if type(event) == int:
event_list = self.actions_connected
else:
event_list = self.controls_connected
for index in range(len(event_list)):
if event == event_list[index][0]:
event_list.pop(index)
break
else:
raise AddonWindowError('The action or control %s is not connected!' % event)
def disconnectEventList(self, events):
"""
Disconnect a list of controls/action codes from functions.
See disconnect docstring for more info.
Raises AddonWindowError if at least one event in the list
is not connected to any function.
"""
[self.disconnect(event) for event in events]
def executeConnected(self, event, connected_list):
"""
Execute a connected event (an action or a control).
This is a helper method not to be called directly.
"""
for item in connected_list:
if event == item[0]:
item[1]()
break
def setAnimation(self, control):
"""
This method is called to set animation properties for all controls
added to the current addon window instance - both built-in controls
(window background, title bar etc.) and controls added with placeControl().
It receives a control instance as the 2nd positional argument (besides self).
By default the method does nothing, i.e. no animation is set for controls.
To add animation you need to re-implement this menthod in your child class.
E.g:
def setAnimation(self, control):
control.setAnimations([('WindowOpen', 'effect=fade start=0 end=100 time=1000',),
('WindowClose', 'effect=fade start=100 end=0 time=1000',)])
"""
pass
class _AddonWindow(_AbstractWindow):
"""
Top-level control window.
The control windows serves as a parent widget for other XBMC UI controls
much like Tkinter.Tk or PyQt QWidget class.
This is an abstract class which is not supposed to be instantiated directly
and will raise exeptions. It is designed to be implemented in a grand-child class
with the second inheritance from xbmcgui.Window or xbmcgui.WindowDialog
in a direct child class.
This class provides a control window with a background and a header
similar to top-level widgets of desktop UI frameworks.
"""
def __init__(self, title=''):
"""Constructor method."""
super(_AddonWindow, self).__init__()
self.setFrame(title)
def setFrame(self, title):
"""
Define paths to images for window background and title background textures,
and set control position adjustment constants used in setGrid.
This is a helper method not to be called directly.
"""
# Window background image
self.background_img = os.path.join(_images, 'AddonWindow', 'ContentPanel.png')
# Background for a window header
self.title_background_img = os.path.join(_images, 'AddonWindow', 'dialogheader.png')
# Horisontal adjustment for a header background if the main background has transparent edges.
self.X_MARGIN = 5
# Vertical adjustment for a header background if the main background has transparent edges
self.Y_MARGIN = 5
# Header position adjustment if the main backround has visible borders.
self.Y_SHIFT = 4
# The height of a window header (for the title background and the title label).
self.HEADER_HEIGHT = 35
self.background = xbmcgui.ControlImage(-10, -10, 1, 1, self.background_img)
self.addControl(self.background)
self.setAnimation(self.background)
self.title_background = xbmcgui.ControlImage(-10, -10, 1, 1, self.title_background_img)
self.addControl(self.title_background)
self.setAnimation(self.title_background)
self.title_bar = xbmcgui.ControlLabel(-10, -10, 1, 1, title, alignment=ALIGN_CENTER, textColor='0xFFFFA500',
font='font13_title')
self.addControl(self.title_bar)
self.setAnimation(self.title_bar)
self.window_close_button = xbmcgui.ControlButton(-100, -100, 60, 30, '',
focusTexture=os.path.join(_images, 'AddonWindow', 'DialogCloseButton-focus.png'),
noFocusTexture=os.path.join(_images, 'AddonWindow', 'DialogCloseButton.png'))
self.addControl(self.window_close_button)
self.setAnimation(self.window_close_button)
def setGeometry(self, width_, height_, rows_, columns_, pos_x=-1, pos_y=-1, padding=5):
"""
Set width, height, Grid layout, and coordinates (optional) for a new control window.
Parameters:
width_, height_: widgh and height of the created window.
rows_, columns_: rows and colums of the Grid layout to place controls on.
pos_x, pos_y (optional): coordinates of the top left corner of the window.
If pos_x and pos_y are not privided, the window will be placed
at the center of the screen.
padding (optional): padding between outer edges of the window and
controls placed on it.
Example:
self.setGeometry(400, 500, 5, 4)
"""
self.win_padding = padding
super(_AddonWindow, self).setGeometry(width_, height_, rows_, columns_, pos_x, pos_y)
self.background.setPosition(self.x, self.y)
self.background.setWidth(self.width)
self.background.setHeight(self.height)
self.title_background.setPosition(self.x + self.X_MARGIN, self.y + self.Y_MARGIN + self.Y_SHIFT)
self.title_background.setWidth(self.width - 2 * self.X_MARGIN)
self.title_background.setHeight(self.HEADER_HEIGHT)
self.title_bar.setPosition(self.x + self.X_MARGIN, self.y + self.Y_MARGIN + self.Y_SHIFT)
self.title_bar.setWidth(self.width - 2 * self.X_MARGIN)
self.title_bar.setHeight(self.HEADER_HEIGHT)
self.window_close_button.setPosition(self.x + self.width - 70, self.y + self.Y_MARGIN + self.Y_SHIFT)
def setGrid(self):
"""
Set window grid layout of rows * columns.
This is a helper method not to be called directly.
"""
self.grid_x = self.x + self.X_MARGIN + self.win_padding
self.grid_y = self.y + self.Y_MARGIN + self.Y_SHIFT + self.HEADER_HEIGHT + self.win_padding
self.tile_width = (self.width - 2 * (self.X_MARGIN + self.win_padding))/self.columns
self.tile_height = (
self.height - self.HEADER_HEIGHT - self.Y_SHIFT - 2 * (self.Y_MARGIN + self.win_padding))/self.rows
def setWindowTitle(self, title=''):
"""
Set window title.
This method must be called AFTER (!!!) setGeometry(),
otherwise there is some werid bug with all skin text labels set to the 'title' text.
Example:
self.setWindowTitle('My Cool Addon')
"""
self.title_bar.setLabel(title)
def getWindowTitle(self):
"""Get window title."""
return self.title_bar.getLabel()
class _FullWindow(xbmcgui.Window):
"""An abstract class to define window event processing."""
def onAction(self, action):
"""
Catch button actions.
Note that, despite being compared to an integer,
action is an instance of xbmcgui.Action class.
"""
if action == ACTION_PREVIOUS_MENU:
self.close()
else:
self.executeConnected(action, self.actions_connected)
def onControl(self, control):
"""
Catch activated controls.
Control is an instance of xbmcgui.Control class.
"""
if control == self.window_close_button:
self.close()
else:
self.executeConnected(control, self.controls_connected)
class _DialogWindow(xbmcgui.WindowDialog):
"""An abstract class to define window event processing."""
def onAction(self, action):
"""
Catch button actions.
Note that, despite being compared to an integer,
action is an instance of xbmcgui.Action class.
"""
if action == ACTION_PREVIOUS_MENU:
self.close()
else:
self.executeConnected(action, self.actions_connected)
def onControl(self, control):
"""
Catch activated controls.
Control is an instance of xbmcgui.Control class.
"""
if control == self.window_close_button:
self.close()
else:
self.executeConnected(control, self.controls_connected)
class BlankFullWindow(_FullWindow, _AbstractWindow):
"""
Addon UI container with a solid background.
This is a blank window with a black background and without any elements whatsoever.
The decoration and layout are completely up to an addon developer.
The window controls can hide under video or music visualization.
Window ID can be passed on class instantiation an agrument
but __init__ must have the 2nd fake argument, e.g:
def __init__(self, *args)
Minimal example:
addon = MyAddon('My Cool Addon')
addon.setGeometry(400, 300, 4, 3)
addon.doModal()
"""
pass
class BlankDialogWindow(_DialogWindow, _AbstractWindow):
"""
Addon UI container with a transparent background.
This is a blank window with a transparent background and without any elements whatsoever.
The decoration and layout are completely up to an addon developer.
The window controls are always displayed over video or music visualization.
Minimal example:
addon = MyAddon('My Cool Addon')
addon.setGeometry(400, 300, 4, 3)
addon.doModal()
"""
pass
class AddonFullWindow(_FullWindow, _AddonWindow):
"""
Addon UI container with a solid background.
Control window is displayed on top of the main background image - self.main_bg.
Video and music visualization are displayed unhindered.
Window ID can be passed on class instantiation as the 2nd positional agrument
but __init__ must have the 3rd fake argument, e.g:
def __init__(self, title='', *args)
Minimal example:
addon = MyAddon('My Cool Addon')
addon.setGeometry(400, 300, 4, 3)
addon.doModal()
"""
def __new__(cls, title='', *args, **kwargs):
return super(AddonFullWindow, cls).__new__(cls, *args, **kwargs)
def setFrame(self, title):
"""
Set the image for for the fullscreen background.
"""
# Image for the fullscreen background.
self.main_bg_img = os.path.join(_images, 'AddonWindow', 'SKINDEFAULT.jpg')
# Fullscreen background image control.
self.main_bg = xbmcgui.ControlImage(1, 1, 1280, 720, self.main_bg_img)
self.addControl(self.main_bg)
super(AddonFullWindow, self).setFrame(title)
def setBackground(self, image=''):
"""
Set the main bacground to an image file.
image: path to an image file as str.
Example:
self.setBackground('d:\images\bacground.png')
"""
self.main_bg.setImage(image)
class AddonDialogWindow(_DialogWindow, _AddonWindow):
"""
Addon UI container with a transparent background.
Control window is displayed on top of XBMC UI,
including video an music visualization!
Minimal example:
addon = MyAddon('My Cool Addon')
addon.setGeometry(400, 300, 4, 3)
addon.doModal()
"""
pass

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,164 @@
# -*- coding: utf-8 -*-
import os
import time
import pickle
import threading
import zipfile
import xbmc
import xbmcvfs
import xbmcgui
import Localization
from net import HTTP
try:
from sqlite3 import dbapi2 as sqlite
except:
from pysqlite2 import dbapi2 as sqlite
rtrCache_lock = threading.RLock()
class Cache:
def __init__(self, name, version, expire=0, size=0, step=100):
self.name = name
self.version = version
self._connect()
if expire:
self.expire(expire)
if size:
self.size(size, step)
def get(self, token, callback, *param):
cur = self.db.cursor()
cur.execute('select expire,data from cache where id=? limit 1', (token, ))
row = cur.fetchone()
cur.close()
if row:
if row[0] and row[0] < int(time.time()):
pass
else:
try:
obj = pickle.loads(row[1])
except:
pass
else:
return obj
response = callback(*param)
if response[0]:
obj = sqlite.Binary(pickle.dumps(response[1]))
curtime = int(time.time())
cur = self.db.cursor()
if isinstance(response[0], bool):
cur.execute('replace into cache(id,addtime,expire,data) values(?,?,?,?)', (token, curtime, None, obj))
else:
cur.execute('replace into cache(id,addtime,expire,data) values(?,?,?,?)',
(token, curtime, curtime + response[0], obj))
self.db.commit()
cur.close()
return response[1]
def expire(self, expire):
# with rtrCache_lock:
cur = self.db.cursor()
cur.execute('delete from cache where addtime<?', (int(time.time()) - expire, ))
self.db.commit()
cur.close()
def size(self, size, step=100):
# with rtrCache_lock:
while True:
if os.path.getsize(self.filename) < size:
break
cur = self.db.cursor()
cur.execute('select id from cache order by addtime asc limit ?', (step, ))
rows = cur.fetchall()
if not rows:
cur.close()
break
cur.execute('delete from cache where id in (' + ','.join(len(rows) * '?') + ')', [x[0] for x in rows])
self.db.commit()
cur.close()
def flush(self):
# with rtrCache_lock:
cur = self.db.cursor()
cur.execute('delete from cache')
self.db.commit()
cur.close()
def _connect(self):
with rtrCache_lock:
dirname = xbmc.translatePath('special://temp')
for subdir in ('xbmcup', 'plugin.video.torrenter'):
dirname = os.path.join(dirname, subdir)
if not xbmcvfs.exists(dirname):
xbmcvfs.mkdir(dirname)
self.filename = os.path.join(dirname, self.name)
first = False
if not xbmcvfs.exists(self.filename):
first = True
self.db = sqlite.connect(self.filename, check_same_thread=False)
if not first:
cur = self.db.cursor()
try:
cur.execute('select version from db_ver')
row = cur.fetchone()
if not row or float(row[0]) != self.version:
cur.execute('drop table cache')
cur.execute('drop table if exists db_ver')
first = True
except:
cur.execute('drop table cache')
first = True
self.db.commit()
cur.close()
if first and not self.first_time():
cur = self.db.cursor()
cur.execute('pragma auto_vacuum=1')
cur.execute('create table cache(id varchar(255) unique, addtime integer, expire integer, data blob)')
cur.execute('create index time on cache(addtime asc)')
cur.execute('create table db_ver(version real)')
cur.execute('insert into db_ver(version) values(?)', (self.version, ))
self.db.commit()
cur.close()
def first_time(self):
scrapers = {'tvdb': 'TheTVDB.com', 'tmdb': 'TheMovieDB.org', 'kinopoisk': 'KinoPoisk.ru'}
ok = xbmcgui.Dialog().yesno(Localization.localize('Content Lists'),
Localization.localize('Do you want to preload full metadata?') + ' (%s)' % (
scrapers[os.path.basename(self.filename).split('.')[0]]),
Localization.localize('It is highly recommended!'))
if ok:
return self.download()
else:
return False
def download(self):
dirname = os.path.dirname(self.filename)
print self.filename
zipname = os.path.basename(self.filename).rstrip('.db') + '.zip'
url = 'http://www.tat-store.ru/torrenter/' + zipname
self.http = HTTP()
response = self.http.fetch(url, download=os.path.join(dirname, zipname), progress=True)
if response.error:
return False
try:
filezip = zipfile.ZipFile(os.path.join(dirname, zipname), 'r')
filezip.extractall(dirname)
filezip.close()
except:
return False
return True

Binary file not shown.

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
# encoding: utf-8
"""
StringMatcher.py
ported from python-Levenshtein
[https://github.com/miohtama/python-Levenshtein]
"""
from Levenshtein import *
from warnings import warn
class StringMatcher:
"""A SequenceMatcher-like class built on the top of Levenshtein"""
def _reset_cache(self):
self._ratio = self._distance = None
self._opcodes = self._editops = self._matching_blocks = None
def __init__(self, isjunk=None, seq1='', seq2=''):
if isjunk:
warn("isjunk not NOT implemented, it will be ignored")
self._str1, self._str2 = seq1, seq2
self._reset_cache()
def set_seqs(self, seq1, seq2):
self._str1, self._str2 = seq1, seq2
self._reset_cache()
def set_seq1(self, seq1):
self._str1 = seq1
self._reset_cache()
def set_seq2(self, seq2):
self._str2 = seq2
self._reset_cache()
def get_opcodes(self):
if not self._opcodes:
if self._editops:
self._opcodes = opcodes(self._editops, self._str1, self._str2)
else:
self._opcodes = opcodes(self._str1, self._str2)
return self._opcodes
def get_editops(self):
if not self._editops:
if self._opcodes:
self._editops = editops(self._opcodes, self._str1, self._str2)
else:
self._editops = editops(self._str1, self._str2)
return self._editops
def get_matching_blocks(self):
if not self._matching_blocks:
self._matching_blocks = matching_blocks(self.get_opcodes(),
self._str1, self._str2)
return self._matching_blocks
def ratio(self):
if not self._ratio:
self._ratio = ratio(self._str1, self._str2)
return self._ratio
def quick_ratio(self):
# This is usually quick enough :o)
if not self._ratio:
self._ratio = ratio(self._str1, self._str2)
return self._ratio
def real_quick_ratio(self):
len1, len2 = len(self._str1), len(self._str2)
return 2.0 * min(len1, len2) / (len1 + len2)
def distance(self):
if not self._distance:
self._distance = distance(self._str1, self._str2)
return self._distance

Some files were not shown because too many files have changed in this diff Show More