From 7b54ba45a17548f2fab08895c604ac6d14eadfd6 Mon Sep 17 00:00:00 2001 From: Pavel Suha Date: Fri, 25 Apr 2025 16:31:48 +0200 Subject: [PATCH] test --- addon.xml | 17 + changelog.txt | 208 +++++++ install.sh | 7 + lib/__init__.py | 0 lib/__pycache__/aadecode.cpython-38.opt-1.pyc | Bin 0 -> 4185 bytes .../cloudflare.cpython-38.opt-1.pyc | Bin 0 -> 4357 bytes lib/__pycache__/resolver.cpython-38.opt-1.pyc | Bin 0 -> 8385 bytes lib/__pycache__/search.cpython-38.opt-1.pyc | Bin 0 -> 2037 bytes lib/__pycache__/util.cpython-38.opt-1.pyc | Bin 0 -> 9327 bytes lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc | Bin 0 -> 14830 bytes lib/aadecode.py | 209 +++++++ lib/cloudflare.py | 189 ++++++ .../__pycache__/provider.cpython-38.opt-1.pyc | Bin 0 -> 8403 bytes .../xbmcprovider.cpython-38.opt-1.pyc | Bin 0 -> 15618 bytes lib/contentprovider/provider.py | 230 +++++++ lib/contentprovider/xbmcprovider.py | 464 ++++++++++++++ lib/crypto/__init__.py | 0 .../__pycache__/__init__.cpython-38.opt-1.pyc | Bin 0 -> 185 bytes .../__pycache__/md5crypt.cpython-38.opt-1.pyc | Bin 0 -> 1776 bytes lib/crypto/md5crypt.py | 75 +++ lib/resolver.py | 289 +++++++++ lib/search.py | 76 +++ lib/server/__init__.py | 31 + .../anyfilesresolver.cpython-38.opt-1.pyc | Bin 0 -> 2966 bytes .../dailymotionresolver.cpython-38.opt-1.pyc | Bin 0 -> 1697 bytes .../divxstageresolver.cpython-38.opt-1.pyc | Bin 0 -> 860 bytes .../eserialresolver.cpython-38.opt-1.pyc | Bin 0 -> 952 bytes .../exashareresolver.cpython-38.opt-1.pyc | Bin 0 -> 1120 bytes .../flashxresolver.cpython-38.opt-1.pyc | Bin 0 -> 1353 bytes .../gosuparkresolver.cpython-38.opt-1.pyc | Bin 0 -> 937 bytes .../hqqresolver.cpython-38.opt-1.pyc | Bin 0 -> 6215 bytes .../koukejseresolver.cpython-38.opt-1.pyc | Bin 0 -> 934 bytes .../koukniresolver.cpython-38.opt-1.pyc | Bin 0 -> 2249 bytes .../ksetresolver.cpython-38.opt-1.pyc | Bin 0 -> 1430 bytes .../__pycache__/letwatch.cpython-38.opt-1.pyc | Bin 0 -> 866 bytes .../mixturevideoresolver.cpython-38.opt-1.pyc | Bin 0 -> 1951 bytes .../moevideoresolver.cpython-38.opt-1.pyc | Bin 0 -> 1098 bytes .../mojevideosk.cpython-38.opt-1.pyc | Bin 0 -> 1014 bytes .../movshareresolver.cpython-38.opt-1.pyc | Bin 0 -> 830 bytes .../munkvideoresolver.cpython-38.opt-1.pyc | Bin 0 -> 1229 bytes .../myviruresolver.cpython-38.opt-1.pyc | Bin 0 -> 1284 bytes .../nahnojiresolver.cpython-38.opt-1.pyc | Bin 0 -> 935 bytes .../novamovresovler.cpython-38.opt-1.pyc | Bin 0 -> 1015 bytes .../__pycache__/openload.cpython-38.opt-1.pyc | Bin 0 -> 8060 bytes .../playedtoresolver.cpython-38.opt-1.pyc | Bin 0 -> 993 bytes .../playmdresolver.cpython-38.opt-1.pyc | Bin 0 -> 929 bytes ...blicvideohostresolver.cpython-38.opt-1.pyc | Bin 0 -> 1337 bytes .../putlockerresolver.cpython-38.opt-1.pyc | Bin 0 -> 3253 bytes .../rutuberesolver.cpython-38.opt-1.pyc | Bin 0 -> 1060 bytes .../servertipczresolver.cpython-38.opt-1.pyc | Bin 0 -> 968 bytes ...ledujuserialyresolver.cpython-38.opt-1.pyc | Bin 0 -> 1271 bytes .../stagevuresolver.cpython-38.opt-1.pyc | Bin 0 -> 855 bytes .../streamcloudresolver.cpython-38.opt-1.pyc | Bin 0 -> 1322 bytes .../streamintoresolver.cpython-38.opt-1.pyc | Bin 0 -> 1033 bytes .../streamujtvresolver.cpython-38.opt-1.pyc | Bin 0 -> 2077 bytes .../trivialresolver.cpython-38.opt-1.pyc | Bin 0 -> 672 bytes .../videobbresolver.cpython-38.opt-1.pyc | Bin 0 -> 4201 bytes .../videomailresolver.cpython-38.opt-1.pyc | Bin 0 -> 1512 bytes .../videonetresolver.cpython-38.opt-1.pyc | Bin 0 -> 1588 bytes .../videoweedresolver.cpython-38.opt-1.pyc | Bin 0 -> 1181 bytes .../videozerresolver.cpython-38.opt-1.pyc | Bin 0 -> 4188 bytes .../__pycache__/videram.cpython-38.opt-1.pyc | Bin 0 -> 871 bytes .../vimeoresolver.cpython-38.opt-1.pyc | Bin 0 -> 1279 bytes .../vkontakteresolver.cpython-38.opt-1.pyc | Bin 0 -> 1603 bytes .../vuuzlaresolver.cpython-38.opt-1.pyc | Bin 0 -> 947 bytes .../youtuberesolver.cpython-38.opt-1.pyc | Bin 0 -> 14598 bytes .../__pycache__/youwatch.cpython-38.opt-1.pyc | Bin 0 -> 1006 bytes .../zideonlresolver.cpython-38.opt-1.pyc | Bin 0 -> 885 bytes .../zkouknitoresolver.cpython-38.opt-1.pyc | Bin 0 -> 1077 bytes lib/server/anyfilesresolver.py | 104 ++++ lib/server/dailymotionresolver.py | 91 +++ lib/server/divxstageresolver.py | 36 ++ lib/server/eserialresolver.py | 27 + lib/server/exashareresolver.py | 53 ++ lib/server/flashxresolver.py | 60 ++ lib/server/gosuparkresolver.py | 42 ++ lib/server/hqqresolver.py | 220 +++++++ lib/server/koukejseresolver.py | 36 ++ lib/server/koukniresolver.py | 88 +++ lib/server/ksetresolver.py | 54 ++ lib/server/letwatch.py | 43 ++ lib/server/mixturevideoresolver.py | 68 +++ lib/server/moevideoresolver.py | 41 ++ lib/server/mojevideosk.py | 47 ++ lib/server/movshareresolver.py | 35 ++ lib/server/munkvideoresolver.py | 45 ++ lib/server/myviruresolver.py | 59 ++ lib/server/nahnojiresolver.py | 29 + lib/server/novamovresovler.py | 36 ++ lib/server/openload.py | 276 +++++++++ lib/server/playedtoresolver.py | 42 ++ lib/server/playmdresolver.py | 28 + lib/server/publicvideohostresolver.py | 48 ++ lib/server/putlockerresolver.py | 159 +++++ lib/server/rutuberesolver.py | 41 ++ lib/server/servertipczresolver.py | 40 ++ lib/server/sledujuserialyresolver.py | 50 ++ lib/server/stagevuresolver.py | 36 ++ lib/server/streamcloudresolver.py | 61 ++ lib/server/streamintoresolver.py | 43 ++ lib/server/streamujtvresolver.py | 78 +++ lib/server/trivialresolver.py | 22 + lib/server/videobbresolver.py | 158 +++++ lib/server/videomailresolver.py | 56 ++ lib/server/videonetresolver.py | 67 ++ lib/server/videoweedresolver.py | 40 ++ lib/server/videozerresolver.py | 158 +++++ lib/server/videram.py | 39 ++ lib/server/vimeoresolver.py | 53 ++ lib/server/vkontakteresolver.py | 57 ++ lib/server/vuuzlaresolver.py | 29 + lib/server/youtuberesolver.py | 577 ++++++++++++++++++ lib/server/youwatch.py | 41 ++ lib/server/zideonlresolver.py | 43 ++ lib/server/zkouknitoresolver.py | 45 ++ lib/storageserverdummy.py | 30 + .../googletracker.cpython-38.opt-1.pyc | Bin 0 -> 2252 bytes .../__pycache__/tracker.cpython-38.opt-1.pyc | Bin 0 -> 6181 bytes .../__pycache__/utmain.cpython-38.opt-1.pyc | Bin 0 -> 2212 bytes lib/usage/googletracker.py | 74 +++ lib/usage/tracker.py | 231 +++++++ lib/usage/utmain.py | 107 ++++ lib/util.py | 359 +++++++++++ lib/xbmcutil.py | 560 +++++++++++++++++ .../resource.language.cs_cz/strings.po | 82 +++ .../resource.language.en_gb/strings.po | 82 +++ .../resource.language.sk_sk/strings.po | 82 +++ .../skins/default/720p/captcha-dialog.xml | 68 +++ .../skins/default/media/ContentPanel.png | Bin 0 -> 103846 bytes .../skins/default/media/button-focus.png | Bin 0 -> 6231 bytes .../skins/default/media/button-nofocus.png | Bin 0 -> 2935 bytes resources/skins/default/skin.xml | 13 + 132 files changed, 6984 insertions(+) create mode 100644 addon.xml create mode 100644 changelog.txt create mode 100644 install.sh create mode 100644 lib/__init__.py create mode 100644 lib/__pycache__/aadecode.cpython-38.opt-1.pyc create mode 100644 lib/__pycache__/cloudflare.cpython-38.opt-1.pyc create mode 100644 lib/__pycache__/resolver.cpython-38.opt-1.pyc create mode 100644 lib/__pycache__/search.cpython-38.opt-1.pyc create mode 100644 lib/__pycache__/util.cpython-38.opt-1.pyc create mode 100644 lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc create mode 100644 lib/aadecode.py create mode 100644 lib/cloudflare.py create mode 100644 lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc create mode 100644 lib/contentprovider/__pycache__/xbmcprovider.cpython-38.opt-1.pyc create mode 100644 lib/contentprovider/provider.py create mode 100644 lib/contentprovider/xbmcprovider.py create mode 100644 lib/crypto/__init__.py create mode 100644 lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc create mode 100644 lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc create mode 100644 lib/crypto/md5crypt.py create mode 100644 lib/resolver.py create mode 100644 lib/search.py create mode 100644 lib/server/__init__.py create mode 100644 lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/eserialresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/flashxresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/gosuparkresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/koukejseresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/koukniresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/letwatch.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/mojevideosk.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/nahnojiresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/openload.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/playedtoresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/playmdresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/publicvideohostresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/putlockerresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/servertipczresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/sledujuserialyresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/videobbresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/videonetresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/videoweedresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/videram.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/vimeoresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/vkontakteresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/__pycache__/zkouknitoresolver.cpython-38.opt-1.pyc create mode 100644 lib/server/anyfilesresolver.py create mode 100644 lib/server/dailymotionresolver.py create mode 100644 lib/server/divxstageresolver.py create mode 100644 lib/server/eserialresolver.py create mode 100644 lib/server/exashareresolver.py create mode 100644 lib/server/flashxresolver.py create mode 100644 lib/server/gosuparkresolver.py create mode 100644 lib/server/hqqresolver.py create mode 100644 lib/server/koukejseresolver.py create mode 100644 lib/server/koukniresolver.py create mode 100644 lib/server/ksetresolver.py create mode 100644 lib/server/letwatch.py create mode 100644 lib/server/mixturevideoresolver.py create mode 100644 lib/server/moevideoresolver.py create mode 100644 lib/server/mojevideosk.py create mode 100644 lib/server/movshareresolver.py create mode 100644 lib/server/munkvideoresolver.py create mode 100644 lib/server/myviruresolver.py create mode 100644 lib/server/nahnojiresolver.py create mode 100644 lib/server/novamovresovler.py create mode 100644 lib/server/openload.py create mode 100644 lib/server/playedtoresolver.py create mode 100644 lib/server/playmdresolver.py create mode 100644 lib/server/publicvideohostresolver.py create mode 100644 lib/server/putlockerresolver.py create mode 100644 lib/server/rutuberesolver.py create mode 100644 lib/server/servertipczresolver.py create mode 100644 lib/server/sledujuserialyresolver.py create mode 100644 lib/server/stagevuresolver.py create mode 100644 lib/server/streamcloudresolver.py create mode 100644 lib/server/streamintoresolver.py create mode 100644 lib/server/streamujtvresolver.py create mode 100644 lib/server/trivialresolver.py create mode 100644 lib/server/videobbresolver.py create mode 100644 lib/server/videomailresolver.py create mode 100644 lib/server/videonetresolver.py create mode 100644 lib/server/videoweedresolver.py create mode 100644 lib/server/videozerresolver.py create mode 100644 lib/server/videram.py create mode 100644 lib/server/vimeoresolver.py create mode 100644 lib/server/vkontakteresolver.py create mode 100644 lib/server/vuuzlaresolver.py create mode 100644 lib/server/youtuberesolver.py create mode 100644 lib/server/youwatch.py create mode 100644 lib/server/zideonlresolver.py create mode 100644 lib/server/zkouknitoresolver.py create mode 100644 lib/storageserverdummy.py create mode 100644 lib/usage/__pycache__/googletracker.cpython-38.opt-1.pyc create mode 100644 lib/usage/__pycache__/tracker.cpython-38.opt-1.pyc create mode 100644 lib/usage/__pycache__/utmain.cpython-38.opt-1.pyc create mode 100644 lib/usage/googletracker.py create mode 100644 lib/usage/tracker.py create mode 100644 lib/usage/utmain.py create mode 100644 lib/util.py create mode 100644 lib/xbmcutil.py create mode 100644 resources/language/resource.language.cs_cz/strings.po create mode 100644 resources/language/resource.language.en_gb/strings.po create mode 100644 resources/language/resource.language.sk_sk/strings.po create mode 100644 resources/skins/default/720p/captcha-dialog.xml create mode 100644 resources/skins/default/media/ContentPanel.png create mode 100644 resources/skins/default/media/button-focus.png create mode 100644 resources/skins/default/media/button-nofocus.png create mode 100644 resources/skins/default/skin.xml diff --git a/addon.xml b/addon.xml new file mode 100644 index 0000000..f1f1208 --- /dev/null +++ b/addon.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + Kodi CZ/SK plugins common python modules mainly for resolving stream URLs 2025 + Common python modules pack that is used by kodi cz/sk plugins + GNU GPL + all + + diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..b8e90ff --- /dev/null +++ b/changelog.txt @@ -0,0 +1,208 @@ +[B]1.6.62:[/B] +- fix dailymotion auto quality stream +[B]1.6.61:[/B] +- fix youtube signature +[B]1.6.60:[/B] +- fix cloudflare scrape protection +[B]1.6.59:[/B] +- fix youtube signature +[B]1.6.58:[/B] +- preserve compatibility with older kodi versions +[B]1.6.57:[/B] +- youtube: age content +[B]1.6.56:[/B] +- fix youtube signature +- recognize youtu.be scheme +[B]1.6.55:[/B] +- duration is now recognized as infolabels attribute +[B]1.6.54:[/B] +- add dailymotion resolver +[B]1.6.52:[/B] +- fix streamujtv resolver +[B]1.6.48:[/B] +- fixed video download code which was failing when there were subtitles to +download +[B]1.6.45:[/B] +- support for filtering by language (needs to be passed by addon as 'lang' setting) +[B]1.6.40:[/B] +- add on_init hook to ContentProvider +- fix hqq & nahnoji parsers, added sk translations (jose1711) +[B]1.6.39:[/B] +- Fixed Exashare, Openload and Youwatch resolvers +- New resolver for Videram +[B]1.6.37:[/B] +- (for all plugins) search items can be now edited (Search Similar) +[B]1.6.36:[/B] +- fix streamujtv resolver to play videos with missing lang +[B]1.6.34:[/B] +- fix usage tracker issue on windows +[B]1.6.30:[/B] +- fix froto compatibility issue +[B]1.6.29:[/B] +- providers have now injected 'lang' - current language of xbmc/kodi user +[B]1.6.24:[/B] +- resolvers: support for displaying additional info (audio track language, etc) +- streamujtv: return all possible streams (incl. audio track combinations) #223 +[B]1.6.16:[/B] +- support for disabled subtitles by plugin setting +- #217 - incorrect item title on some skins +[B]1.6.15:[/B] +- fix streamujtv resolver to correctly handle subtitles +- another possible fix of showing captcha on windows +[B]1.6.14:[/B] +- fixed playedto resolver +[B]1.6.12:[/B] +- fixed streamujtv resolver +- better support for dynamic context menu items +[B]1.6.11:[/B] +- fixed putlocker and flashx resolvers (ibv) +[B]1.6.10:[/B] +- attempt to fix showing captcha image on windows (when there are special +chars in profile path) +[B]1.6.9:[/B] +- attempt to fix showing captcha image on windows (when there are special +chars in xbmc profile path) +- added anyfiles resolver +[B]1.6.8:[/B] +- added streamujtv resolver +[B]1.6.7:[/B] +- add ContentProvider.findstreams - wrapper for resolver.findstreams +[B]1.6.6:[/B] +- added koukejse resolver +- improved @cached decorator +- captcha image is now downloaded to file before showing up captcha dialog +[B]1.6.5:[/B] +- fixed youtube resolver (mx3L) +- download is now more robust #174 +[B]1.6.4:[/B] +- fixed youtube resolver (by mx3L) +- added concurrent function (by mx3L) +[B]1.6.2:[/B] +- updated zkouknito resolver, added kset, publicvideohost resolvers (by mx3L) +[B]1.6.1:[/B] +- fixed downloading of files with special chars +- support for @cached decorator for provider methods +[B]1.5.6:[/B] +- fixed koukni - works back with mp4 +[B]1.5.5:[/B] +- contentprovider fixes (does not fail when item title starts with $, supports +infolabels +[B]1.5.4:[/B] +- fixed koukni resolver to properly resolve rtmp +- removed providers - moved to plugins +[B]1.5.2:[/B] +- fixed youtube resolver +[B]1.5.1:[/B] +- when downloaded file does not have extension, it is autoappened (.mp4) +[B]1.5.0:[/B] +- added resolvers flashx, videomail (ivo, mx3L) +- resolvers can now return headers (if required for playing/downloading) +- ContentProviders can now raise ResolveException +- ulozto provider raises ResolveException when ulozto server is overloaded +[B]1.4.6:[/B] +- fixed youtube resolver +- subtitles now work in munkvideo resolver +[B]1.4.5:[/B] +- ulozto provider can now search for all file types +[B]1.4.4:[/B] +- added munkvideo resolver +[B]1.4.3:[/B] +- fixed eserial resolver to load subs correctly #136 +[B]1.3.12:[/B] +- fixed eserial resolver to support subtitles and work for more streams +- fixed quality filtering: we always return exactly 1 stream unless user wants to select himself +[B]1.3.11:[/B] +- sledujuserialy resolver resolves mp4 videos as well +- fixed #127 - searching does not work on frodo beta1 +- added eserialresolver +- resolvers have now priority (higher=resolver is asked fist) +[B]1.3.10:[/B] +- fixed #127 - searching does not work on frodo beta1 +- fixed ulozto to show search results again +- fixed sledujuserialy resolver (to resolve mp4 videos) +[B]1.3.9:[/B] +- got rid of old resolver API +- fixed ulozto provider to be able to retrieve non-VIP files again +- hellspy provider has now configurable server url and supports http://stiahnito.sk (slovak mirror) +[B]1.3.8:[/B] +- resolver.findstreams - prevent multiple resolving same url +- added sledujuserialy resolver +[B]1.3.7:[/B] +- fixed vkontakte resolver +[B]1.3.6:[/B] +- fixed vkontakte resolver +[B]1.3.4:[/B] +- fixed youtube resolver +- added streamcloud & gosuparg resolvers (Ivo) +[B]1.3.3:[/B] +- ulozto and hellspy fixed: login() failed when called repeatedly +[B]1.3.2:[/B] +- fixed bezvadata content provider +- added support for delayed file download (bezvadata uses it) +[B]1.3.1:[/B] +- vkontakte resolver now supports 720p videos +- added videonet resolver (thanks to Ivo Brhel), rutube resolver +[B]1.3.0:[/B] +- introduced ContentProvider API +- util.py splitted to util.py and xbmcutil.py (so we're now able to run tests in non-xbmc environment) +[B]1.2.8:[/B] +- added nahnoji and moevideo resolvers by Ivo Brhel +- fixed putlocker (by Ivo), videozer & videobb resolvers +[B]1.2.7:[/B] +- youtube resolver fixes +- all resolvers now return non-empty 'subs' key within resolve2 or findstreams +[B]1.2.6:[/B] +- resolvers can now retrieve subtitles +- added koukni.cz resovler (supports subtitles) +[B]1.2.2:[/B] +- bugfixes +- search.py now supports multiple search items within one addon +[B]1.2.0:[/B] +- added method to prelace czech diaktitic chars +- fixed divByZero when downloading, made notifications more robust +[B]1.1.7:[/B] +- new API for stream resolving - quality aware API +- youtube and vkontakte now implement quality aware API, others have dummy +impl only +[B]1.1.6:[/B] +- another small fix of youtuberesolver +- fixed videobb and videozer resovlers, thanks to Lynx187 +[B]1.1.5:[/B] +- added higher level searching API +- fixed youtuberesolver +[B]1.1.4:[/B] +- added dependency on script.usage.tracker +[B]1.1.3:[/B] +- download notification: added EST +[B]1.1.2:[/B] +- saved searches can be removed now +[B]1.1.0:[/B] +- added new API for retrieving common icons from github +- fixed downloading to report dialog on finish (when not playing) +[B]1.0.8:[/B] +- added new API for downloading, browsing local folders +- fixed string encoding issues +- fixed zideo resolver +[B]1.0.7:[/B] +- added servertip.cz resolver +- fixed youtube +- added api for search history +- added simplejson dependency +[B]1.0.6:[/B] +- added zideo.nl +- stagevu - do not fail if movie was removed +- videobb - proper resolving +[B]1.0.5:[/B] +- added novamov, youtube, trivial - special does nothing +- fixed megavideo and videozer +[B]1.0.4:[/B] +- added megavideo, videozer resolvers +- fixed zkouknito +[B]1.0.3:[/B] +- added other resolvers +- added simple debug logging +- all resovlers renamed not to collide with other 3rd party on Camelot +[B]1.0.1:[/B] + - added putlocker resolver +[B]1.0.0:[/B] +Initial version diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..dd30da9 --- /dev/null +++ b/install.sh @@ -0,0 +1,7 @@ +#/bin/sh +# do NOT use this script from Kodi addons directory, it is intented for development only +DESTDIR=~/.kodi/addons/script.module.stream.resolver + +rm -rf ${DESTDIR} +mkdir -p ${DESTDIR} +cp -a * ${DESTDIR} diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/__pycache__/aadecode.cpython-38.opt-1.pyc b/lib/__pycache__/aadecode.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e82e1049aca75f809eaf36045424dd3ceed0d2d1 GIT binary patch literal 4185 zcmaJ^O>i7X74Gi&+1c6Ej}=FjLm-os!5XV9+i^ndI1X_v6iAFhY&n7T%FAebEUmQK zUG>bGu*{~QWPV%)RW8a0II-deCk`POa1J?e1S-cl8ZP4EE9HWM@Ad3TE0@D;O;7jh zey?X+M^~Z5v#2uYMlkgs1R*G$runMPbq$O?Igf<}K zWLS=2;{$SHWgM%7WZ2?~g?178Cyssc;r*Zfh+jx5-=64?h0& zUw(9OnP33t@wBAS+RY;#J%JXW8Ry_cK*3VUnmR9Q}afx zAu-6&z^KXh^$BJTIXNxXqDm0-Px$~O5pkCBB6qkcCUAEq_nbVuc!!*;$lFzN@!+DJ zXlw2A4my%|%|yg3F}L`-siv}CSHw7gE#9?OSl3Q0HJ8{uk(h~dq2BQ=8MD7AxD zD{RQrTx-@EsiQ(5HwvYn%baRA+8fKEO3mdU3e#K*zy}FZTh>-;ag-XEP7NUbQODw=02D^Gydey--hK`L}yL}#~0=FAd!1aW3 z;|^D^OQUNg)*T+5j2WEI*5IZsKF5}x!U6D}L6XEi%*cswpy=|=W76#Aq}6q#-Ob~m zT&%NXZkwxrV5R`uwq!pNhZ(G>ZZRoH`y~dsqdrQC(gC|ZO7hY^!@7AqO^oj+u#x0w zuI^|uO1y?2JV1rkn8_%tn0NRl`WSOqF+ac-i*RhZaJt24Fqu;7;D$# zl-E;^7B+7N^>+9u>1dIs?jAh1U!@ACTzfNO3-K^83zC<7&v@V%ZmLulYnU) zoTd@^mmIwqLYjZL|ssJ4fO|>7(v2G!zo!OexC)pzC!Fzd< zBt?pEeL84b0G6X5MiI8$hC@A221Lm$(E2@K%JFW2Gr;!D=KZ7qI)m6(SnT!+sJ{#( zFoRNCFHc^gJz)C=$eM4Maso8ua$*4S{W_a}23?BS8+6gtTW4gtJUu(}m9k&<{qju5 z@ckK0gnq1?QB;qqXQ=Vebj)XGt}UhfR7V{Dpd(Jrbi}nKMfJ@5;=}y)ax|0RKB6eE z`y;6pwd%E4GY9<`bwWz`*L=||QqN&wYKFkEdKTS`UzDCwp+a!1y{ta3ZD~4{azv+E zBUWF^mZ*}cLWPRvX3DFj5zU@u6;#*5I4Ys$k%K{MqCN@L3EG2dJmphErOsZWR7WuU z4({kpGz=60T}($55uwM$6iDOXSp*e|Ab}|uXM*DQ1V4zCE@p{74hUunXn-yzR*&z? zuEQ~6Ul~KC@@`JO48Fv=h(R_)$lgZaM#YU&hZN^evY4%zz=23miLX2AmDrNVzLX<@ zQqi-e4W1b?2d+6?3u(emkSsKIUr3BzE`E^6x+^1>82#UK%r1X;8UF{-d|0XdI zvPIWuJO_JhFsJ#6mC()&X&1T}V=uyK9@_s*a#&&QuE@zERxoe|Oa<#Gh4e!>-fD!t zC$_N9=pmLMnaHt2U`hWQ?bwYANj`D55Cw?Uo0q}!OnU*^*waybUSt1}qFmSHf1pZkrcGIV znyR|2P;B~0J(O-V({>1vJH)3>vC;H4g1EZsz3h3NoZD>2-gz(OZl^Fb;};cuh&oG+ zE(h%>3{-VBwddYmym0AKYPA#ylUggP*={MKR+h<5qL&)gRfU?d5&LeIuTb2?ai|(P zaik`FTghDxS85HNs!C2!kF%s@;Dyu|$sBP}QRVswpHLv_vj10@issQUF<}s0DASn2 zMc)KPT#$vv;~0;EKsHKJ^fiS*AW+N!!Ax-wJ!B@)8Q*pOY_`Dys$X)e_M@Mt{}_#6 zBdkKWhA2a(#%<+ZRHYuV$>q|PhZ8@)=UM5K+1{{bgDW#j+= literal 0 HcmV?d00001 diff --git a/lib/__pycache__/cloudflare.cpython-38.opt-1.pyc b/lib/__pycache__/cloudflare.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbf9106739f85861321c9d578a62f9ec779a05ff GIT binary patch literal 4357 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 cCZQ4RxpK7Jd%yP%+U+L8c>aCsXcMcyX>)xz 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,kOlXYoBogJ z5$9-?U1B^xNZND+Up$E9 z5sZ#Ukf@&;#6o1O3hINvvg!@8{_Q}_$9Yq_5Cx4&B3)1;Oa%=HrK__^5HFP;2%set ze@F|PNMLe}i$=8sPJ!+ZD==tF$pYz>xNxRYD+iVMY+>ijE>%t|knDd3pMvVV(K7B0 z!Zd=ActjH4_{98ubA@Cr%bS9rdS%XU8l4&H~MOzmn3F8FKOZb#cPWp31 zJ0;Ciq>tn2iHZ3rS!m1_0ELN4v(}gmmnI(0E|kOZ(h95GH=|?trgk62jT^I&jbu8+L{J8Rlx5;KQ0n4DIuL0jb)Xp( z=3!PPbS&`WI=JrMN$4Y!4m3&}NV*7Giks?~Fg#iow?+)r3jH$zqhLg_)XD&e)kQr8 z0SY@^f!-@hCy2{YB;9DfS`*z{DI{y8uNPB|s`by@En>Ac4iy0#M+ln0J6FrvAtRB|+3Nq_;6>%RMm+=@RL+ zQc^3Cx2n`87*_=4a5g9}$#fmAY*GpzmciU)lt`c*<93PPtXiqF$lqXC$Lln40T|Fr zXz4nu{5A=^51HqDN2*lNs|OMa z;gGBW%EcmRSGCiM8=z7Zut^u65EP$~^xR}G2@($-z&!NL$%ju!_=4}8d4WKfw8{%Y zTA*0S!>MvzP!I}Uk+U2meE5ecU#X@C(iCQ4TS4HM%bBqXQ$%svw~ zv^ivD05Z=MzlHWLaCI?b6}=>})>#5qFdk^(GR+e^T8o&?Y4j_s-b<=YdtkYA7R>HY-dxrK)j zkg~=CIbjbls_ETTK);uJjFOIF)treF zs0@)(ufXmQ0lKt3WeIYPe1t$C(91A;cd?h;)0y*hmFEfcDuPF%8h-;2-OB9jx=kAE zZr|qLQE4HxNh+GNQDe=B)ZJis;%4eW!4W@g8OqIrDZ;f)OlGKsUV9&2q?G>iVSKus zR<_R|dl_?VM8i@P3ZYdK2mKCdNI-6C%|%-Zj~ec%fl(etrO=;JdzvC?rMNu`!w5M> zoH16C#05;D4AlH**F5FzvzA8?M-(lM#}3pm@~>6UBvaHXlPqZkbH%=!Y(l95ahIAY z0I1YQz6W7bSh?hC-dB+*iZ3gqz>k8jpGZdp)%j3-lctoANN09631gX>6SZa7&Eg~) z^J#4=QgkL*_ItEVtR6+gEc*T4awQC^A=e##Z#{@aT$|HX@dFytqxRD+lI>R6YMDHt z`Va;i%8;>{n z<|3Jl;%_mci=jw7r0^;6@M9E6k&}x{@Wf|bG!QuiL_`|~o=pa1+W{k~cNf&j{z0%f z!JA!@rgs+o2PT;W8={4T3fRJda=MPSGhnnHU?a6|4ikf*7j_eNSfO6VVJX2QRQn~_ zcCXqKd$fCrMF`qF1BmxD|4}^~T!GyXl#@8O_yFX-8j2jA4;pp6F;wTG`G!z2t{fh0 zau7ugQH^s^k_+NokmFIAV)cPoEo9VE(~4$uimv5T_;kLn#l&dk0X&&)m{ORu?irl5c@Gd6bjn=|=)N6zJE@~8b? zeWMcIeuH{g%=d{8F&dAaVIay2lD#3>E%BRZ5`RkVh+HeuLo>Pq=rGS1EAM^NDnBvFwDfy(9=b= zbn9&iK)Pnjh2FBxK_hn2_sHKrj<-84CYj$$sDrD3kTcK&o503gc#5AQ@%s=Cz^-`URgZisbn0h2jcUd1ntt*j zXV&kdU=l(|^gxfdLg7(kFI}OajCVZS3))|h$P?Eod5e->N(czz0wshN-Qc2{fQ@y~EvK1Lh$mdt@h3ZXBMcWnB#Chtt zNC^ebRGdo1hQwp4&QU@^1m$NdZ&iCK-=elhl#qp~tZf~b|1nj6Ldh&8B(4Cg0e>Ffah_IUlC t?YV<#FXi=kY3~`Y&%|iY@~o^0Kgjm%KFfq}^cDX8!yWN5nPIFk^gnJ50<-`C literal 0 HcmV?d00001 diff --git a/lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc b/lib/__pycache__/xbmcutil.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bdcf05a6ca236b622c096ae42e898dae49e65012 GIT binary patch literal 14830 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 literal 0 HcmV?d00001 diff --git a/lib/aadecode.py b/lib/aadecode.py new file mode 100644 index 0000000..92135a2 --- /dev/null +++ b/lib/aadecode.py @@ -0,0 +1,209 @@ +#-*- coding: utf-8 -*- +# +# author : Djeman +# Updated by Shani-08 (https://github.com/Shani-08/ShaniXBMCWork2) + +import re + +class AADecoder(object): + def __init__(self, aa_encoded_data): + self.encoded_str = aa_encoded_data.replace('/*´∇`*/','') + + self.b = ["(c^_^o)", "(゚Θ゚)", "((o^_^o) - (゚Θ゚))", "(o^_^o)", + "(゚ー゚)", "((゚ー゚) + (゚Θ゚))", "((o^_^o) +(o^_^o))", "((゚ー゚) + (o^_^o))", + "((゚ー゚) + (゚ー゚))", "((゚ー゚) + (゚ー゚) + (゚Θ゚))", "(゚Д゚) .゚ω゚ノ", "(゚Д゚) .゚Θ゚ノ", + "(゚Д゚) ['c']", "(゚Д゚) .゚ー゚ノ", "(゚Д゚) .゚Д゚ノ", "(゚Д゚) [゚Θ゚]"] + + def is_aaencoded(self): + idx = self.encoded_str.find("゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); ") + if idx == -1: + return False + + if self.encoded_str.find("(゚Д゚)[゚o゚]) (゚Θ゚)) ('_');", idx) == -1: + return False + + return True + + def base_repr(self, number, base=2, padding=0): + digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + if base > len(digits): + base = len(digits) + + num = abs(number) + res = [] + while num: + res.append(digits[num % base]) + num //= base + if padding: + res.append('0' * padding) + if number < 0: + res.append('-') + return ''.join(reversed(res or '0')) + + def decode_char(self, enc_char, radix): + end_char = "+ " + str_char = "" + while enc_char != '': + found = False + + if not found: + for i in range(len(self.b)): + enc_char=enc_char.replace(self.b[i], str(i)) + + startpos=0 + findClose=True + balance=1 + result=[] + if enc_char.startswith('('): + l=0 + + for t in enc_char[1:]: + l+=1 + if findClose and t==')': + balance-=1; + if balance==0: + result+=[enc_char[startpos:l+1]] + findClose=False + continue + elif not findClose and t=='(': + startpos=l + findClose=True + balance=1 + continue + elif t=='(': + balance+=1 + + + if result is None or len(result)==0: + return "" + else: + + for r in result: + value = self.decode_digit(r, radix) + if value == "": + return "" + else: + str_char += value + + return str_char + + enc_char = enc_char[len(end_char):] + + return str_char + + + + def decode_digit(self, enc_int, radix): + + rr = '(\(.+?\)\))\+' + rerr=enc_int.split('))+') + v = '' + + #new mode + + for c in rerr: + + if len(c)>0: + if c.strip().endswith('+'): + c=c.strip()[:-1] + + startbrackets=len(c)-len(c.replace('(','')) + endbrackets=len(c)-len(c.replace(')','')) + + if startbrackets>endbrackets: + c+=')'*startbrackets-endbrackets + + c = c.replace('!+[]','1') + c = c.replace('-~','1+') + c = c.replace('[]','0') + + v+=str(eval(c)) + + return v + + mode = 0 + value = 0 + + while enc_int != '': + found = False + for i in range(len(self.b)): + if enc_int.find(self.b[i]) == 0: + if mode == 0: + value += i + else: + value -= i + enc_int = enc_int[len(self.b[i]):] + found = True + break + + if not found: + return "" + + enc_int = re.sub('^\s+|\s+$', '', enc_int) + if enc_int.find("+") == 0: + mode = 0 + else: + mode = 1 + + enc_int = enc_int[1:] + enc_int = re.sub('^\s+|\s+$', '', enc_int) + + return self.base_repr(value, radix) + + def decode(self): + + self.encoded_str = re.sub('^\s+|\s+$', '', self.encoded_str) + + # get data + pattern = (r"\(゚Д゚\)\[゚o゚\]\+ (.+?)\(゚Д゚\)\[゚o゚\]\)") + result = re.search(pattern, self.encoded_str, re.DOTALL) + if result is None: + print("AADecoder: data not found") + return False + + data = result.group(1) + + # hex decode string + begin_char = "(゚Д゚)[゚ε゚]+" + alt_char = "(o゚ー゚o)+ " + + out = '' + + while data != '': + # Check new char + if data.find(begin_char) != 0: + print("AADecoder: data not found") + return False + + data = data[len(begin_char):] + + # Find encoded char + enc_char = "" + if data.find(begin_char) == -1: + enc_char = data + data = "" + else: + enc_char = data[:data.find(begin_char)] + data = data[len(enc_char):] + + + radix = 8 + # Detect radix 16 for utf8 char + if enc_char.find(alt_char) == 0: + enc_char = enc_char[len(alt_char):] + radix = 16 + + str_char = self.decode_char(enc_char, radix) + + if str_char == "": + print("no match : ") + print(data + "\nout = " + out + "\n") + return False + + out += chr(int(str_char, radix)) + + if out == "": + print("no match : " + data) + return False + + return out diff --git a/lib/cloudflare.py b/lib/cloudflare.py new file mode 100644 index 0000000..855d90d --- /dev/null +++ b/lib/cloudflare.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 tknorris (Derived from Mikey1234's & Lambda's) +# +# This Program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This Program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with XBMC; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# http://www.gnu.org/copyleft/gpl.html +# +# This code is a derivative of the YouTube plugin for XBMC and associated works +# released under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 3 + +import re +import urllib.request, urllib.parse, urllib.error +import urllib.parse +import util +import xbmc + +MAX_TRIES = 3 +COMPONENT = __name__ + + +class NoRedirection(urllib.request.HTTPErrorProcessor): + + def http_response(self, request, response): + util.info('[CF] Stopping Redirect') + return response + + https_response = http_response + +def solve_equation(equation): + try: + offset = (1 if equation[0] == '+' else 0) + ev = equation.replace('!+[]', '1').replace('!![]', + '1').replace('[]', '0').replace('(', 'str(')[offset:] + ev = re.sub(r'^str', 'float', re.sub(r'\/(.)str', r'/\1float', ev)) + # util.debug('[CF] eval: {0}'.format(ev)) + return float(eval(ev)) + except: + pass + + +def solve(url, cj, user_agent=None, wait=True): + if user_agent is None: + user_agent = util.UA + headers = {'User-Agent': user_agent, 'Referer': url} + if cj is not None: + try: + cj.load(ignore_discard=True) + except: + pass + opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) + urllib.request.install_opener(opener) + + scheme = urllib.parse.urlparse(url).scheme + domain = urllib.parse.urlparse(url).hostname + request = urllib.request.Request(url) + for key in headers: + request.add_header(key, headers[key]) + try: + response = urllib.request.urlopen(request) + html = response.read() + except urllib.error.HTTPError as e: + html = e.read() + + tries = 0 + while tries < MAX_TRIES: + solver_pattern = \ + 'var (?:s,t,o,p,b,r,e,a,k,i,n,g|t,r,a),f,\s*([^=]+)' + solver_pattern += \ + '={"([^"]+)":([^}]+)};.+challenge-form\'\);' + vc_pattern = \ + 'input type="hidden" name="jschl_vc" value="([^"]+)' + pass_pattern = 'input type="hidden" name="pass" value="([^"]+)' + s_pattern = 'input type="hidden" name="s" value="([^"]+)' + init_match = re.search(solver_pattern, html, re.DOTALL) + vc_match = re.search(vc_pattern, html) + pass_match = re.search(pass_pattern, html) + s_match = re.search(s_pattern, html) + + if not init_match or not vc_match or not pass_match or not s_match: + msg = \ + "[CF] Couldn't find attribute: init: |%s| vc: |%s| pass: |%s| No cloudflare check?" + util.info(msg % (init_match, vc_match, pass_match)) + return False + + (init_dict, init_var, init_equation) = \ + init_match.groups() + vc = vc_match.group(1) + password = pass_match.group(1) + s = s_match.group(1) + + equations = re.compile(r"challenge-form\'\);\s*(.*)a.v").findall(html)[0] + # util.info("[CF] VC is: %s" % (vc)) + varname = (init_dict, init_var) + # util.info('[CF] init: [{0}]'.format((init_equation.rstrip()))) + result = float(solve_equation(init_equation.rstrip())) + util.info('[CF] Initial value: [ {0} ] Result: [ {1} ]'.format(init_equation, + result)) + + for equation in equations.split(';'): + equation = equation.rstrip() + if len(equation) > len('.'.join(varname)): + # util.debug('[CF] varname {0} line {1}'.format('.'.join(varname), equation)) + if equation[:len('.'.join(varname))] != '.'.join(varname): + util.info('[CF] Equation does not start with varname |%s|' + % equation) + else: + equation = equation[len('.'.join(varname)):] + + expression = equation[2:] + operator = equation[0] + if operator not in ['+', '-', '*', '/']: + util.info('[CF] Unknown operator: |%s|' % equation) + continue + + result = float(str(eval(str(result) + operator + str(solve_equation( + expression))))) + #util.info('[CF] intermediate: %s = %s' % (equation, result)) + + #util.debug('[CF] POCET: {0} {1}'.format(result, len(domain))) + result = '{0:.10f}'.format(eval('float({0} + {1})'.format(result, len(domain)))) + util.info('[CF] Final Result: |%s|' % result) + + if wait: + util.info('[CF] Sleeping for 5 Seconds') + xbmc.sleep(5000) + + url = \ + '%s://%s/cdn-cgi/l/chk_jschl?s=%s&jschl_vc=%s&pass=%s&jschl_answer=%s' \ + % (scheme, domain, urllib.parse.quote(s), urllib.parse.quote(vc), urllib.parse.quote(password), urllib.parse.quote(result)) + # util.info('[CF] url: %s' % url) + # util.debug('[CF] headers: {0}'.format(headers)) + request = urllib.request.Request(url) + for key in headers: + request.add_header(key, headers[key]) + + try: + opener = urllib.request.build_opener(NoRedirection) + urllib.request.install_opener(opener) + response = urllib.request.urlopen(request) + # util.info('[CF] code: {}'.format(response.getcode())) + while response.getcode() in [301, 302, 303, 307]: + if cj is not None: + cj.extract_cookies(response, request) + + redir_url = response.info().getheader('location') + if not redir_url.startswith('http'): + base_url = '%s://%s' % (scheme, domain) + redir_url = urllib.parse.urljoin(base_url, redir_url) + + request = urllib.request.Request(redir_url) + for key in headers: + request.add_header(key, headers[key]) + if cj is not None: + cj.add_cookie_header(request) + + response = urllib.request.urlopen(request) + final = response.read() + if 'cf-browser-verification' in final: + util.info('[CF] Failure: html: %s url: %s' % (html, url)) + tries += 1 + html = final + else: + break + except urllib.error.HTTPError as e: + util.info('[CF] HTTP Error: %s on url: %s' % (e.code, + url)) + return False + except urllib.error.URLError as e: + util.info('[CF] URLError Error: %s on url: %s' % (e, + url)) + return False + + if cj is not None: + util.cache_cookies() + + return final diff --git a/lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc b/lib/contentprovider/__pycache__/provider.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2599caf28381244f2ff3c326c59d120e7aacce5 GIT binary patch literal 8403 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 literal 0 HcmV?d00001 diff --git a/lib/contentprovider/__pycache__/xbmcprovider.cpython-38.opt-1.pyc b/lib/contentprovider/__pycache__/xbmcprovider.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11d95fb6f23fed48387b410225b3d0828982d511 GIT binary patch literal 15618 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 literal 0 HcmV?d00001 diff --git a/lib/contentprovider/provider.py b/lib/contentprovider/provider.py new file mode 100644 index 0000000..8536a39 --- /dev/null +++ b/lib/contentprovider/provider.py @@ -0,0 +1,230 @@ +# * Copyright (C) 2012 Libor Zoubek +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ +from collections import defaultdict + +import util +import resolver + + +try: + import StorageServer +except ImportError: + print('Using dummy storage server') + import storageserverdummy as StorageServer + + +class ResolveException(Exception): + pass + + +class ContentProvider(object): + """ + ContentProvider class provides an internet content. It should NOT have any xbmc-related imports + and must be testable without XBMC runtime. This is a basic/dummy implementation. + """ + + def __init__(self, name='dummy', base_url='/', username=None, password=None, filter=None, tmp_dir='.'): + """ + ContentProvider constructor + Args: + name (str): name of provider + base_url (str): base url of site being accessed + username (str): login username + password (str): login password + filter (func{item}): function to filter results returned by search or list methods + tmp_dir (str): temporary dir where provider can store/cache files + """ + self.name = name + self.username = username + self.password = password + if not base_url[-1] == '/': + base_url += '/' + self.base_url = base_url + self.filter = filter + self.tmp_dir = tmp_dir + self.cache = StorageServer.StorageServer(self.name, 24) + self.lang = 'cs' # initialize, current language could be set by XBMContentProvider + + def __str__(self): + return 'ContentProvider' + self.name + + def on_init(self): + """ + This function gets called by XbmcContentProvider after it initializes itself + and sets eveything up (especially 'lang' property of ContentProvider which gets detected + from kodi) + """ + pass + + def capabilities(self): + """ + This way class defines which capabilities it provides ['login','search','resolve','categories'] + It may also contain '!download' when provider does not support downloading + """ + return [] + + def video_item(self, url='', img='', quality='???'): + """ + Returns empty video item - contains all required fields + """ + return {'type': 'video', 'title': '', 'rating': 0, 'year': 0, 'size': '0MB', 'url': url, 'img': img, + 'length': '', 'quality': quality, 'subs': '', 'surl': '', 'lang': ''} + + def dir_item(self, title='', url='', type='dir'): + """ + Returns empty directory item + """ + return {'type': type, 'title': title, 'size': '0', 'url': url} + + def login(self): + """ + A login method returns True on successfull login, False otherwise + """ + return False + + def search(self, keyword): + """ + Search for a keyword on a site + Args: + keyword (str) + + returns: + array of video or directory items + """ + return [] + + def list(self, url): + """ + Lists content on given url + Args: + url (str): either relative or absolute provider URL + + Returns: + array of video or directory items + + """ + return [] + + def categories(self): + """ + Lists categories on provided site + + Returns: + array of video or directory items + """ + return [] + + def findstreams(self, data, regexes=None): + """ + Finds streams in given data (see resovler.findstreams for more details) + + :param data: A string (piece of HTML, for example) or an array of URLs + :param regexes: An array of regexes to be used for extracting URLs from + 'data' of type 'string' + :returns: An array of video items + """ + resolved = resolver.findstreams(data, regexes) + if resolved is None: + raise ResolveException( + 'Nelze ziskat video link [CR]zkontrolujte jestli video nebylo odstraneno') + elif isinstance(resolved, list) and not resolved: + raise ResolveException('Video je na serveru, ktery neni podporovan') + elif not resolved: + raise ResolveException( + 'Nebyl nalezen zadny video embed [CR]zkontrolujte stranku pripadne nahlaste chybu pluginu') + result = [] + for j in resolved: + i = defaultdict(lambda: '', j) + item = self.video_item() + item['title'] = i['name'] + item['url'] = i['url'] + item['quality'] = i['quality'] + item['surl'] = i['surl'] + item['subs'] = i['subs'] + item['headers'] = i['headers'] + item['lang'] = i['lang'] + item['info'] = i['info'] + result.append(item) + return result + + def resolve(self, item, captcha_cb=None, select_cb=None, wait_cb=None): + """ + Resolves given video item to a downloable/playable file/stream URL + + Args: + url (str): relative or absolute URL to be resolved + captcha_cb(func{obj}): callback function when user input is required (captcha, one-time passwords etc). + function implementation must be Provider-specific + select_cb(func{array}): callback function for cases when given url resolves to multiple streams, + provider class may call this function and require user interaction + wait_cb(func{obj}): callback function for cases when url resolves to stream which becomes available + somewhere in future (typically in several seconds). Provider may call this and require waiting. + Returns: + None - if ``url`` was not resolved. Video item with 'url' key pointing to resolved target + """ + return None + + def _url(self, url): + """ + Transforms relative to absolute url based on ``base_url`` class property + """ + if url.startswith('http'): + return url + return self.base_url + url.lstrip('./') + + def _filter(self, result, item): + """ + Applies filter, if filter passes `item` is appended to `result` + + Args: + result (array) : target array + item (obj) : item that is being applied filter on + """ + if self.filter: + if self.filter(item): + result.append(item) + else: + result.append(item) + + def info(self, msg): + util.info('[%s] %s' % (self.name, msg)) + + def error(self, msg): + util.error('[%s] %s' % (self.name, msg)) + + +class cached(object): + """ + A method decorator that can be used on any ContentProvider method + Having this decorator means that results of such method are going + to be cached for 24hours by default. You can pass number argument + to decorator, for example @cached(1) would cache for 1 hour. + """ + + def __init__(self, ttl=24): + self.ttl = ttl + + def __call__(self, f): + def wrap(*args): + provider = args[0] + cache = StorageServer.StorageServer(provider.name + str(self.ttl), self.ttl) + return cache.cacheFunction(f, *args) + + return wrap diff --git a/lib/contentprovider/xbmcprovider.py b/lib/contentprovider/xbmcprovider.py new file mode 100644 index 0000000..f7e1401 --- /dev/null +++ b/lib/contentprovider/xbmcprovider.py @@ -0,0 +1,464 @@ +# * Copyright (C) 2012 Libor Zoubek +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ + +import sys +import os +import re +import traceback +import util +import xbmcutil +import resolver +import time +import xbmcplugin +import xbmc +import xbmcvfs +import xbmcgui +import urllib.parse +import urllib.request, urllib.parse, urllib.error +from collections import defaultdict +from provider import ResolveException + + +class XBMContentProvider(object): + ''' + ContentProvider class provides an internet content. It should NOT have any xbmc-related imports + and must be testable without XBMC runtime. This is a basic/dummy implementation. + ''' + + def __init__(self, provider, settings, addon): + ''' + XBMContentProvider constructor + Args: + name (str): name of provider + ''' + self.provider = provider + # inject current user language + try: # not fully supported on Frodo + provider.lang = xbmc.getLanguage(xbmc.ISO_639_1) + except: + provider.lang = None + pass + self.settings = settings + # lang setting is optional for plugins + if not 'lang' in self.settings: + self.settings['lang'] = '0' + + util.info('Initializing provider %s with settings %s' % (provider.name, settings)) + self.addon = addon + self.addon_id = addon.getAddonInfo('id') + if '!download' not in self.provider.capabilities(): + self.check_setting_keys(['downloads']) + self.cache = provider.cache + provider.on_init() + + def check_setting_keys(self, keys): + for key in keys: + if not key in list(self.settings.keys()): + raise Exception('Invalid settings passed - [' + key + '] setting is required') + + def params(self): + return {'cp': self.provider.name} + + def run(self, params): + if params == {} or params == self.params(): + return self.root() + if 'list' in list(params.keys()): + self.list(self.provider.list(params['list'])) + return xbmcplugin.endOfDirectory(int(sys.argv[1])) + if 'down' in list(params.keys()): + return self.download({'url': params['down'], 'title': params['title']}) + if 'play' in list(params.keys()): + return self.play({'url': params['play'], 'info': params}) + if 'search-list' in list(params.keys()): + return self.search_list() + if 'search' in list(params.keys()): + return self.do_search(params['search']) + if 'search-remove' in list(params.keys()): + return self.search_remove(params['search-remove']) + if 'search-edit' in list(params.keys()): + return self.search_edit(params['search-edit']) + if self.run_custom: + return self.run_custom(params) + + def search_list(self): + params = self.params() + params.update({'search': '#'}) + menu1 = self.params() + menu2 = self.params() + xbmcutil.add_dir(xbmcutil.__lang__(30004), params, xbmcutil.icon('search.png')) + for what in xbmcutil.search_list(self.cache): + params['search'] = what + menu1['search-remove'] = what + menu2['search-edit'] = what + xbmcutil.add_dir(what, params, menuItems={xbmcutil.__lang__( + 30016): menu2, xbmc.getLocalizedString(117): menu1}) + xbmcplugin.endOfDirectory(int(sys.argv[1])) + + def search_remove(self, what): + xbmcutil.search_remove(self.cache, what) + xbmc.executebuiltin('Container.Refresh') + + def search_edit(self, what): + kb = xbmc.Keyboard(what, xbmcutil.__lang__(30003), False) + kb.doModal() + if kb.isConfirmed(): + replacement = kb.getText() + xbmcutil.search_replace(self.cache, what, replacement) + params = self.params() + params.update({'search': replacement}) + action = xbmcutil._create_plugin_url(params) + xbmc.executebuiltin('Container.Update(%s)' % action) + + def do_search(self, what): + if what == '' or what == '#': + kb = xbmc.Keyboard('', xbmcutil.__lang__(30003), False) + kb.doModal() + if kb.isConfirmed(): + what = kb.getText() + if not what == '': + maximum = 20 + try: + maximum = int(self.settings['keep-searches']) + except: + util.error('Unable to parse convert addon setting to number') + pass + xbmcutil.search_add(self.cache, what, maximum) + self.search(what) + + def root(self): + searches = xbmcutil.get_searches(self.addon, self.provider.name) + if len(searches) > 0: + self.provider.info('Upgrading to new saved search storage...') + for s in searches: + self.provider.info('Moving item %s' % s) + xbmcutil.search_add(self.cache, s, 9999999) + xbmcutil.delete_search_history(self.addon, self.provider.name) + + if 'search' in self.provider.capabilities(): + params = self.params() + params.update({'search-list': '#'}) + xbmcutil.add_dir(xbmcutil.__lang__(30003), params, xbmcutil.icon('search.png')) + if not '!download' in self.provider.capabilities(): + xbmcutil.add_local_dir(xbmcutil.__lang__(30006), self.settings[ + 'downloads'], xbmcutil.icon('download.png')) + self.list(self.provider.categories()) + return xbmcplugin.endOfDirectory(int(sys.argv[1])) + + def download(self, item): + downloads = self.settings['downloads'] + if '' == downloads: + xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30009)) + return + stream = self.resolve(item['url']) + if stream: + if not 'headers' in list(stream.keys()): + stream['headers'] = {} + xbmcutil.reportUsage(self.addon_id, self.addon_id + '/download') + # clean up \ and / + name = item['title'].replace('/', '_').replace('\\', '_') + if not stream['subs'] == '': + xbmcutil.save_to_file(stream['subs'], os.path.join( + downloads, name + '.srt'), stream['headers']) + dot = name.find('.') + if dot <= 0: + # name does not contain extension, append some + name += '.mp4' + xbmcutil.download(self.addon, name, self.provider._url( + stream['url']), os.path.join(downloads, name), headers=stream['headers']) + + def play(self, item): + stream = self.resolve(item['url']) + if stream: + xbmcutil.reportUsage(self.addon_id, self.addon_id + '/play') + if 'headers' in list(stream.keys()): + headerStr = '|' + urllib.parse.urlencode(stream['headers']) + if len(headerStr) > 1: + stream['url'] += headerStr.encode('utf-8') + print('Sending %s to player' % stream['url']) + li = xbmcgui.ListItem(path=stream['url']) + li.setArt({'icon': 'DefaulVideo.png'}) + il = self._extract_infolabels(item['info']) + if len(il) > 0: # only set when something was extracted + li.setInfo('video', il) + try: + local_subs = xbmcutil.set_subtitles(li, stream['subs'], stream.get('headers')) + except: + xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, li) + xbmcutil.load_subtitles(stream['subs'], stream.get('headers')) + else: + xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, li) + + def _handle_exc(self, e): + msg = e.message + if msg.find('$') == 0: + try: + msg = self.addon.getLocalizedString(int(msg[1:])) + except: + try: + msg = xbmcutil.__lang__(int(msg[1:])) + except: + pass + xbmcgui.Dialog().ok(self.provider.name, msg) + + def resolve(self, url): + item = self.provider.video_item() + item.update({'url': url}) + try: + return self.provider.resolve(item) + except ResolveException as e: + self._handle_exc(e) + + def search(self, keyword): + self.list(self.provider.search(keyword)) + return xbmcplugin.endOfDirectory(int(sys.argv[1])) + + def list(self, items): + params = self.params() + for item in items: + if item['type'] == 'dir': + self.render_dir(item) + elif item['type'] == 'next': + params.update({'list': item['url']}) + xbmcutil.add_dir(xbmcutil.__lang__(30007), params, xbmcutil.icon('next.png')) + elif item['type'] == 'prev': + params.update({'list': item['url']}) + xbmcutil.add_dir(xbmcutil.__lang__(30008), params, xbmcutil.icon('prev.png')) + elif item['type'] == 'new': + params.update({'list': item['url']}) + xbmcutil.add_dir(xbmcutil.__lang__(30012), params, xbmcutil.icon('new.png')) + elif item['type'] == 'top': + params.update({'list': item['url']}) + xbmcutil.add_dir(xbmcutil.__lang__(30013), params, xbmcutil.icon('top.png')) + elif item['type'] == 'video': + self.render_video(item) + else: + self.render_default(item) + + def render_default(self, item): + raise Exception("Unable to render item " + str(item)) + + def render_dir(self, item): + params = self.params() + params.update({'list': item['url']}) + title = item['title'] + img = None + if 'img' in list(item.keys()): + img = item['img'] + if title.find('$') == 0: + try: + title = self.addon.getLocalizedString(int(title[1:])) + except: + pass + menuItems = {} + if 'menu' in list(item.keys()): + for ctxtitle, value in item['menu'].items(): + if ctxtitle.find('$') == 0: + try: + ctxtitle = self.addon.getLocalizedString(int(ctxtitle[1:])) + except: + pass + menuItems[ctxtitle] = value + xbmcutil.add_dir(title, params, img, infoLabels=self._extract_infolabels( + item), menuItems=menuItems) + + def _extract_infolabels(self, item): + infoLabels = {} + for label in ['title', 'plot', 'year', 'genre', 'rating', 'director', + 'votes', 'cast', 'trailer', 'tvshowtitle', 'season', + 'episode', 'duration']: + if label in list(item.keys()): + infoLabels[label] = util.decode_html(item[label]) + return infoLabels + + def render_video(self, item): + params = self.params() + params.update({'play': item['url']}) + downparams = self.params() + downparams.update({'title': item['title'], 'down': item['url']}) + def_item = self.provider.video_item() + if item['size'] == def_item['size']: + item['size'] = '' + else: + item['size'] = ' (%s)' % item['size'] + title = '%s%s' % (item['title'], item['size']) + menuItems = {} + if "!download" not in self.provider.capabilities(): + menuItems[xbmc.getLocalizedString(33003)] = downparams + if 'menu' in list(item.keys()): + for ctxtitle, value in item['menu'].items(): + if ctxtitle.find('$') == 0: + try: + ctxtitle = self.addon.getLocalizedString(int(ctxtitle[1:])) + except: + pass + menuItems[ctxtitle] = value + xbmcutil.add_video(title, + params, + item['img'], + infoLabels=self._extract_infolabels(item), + menuItems=menuItems + ) + + def categories(self): + self.list(self.provider.categories(keyword)) + return xbmcplugin.endOfDirectory(int(sys.argv[1])) + + +class XBMCMultiResolverContentProvider(XBMContentProvider): + + def __init__(self, provider, settings, addon): + XBMContentProvider.__init__(self, provider, settings, addon) + self.check_setting_keys(['quality']) + + def resolve(self, url): + item = self.provider.video_item() + item.update({'url': url}) + + def select_cb(resolved): + + quality = self.settings['quality'] or '0' + filtered = resolver.filter_by_quality(resolved, quality) + lang = self.settings['lang'] or '0' + filtered = resolver.filter_by_language(filtered, lang) + # if user requested something but 'ask me' or filtered result is exactly 1 + if len(filtered) == 1 or (int(quality) > 0 and int(lang) == 0): + return filtered[0] + # if user requested particular language and we have it + if len(filtered) > 0 and int(lang) > 0: + return filtered[0] + dialog = xbmcgui.Dialog() + opts = [] + for r in resolved: + d = defaultdict(lambda: '', r) + opts.append('%s [%s] %s' % (d['title'], d['quality'], d['lang'])) + ret = dialog.select(xbmcutil.__lang__(30005), opts) + if ret >= 0: + return resolved[ret] + try: + return self.provider.resolve(item, select_cb=select_cb) + except ResolveException as e: + self._handle_exc(e) + + +class XBMCLoginRequiredContentProvider(XBMContentProvider): + + def root(self): + if not self.provider.login(): + xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011)) + else: + return XBMContentProvider.root(self) + + +class XBMCLoginOptionalContentProvider(XBMContentProvider): + + def __init__(self, provider, settings, addon): + XBMContentProvider.__init__(self, provider, settings, addon) + self.check_setting_keys(['vip']) + + def ask_for_captcha(self, params): + img = os.path.join(str(xbmcvfs.translatePath( + self.addon.getAddonInfo('profile'))), 'captcha.png') + util.save_to_file(params['img'], img) + cd = CaptchaDialog('captcha-dialog.xml', + xbmcutil.__addon__.getAddonInfo('path'), 'default', '0') + cd.image = img + xbmc.sleep(3000) + cd.doModal() + del cd + kb = xbmc.Keyboard('', self.addon.getLocalizedString(200), False) + kb.doModal() + if kb.isConfirmed(): + print('got code ' + kb.getText()) + return kb.getText() + + def ask_for_account_type(self): + if len(self.provider.username) == 0: + util.info('Username is not set, NOT using VIP account') + return False + if self.settings['vip'] == '0': + util.info('Asking user whether to use VIP account') + ret = xbmcgui.Dialog().yesno(self.provider.name, xbmcutil.__lang__(30010)) + return ret == 1 + return self.settings['vip'] == '1' + + def resolve(self, url): + item = self.provider.video_item() + item.update({'url': url}) + if not self.ask_for_account_type(): + # set user/pass to null - user does not want to use VIP at this time + self.provider.username = None + self.provider.password = None + else: + if not self.provider.login(): + xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011)) + return + try: + return self.provider.resolve(item, captcha_cb=self.ask_for_captcha) + except ResolveException as e: + self._handle_exc(e) + + +class XBMCLoginOptionalDelayedContentProvider(XBMCLoginOptionalContentProvider): + + def wait_cb(self, wait): + left = wait + msg = xbmcutil.__lang__(30014).encode('utf-8') + while left > 0: + xbmc.executebuiltin("XBMC.Notification(%s,%s,1000,%s)" % + (self.provider.name, msg % str(left), '')) + left -= 1 + time.sleep(1) + + def resolve(self, url): + item = self.video_item() + item.update({'url': url}) + if not self.ask_for_account_type(): + # set user/pass to null - user does not want to use VIP at this time + self.provider.username = None + self.provider.password = None + else: + if not self.provider.login(): + xbmcgui.Dialog().ok(self.provider.name, xbmcutil.__lang__(30011)) + return + try: + return self.provider.resolve(item, captcha_cb=self.ask_for_captcha, wait_cb=self.wait_cb) + except ResolveException as e: + self._handle_exc(e) + + +class CaptchaDialog (xbmcgui.WindowXMLDialog): + + def __init__(self, *args, **kwargs): + super(xbmcgui.WindowXMLDialog, self).__init__(args, kwargs) + self.image = None + + def onFocus(self, controlId): + self.controlId = controlId + + def onInit(self): + self.getControl(101).setImage(self.image) + + def onAction(self, action): + if action.getId() in [9, 10]: + self.close() + + def onClick(self, controlId): + if controlId == 102: + self.close() diff --git a/lib/crypto/__init__.py b/lib/crypto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc b/lib/crypto/__pycache__/__init__.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea61905be66b39442c54b2e609359cdca89caa7f GIT binary patch literal 185 zcmWIL<>g`kf{;M&3=sVoL?8o3AjbiSi&=m~3PUi1CZpd2 zCSx263S1IP5@Uk$6LT~3(qp{yQ!--`Q&RHtierkCi!uvJ^m6l4N^?^6ic5-86La;7 uQj7C*%2JDBax#-*l8Y(}O7dgk<1_OzOXB183My}L*g(|Vf$aSZ#0&rr1~Gg9 literal 0 HcmV?d00001 diff --git a/lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc b/lib/crypto/__pycache__/md5crypt.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dcc6efa5798a27b96421b5ac2f5d0b5a7916fc92 GIT binary patch literal 1776 zcmZuxOLH4V5T2QRXtk0Z#Td)xNV%Xy6#@sxHWX#-*pQecN=R%Ti$Jy3tgXe;YCSuC z*6vB+O7RoyBUgR}zhRDWVQ*CBlFG$&uO3lS-mU5B>HemBrl-4iCMIkG-%h>I`Y}hy zKe!nGbXcsxi0%R5gwr;02xkuEn!~uxjc3HsxXE)c>wJP+JpYV3hG#gYm*e&U6Pupp z8N9%YJ=UWXEIcKZ$rP-S+POBrFn_*Q{pjO`Tc0f6URqxHbp6i8=9gdHy|?xC{RatVR*O1J)`>vsHLzka{V{kqF}H;C#{ zQ~05*cDnqi?NuWwJhxL7Ues+Ld!pX@F?hVWJ`sLUn4< z5)7LP6ucG&49O@AsKtxJHEBq=;Sm98lt274^K8EI$n!l4oN#Z0m`M344w4|iqzGfSCgd+7P_1)oR?aY2IhEQ+~R0^B6TQ(q-_kiRhp zGcWZR_!=VN{=APYjv;SegPa;6^)cjI*B}ccS-u823;LzA+v+_`Zi_dZuK5Ur5*I$fcOKDJHAH zY!+BLDdumQaTCa)BE^JH^GI$W!Mab;SBmvfDJbLN`rWM!Wk=E#GCJ{POKEK{P(~EC zeW^?@Xm+`$axFJ%L76KJ>Q0&5-}fS^4B-a*9?1M4tLREmpg)1Gl+khb{ib7uCym`R z=|zr>6*}MxnX0rs*sLIW`+sf-wnFJ=0?Q84epTC8pC4M*kGctShV0=(v8+Xu#oI>1Zui zR%oq~7w>~d@h%c 0: + ret += ITOA64[v & 0x3f] + v >>= 6 + n -= 1 + return ret + +def apache_md5_crypt(pw, salt): + return unix_md5_crypt(pw, salt, '$apr1$') + +def unix_md5_crypt(pw, salt, magic=None): + if magic is None: + magic = MAGIC + + if salt.startswith(magic): + salt = salt[len(magic):] + + salt = salt.split('$', 1)[0][:8] + + pw_bytes = pw.encode('utf-8') + salt_bytes = salt.encode('utf-8') + magic_bytes = magic.encode('utf-8') + + ctx = pw_bytes + magic_bytes + salt_bytes + final = hashlib.md5(pw_bytes + salt_bytes + pw_bytes).digest() + + for pl in range(len(pw_bytes), 0, -16): + ctx += final[:min(16, pl)] + + i = len(pw_bytes) + while i: + if i & 1: + ctx += b'\x00' + else: + ctx += pw_bytes[:1] + i >>= 1 + + final = hashlib.md5(ctx).digest() + + for i in range(1000): + ctx1 = b'' + if i & 1: + ctx1 += pw_bytes + else: + ctx1 += final + if i % 3: + ctx1 += salt_bytes + if i % 7: + ctx1 += pw_bytes + if i & 1: + ctx1 += final + else: + ctx1 += pw_bytes + final = hashlib.md5(ctx1).digest() + + passwd = '' + passwd += to64((final[0] << 16) | (final[6] << 8) | final[12], 4) + passwd += to64((final[1] << 16) | (final[7] << 8) | final[13], 4) + passwd += to64((final[2] << 16) | (final[8] << 8) | final[14], 4) + passwd += to64((final[3] << 16) | (final[9] << 8) | final[15], 4) + passwd += to64((final[4] << 16) | (final[10] << 8) | final[5], 4) + passwd += to64(final[11], 2) + + return magic + salt + '$' + passwd + +md5crypt = unix_md5_crypt + +if __name__ == "__main__": + print(unix_md5_crypt("cat", "hat")) diff --git a/lib/resolver.py b/lib/resolver.py new file mode 100644 index 0000000..063f107 --- /dev/null +++ b/lib/resolver.py @@ -0,0 +1,289 @@ +# -*- coding: UTF-8 -*- +# * Copyright (C) 2011 Libor Zoubek +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ + +import sys +import os +import re +import traceback + +import util + +sys.path.append(os.path.join(os.path.dirname(__file__), 'server')) + +RESOLVERS = [] +util.debug('%s searching for modules' % __name__) +for module in os.listdir(os.path.join(os.path.dirname(__file__), 'server')): + if module == '__init__.py' or module[-3:] != '.py': + continue + module = module[:-3] + exec('import %s' % module) + resolver = eval(module) + util.debug('found %s %s' % (resolver, dir(resolver))) + + if not hasattr(resolver, '__priority__'): + resolver.__priority__ = 0 + RESOLVERS.append(resolver) + del module +RESOLVERS = sorted(RESOLVERS, key=lambda m: -m.__priority__) +util.debug('done') + + +def item(): + return {'name': '', 'url': '', 'quality': '???', 'surl': '', 'subs': '', 'headers': {}} + + +def resolve(url): + """ + resolves given url by asking all resolvers + + returns None if no resolver advised to be able to resolve this url + returns False if resolver did his job, but did not return any value (thus failed) + returns Array of resolved objects in positive usecase + """ + url = util.decode_html(url) + util.info('Resolving ' + url) + resolver = _get_resolver(url) + value = None + if resolver is None: + return None + util.info('Using resolver \'%s\'' % str(resolver.__name__)); + try: + value = resolver.resolve(url) + except: + traceback.print_exc() + if value is None: + return False + default = item() + + def fix_stream(i, url, resolver, default): + """ fix missing but required values """ + if 'name' not in list(i.keys()): + i['name'] = resolver.__name__ + if 'surl' not in list(i.keys()): + i['surl'] = url + for key in list(default.keys()): + if key not in list(i.keys()): + i[key] = default[key] + + [fix_stream(i, url, resolver, default) for i in value] + return sorted(value, key=lambda i: i['quality']) + + +def _get_resolver(url): + util.debug('Get resolver for ' + url) + for r in RESOLVERS: + util.debug('querying %s' % r) + if r.supports(url): + return r + + +def can_resolve(url): + """ Returns true if we are able to resolve stream by given URL """ + return _get_resolver(url) is not None + + +def filter_resolvable(url): + if url.find('facebook') > 0 or url.find('yield') > 0: + return + return url.strip('\'\"') + + +def findstreams(data, regexes=None): + """ + Finds streams in given data. Respects caller add-on settings about + quality and asks user if necessary. + + :param data: A string (piece of text / HTML code), an array of URLs or an + array of dictionaries, where 'url' key stores actual URL and + all other keys not present in item() are being copied to the + resolved stream dictionary + :param regexes: An array of strings - regular expressions, each MUST define + named group called 'url', which retrieves resolvable URL + (that one is passed to resolve operation); only used + with 'data' of type 'string' + :returns: An array of resolved objects, None if at least 1 resolver failed + to resolve and nothing else was found, an empty array if no + resolvers for URLs has been found or False if none of regexes + found anything + """ + + def get_url(obj): + return obj['url'] if isinstance(obj, dict) else obj + + urls = [] + resolvables = [] + resolved = [] + not_found = False + if isinstance(data, str) and regexes: + for regex in regexes: + for match in re.finditer(regex, data, re.IGNORECASE | re.DOTALL): + urls.append(match.group('url')) + elif isinstance(data, list): + urls = data + else: + raise TypeError + for url in urls: + if isinstance(url, dict): + url['url'] = filter_resolvable(url['url']) + else: + url = filter_resolvable(url) + if url and url not in resolvables: + util.info('Found resolvable ' + get_url(url)) + resolvables.append(url) + if len(resolvables) == 0: + util.info('No resolvables found!') + return False + for url in resolvables: + streams = resolve(get_url(url)) + if streams is None: + util.info('No resolver found for ' + get_url(url)) + not_found = True + elif not streams: + util.info('There was an error resolving ' + get_url(url)) + elif len(streams) > 0: + for stream in streams: + if isinstance(url, dict): + for key in list(url.keys()): + if key not in stream: + stream[key] = url[key] + elif key not in item(): + if isinstance(stream[key], str) and \ + isinstance(url[key], str): + stream[key] = url[key] + ' +' + stream[key] + elif isinstance(stream[key], list) and \ + isinstance(url[key], list): + stream[key] = url[key] + stream[key] + elif isinstance(stream[key], dict) and \ + isinstance(url[key], dict): + stream[key].update(url[key]) + resolved.append(stream) + if len(resolved) == 0: + if not_found: + return [] + return None + resolved = sorted(resolved, key=lambda i: i['quality']) + resolved = sorted(resolved, key=lambda i: len(i['quality'])) + resolved.reverse() + return resolved + + +q_map = {'3': '720p', '4': '480p', '5': '360p'} + + +def filter_by_quality(resolved, q): + util.info('filtering by quality setting ' + q) + if q == '0': + return resolved + sources = {} + ret = [] + # first group streams by source url + for item in resolved: + if item['surl'] in list(sources.keys()): + sources[item['surl']].append(item) + else: + sources[item['surl']] = [item] + if q == '1': + # always return best quality from each source + for key in list(sources.keys()): + ret.append(sources[key][0]) + elif q == '2': + # always return worse quality from each source + for key in list(sources.keys()): + ret.append(sources[key][-1]) + else: + # we try to select sources of desired qualities + quality = q_map[q] + # 3,4,5 are 720,480,360 + for key in list(sources.keys()): + added = False + for item in sources[key]: + if quality == item['quality']: + ret.append(item) + added = True + if not added: + util.debug('Desired quality %s not found, adding best found' % quality) + ret.append(sources[key][-1]) + # sort results again, so best quality streams appear first + ret = sorted(ret, key=lambda i: i['quality']) + if not q == '2': + ret.reverse() + return ret + +def filter_by_language(resolved, lang): + util.info('filtering by language setting ' + lang) + if lang == '0': + return resolved + ret = [] + # first group streams by source url + for item in resolved: + if 'lang' in item and item['lang'] != '': + util.info(item) + if lang == '1' and re.match('en', item['lang'], re.IGNORECASE): + ret.append(item) + if lang == '2' and re.match('cs|cz|čeština', item['lang'], re.IGNORECASE): + ret.append(item) + return ret + +def findstreams_multi(data, regexes): + """ + Finds streams in given data according to given regexes + respects caller addon's setting about desired quality, asks user if needed + assumes, that all resolvables need to be returned, but in particular quality + @param data piece of text (HTML code) to search in + @param regexes - array of strings - regular expressions, each MUST define named group called 'url' + which retrieves resolvable URL (that one is passsed to resolve operation) + @return array of dictionaries with keys: name,url,quality,surl + @return None if at least 1 resoler failed to resolve and nothing else has been found + @return [] if no resolvable URLs or no resolvers for URL has been found + """ + resolved = [] + # keep list of found urls to aviod having duplicates + urls = [] + error = False + for regex in regexes: + for match in re.finditer(regex, data, re.IGNORECASE | re.DOTALL): + print('Found resolvable %s ' % match.group('url')) + streams = resolve(match.group('url')) + if isinstance(streams, list) and streams: + util.debug('There was an error resolving ' + match.group('url')) + error = True + if streams is not None: + if len(streams) > 0: + for stream in streams: + resolved.append(stream) + if error and len(resolved) == 0: + return None + if len(resolved) == 0: + return [] + resolved = sorted(resolved, key=lambda i: i['quality']) + resolved = sorted(resolved, key=lambda i: len(i['quality'])) + resolved2 = resolved + resolved2.reverse() + qualities = {} + for item in resolved2: + if item['quality'] in list(qualities.keys()): + qualities[item['quality']].append(item) + else: + qualities[item['quality']] = [item] + # now .. we must sort items to be in same order as they were found on page + for q in list(qualities.keys()): + qualities[q] = sorted(qualities[q], key=lambda i: resolved.index(i)) + return qualities diff --git a/lib/search.py b/lib/search.py new file mode 100644 index 0000000..69b727f --- /dev/null +++ b/lib/search.py @@ -0,0 +1,76 @@ + +# -*- coding: UTF-8 -*- +#/* +# * Copyright (C) 2011 Libor Zoubek +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ +import os,re,sys +import xbmcplugin,xbmcaddon,xbmc +import xbmcutil,util + +def _list(addon,history,key,value): + params = {} + menuItems = {} + if key: + params[key] = value + menuItems[key] = value + params['search'] = '#' + xbmcutil.add_dir(xbmcutil.__lang__(30004),params,xbmcutil.icon('search.png')) + for what in xbmcutil.get_searches(addon,history): + params['search'] = what + menuItems['search-remove'] = what + xbmcutil.add_dir(what,params,menuItems={xbmc.getLocalizedString(117):menuItems}) + xbmcplugin.endOfDirectory(int(sys.argv[1])) + +def _remove(addon,history,search): + xbmcutil.remove_search(addon,history,search) + xbmc.executebuiltin('Container.Refresh') + +def _search(addon,history,what,update_history,callback): + if what == '' or what == '#': + kb = xbmc.Keyboard('',xbmcutil.__lang__(30003),False) + kb.doModal() + if kb.isConfirmed(): + what = kb.getText() + if not what == '': + maximum = 20 + try: + maximum = int(addon.getSetting('keep-searches')) + except: + util.error('Unable to parse convert addon setting to number') + pass + if update_history: + xbmcutil.add_search(addon,history,what,maximum) + callback(what) + +def item(items={},label=xbmcutil.__lang__(30003)): + items['search-list'] = '#' + xbmcutil.add_dir(label,items,xbmcutil.icon('search.png')) + +def main(addon,history,p,callback,key=None,value=None): + if (key==None) or (key in p and p[key] == value): + if 'search-list' in list(p.keys()): + _list(addon,history,key,value) + if 'search' in list(p.keys()): + update_history=True + if 'search-no-history' in list(p.keys()): + update_history=False + _search(addon,history,p['search'],update_history,callback) + if 'search-remove' in list(p.keys()): + _remove(addon,history,p['search-remove']) diff --git a/lib/server/__init__.py b/lib/server/__init__.py new file mode 100644 index 0000000..56e0887 --- /dev/null +++ b/lib/server/__init__.py @@ -0,0 +1,31 @@ +#/* +# * Copyright (C) 2011 Libor Zoubek +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ + + +##########################################################3 +# all resolvers modules in this directory must have following methods: + +# __name__ - name of the resolver module - can override module filename +# def supports(url) - returns true iff resolver is able to resolve url to stream otherwise false +# def resolve(url) - returns array of all hashmaps that were resolved +# - if resolving fails, nothing is returned +# - a hash MUST contain key 'url' - it's value is stream URL +# - optional keys are 'subs' (link to subtitle), 'quality' (quality string like '240p' or just 'HD' diff --git a/lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/anyfilesresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d004828654a5f50c5bc3248fb4f5dfc4402fabf GIT binary patch literal 2966 zcmZV=TYJ;U&Klc^b7^4<3oWOdEjcNSYZ**4D^KQsd|O(^TX6sHXi-4ZeN^ z5AO2k|G@w?gr-&;U?_@ctRG|J7!Ucw@o?TOnvs007>jVc7)MdGP%kFXK=Ti+n8X;y z8K=;~1miR&F~xWU(>TI-6ycec89ic8v_jFUv=3@d)AqaLjUez=W@q;twB78Go%Nbk zP@>0=qSbEh^XKzcwLlBC4}jW^NNcPY3K^hSS=x2`{a-+OM>hY`q{)JxYWG)4TfQy* z2$t48Z=(`aN}Fz_<+SUiUtDxb6-2k~m;9=9ydc|hvD37(ejx2iD=Tf^ZSL8!)O6mJ zSdSuyZVux*Lw z`l2SzXnl2PdZBAx#Oy_s>KHNx#nG-htxA*P8^=UUR@&&cD6X*64l;tkA;9Pe1|Y_3 z=~TIHx61?J^0AilJTdfF9ql&*Mva2o%EKjKBagfn;< zGk68B;x)XES)9c=oW})R#3j6eH}Mv)i?OXMykdfRBfRE&ww`7mW_ey&NwB>WY-NUZ zr`X0MYaZD9j(y3ng&$boRrbVUpBLE6BwM=19$#l$7ub_A_H>TDx-qa9@csh`%@6QF zY-n#*dhyT<;}crX3S$vYgxaA9Exg?qJq}T(6QMPv@phO{^helDBk*6rp(LM@U zWKJ?n;xdmw{u#!@Kgvm9HSFD`XdP&y#Hsl+b&~WvR#?%T^ zYj-tV&TD72P$01EfSXg?NM@B~?(k^!SY{=XQQSOp??y8DiNX5SHQsyVVLtM=pzJH# zEWN%Xcn4D-#crW{I@ALr6k&3LcXmtb=`Xc*8Z`C3*4pCL7mvj*?*^CI`#5|5IMg}T z?-imTC#}1_e((Oqga1DK>Cw->JpQ%tN zCRiqD$q^2Bf;j@8;03`uf<=N`pCBjG(ab%9J%TNQp9q!+HVE1TI|R20-V;10=ny<4 z*e9qF6bODMxJmGmV4k2(&?RsQ9udf*UM`o93|4xi68lKihNC$JPAIUZfUbZ_-J`n< zGA5avymg!OrrqXjx4mYiYLiiO+DLk}Au}m5y^5@~ie}TTmKO(TX@C|;=bSH^jf&rx zAE1S3Q$=0c^9t}GZU4Py#8shi(Z0YA_!|r~k%lA~!hqX@S04ilEas-+By&f>fJrz5 zX)y&q!X$H&FvdIqry<5~TBL=1#(KV~H0p5GMx#nI#RoDd@jZ@n&WFhWwz)=fg@m36 z#kO&1BJ|-fhN2Isa|Y_6IE?o>%Fqa`5DpV)_CeLqo)PjojQuHkW{~8JG(wZJFczc^ z(-;q9OvfXdbC_u>cH8&z4(2ZUm-ZbD8o8SbbC(*nQ*Q*h#iiv- zp6ll>`8!TKH%G!j{$IofYAC*G*KBFa?j*X^PRniw*=l59DmOKK`S-WeZ?5Ju)7|sF ztmdXKmu9`HZ|^njpp;iZl8Xc~@&(^ySFBD+b)DU7v$dwX&#`)7%dbdQ+-ljf>QtK7 z7qbi5`I}jPzb0R*CFf@NA3n2^y^^1~tXhQM-_Ee%mba9d>7JjdR=mJ*+gtw>;b}de z&u4^GiAC|wClJ0R2d zcrlsPrt8~e-CKY5puDyDn9RC#J02OGz-f|@HpP6qBC8FGZ9I9t{+I>0d2qZ;I&wm) z%prrD1+QrOw(nPqMvV>1HxVDX9Vv>|Jbw$tSgX9pQwsZx$vSQ^{_!Vg*Q37V3G48$ zG0->%Ik!0PEY7whQ-k+h#5)Nmc~<4$7{r9!Vfk;ePZglg*>{EqgT5qWye;_C?`eHi zpGBd2C2G}5pL?|uH*2%|B^7Zc6>^vJzpXiVr-WBC8ABrLk~RZMK6+SqviaaXAEK-& zuBvT+D>l*f)R{kQNYr)lttl-u$_K)?#vNI7X|kx8>0>4rJ-(vh$d=NTQgz2tbz8Ek zM6GhUU1`~6?tc_d^=48vJW(x*jwCYwl}oCDlS{rnQchmxZoPh=7{HnTV39g00 L(k;WvTI2r*n^xw# literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/dailymotionresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5229306f7469fa06ba9265b6a4837877878d980 GIT binary patch literal 1697 zcmZ8hOK%%D5GJX8Nm|LW<2;+9>dmTA2?cs`?K*}XphXJ@P3+57sjwDlYwLZ9q~b{6 zo|0<;qd&lqj{OTg^qfO~0k1vfFXYk=R}SKIg&A^&^Y~^&&T*qrL+~B_R39DG5&Fjn zH$MWxBOvh|Fp4OSQD9aQzu%$R^U>bI@zx$Gtxoy4pwBxhmLFj4-bP|ESPaP#9!op?^c!656lOD`>jJb3%zJd0wHF1uih0 zSqhso)9?rv#55e%inQi7ttxyMDY77iC7rpeEby_FO>6g2;Yd$8^XdY0R)+2Ob30`>@u)9x!5biO1{X*Q$1j#T%#(FqZ__ zAp3hw1uF5CZ%#f`VY+8>Lf`Owg+5r|d8_Q+iduo}Uqd%q#S%Y)tOkFJegeJzxCVoy ztO1bxu4-^eA0EycM1WKrIcq8)y{sFokwi85{K#84okmjpPueyr>C>8MpEWmz23#eMfd;N@;IC_ z-bcv419Gqwv}NFFH)R*g&2pj$ zehWOy;k0C43GsT6d=7%3z!0wCH6RauHBA1sh}rwZ#}>ATAs;ufM|{|OAbFUOH8?Z& z;Bl>AN-ca3`?v*hTCf@$AE=3W5At+sW$T0L%OvhH7BBYT5E m8YXNoDCb(~f1?sJ)_7Ttx4oRA->!p7G=VL1LSAB3JnKK24Z~0X literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/divxstageresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..503d78877d1d98b5c84ad1b9c5238ffde701d2b2 GIT binary patch literal 860 zcmZ8fO>fgc5ZzsWrX;|jDmidK;Zm!TdqW7dg^*T=Qn{iZ7h{Or?8oQABZu z!g<6Y);S3YC80qLYW_l@NiAwaw5UT}h&IK?$ZOm}HwwZAO~+TFj1xA%s`B@!yZTWN zOgsixq8j~#@t35DpdAMumUf@Bgk9ZWX;eJBK^)+syQKFfIu(oy?W1l{?8apr9p&*b z9VOAHoTgDsX+9E>81S?x{b5e4jQOJEEFSuti98!K9%boS1as7N33Bn#FRrC4s-nnw zDcTC8LW?kWz&~JOK*0vnOai$4V84{?%FT7jXK>5ms|vX*vDF5EWOuZ+d%RmFoiPo z6*F{29w2bW9T(^2p5O!i`4;C^BEnlFN^tQvZ|~A`2q_JxtvEjLl}}G1e-w-d^nJtv z>N6INC6#><#L62*ED5d+aLflM7=U{lmF#Tgc@Ot3u67z}NmdS;5ZK2`HiQsuza$od^ zd1)Y`Bz8;1H9Ud?(+%(#Mw6}u5lwdsrV-66i*9U!A|C-m*uge_jJM!%@DnWb$x1i$ zRQ>Hn4HSiD7&-v*69KZ`pt%Jokxu$zQ4ZDhTnCIt)Qg8PZC(Z;Ux8u=>RE$WLjNRr z_1)Xf@yTJkb$XbW501}TM@ItwnPN8Yvo{EAgZ*xDG$K&G4wO5+-q;`UUeC;vDZ|`f ZLO{=zFfcfK#rW=PSLp!LC^)uL{R1Am+U5WN literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/exashareresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fe3e38e2b9bed8c89e1cb2507a89edf29c77ff9 GIT binary patch literal 1120 zcmY*Y&2H2%5VoD4&2D$0;s8`au&e}Xs2ao#l|m_Sfuc%KxTG$TH+H+B$tJZEexmG+ zBagsg_sGj|;OHwSUI7V-NzzKi(Tr!t-;BpIledFG55ao#YH1=Ignre^`9(l^45aP@ z1ectrAi- zoNi~?GnO;@I%P>TJ)%1)k0|3johqtA8D+Vbq`ZiQr*bJ+;z^;>_=Au%j>Z(?)Z$r2 zXJ=maSv#u8vQ*~kdi|KX#0X2MKKk#)bxK{J=EXljXkVO@xdE@nIeKpbSx?ZynA@Bz z@CM4;I8C_lV*0x;gKF|ZG zJv1i}DrA``m+8kfI0d+S&>%`e@0FJ}hIg9C`)s}3UiaaK(%Kl&dn4UZX(2Lt2oOHsJt(F`FK_OVR+7tVdIuMIObvBgIJ} zRIc%JjrX*5B-0|(HWy*ag*L!f)-BgI%Q7+LuH6Ln!HvZ*jN0TdblQf2DdHR^waHIg zO?u6owyaaf0H1b2WwQjWgrXG^5+k4+;75W zy}L8&IfVz{0XWP^TzD5A0Qt%Zi6=lvQ10G{;Fhba%63(KNi3>VckiwgIpd%BXdCdO-O#&9M5dI;<&(Jvf z1FWF4F*d_51TT20pG(d`3>oM3ysB3de<7&KZsyqTyd9P&FrUCw2S7C1Vuo9^a~Qga zz_4?+zTY2AV||tIV#Zne;kVzV@r(D)6eVrDb+Onx}rs1F4He7mZy}ls#K|(%e>ZcQL#qwSZT>=5lgNram8gS@>vRY zEZc|kY9+2$CR9ybS5m9DEe@Mmge4>${SWLA6js@ReFvahurM8fi+6Z~Tl^!I_W?e> z@GZAQ8>fYI9HXb`#h)7&WYW6KIYwKTxjQU>)@18-7CLQb#cRE-&&UpM1LlEMALNkv zI}A2H+5|1xMl5LkHh}eT2dQS!g4{!$JlJ72d<(JBPtFcwbXk{&-TuAaYYtIjqz4ew z{xN!q*_{glaUQe;Y(L(Fy&m`nn@A7a@Fm_1+Q`P+4%opdVsC(Sc#Oc?fZaVst+S0< z@ClxNyZrU?gVt%uCxF><&!GO``j5^`psGgBxiVyt3%>SZRpeqljFXI0mK0o5LuOR* z_1!p`Rcw{0>s-&XvsgXSvf;*6a=w0$ zX+^k6qc5HUn2qNdy z{)`@yBY$bHJ#p@>r_OAe7FL>X9@dO!zL~RneUZRB`?NIp`@H-eQ0C-7KmH{ zP^2Q?C?}U}nLsve`&7GK5%=SpJF2Zxq<2I>m*n?(YbQLDv5*GC{h~OCR1}`%QI-t* z;fI_jVZ?bpl%ed2q)PCsYD!Qfrw?EUdJL#ldmDvL37^Y{991mY7g2ja-rli z>=BzKRMY?;4dOxo#SAh6V>ZZ`R#aRoI$<2FiN)EJHprBYnX)Giw<^#VE=cVynOb8Y za%`I~PDQ3{>{O2UQaO<4PARW7$k?tdBu8FXV9nwaZ%VlLgY|)Kj-)NLM{5J6ikW`D@l_d1rJcUW%R(~|YgfjR=neGZ`*-c*lf%~j>7jNHjz8}o9cc$> zPYT~Pr8Ld7&0$@d4h3GHrdMXGGe9Eo2WCMpz>#YJgnHCwYjmC3bQxp?e9?gTqeWoz z{}!<2Zc;YGq$%oJJUVvU3vhkFso;K^*^n@ B(TM;6 literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/hqqresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b06755a46a3bcc271c623c40f27e4968a4f5f53 GIT binary patch literal 6215 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 Awg3PC literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/ksetresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e7dd9bd300c63492113dce087c57638b6da9b60 GIT binary patch literal 1430 zcmZ8h&2Jk;6rY)${qR=~tr}=Sq9B5>5SvP^5LBgUNGgP=Tu8~qEV4F!V|$Zb?{;Qf zT5Ej@SN?+@>?8jRf5BWiap#1D%A2txr5WwK_vT~Yy!ripyT_eQlVCgku{EhfLjJOY z%Z=gS5q32}0|aPJ`fE@7)Qwr6Iq&s7=Xswy@Am`e!;O#7vwj0S;J=Z66MP8Jw;+TF zeH$9kMBf4WjC5On<7uSV=VwajA+>`|v$3t|0K3{kqe)H9v63^^Cio`pb*TvkQjEn* zjJ%o5{~(g%#(z%^?kC4eNabv@UsZ>xPLrcDEwbr2`M!iKNdd~KO4Lwhm5z%NX1R!! zmLe@;DO8!i5HiWLlLYhF(AIY`A6KtTq-IrBO090%`fQjeKyry zW@u(8xa+TtkMEjmV=*19_YO8m-pBciayuBmg}E)&zUa!WF<4{1pf!~*HQFTux}xWF z!GM96(S=tt;5C-6JejMd z2P@3C2y4F%p>>l$`!yRQ2w)lR^bE_ughyf|q>%IXCR$hbcXySJEgSLKe7DN+iPf*8 z`Nx~ZinP$oD@mH6VGX(M&Gh~h-`b|ZI`p@l14Ubp!J zOq3Q%-f)Kjirw%`i{h#tumY#-+qh3zB@h}>PPZ5%><#xB4QTXV;B$J5UZZjc*WZ$d zy_}V}lgBL#)@rWNpEB&;H)Q2mu^@S@H@|xc#l*68iIc=4npjpo-TUD|1`mI}^Z8f5 zeAY#|Gbv1A4sQDNAXh!C;D-8VuaW?&x(u hxsxu{V=IvEBa9xb|1J-=aZ1^r@FT`)ffkv{C-@7`D*2PB5EK46-Fv}~wNSc4_NK1)Gn27TRaO?$ zGj>q%f@PdnQ_Xa)irOS)#jO;HHcDh=qJ*yGSD~0J#tidx$6GE*edQu;>$*}#KWaI& zdJ<4W#Q%u4Bm=aLXpM)SsG)NY$$}y0KHA_dS$H#OKuHtK!v^LNrwf>e+*`l`X2AUh zE&?Ub$c#4Vt&x3lvN7`-Iv+M3AFL6?V2$wb2gU)peBaO`>?aBt9bP}#=^6Ig?XpA* zV{5jxg%|P3_HNALmp5%o+zOIMbyA`8tQIcRl~uXW&i_(K;RZ4r3shaTj9hqWGg+9c zcuBP^6^mVWDN}gYQNDUrkimkN^Mx literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/mixturevideoresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7cf04cae45f68c20f24421b66ff41565136736f GIT binary patch literal 1951 zcmZt{%Wm67aCb?O)YFdhLJQPA)HNcs6@s>hqHu~hPLLFJmDF+2mQ_nI+_glR5}D;H zum!3Y=L7l)(lNiZ*Pe3e2lUjLrQ`=13Olczot>SRUNoCE0^f_D>VsE6{f*4!&j#i` ztULk038xA1OH6%gc;;IM+e=(oV#jxk-1S|~e2-h)enb4~W5O%kc|*7(tYK|fKePM> zcew|9O4w6ehU&maetNl)Ps=a)YWvNv8hv+9d zOsI4LMo3=%aPK~>{00C)Fu*?Jlrsp5#VvS}t#AH#`sDEN*@56O#6&#_$2<`-a^S@3 zL)wF7;<5!qQJF*L7PKG%RcvSt?Rljj1ua-%6?RcsIAEG}eQ!UFLKUauwxg?kp#~!4 zLTWErinRkFgcoa9idPenD}DVvzUbCKsVopurZV{I?w4T9&P8&hEjXpwQKs!EKB6QfhT z(7BCvYdeJ30XKHYcoX!ROTE_r^$M>~e}>kh=BGC1*YO3vk}>-Qh6WVbS{G^oib&g- zi0Q_F_n@UhCGOq%?T_B}@1M2bVOLMr%RUnlIh()2hM&tw{jJWr*m z1l@Q!i4z{AnHWQ3wBj*1l_V?PJy^Dd3r|EHU4f|u;{j)^_Etkd+p!WOc@u?93hm?~ zl+i#}`ZAqlx~gOtiI-t?tgD%f$0`sfQM+#5r0K}7b8#dGW#9X2k#RFyx#_RT1>jmlp1t#=huoO!fmrZj}=b2D}+?rZRM6DKsPPoXbK9alC`qe1ZV;Y3BMxC7Gfz0VwP2!ku%Uw6{~v UsP8ZmIabOy%x0c-!}T`*182eKE&u=k literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/moevideoresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9409dac732f095188fe78f10bb85ec4e60450f4 GIT binary patch literal 1098 zcmZ8gPfy!06t@#64W%$?n^q?6Fy%1O4Ygc$sThm_nx>(`VC+!Bs!*HOkS4})+18~e z_CeYsU&+^<_7ynoIi*vFqxa8wetz%wo{tt5iU`)xr}^$v7op$Am|YeaZ-LT%U=&dt zq2@&5CeCfEY3GvMNoPh{O_$n~{6ftFb*Kx{qXp`LoTK;Pvh*eW1S=g&emL|~^lR?B!1BhePX^zu> zRq;c6XV9&`M8Pj6sK+O3u=C}heQv+71 zp)F1|mV=kazWTDe+Bv^WUi&M@zL0=D`&K)Pu3Eeoc9_5N)9-WY*ZskBe|U1wCF1eL zj0XXalR>$wl&IHgq2SesDXvbrs>V#+;y|N8IX@HB{pF4QHKVT|fAvpZRipt$6SGtC zq(M(R9hvlnCVj;tO+=C^U64#fVVh}3NFFO~Nv7SDg|gkPxRVgLCp|bVALQ{Uv|5&o zZDwJ@Obc?~XP~5yfg$W+f{S z^XBfZw9Vgp`5T0mfu@6I>`HMG$h;4|2u}be7RL9o&j31iV(Ze~K9e$$|11&8lxeTk mio+gjweq>m5y)FHb9tVIK|y)3ouSaX=tVuxXbr5M{NudQF;OJDpag zCphyrv`79WUpei}Z5Mb>(byclpP#>ee$T&`!&a+~pdEf{^nbeu{j!6bjlkeFnA!$l zh~XG@=RNM?a!k6U?47Q|NS89F>oUsR@2Fd06;_45%J2bddzZj~_QtjMsp2d9q92W$ zNqWXdg7MT>gC4f)|C;qF*e;k_0MKZP87|NaE*sQX zszCLm$h4oNERVUbwB%9ZORmy*#AOhR;{bT9dmWG(_}PW=RGww2)as#4!#*X#vI=-B zS36MwD|7t;jzAM^^DXcUW1BgFyzzu+N7JdR(OG3$oxu#l%ZQO>H1%fa8)nWjnmD>P ztutDX8JW<6s*emEpn^^t%moIjo4Pe^+1(SDRVE-$Q7N419p# zmXN@wR=CFAxCOcYSLzCPq!nmKYBjqm$O_WYI7J5SUq2Pk&abRev^^Q?W>&|HhyG{jKo z5M+c`H&w~M=3Hq*V*yynL~jstbq@gTNGD8RaLP;xxbnMmcsZ8_C zIARDKS|r?3W^vTx?dm+Z>YPIX0aS;yKZ_wscOBSm=y3$X+iIGFRnan(h4B~$>Jb3K z6xZQfz~m1B^S~%+!mORQy?g+>+TJ{{Cirs6f~|W~55p+e{Zxjb@xpKzB|HqvA}UYa cxV?;{48hx;m+H$!U{H4eJY0SR?sPo*7b3_71poj5 literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/movshareresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aed5b988a92292ddd9ccaf5b995d072e40a18a3c GIT binary patch literal 830 zcmZ8fO>fgc5Z(1x+>$^A6>#VQA`Z zl4JkUUODwI^u+9jK+8(=W_Rrwz4vz7wc0Ad)&6jA_{b&Xmp?eC)F_IBSjN#w9%spD5WUYK ziDH0!BqGu0Ng=~5hcacMketO?$eGB~G2>C1^djt|w@Z%8Z((tvd{Guf&ZT&&Iclf;1hz75Iv{D$sNs)G0T>YJaw)IV-4?4fW~;p}_Kgj5!1c*$bkAF9>2Z(7GEht0<6q4ExnKQ@kzlsn*gSp>G} zr5r$S@qc5T@qYnYvhHcD+`xnI5eTi)b-F?MI_~e-q}lzo=`kK8nV}_9%x4yct!+Tb zR%`Nb`557d$?eT>>qQVayoNPg4+?*vVNHYf{y$DedL8eV52mdMyhXVlcjb4xqc~&T gu6awt)D+!nq{oaKh5qhhcJ?)8BXNy7)OUREH!!NU@c;k- literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/munkvideoresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24adaeb23449c896e51b148e5bc7d52274c2d3c7 GIT binary patch literal 1229 zcmZ8g&rTFE7;mSYKRbXTDn>o&$w9LaFC@f>2tn8WG6_taPUuec_H4CTu6)YQc^%Dau~P=&0q?}#5PtiznLu{tapio%O{`&9f^{xDPx1-@0l#=n6_jPj{7ZNMhJ z5u;Ncqw}ct4!$dY6UYKDd8IYD4pp6t7v8JKN3c2e}jMIf(z~Rc-wioqUrqOxne*T7Z)Qcs6BE}C0Sa2(iHqi z?eFr00cRp&x5WgU5my1CF=1X-)2d7}?F)WTaan3daP3K+h@D*>zIxGG-&kE<+FaG% z%KF=-wKeT_MZYSvD+_Rxnt)8BI_9>>$p6zqt5 zaN`1FO$m)<%h+j{lxT>7nIQKZ7{o9WUe0L^Zk#nn+}s(=+CH9z5>LS`aoxZT12+v2 z1Exd&$8Jme5LExV$CNtqM%PU~I_Pxrq{llQ+gLVMd+D4ZYtmLCcrxnbrwwD$G~Fa5 I8qnbGUm61)9smFU literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/myviruresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f538c42df88e6f9b1301fffe6213db84000c6150 GIT binary patch literal 1284 zcmZt`O>ZML)b@NQleAljE?q8gC}I}K95^7Yx?8GNLV#4kuEJpyH1fu7;&hUk+MY^{ zCO3Xaa^w$ixv@`sMk3&Ui5BAvoio`t$Pup+Bwg_&6YZ1E6jI zF~o3zvX0^mw=&6`mb!cD?Wv#npeIGJ46*z4Wy$J-kCc|2E=Q89vbg7R zQi!(`@MGC7iT5|7>cNDnuBuXMb!w9!fDo1;;D2RmyJdrFGyMY?fi`%CHpFJ!;2p+D zldaR`zI7Sd;f(`#9&;M!7=h$!e;YI|a9-mv_XIHyz&}A7Ux(XBBio-HSh4{0AwUFo zLF2RD3A#eRoNdA;WCV0k6S2OI7lZAv2^I%C{1koNX=8o3=gIa7BTXdQ$nL6!$ln|C z1}3P3WN^KHHhTWJ73pW^Kb}GLUyVLbqz4(UpZ|a3XeH901>cMd|G7W?>S`T- zYHh!~nDLc&gU@-_;yKunjdn#|<)2iYtmh{>hnV|Ea zj1Mza7upcP43;Le>%*WLGJ*gyx2~=JCX2e+gjd=b?+|7UJ^_QS zTD?4{wVszU&rO);E4t))E|2Z~Q;SY4vK!LA`X~>(AhJy+JnL5Rm982Cc(5z$cVDov;ik^ZD+75J+VK) zokMrzFZs%8e}M};r=7In$j{Hue$Vgw-pfh9-$O7?-rbuR524>~b9)fj?8DU0K`_Ly zK+$r=5oRPJ%!@omS?3Z(l=-X+Ylq=uGz_l6106-(GM$vw=luFFj0`*DYB-(3Jbe&VL+8x^ literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc b/lib/server/__pycache__/novamovresovler.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..944d12dc13e8a8946bbb25bbdd8a604d540a8979 GIT binary patch literal 1015 zcmZ8f&2G~`5Z?7)+%!cMLPc=kP(+1=?U#RKX&buM(zm? zJ@638v9GjOPJIO~FtZ72%SQ9f&a7uL^UWMAE!7E}gO7{DKQ}!dl}=h_2LSrKW@NGTTo4~2IU9~T%M~5R#sU=v8oCF zwdAr;hTzJPJQTg5s=t5N-rd{Y+}Ph%&erayjh!834@6exo~?5zi@`Q1nkbq=lpeX> zn;AIpH<($kzymPkG6112tHA}ii c9dsj+PN{ozU(UAv6}BQ>fMse?*L3TD0o=~=5&!@I literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/openload.cpython-38.opt-1.pyc b/lib/server/__pycache__/openload.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd96ad2f9e8dc022379653ca7c5f2aacfee61e9a GIT binary patch literal 8060 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 F{{yzRmRE zL7md$4kkv&ASSWCqmD^z;()h^OFZy4!H3ANTtO6SPbzs5ol_EstcTUjzoqu-gKaRr z2p~{_zF~sC7&Qdpxb0)v5ui9{40WKkNA z2H}T{#9>58HsT@gu{ak&nvror11=bi(tuH(C0{8EllVAw< zd!h(jXfq3{kv2__DUnXxXHiNg%R?dZ^=9)%mF5dRsrTcAu0LCC1ZypSbz-(!tx4^y z5t21O42B|2u9RF7=e{A`({Yr<;#_C;=rL(jDd24S^+MrCF`)*k=PNl^`Z)p+GRpJq=>EBPa{3R&H zRer7HQ#kMy0E9hk;U&C?SHRc67U70HftNd)scvpGQ-K00oeJ%&ehJd(uC{ zLwhmdUz)2Y{{>IJnFWOCwC`(n-|PGR-fL%NrH0^i-YuX0pa}i6!R0A6v?n5h03!pLdIEo977%J+7^fW`bnc0n^2WSkxQ-W zwnN)0!ZHAT2Zn9P2XQbiz^PzJt?`^p(E`t~rgO&e6fek(IG;G5I-fe9O^MV~3c{r3 z5Y0XAE%20GSz6f>x`t-t=9X~(I~f4e+!7YFC+o+F5|khYonDQ_I=Ex#1)A)O{rbgb^{pd6{IX9#^q1Ejxxmer5tG&P$=R zAyOD$iA45KjW>{aSwx;;Svop1jKjQKu3@$Sh>g5NI^VKt0&c1cbgs3kp<`_9V0ek6|ezCYRw&X{T&zuV1wf4)$C{s3Bj)VTlv literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/publicvideohostresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/publicvideohostresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2166586fe1b6bd0294029a52efcd443b4b875ae GIT binary patch literal 1337 zcmZ8h&2Ah;5bo~j`PsGCAbt#l9Kce9hbZG5A_P|O#@G=;L>rRe%Vc0R-tO#Yg=nwxzDGs-}OczN((HUaw1VoITl^{OJ+$mocsn zhQ{#S6x9G;6MSqO}B8xl15&Sn2cZDlF#4X{A0C8J% zL>I9q=ra;-{f)PgLHt%VJI}MxJQK2oA6F>svQ(HX~=5~>KC?6@DMr( zsrH5-$MRRyoEhYQ5a77*pW@^&Ia3mpmC12cou+k~JT22An~sz3OOYk15am=QY6MwT zM@1=Sxr|f|GA$yIs?6sSk~}+4@EoIVgnG3ks$S_p&8n(|T76_fH8B$k$Tk^S-Pkyc z9^kb3?4eSVB^9)0OGmJVtk_zkEB1ozl9eIK8C<%zM087lVI$<)7jl#pX|Da#wc7nsI^*Upj)r*<#ArQbL!`T-`gaO+^%5xhaX=;GY^hO8Xi@8UPF z;f*J}-`N-w7N)TNK-0PA_rAPaU4AyH>+0~}Ko;jxL>o1TN*Eu^qiRz1FK@iaa+w_N zN1yk@z5V_V_pu$0p8ve})9nx(j2>z6U(V7zt6#PDHkg8F2PxC;7|L0t`K->6S&%Ph zQq|hSBd`IqTcx0+?w~2Bqf$uCKx$9P6h;%>{qEbreS3wb zg=U%19_~*I1#g@CxWG=-e2#~0RtPV)ZTAVTQ}3Z5G@zXBFh@; zGvx3gUIM1q!CVI)qX?Nz8B9xU2?O>;{Q-IGgQr9JEq|iFql1UO^eKNKPdR5nQk0x_QgZf!y_`Mw?;MoN1r2@& ze=7F=eO=T3gCFxh9X=jF6EC2n8YRBg%tq2A>RWH>RBsy8pyuDSrb%4`MzG#;5yl_k<@B)cEnD;BdIau{&yO;;NTBO4>wx}f^nhx)@~F%a;4jP8oC27 zIBNYdq+ZLVGz>&bbhsDE`XHo3pVfur%pKG@6QTc_@s{r$wtx;!XG~5v>(Oayi(wRn zT#8>JlgO<`xDEX!NpRhSrX+D-BDFE0B+<_G5lIMNOUPJ12bri24P`U-<7%U^QNMM+ z*4Vhe+4X#uu)!gt@cEZJw|}>LyZOzn+Fs2_btFqe0FsddD|VI*og&%q-p;Yu4> zm&cXGBPQFuP{_6$(6&cEf<=MsYNSL=V(YvN^Nx}OSK)_q1DfLfS1>`o`xdWIeWV2@ zHAWgW6D=WKeHA&+?Q^^mFCQNt*JnDbcfvuoB*4(3=W>=U^`wk88;y0*KpA0F+}8Be zL^e}IW0?rLO)T9cdfeE7N=9IUm!NcMoD*>!)h^9( z4h%v@L|WrKH4<{J^Lx^!<{2Rx&CMCd+K)e+@coXEjZRdLdePVMGcenhh}^*G_^uG! z5f9r#aHXBrhVvBX#-iDE{!P=!*z*G2$ z@~0CpO%~PP>Rd|jtTWPrm>B6&Pe`Jl=_4bqNMmd!V5pJ#TnlzgP3B%&FZ2BZ_lY*j z5iR}z%tVsh%Z0>1FGPbq*Z!=%HM4jc<^Gy`0pEfCLIZ!bWMN!Pti*)q%O`pM7LA}Vk< zj=v?`nd7ZV19X?^D*Xjm&KOma zN>cc(HmXdlS$Wr*-~pQx-MlZaP3&1Y)Bl$>mF&w?*?~fHrkO=$o3*KJlEtZ1N+Qfu zH}>O=S%TVhjzZ~RR6C6GkUQ(t=`z{r0bV%kVlO_}a-AM$-Ru0j%|0(W}NE&B-R15MCHKxZ?yMsCp@0 zc^C%L3x>>bMV4z2G*$A2gUx&=47wg4#GlNOdRI*mBHpKh79*6V$Fym5;QQMV^+fKIVG|#Zid_g6}tZR-M9z<4+1OYbETl%O?MD|{sXxg zPibC4m!Cm%sj-2NPO-IqD_;4@Tk(f8Za2ZYSR^>W&V?>(?ss^S%6VF@KXW=00$)FL29C_@e-aYP5h#be$gxg-BTZ6 zqKzD-iCC}(H%!wg0D9P@Kt2FC&qmvn^w5^T`OjUl%Fr-rMuW+H8~dri2s>$qRfiW9B~wx etIE{g$|~@k8?dBGi8XK#+qCm`!CthV+t!bw>?~ve literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/rutuberesolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1174860dac076bbf1d084757d23099a4fb80398 GIT binary patch literal 1060 zcmZ8g%TC)s6rHgrj(MbI1ypp=1&dg%l1i;Agd$oBsH%!W8%h_!C2}&;V6bB|Ge!`F zRlDjpbdfCirP+4HFLc#=?Seur-IvERKIhy!2bIb!f#=}U-0{y6A-}Phek>^7fT=Zb zoNyYEb`fcta;t4|o3kIJZSxX$ATnO&F2oX_;j<7OPWOp7cLTdgv+c-CXI;^wSpIu3 z$G>cWsR!UR8IkXllT&M+z+T$)sBt<{^u@(BHFhSWYa#(F`NOx?{R1VW%DumtrrUuI z{8kdg;h^t-NO+C>}>fNBu-i$>2hGja8Oj(MvOek$ieVJsbv7|7L5`pX; zo7wm8ntQFCtp|N2cMEB9nWIM~ZvwpB=`WP741ipH%+FcIAT#qLC4%{F`W@_8zAQp~VH5DQa2%YrD> zmjyniU{XA*DBMm5Kk0Nl%g_v1<^%8_ZWJUR2*ja~Nr7FiKz0u`{`Rifi%@7XMywD| zEHE1z3C*&a!)Ha8d~8$oJmey&i?}QJDED5y`ubxdp+DNVdI%WL;L4W(6j7X_ zu#q^##wMX6b|e!MCJLSpu=dQ4 z)^*}7xN;ewKsEXa>AsOA1i$0Hhm{ixHe^4}v9d~$o+AO}qrZG>JvxG@(qOci=i5n< zMEhAX;^SfTI-@*FD9y$)k^{l>!XIU{OqnkW!IF_Ln9NcrDN6Zq)KoVYy2$GCwV$6V zSC)C6i9+7dW$Jnn7Hz<5y;$II;F?|zL1}ssZwfpmHJafw44PGAiYb{{XSiA`>}iXF zXGW$Dwc!h*Nvz2#;&_PP0Rz}PAJCY*hhYFC?S6H8gYwBePM@shX>!U$Jjf`kS{nhl ztD9faNmNJm!;hb%`qLxtLQ^bkfGRjCihMl?7NhV7RWKOx$H#me(9{PtxG#eVr!4a! zjMp|zD!VLrs@j5mFPSWqBgNVx*Ly7tMeatktWR~X(gk>kZS2}^ F_a8X4=D+{| literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/sledujuserialyresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/sledujuserialyresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0fb6cc685125d5deba73a88f388fdd12fa3f17b GIT binary patch literal 1271 zcmZuxUvJY$5Z|@ej+3T6gv5U#`j%QqE=W986{Qq}5GdTiRXXK-irnnBb>qZlcHL0R z<)wU(nwg#TXm;lJo6YX>a);pB{cGv)t53)$#{#@gz}6{5nr2>GU9ek@GZ8IM1gtmLo_D6-{zpW+IXbL^6p$ z$UHk15NGLLjCssfW`dVTC3Vp(YH)w(}l0 zO*^Nwwk^p@(YaHT1wD0YXYO*kpxhZVs1%!f+^wC3bL!SER4F-SwLAAYThN+uwn=P+ zXCu7YgG{w*ulasvB7BSpYIa5`so4R7&kcO7es$$&R%FSEfVJL4@HA;0Fu+%iU5yRY48@-QQm$#*mU|U9$`nXzv_}q#`Q%O(tSEv~S7MWkIk5^%G|1#e-!(*leEh SLxYAi|8n;byTA=D1^zejXD*Ha literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/stagevuresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4aa8c815b86a8ca586b7c7d17c968147d19c5c02 GIT binary patch literal 855 zcmZ8f!EVz)5Z$#M$0-dUfeHr>l{hpelFJPVr3oRe5T#0?C6{DF!he6s~OFEvz>BzgW&0Wx_9=fK*(>M+&%^-Z_wmR1Ry{| z(p`DlrC@XoFv0pox+WCBMsGn89P|aCr^GE?V-;z4i!$}c{307sov)9Mb^8{YypNEQ zIr&L}d^I)+7Spy%l|2xA%)ehzWo9D0A_9jbe|YOP>qstSj;xs^`+n-Plh}`f$(VhJ zAz(g0JdsR}M3AIj6hjtrPo{$Vkteu}!wW807z`Qq(cyZ;n

1RF2G&Bo?WBsz=nb z5-N&_cX*h1VWZ_d+8C^Oh{KdF3^a32mK0E|rTK%V)}jFB9R0bCR(wnF)|eZbwLAoC zZY&%qENMW;C?IGZ?DovA4P&^h&q}r36`%U-3Ch8GJqGu>LE?^JqUcKTIUSUAD8 z*48cMvnne~gHYKr8zOsE6#QGpWvUFp@sj&ubfz}mziS_#9JHFJ2g=?*{@gq|QiZXI zv&1#?hROmISkWlge+8PeZkJ2>3=^4uCF;<}bPG{D!n-?4>8buHwS`Ga7O2b|{kef= zYBi9t(Vo4iOs7-kxsCN&yipwOR=jG%%>vk~G(Oe>sOP!sT4ED>iLwU$kHiBFdM*D= zEA$GVquuOM=dQgfFJY{l!C>M?d@#tR%NgWFw;JgWD01#jZS||_nu>G~9#V@smQ(x- Dtkk=- literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/streamcloudresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..592973e4d2605129eab12e82f735ebff2c4f924a GIT binary patch literal 1322 zcmZ8hU2hyU6tz8`kL;!)NDvA_uv&r0P_pR*iXhr1q)9=DY6}ff%d*vKx^^~GW_KpG zr=hFaC-@mWWM3+kpX67b`WJZOUQY`fX5{PZJNEHC*T-{m?OKDtbMiy;%nu3q3kS;& zgTYtO^cP?R5i}$Hx~F|ASf2?`@ZU+_6TS$b=OPpl^uAaT4d??wk4fBo39Cu3-_%CQ zWHQR~Qj93ZUOnjJR)^5^8W=+=@*5T87q&_Ofc9c)gQ1dR`Rfa6y;5Z_i1K0L-|6A* z;6zKMogEw$#Zh9C!Tmg$q|@=>yIiD$M2LK<2YRH^!gMCNC^OlqbLl9l^X#cqgDibK z0339e(tQ27Q#`YgE{h^pMqkG?F<(LzRE=EW5gg57gU-}iVnBh+XXyI+L@Q%o%>vO;IFC)PhwDO$iFKCyX-=3`l(n>`t zZ^7ol12TP|5_}=r5J5#YU=K=uA|gB{4+)?*MN|baH>^VSyb1wxWkJVqNCNBJ=*`|3 zWr^0e+J(wbb2Vwt!tORYd$XNxIxR}mxv@V@Ch}H$bN~A;dV8Hu_v1E%pC(!PD(>9+ zgZ16Oa-2K+WU02ht#NKzXT}sQsZ_37r_zkh;M7_l>#t{Pr)eg4TkTCij@zHTM2*C& z3d?SJY0}IFx_k_9HdOMwl-gLPq>WC~sYs1fw(;#Zy?giX93C9svBA;39}n){wf5jg+JMzstG1?0otg=-Z`LGfPXL?k?p2h+sk8 zrPu*9jnt3~B;5w@O47J1es@W4fMdudrV9NN2F*kLg05I^7UMbZZf|d1TwHYG{UsT6 zWV!wG&8;sFw))A|?9q*wEBpnhx6!Plc@vF?1}n5waV%OF+Uv`R<2bCBH*qQ+hr6PV hz9Zliv;^cJk%W!9^{-(MnP~hmt^?~RjNXZYe*o*ZG6nzu literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/streamintoresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f8114b3a46572dc751024be636d932aed10687a GIT binary patch literal 1033 zcmZ8g%}(1u5Z?7a1X55DR8(p@njZEJbeCBnBroyXy*E`_w*0 z4+)1#ywYBK;uU)8?7At?wdR}Iv3GW6zVXTI>@Qfr6#zj5 z$EaOt+{Q#}YeXl;chuI2Ni5I?v55oPB={J)Q@3ymwelL{Gz*xxv9LNc?1h&%Sm+szZxA)%!{7bghJV6B$`tzBX}TBiz&}dl zJw5+@LLxsPBpEWFb!n7xZ;+5I4!tTDkA^IXzl79}qt8CXk>(^#o=o0)>6LI;mZk~i z?1{`$ri(DO0Uwki9nit5l%HT!rIfh9^qS+5M!*{BL>uEJG{&XIjgd+80`!>$V(*}_ zUh2wg{O2`@S(@sNRiLp}T7^Z2g$2>kKSZ{;*2*9EIZqoK855 z3~6I|MY&dTz1eK$GnZ>VS$BP}&j<0X1E0GFm9tJCNtlx`FF}_e zXZtQ!)tZ;!nv9)nm1|YUYhVkf(-{VXu+vc!sWMiF?gdgIRpriR^$%>x)R_Y?2gN#u GQ~v{y8ttb5 literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/streamujtvresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b170a80b9b11dacce6db95e3ba1473035a82983 GIT binary patch literal 2077 zcmZ`4%W~94aAsE0Y9C&&u`!qsYC|BE4R}qZT#!(XD~?TtRESA1=CG{Ul0D=uP zk^E3br*KUbAHZRKNfnnIa?TgzkPnzECw_rSD(R8zfSp3B>2CFOPe0V%vpO5vn@g*#A7XU>Rd#Igb+{W2AX%oQ2D-4XbF)-UEMzqkjs6oxAs9mHM zEdsWwO-q1F6tAFW`4yaomYYPC)^E;Iwh>U4k<8a*yDTEkoI%`=b~msVJ$f{)Jz9h% zssJJ+(NEyVb_q_!%T27TE@wTq{{pK*%)J+g7eVax*5d8Xs$g7Xu(J?`OHSl;9t6(7 z^?RLf1L}4hN&{bX#0Gc6s5J;^?6FqvNQ*NOc)N^uJa@eV=VM@Icuj4#R*) zVodv}okf_J0GKhcLRapVK-(@j_l@p#+WbwrJ2*g>Fl_j*+Pe4?- zoCjvBxzUDoKxEtJ-t;hpaxc1@c*zex-J2K{ygV2k6u0Pundz;DhJG2Ijy3Ub8jPj_W3B4`>kZ5f(h1Qa|uNU4V zljsrDgX|Bh7wjc8O53~tw z%YbH1E*y1FYt9Qc98b)*{u68#lCtjF=><@YRPh1ZWSntjEe651%aRH1cWys^(z(3$ zN$2v%Inhkcd5+%`r#G%O6?&B={~{Rt(e)|YPya6j6U5B+l8Yio>5z*%vE#YXfhuk>hr;wxhW3q5!DdoyTx);{;Az+# z<2obCh$Gii71wv8F3c|&H>$|lPRvB4j7{)NnT+#*E3&$vjDFzys?y!a($VcZz~)R@ zf;oI+OO?O*dinl?JBte|ca*hs|KY;Dd&=zbAP$u&LU2r3n*kpXI2ORsDRKl{`Tpjf!MRbdluA19{|EOwqTWU4VHloVqk(N{fmS`^SApW6gS8VaqN_*$*KBA{0uiAh zrdR^*6Ica3OriP7ax#8=IdpX2WTH2;7zY&rSKiFj_(g$ z8qIR9{vOcq=SAQvYuyp-<}BB}%L|%sce}b5yWQ-{$wJ5ud&@v(D(gslF2CgF_4Oob Q0CZMtTqj1sHtf&-1$Z47TL1t6 literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/trivialresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e20ed53d0687225e81fa55bd075fb16778e3f61 GIT binary patch literal 672 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 Nxy(y;%C_u%{{{AYz8(Mo literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/videomailresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35820c957990914e950f783b05184e72141c3548 GIT binary patch literal 1512 zcmZ8h-ESi`5V!X`n~y7{+)?rIP@z+oavO1im!rKB+7lp7>Cw=ablYefn`G17?ACT7 z8f~87uRuH`kNh`0`<18r7Z4JRo%D2fw(RkYXT~$*pM6%Z`v}_EkJaIchtS`OdA%9H zd<}zt2NOXAr>MK4xQjJ!bWKgIHMM~nU5A*&`VDnmViO0bM_l3otq|NnLFFZQL+x&5 znh=_2agz42lB}CrtAmeV@CHl*mFQPE?%Jp!FvslxOQ*-^fc^q2bHdU;5wk$?pTW_S z=!{dw^&GW|;y4y@behLmG9E-f<>c%LPO2(z3_QW|o>Xq<(N@;seV7NyBm z1b)==Rgia^VR0)xJ}HWv3I4tcQbmg}b6|cW6BWD*LuY~@uT>^v;pItZ_v^_Mi*OiuXGoir3VrDONa@ho8L*b zbV+R+C3s!||L-ogN(cP6a%f726(BJ^|C_l{Q>cTpTA0@ z1F(U++h{Wr3Nmw}hv%1{Tzo3g%xTi>ijvs}Lm`SM&8Dt*TqI$38!BvsY|@QLZmsMLi*o-+desccGT)Tlg{zU zPp#+Ar4tumM5LvXmv)-Rga?gPyf~9KxX8G)rr=$g5QK8i3uzGmeb<^on$qd(YOD^j zl!3%kR#(SbX)?an?`AdGN(i~=C-5OK{4!b)`y`W9x& z;EflcE?Yed{Q|y#g+L1NCD4%p!&K;-V=Xn_lEUjK9xLPoW_z~3Mn|}Js6Y_`Dqy)^ z#0;M8-r+P(C-h(^%clwLu>QgB;fsB!%G2}99fgbF4p*QWX;di(QS(j>#6c7eMV1B@ z+kh~o!)eU=L-r9c>|>Y$b5(aE`&=5?GgW!IHSFu{#$f;wb$29SN|T&2Jk;6rb5$uQzGZ7z9x(@ezrVc7tRkB`qKMB26kjTDj0R7vth?{l>|*Yp*l2 zE{U~1l`GLmjAYr<2M|ouL>hSjJ@$<%vXGG?Z|d zL_R?UkSXGcI3j+C8{!3~;WW%Z>G4dTW3qg}hnVm-yYo`t$=Sfkot)>+SvGX~Y=1E) z5?-kMMaTX=>=OL(wS{l2@yO1;v$G#8{Al4Pv-ad$3wHaH@9o;p7Jjktt9AJ;oay=V zZyboFm0l5XX%WUsbH%Q_H!>{U8yV1pwlr~z!M8^am&Tr##~-_VY2*@(urb2%A@7Kl z(A8cNt4w{is=FD_oPi5CA9&ylExIH0hP>&{ zvLS!O`%YFE_)y3Z?-y|dSJ=?)ySXdx<}U8_hkO@r3Q(1pK#OQuk zAi;%QnRYwckwW`QM6$7`CqMqMa^vRJt<76kwZDDi_U85Lp=VF$E$so`FU_0Jk#5$P z@f0`?Ry13tsk~kZ)qmYHrvB3uQI=R&Ap`&>h(=#{zxwwf5bIVsT4n^%lv18N=sGW$K zj-_=I^(-nG8&Ir>_Z=KA)e~#yRC+5htXgf`A7RyuiOOe<#-iB{8yPK=+ZrW2D`Kn;>3Hc*q`T~x_m!7Ws+Xw29`VjKFLc5 z>#+DRK!P�~2(jl@MIV)e06?ozW)!eu0HSyu`ae%!HHI&Gkihk5k4Kx@&Q~0a|w_ z@bQtAPa7vjQ_=hgok!(N8Tcs1937e;J z9I=E?$|z;H2(ujEy;PufNG_GiFJSIc;g<>SY8mS41S;0kdiWf}j*($myPgh`*|mt7 z0X@#avfrYf-pvyWPHIY*dr+q}+CI=22`2p>#J5&E@PUzX4a%YCmd$B(s2i_TF4zNz#idW^C8D3U~fTqy7 z<+M%ck>94I+=vL}19I%~0kJcdvS4|3_2UxImHh*^3_%JVgi3+Q#tI`%{6ORw{hCsq zNCw>zp6@!g{1@=h7sDv>Ix&-;) z?xIztdI4Lvk3p6Wa#q(%zze%xZ+juF*VQ{wajQ!MhLlN_yS;StFjr&(+y)rI2DWv( F^cO2WCHDXT literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/videozerresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..547c3f29c3b626ef6370a0cb49f3de98fa8ea1ea GIT binary patch literal 4188 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 KC+Q@dz5fM0FwMOH literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/videram.cpython-38.opt-1.pyc b/lib/server/__pycache__/videram.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfeed3428bef9fde8b29a51723b3afb12a58e335 GIT binary patch literal 871 zcmZ8gOK;RL5T0=!n=TZgB3?pBrSw1wia2npRtu;-fmA`O3RqI2bZvH{CY#hwDjKCH z`Um(2yGQ<#ubg`1%87Ayky>o|^NgK&edFveFL!~){`+g=-!{N6qg)=0lvlLWGXf4A zQy9#593W={#;t+HZSI`Iz~(OR(C%>D1K+zK3)rcb-sDQG?k3|{cTN&6WSkArTrGx? zDKc!)Qa1@om?FmpW{ktp5_w`f^-7V(r(v+L4})Qz`Ajn@w5vochhrbLRmt=xNS9pv zIoR3^_mz;U#qf1eY{z9B?&fipOh)0`oF`$-c|K908p@<7gDmG&DgsqX5oduEDo;;@ z4AbN=BtJ%61P_W+?Ww9La#^Z}CTYVFp!5i@RA$kE$)>IB9SQY1C(h326NyJdOM@y{| zjG%#oduPa@!6R4NL~FY@_=6qWhT7Wsnxta0wu7vA(Q;>a4W4WK)@pO;kT2csrHLwu z`J(^W1Tgiwz`&zfKUgpp^wNA7r>alFzpra&T$aVAG@Y(*2ODj~)s3|$pTDdfJbme} z1sfOUPC5j>tL>^xQr(f_xDu+=T~(w>nI;pVq@h%r616juc~$sUYou*X?bS>3?$JB= zKX-E0HuMphs_O)R4A;>`4_8p$BHefA&X-Bn-6)Eya-7R3(q0rz;!H$Q+u-)wOWsX? ZWlN9On?Irc9Xe9xukz4iHrkeF{{1cFct$xfpPP?fZVqyj|cmWK4fT4XJriM`40&gRV6 z!qu7=;D6W>5A-k1D^L6do;WkMt2Vnjm+#EwoH>{C&CPX!-}#Rl!_Oi@{;}ZAk0JOP zNma-=;WQ`Rg`{2Pu(y={Qm*VPyjl;sf!iN;Lnotd#924y9{2ws-8CNY5al|Lc#Lv` z(=*as`xm{EPB$25g^+z}(eksp4ekU)oH5JO)rPRw4N)7cHehq(uva zlKEIbl4rjpc#lOs3*<>^E~%cY>zmqmNL-yUaN$o8kI-IwJ>Hh82bl{hM-9HhCF)h>t&0U+RL zcX7CfervC_x7+8X+pzSPE?lA2dNVBJCIWFi5=xukBFlv`{&4?`1A}dY zQJ6qyIv3b#BS>{7OB3dz9O$88LvH*p_xCE}AAE&8x*nx@rYGQIC7LnVsu_QzvmDl} z8Vo>=aPy_YOM+ac96eo$i85hYRifn0$Q?DbFdhhPe2$F;8+aDpb$4Z26s`#u%$DB9 z?)vEy1nPZcgvQjz|2^8E+ekk0ZA4aw4{-gh7P5ozx`l2d&}2rjWl&x+B<~wC_po5B z^YY`H8D5s@-lJ!S8Go4Y&znu(Fc2nGB8C3YtbhM)=jrpuC&y=xO?dkBr{gD2z(XH5 z-rzEIfc90R+sri^@F5Dbg|)>=!LeDCC|qFESlX);lz5G>EhLx_(vo9sBQE+`k>+sA x-W2zGWmF6{W;fx}+s4QPno2NvRN3DjFgrbLHe@Hh1p%Y-hbb zB`2SR6h1(o5f8~D-=q(Gf_>$wUx6oP_ed`hXKQ9>=eIMS8PCpcys?EX;g?V|Lz z+FX1L+4BZr`sNtPKUEDJ3`6KdGsgY79RYkyZNBKn=zhg z-rh*ltw03rmq{=P<8J%=1j2Rzki=O#+vi~_T7v}gh_y1oSukjEmL<`U@pcsMwb73f z-v@aPw$fwiW_g+>Tx8c(kSbb2xr6*tCQVe7k#r`1VP4Zr-q7r=pr=Mb&ght;WEK># zbY~{+tb$^`X5V^4`CDP1I)EBszb+xUPso{hS_T{K42?N6F;iounV6+9cP3_QtUMEQ zG*+33VQd&@btdLQtuP9!un)|6GAmqk1pN#G|F3e`()-b;oq{#aVI)hO9p)?((ouc{muAFbX>f)L7V!O^tbF%u`^C$rn;Sb%rL*8>50;mg-p`^I&x>KdwVw>U<)zl0b&vtd~m6s=s6PybL!{~#Z+`UZhiiO5A97?$9*`NpO4iSGZ)z$CheN;C;tMa;EMeK literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/vuuzlaresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19703325b4cef764468fd83c37df6b4771a31909 GIT binary patch literal 947 zcmY*Y&2H2%5VjpBoBp8+q+UQADvDSYB^)?a+m_N6gs8UA(#sN9g&ngSn%x9DY3V9G z!Gr7``yM=nubg-VPK=#aZ7unmu{|^K_lKOtM zpegCpmUbwx4g&}HFVb$1aoP-NwwFNqAz}2Q0C;fqp)BEYP84qR08WyHs7_-ZhZV4J^r-X96uivMZOk> zr>Cbu%_`_+!!XYC5JZ|F*)UJzvoujfxS}>qi4~<;0@^`;{GfG`fXIT?O;mh+*;Gdd zt1n}5$&q2xlSU2A%OXj2LyGUEP=#hvXite)_6FMR%dE_su8r4ps5xMVDmvTf#59DW55b~9bhaxmIt%i?~Ws^(ObGd+^XWzVT?;q@LZyoMxZ)g9**500U z%&F=8H3KWjecZZwDMlhx$`&&JnD)Ef5q_%MwU5ZMu}L=y*^*Wdzg0ik4I`QI5f&Mz IKKJMT13=x~FaQ7m literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/youtuberesolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fa170cca9acd0b513b7beb58d9f43e5dd06beff GIT binary patch literal 14598 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 literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc b/lib/server/__pycache__/youwatch.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63e4e6e2a3b51c28d59d34a277b0a869357fbb56 GIT binary patch literal 1006 zcmY*YO>fgc5ZzfjjvLg1K%zwmDUCSb7F-UTs!%@K3lvqNQ3a%KE!VqEOzqg+wV{oi z6a53+IV2Jn{?cAK^)GN@)=8yyt(l#5)^Fy`o9xZcHwdo1kGJ~2YlQsOhwF>t;We6k zijENmG3ics=mMi%%8ahTOjf%fU6Wa?j(d$khdA~nenEDIOYc+3o4ZjzSo=^MrAZ{3 z+eOGlpzQOs_#Wh8KLmYswsdTwZKBCL=yEa!1|^wLMo#7sneGgiuRitMRP@$(a>SYU zZ2!=64^}<5pC_?H6%|}rk_RH}JD`k0#D9pe5b@vP=DN2hxsa9j-elQUkO$sw8YI!6 z=WVAf@&d-vf%If3qAYilloc^|WiEJ-xPr?x{>FtDM@Jsw=>sucR-Kl+a;f#WQMRU|1?r&5Cb}Le$-&YH7%Abz5+;gO?IrxAr=}KcSUZkl zzCJYFBzsXwpyhF&D>$o8Q&&eU)o90&%)>OvHXiE)x@#7p#Kvd%aMpOY9j9Rs%MA>! zZVO%L@SeL;rJ6B#r75WTEuCI_-VmquHc=5 zj;i%UT4bsrvpCA*XuzdYt1!9^F~!8GQYORcQFGJbz!zdcqiSid5kur^XqNGHHP-}M zK%og1*l-t3EaESYH7)5j#;W1_L6P@U;rq(={Q=73`_&v(cduora#j7a+tX3qxUci* NKer7wHNiA&^FR4U?_mG{ literal 0 HcmV?d00001 diff --git a/lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc b/lib/server/__pycache__/zideonlresolver.cpython-38.opt-1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08a0b436c7ffc0db02e5f3207977e76d39209284 GIT binary patch literal 885 zcmZ8f&2AGh5cYU~vXnGJMWRAN95{80wBcMuEg`KCqDn>17Xc@CGCmk6%IH+N5dIfVSw&do*9d5$8Vp<;wV zLV61eJz%s)nZ@iMq-QaQxoB-xV;)+E!2$8_Tw)gK_MBDDxz>di^g#}3SVa| z4kO00REBaO;=BmP87mVW$U^XF90)G6xTH zx>o|ChWZu`8v`zi!4cwNfnx@h!6J_em{W#SRpi{7k;0nW%&N#LIfogYKE_*z*%dvf zbC)?7vzS|1F^u3HvH)hkCn-Hb(vg)~ciITn^CUXsB9N0|xTVPS$w^V<8_niqG6@Wh zAWfQ!H_bto4&%|*bh$N*6TZ3j;Zy75Q~y%4FLdjEO=x6_3a%_!9xJ;n;zZR1|59>U zC|B}G3{I3Y5?Pu1wnfgM7`C0XP0KnEnkGQIqBfh9!nD)Wt?Ww(F?Fyp0bQU9wby1%lVH31s8TL) z+<~9a9{EeYa@t?u#OoBsItfSbXZ-QL&to4Jivz zI6#ew#SKhJgHVH--%!J#8JY!a(j2wGW+*;DcK!-hp?V`f=tli+=*y^ub>`2|zCK_J zM9hMcC_!H_MO`w3U^%YaSY?}>wb?m%MxO^i5I14sulCledngzesdhKwc-xbnyB~Qy zKWw`%BkH>zrBNtc(c*qAonA!y0ds`p%mvXDbm9wTiGCbM zT#5&}NL?+$9FRg&o#>kKAZaJx!I^Y2gkFXu(H-Ua<-n16L?}-1$e?75=g}As4QY-t z)JVXdWI=M9XlxA0TNJLCNLt+jKgCFYdTsJ=g?W3$dlZs)C?WikW)cD?WXIUYZJ>c_ zEOYh2W=G0+wNg1dJ9Dmkby|Z;9C#OuS0+G-%BfFTqlu;(2{6cyiwXNV literal 0 HcmV?d00001 diff --git a/lib/server/anyfilesresolver.py b/lib/server/anyfilesresolver.py new file mode 100644 index 0000000..c6fec63 --- /dev/null +++ b/lib/server/anyfilesresolver.py @@ -0,0 +1,104 @@ +# -*- coding: UTF-8 -*- +# * GNU General Public License for more details. +# * +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# * +# * thanks to http://code.google.com/p/sd-xbmc/ +# */ + +import re +import urllib.request, urllib.parse, urllib.error +import urllib.request, urllib.error, urllib.parse +import random +import decimal + +import util + +__name__='anyfiles' + +BASE_URL = 'http://video.anyfiles.pl' + +def supports(url): + return not _regex(url) == None + +def _gen_random_decimal(i, d): + return decimal.Decimal('%d.%d' % (random.randint(0, i), random.randint(0, d))) + + +def _decode(param): + #-- define variables + loc_3 = [0,0,0,0] + loc_4 = [0,0,0] + loc_2 = '' + #-- define hash parameters for decoding + dec = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + hash1 = ["L", "y", "c", "X", "2", "M", "a", "l", "p", "5", "Q", "e", "R", "t", "Z", "Y", "9", "m", "d", "0", "s", "V", "b", "3", "7", "="] + hash2 = ["i", "B", "v", "U", "H", "4", "D", "n", "k", "8", "x", "T", "u", "G", "w", "f", "N", "J", "6", "W", "1", "g", "z", "o", "I", "r"] + hash1 = ["c", "u", "4", "V", "z", "5", "k", "m", "y", "p", "L", "J", "I", "d", "0", "M", "9", "e", "3", "8", "v", "l", "i", "7", "n", "="]; + hash2 = ["t", "Y", "T", "x", "B", "g", "G", "b", "2", "X", "1", "R", "a", "N", "w", "Q", "f", "W", "U", "D", "Z", "s", "6", "H", "o", "r"] + + #-- decode + for i in range(0, len(hash1)): + re1 = hash1[i] + re2 = hash2[i] + + param = param.replace(re1, '___') + param = param.replace(re2, re1) + param = param.replace('___', re2) + + i = 0 + while i < len(param): + j = 0 + while j < 4 and i+j < len(param): + loc_3[j] = dec.find(param[i+j]) + j = j + 1 + + loc_4[0] = (loc_3[0] << 2) + ((loc_3[1] & 48) >> 4); + loc_4[1] = ((loc_3[1] & 15) << 4) + ((loc_3[2] & 60) >> 2); + loc_4[2] = ((loc_3[2] & 3) << 6) + loc_3[3]; + + j = 0 + while j < 3: + if loc_3[j + 1] == 64: + break + try: + loc_2 += chr(loc_4[j]) + except: + pass + j = j + 1 + + i = i + 4; + + return loc_2 + +def resolve(url): + m = _regex(url) + if m: + resp = urllib.request.urlopen(url) + sessc = resp.headers.get('Set-Cookie').split(';')[0] + resp.close() + furl = "%s/w.jsp?id=%s&width=620&height=349&pos=&skin=0" % (BASE_URL,m.group('id')) + headers = {'Cookie':sessc, 'Referer':url} + data = util.request(furl,headers) + m1 = re.search('document.cookie = "([^"]+?)"',data) + m2 = re.search('src="(\/pcsevlet\?code=[^"]+)', data) + if m1 and m2: + headers['Cookie'] = headers['Cookie'] + '; ' + m1.group(1) + headers['Referer'] = BASE_URL + '/flowplayer/flowplayer.commercial-3.2.16.swf' + data = util.request(BASE_URL + m2.group(1),headers) + m_vurl = re.search("'url':.*?'(http[^']+?mp4)'", data, re.DOTALL) + m_surl = re.search("'captionUrl':.*?'(http[^']+)'",data, re.DOTALL) + if m_vurl: + resolved = {'url':m_vurl.group(1).strip(),'quality':'???'} + if m_surl: + resolved['subs'] = m_surl.group(1).strip() + return [resolved] + else: + return [] +def _regex(url): + return re.search('video\.anyfiles\.pl/w\.jsp\?id=(?P\d+)',url,re.IGNORECASE | re.DOTALL) diff --git a/lib/server/dailymotionresolver.py b/lib/server/dailymotionresolver.py new file mode 100644 index 0000000..538db52 --- /dev/null +++ b/lib/server/dailymotionresolver.py @@ -0,0 +1,91 @@ +# -*- coding: UTF-8 -*- +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * + +import re +from xml.etree import ElementTree +import util +from copy import deepcopy +import json + +__name__ = 'dailymotion' + + +def supports(url): + return re.search(r'dailymotion.com/embed', url) is not None + + +def resolve(url): + print('The url is ::', url) + id = re.search(r'dailymotion.com/embed/video/(.+)', url).group(1) + print('The id is ::', id) + headers = {'User-Agent': 'Android'} + cookie = {'Cookie': "lang=en; ff=off"} + r = util.request("http://www.dailymotion.com/player/metadata/video/" + id, + headers) + content = json.loads(r) + cc = content['qualities'] + cc = list(cc.items()) + + cc = sorted(cc, reverse=True) + m_url = '' + other_playable_url = [] + + items = [] + result = [] + + for source, json_source in cc: + source = source.split("@")[0] + for item in json_source: + + m_url = item.get('url', None) + # xbmc.log("DAILYMOTION - m_url = %s" % m_url, xbmc.LOGNOTICE) + if m_url: + if source == "auto": + continue + + elif '.mnft' in m_url: + continue + + if 'video' in item.get('type', None): + item = {} + item['url'] = m_url + item['quality'] = source + item['title'] = 'video' + items.append(item) + + other_playable_url.append(m_url) + + if items: + for item in items: + newitem = deepcopy(item) + item['lang'] = '???' + item['headers'] = headers + result.append(newitem) + if not result and cc[0][0]=='auto': + json_source=cc[0][1] + m_url=json_source[0].get('url', None) + r = util.request(m_url) + streams = re.compile(r'RESOLUTION=\d+x(\d+).*\n([^\s]+)').findall(r) + for quality, url in streams: + item = {} + item['url'] = url + item['quality'] = quality + 'p' + item['title'] = 'video' + result.append(item) + return result diff --git a/lib/server/divxstageresolver.py b/lib/server/divxstageresolver.py new file mode 100644 index 0000000..44d520d --- /dev/null +++ b/lib/server/divxstageresolver.py @@ -0,0 +1,36 @@ +# -*- coding: UTF-8 -*- +#/* +# * Copyright (C) 2011 Libor Zoubek +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ +import re,util,resolver +__name__ = 'divxstage' +def supports(url): + return not _regex(url) == None + +# returns the steam url +def resolve(url): + if not _regex(url) == None: + data = util.substr(util.request(url),'') + link = re.search('src=\"([^\"]+)',data,re.IGNORECASE | re.DOTALL) + if link: + return [{'url':link.group(1)}] + +def _regex(url): + return re.search('embed.divxstage.eu/(.+?)',url,re.IGNORECASE | re.DOTALL) diff --git a/lib/server/eserialresolver.py b/lib/server/eserialresolver.py new file mode 100644 index 0000000..0b8dc80 --- /dev/null +++ b/lib/server/eserialresolver.py @@ -0,0 +1,27 @@ +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ +import re +__name__='eserial' +def supports(url): + return not _regex(url) == None + +# returns the steam url +def resolve(url): + m = _regex(url) + if m: + stream = re.search('(?P.+?)(\&|$)',m.group('url')).group('url') + show = re.search('serial=(?P.+?)(\&|$)',m.group('url')) + tit = re.search('srt=(?P.+?)(\&|$)',m.group('url')) + if show and tit: + return [{'url':stream,'subs':'http://www.eserial.cz/titulky/%s/%s.srt' % (show.group('url'),tit.group('url'))}] + return [{'url':stream}] + +def _regex(url): + return re.search('eserial\.cz/video\.php\?file=(?P.+?)$',url,re.IGNORECASE | re.DOTALL) + diff --git a/lib/server/exashareresolver.py b/lib/server/exashareresolver.py new file mode 100644 index 0000000..5ccccd0 --- /dev/null +++ b/lib/server/exashareresolver.py @@ -0,0 +1,53 @@ +# -*- coding: UTF-8 -*- +# /* +# * Copyright (C) 2015 Lubomir Kucera +# * +# * +# * This Program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License as published by +# * the Free Software Foundation; either version 2, or (at your option) +# * any later version. +# * +# * This Program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# * +# * You should have received a copy of the GNU General Public License +# * along with this program; see the file COPYING. If not, write to +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# * http://www.gnu.org/copyleft/gpl.html +# * +# */ +import re +import util +from demjson import demjson + +__author__ = 'Jose Riha/Lubomir Kucera' +__name__ = 'exashare' + + +def supports(url): + return re.search(r'exashare\.com/embed\-[^\.]+\.html', url) is not None + + +def resolve(url): + realurl = re.search(r'