From 3bb5176ef8200210ea5fcdf1537c02660561f851 Mon Sep 17 00:00:00 2001 From: coder-alpha Date: Sun, 28 May 2017 08:49:57 -0400 Subject: [PATCH] New site added and new hosts --- Contents/Code/__init__.py | 2 + Contents/Code/common.py | 8 +- Contents/Code/common_fnc.py | 20 +- Contents/Code/desitashan.py | 3 +- Contents/Code/yodesi.py | 311 ++++++++++++++++++ Contents/DefaultPrefs.json | 2 +- Contents/Resources/icon-google.png | Bin 0 -> 12979 bytes Contents/Resources/icon-speedwatch.png | Bin 0 -> 2042 bytes Contents/Resources/icon-tunepk.png | Bin 0 -> 4313 bytes Contents/Resources/icon-vidwatch.png | Bin 0 -> 1424 bytes Contents/Resources/icon-yodesi.png | Bin 0 -> 2485 bytes Contents/Services/ServiceInfo.plist | 23 ++ Contents/Services/Shared Code/jsunpack.pys | 160 +++++++++ Contents/Services/URL/Google/ServiceCode.pys | 128 +++++++ .../Services/URL/SpeedWatch/ServiceCode.pys | 137 ++++++++ Contents/Services/URL/TunePK/ServiceCode.pys | 147 +++++++++ Contents/Services/URL/TvLogy/ServiceCode.pys | 6 +- .../Services/URL/VidWatch/ServiceCode.pys | 147 ++++++--- 18 files changed, 1040 insertions(+), 54 deletions(-) create mode 100644 Contents/Code/yodesi.py create mode 100644 Contents/Resources/icon-google.png create mode 100644 Contents/Resources/icon-speedwatch.png create mode 100644 Contents/Resources/icon-tunepk.png create mode 100644 Contents/Resources/icon-vidwatch.png create mode 100644 Contents/Resources/icon-yodesi.png create mode 100644 Contents/Services/Shared Code/jsunpack.pys create mode 100644 Contents/Services/URL/Google/ServiceCode.pys create mode 100644 Contents/Services/URL/SpeedWatch/ServiceCode.pys create mode 100644 Contents/Services/URL/TunePK/ServiceCode.pys diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 55ca25d..8a79515 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -8,6 +8,7 @@ import desirulez import desitvbox import desitashan +import yodesi PREFIX = common.PREFIX NAME = common.NAME @@ -31,6 +32,7 @@ def Start(): @handler(PREFIX, NAME, art=ART, thumb=ICON) def MainMenu(): oc = ObjectContainer() + oc.add(DirectoryObject(key=Callback(yodesi.ChannelsMenu, url=yodesi.SITEURL), title=yodesi.SITETITLE, thumb=R(yodesi.SITETHUMB))) oc.add(DirectoryObject(key=Callback(desitvbox.ChannelsMenu, url=desitvbox.SITEURL), title=desitvbox.SITETITLE, thumb=R(desitvbox.SITETHUMB))) oc.add(DirectoryObject(key=Callback(desitashan.ChannelsMenu, url=desitashan.SITEURL), title=desitashan.SITETITLE, thumb=R(desitashan.SITETHUMB))) oc.add(DirectoryObject(key=Callback(desirulez.TypeMenu, url=desirulez.SITEURL), title=desirulez.SITETITLE, thumb=R(desirulez.SITETHUMB))) diff --git a/Contents/Code/common.py b/Contents/Code/common.py index b656fea..672ee10 100644 --- a/Contents/Code/common.py +++ b/Contents/Code/common.py @@ -1,6 +1,6 @@ ################################################################################ TITLE = L('Title') -VERSION = '0.06' # Release notation (x.y - where x is major and y is minor) +VERSION = '0.07' # Release notation (x.y - where x is major and y is minor) GITHUB_REPOSITORY = 'coder-alpha/DesiTelly.bundle' PREFIX = "/video/desitelly" ################################################################################ @@ -86,9 +86,9 @@ #DesiTashan TV_NEWS = 'TV News' -VALID_SOURCES_DOMAIN = ['dailymotion.','playwire.','vidshare.','openload.','playu.', 'cloudy.', 'vmg.','watchvideo','tvlogy'] -VALID_SOURCES = ['Dailymotion','Flash Player','Flash','Playwire','Letwatch','Openload','PlayU','StreamHD','HDStream','Watchvideo','TvLogy'] -VALID_SOURCES_ICONS = ['dailymotion','playwire','playwire','playwire','letwatchus','openload','playu','vmg','vmg','source-watchvideo','tvlogy'] +VALID_SOURCES_DOMAIN = ['dailymotion.','playwire.','vidshare.','openload.','playu.', 'cloudy.', 'vmg.','watchvideo','tvlogy','google','mediatv.','vidwatch3.','speedwatch.us','tune.pk'] +VALID_SOURCES = ['Dailymotion','Flash Player','Flash','Playwire','Letwatch','Openload','PlayU','StreamHD','HDStream','Watchvideo','TvLogy','Google','VidWatch','Vid Watch','SpeedWatch','Speed','TunePK','Tune'] +VALID_SOURCES_ICONS = ['dailymotion','playwire','playwire','playwire','letwatchus','openload','playu','vmg','vmg','source-watchvideo','tvlogy','google','vidwatch','vidwatch','speedwatch','speedwatch','tunepk','tunepk'] #################################################################################################### diff --git a/Contents/Code/common_fnc.py b/Contents/Code/common_fnc.py index e2010dc..c08bbf3 100644 --- a/Contents/Code/common_fnc.py +++ b/Contents/Code/common_fnc.py @@ -150,6 +150,11 @@ def GetTvURLSource(url, referer, date='', key=None): elif len(html.xpath("//iframe[contains(@src,'openload.')]/@src")) > 0: #Log('openload') url = html.xpath("//iframe[contains(@src,'openload.co')]/@src")[0] + elif len(html.xpath("//iframe[contains(@src,'tune.')]/@src")) > 0: + #Log('tune') + url = html.xpath("//iframe[contains(@src,'tune.')]/@src")[0] + html = HTML.ElementFromURL(url=url, headers={'Referer': url}) + url = html.xpath("//iframe[contains(@src,'tune.')]/@src")[0] else: #Log('Undefined src') orig_url = url @@ -179,11 +184,17 @@ def GetTvURLSource(url, referer, date='', key=None): except: pass - #Log(url) + if Prefs["use_debug"]: + Log("common_fnc.GetTvURLSource : %s" % url) + if url.startswith('//'): url = 'http:' + url url = CheckURLSource(url=url, referer=referer, key=key, string=string, html=html) + + if Prefs["use_debug"]: + Log("Post CheckURLSource") + Log("common_fnc.GetTvURLSource : %s" % url) return url @@ -193,8 +204,6 @@ def CheckURLSource(url, referer, key=None, string=None, html=None, stringMatch=F if string == None: string = HTTP.Request(url=url, headers={'Referer': referer}).content - #Log('string: ' + string) - try: if string.find('dailymotion.com') != -1: #Log('dailymotion') @@ -206,6 +215,11 @@ def CheckURLSource(url, referer, key=None, string=None, html=None, stringMatch=F page = HTTP.Request(url, headers={'Referer': referer}).content if 'Content removed' in page or 'Content rejected' in page or 'This video got removed' in page or 'ERROR' in page: url = 'disabled' + elif string.find('tune.') != -1: + #Log('tune') + page = HTTP.Request(url, headers={'Referer': referer}).content + if 'Content removed' in page or 'Content rejected' in page or 'this video has been deactivated' in page or 'ERROR' in page: + url = 'disabled' elif string.find('vidshare.') != -1: #Log('vidshare') try: diff --git a/Contents/Code/desitashan.py b/Contents/Code/desitashan.py index 5a826ec..f7b91c7 100644 --- a/Contents/Code/desitashan.py +++ b/Contents/Code/desitashan.py @@ -26,7 +26,6 @@ def ChannelsMenu(url): # Channel title channel = item.xpath("text()")[0] # Log("Channel = "+channel) - # Log("item "+ etree.tostring(item, pretty_print=True)) # Channel link link = item.xpath("@href")[0] if link.startswith("http") == False: @@ -118,7 +117,7 @@ def EpisodesMenu(url, title): # Add the found item to the collection if 'Watch'.lower() in episode.lower(): - episode = episode.replace(' Video Watch...','') + episode = episode.replace(' Video Watch...','').replace(' Video Watch','') oc.add(PopupDirectoryObject(key=Callback(PlayerLinksMenu, url=link, title=episode, type=L('Tv')), title=episode)) # Find the total number of pages diff --git a/Contents/Code/yodesi.py b/Contents/Code/yodesi.py new file mode 100644 index 0000000..8f2853e --- /dev/null +++ b/Contents/Code/yodesi.py @@ -0,0 +1,311 @@ +import common, common_fnc +import messages +import re + +SITETITLE = 'Yo-Desi' +SITEURL = 'http://www.yodesi.tv' +SITETHUMB = 'icon-yodesi.png' + +PREFIX = common.PREFIX +NAME = common.NAME +ART = common.ART +ICON = common.ICON + +MC = messages.NewMessageContainer(common.PREFIX, common.TITLE) + +SWITCH_1 = ['Flash','Yodplayer'] +SWITCH_2 = ['Google','TvLogy'] + +#################################################################################################### + +@route('%s/%s/channels' % (PREFIX,SITETITLE)) +def ChannelsMenu(url): + oc = ObjectContainer(title2=SITETITLE) + + html = HTML.ElementFromURL(url) + + for item in html.xpath(".//nav[@id='navigation']//li//a"): + try: + # Channel title + channel = item.xpath("text()")[0] + #Log("Channel = "+channel) + # Channel link + link = item.xpath("@href")[0] + if link.startswith("http") == False: + link = SITEURL + link + + #Log("Channel Link: " + link) + except: + pass + + try: + image = common.GetThumb(channel.lower()) + except: + continue + + if channel.lower() in common.GetSupportedChannels(): + oc.add(DirectoryObject(key=Callback(ShowsMenu, url=link, title=channel), title=channel, thumb=image)) + + # If there are no channels, warn the user + if len(oc) == 0: + return ObjectContainer(header=SITETITLE, message=L('ChannelWarning')) + + return oc + +#################################################################################################### + +@route('%s/%s/showsmenu' % (PREFIX,SITETITLE)) +def ShowsMenu(url, title): + oc = ObjectContainer(title2=title) + #Log("Shows Menu: " + url + ":" + title) + html = HTML.ElementFromURL(url) + + for item in html.xpath(".//div[@id='tab-0-title-1']//div[contains(@class, 'one_fourth')]"): + #Log("item "+ etree.tostring(item, pretty_print=True)) + try: + # Show title + show = item.xpath(".//p//text()")[0] + thumb = item.xpath(".//img//@src")[0] + #Log("show name: " + show) + # Show link + link = item.xpath(".//p//@href")[0] + #Log("show link: " + link) + if link.startswith("http") == False: + link = SITEURL + link.lstrip('/') +# Log("final show link: " + link) + except: + #Log("In Excpetion") + continue + + # Add the found item to the collection + oc.add(DirectoryObject(key=Callback(EpisodesMenu, url=link, title=show), title=show, thumb=thumb)) + + for item in html.xpath(".//div[@id='tab-1-title-2']//div[contains(@class, 'one_fourth')]"): + #Log("item "+ etree.tostring(item, pretty_print=True)) + try: + # Show title + show = item.xpath(".//p//text()")[0] + ' (Archived)' + thumb = item.xpath(".//img//@src")[0] + #Log("show name: " + show) + # Show link + link = item.xpath(".//p//@href")[0] + #Log("show link: " + link) + if link.startswith("http") == False: + link = SITEURL + link.lstrip('/') +# Log("final show link: " + link) + except: + #Log("In Excpetion") + continue + + # Add the found item to the collection + oc.add(DirectoryObject(key=Callback(EpisodesMenu, url=link, title=show), title=show, thumb=thumb)) + + # If there are no channels, warn the user + if len(oc) == 0: + return ObjectContainer(header=title, message=L('ShowWarning')) + + return oc + +#################################################################################################### + +@route('%s/%s/episodesmenu' % (PREFIX,SITETITLE)) +def EpisodesMenu(url, title): + oc = ObjectContainer(title2 = unicode(title)) + + pageurl = url + + html = HTML.ElementFromURL(pageurl) + + for item in html.xpath(".//div[@id='content_box']//article"): + try: + # Episode title + episode = unicode(str(item.xpath(".//header//h2//text()")[0].strip())) + thumb = unicode(str(item.xpath(".//a//img//@src")[0].strip())) + + # episode link + link = item.xpath(".//h2//@href")[0] + if link.startswith("http") == False: + link = SITEURL + link.lstrip('/') + #Log("Episode: " + episode + " Link: " + link) + except: + continue + + # Add the found item to the collection + if 'Watch'.lower() in episode.lower(): + episode = episode.replace(' Watch Online','') + oc.add(PopupDirectoryObject(key=Callback(PlayerLinksMenu, url=link, title=episode, type=L('Tv')), title=episode)) + + # Find the total number of pages + next_page = ' ' + try: + next_page = html.xpath(".//div[@id='content_box']//nav//a[@class='next page-numbers']//@href") + oc.add(DirectoryObject(key=Callback(EpisodesMenu, url=next_page, title=title), title=L('Pages'))) + except: + pass + + # If there are no channels, warn the user + if len(oc) == 0: + return ObjectContainer(header=title, message=L('EpisodeWarning')) + + if common_fnc.CheckPin(url=url): + oc.add(DirectoryObject( + key = Callback(common_fnc.RemovePin, url = url), + title = "Remove Pin", + summary = 'Removes the current Show from the Pin list', + thumb = R(common.ICON_PIN) + )) + else: + oc.add(DirectoryObject( + key = Callback(common_fnc.AddPin, site = SITETITLE, url = url, title = title), + title = "Pin Show", + summary = 'Adds the current Show to the Pin list', + thumb = R(common.ICON_PIN) + )) + + return oc + +#################################################################################################### + +@route('%s/%s/playerlinksmenu' % (PREFIX,SITETITLE)) +def PlayerLinksMenu(url, title, type): + oc = ObjectContainer(title2 = unicode(title)) + + html = HTML.ElementFromURL(url=url, headers={'Referer': url}) + content = HTML.StringFromElement(html) + thumb = GetThumb(html) + + oc.art = Resource.ContentsOfURLWithFallback(thumb, fallback=R(ART)) + + if '/images/xfuture.png.pagespeed.ic.WVkcd7CGfW.png' in content: + return ObjectContainer(header=title, message=L('ComingSoonWarning')) + + sources = html.xpath(".//*//div[@class='thecontent']//span//text()") + + for source in sources: + source = Switch(source, SWITCH_1, SWITCH_2) + s_source, i = common_fnc.GetArrayItemMatchInString(common.VALID_SOURCES, source.lower(), False) + if s_source <> None: + if '720p' in source.lower(): + if 'single' in source.lower(): + oc.add(DirectoryObject(key=Callback(EpisodeLinksMenu, url=url, title=title, type=source, thumb=thumb), title=(common.VALID_SOURCES[i] + ' HD (Single Link)'), thumb=R('icon-'+common.VALID_SOURCES_ICONS[i]+'.png'))) + else: + oc.add(DirectoryObject(key=Callback(EpisodeLinksMenu, url=url, title=title, type=source, thumb=thumb), title=(common.VALID_SOURCES[i] + ' HD'), thumb=R('icon-'+common.VALID_SOURCES_ICONS[i]+'.png'))) + elif 'hd' in source.lower(): + oc.add(DirectoryObject(key=Callback(EpisodeLinksMenu, url=url, title=title, type=source, thumb=thumb), title=(common.VALID_SOURCES[i] + ' HD'), thumb=R('icon-'+common.VALID_SOURCES_ICONS[i]+'.png'))) + elif 'dvd' in source.lower(): + oc.add(DirectoryObject(key=Callback(EpisodeLinksMenu, url=url, title=title, type=source, thumb=thumb), title=(common.VALID_SOURCES[i] + ' DVD'), thumb=R('icon-'+common.VALID_SOURCES_ICONS[i]+'.png'))) + else: + oc.add(DirectoryObject(key=Callback(EpisodeLinksMenu, url=url, title=title, type=source, thumb=thumb), title=common.VALID_SOURCES[i], thumb=R('icon-'+common.VALID_SOURCES_ICONS[i]+'.png'))) + elif Prefs['allow_unknown_sources']: + oc.add(DirectoryObject(key=Callback(EpisodeLinksMenu, url=url, title=title, type=source, thumb=thumb), title=source, thumb=R('icon-unknown.png'))) + +# If there are no channels, warn the user + if len(oc) == 0: + return ObjectContainer(header=title, message=L('PlayerWarning')) + + oc.objects.sort(key=lambda obj: obj.title, reverse=False) + + return oc + +#################################################################################################### + +@route('%s/%s/episodelinksmenu' % (PREFIX,SITETITLE)) +def EpisodeLinksMenu(url, title, type, thumb): + + oc = ObjectContainer(title2 = unicode(title)) + oc.art = Resource.ContentsOfURLWithFallback(thumb, fallback=R(ART)) + + html = HTML.ElementFromURL(url) + # Summary + summary = GetSummary(html) + items = GetParts(html, Switch(type, SWITCH_2, SWITCH_1)) + + links = [] + + for item in items: + + try: + # Video site + videosite = item.xpath(".//text()")[0] + videosite = Switch(videosite, SWITCH_1, SWITCH_2) + if Prefs["use_debug"]: + Log("Video Site: " + videosite) + # Video link + link = item.xpath(".//@href")[0] + if Prefs["use_debug"]: + Log("Link: " + link) + if link.startswith("http") == False: + link = link.lstrip('htp:/') + link = 'http://' + link + + # Get video source url and thumb + link = common_fnc.GetTvURLSource(link,url) + if Prefs["use_debug"]: + Log("Video Site: " + videosite + " Link: " + link + " Thumb: " + thumb) + except: + continue + + links.append(link) + + # Add the found item to the collection + if common_fnc.IsArrayItemInString2(common.VALID_SOURCES_DOMAIN, link, False) or (Prefs['allow_unknown_sources'] and URLService.ServiceIdentifierForURL(link) <> None): + + if link.find('openload') != -1 and not common_fnc.is_uss_installed(): + return MC.message_container('Error', 'UnSupportedServices.bundle Required') + + oc.add(VideoClipObject( + url = link, + title = videosite, + thumb = Resource.ContentsOfURLWithFallback(thumb, fallback=R(ICON)), + art = Resource.ContentsOfURLWithFallback(thumb, fallback=R(ART)), + summary = summary)) + + # If there are no channels, warn the user + if len(oc) == 0 and 'disabled' in links: + return ObjectContainer(header=title, message=L('DisabledWarning')) + if len(oc) == 0: + return ObjectContainer(header=title, message=L('SourceWarning')) + + return oc + +#################################################################################################### + +def GetParts(html, keyword): + + xpath_str = ".//*//div[@class='thecontent']//span[contains(text(),'"+keyword+"')]//following::p[1]//a" + + try: + items = html.xpath(xpath_str) + except: + items = [] + + return items + +#################################################################################################### + +def GetSummary(html): + try: + summary = html.xpath("//h1[@class='entry_title entry-title']/text()")[0] + except: + summary = '' + return summary + +#################################################################################################### + +def GetThumb(html): + try: + thumb = html.xpath(".//meta[@name='twitter:image']//@content")[0] + Log ('Thumb: ' + thumb) + except: + thumb = R(ICON) + return thumb + +#################################################################################################### + +def Switch(mystr, arr_in, arr_to): + try: + for i in range(0, len(arr_in)): + mystr = mystr.replace(arr_in[i], arr_to[i]) + except: + pass + return mystr \ No newline at end of file diff --git a/Contents/DefaultPrefs.json b/Contents/DefaultPrefs.json index 989c886..ac48e4e 100644 --- a/Contents/DefaultPrefs.json +++ b/Contents/DefaultPrefs.json @@ -1 +1 @@ -[ { "id": "allow_unknown_sources", "label": "Allow Hosts natively NOT supported by this plugin (Default is False | Experimental feature)", "type": "bool", "default": "false" }, { "id": "consolidate_parts", "label": "Consolidate Parts - Flash/Playwire [Desitvbox] (Default is False | Experimental feature)", "type": "bool", "default": "false" } ] \ No newline at end of file +[ { "id": "allow_unknown_sources", "label": "Allow Hosts natively NOT supported by this plugin (Default is False | Experimental feature)", "type": "bool", "default": "false" }, { "id": "consolidate_parts", "label": "Consolidate Parts - Flash/Playwire [Desitvbox] (Default is False | Experimental feature)", "type": "bool", "default": "false" }, { "id": "use_debug", "label": "Debug Mode", "type": "bool", "default": "false" } ] \ No newline at end of file diff --git a/Contents/Resources/icon-google.png b/Contents/Resources/icon-google.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd695bbf7bd67784a0657645e7c6767ab501863 GIT binary patch literal 12979 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 literal 0 HcmV?d00001 diff --git a/Contents/Resources/icon-speedwatch.png b/Contents/Resources/icon-speedwatch.png new file mode 100644 index 0000000000000000000000000000000000000000..7fcec45d939e9520840fd5bbb12a35e7de658b62 GIT binary patch literal 2042 zcma)-`9IVN1INE}){J}P7{?%mG1RmQVNvdB+>^VEYo3XUR+iG(8KX!d$GT~9A7OG% zA;&TvXR->p@3U0T(72!WKiJpn^Lf2LzkGgvKen^65JJiz0RRxPvNUx70MO3~0-%te zsPw42`V)9NBFXG0f$BX7LJ%mJLrBO%gLB|YnnolaXmlOSCG41qJLt~=IR0{my~Pm4 z#Y5|iqt+_@=LHdD=^6|G0^N;KX>P=cmdFh&NQ#-O@a7v5Iufn4zsX;FU zwQtYuDrWjx3!RAlY-s(UFS({It&rWly~pLChtN_Gd4Yon#rfvoft`qm66a_O;y@OgOIOJ)f52=dQj|S)x_RrSu!D_j*Ww>q}{UoEWWy;xq*O?@cwPBi! z0J;{Gv}O#X!u1)fAcIMC%a6A*`_G`-8+Fzk{Y1UH@XWpjRG9ODFf3Qo%~NelM}zx` zLo;9{_$OG1)WN5&&lIoasZBn1`Or-%9CB~}aBl8+N(Isy`;JUv4qq(HjH4`3OP?= zT;#B+Aq1Z8rsW?rNCH^yeX|O5pkpqB{#NDVEU_FD42|nf8K3t3IDu%996g^jz&>}e zutA8}HfG_Pu_!8f>owY?c<-c9@bb&E^(#b93|M|!eJfHu`_20OkP&!CEn7cEpwFk0 z+7MR5?N|7)iZFgVarN6ekLN#I7O` z3A$NT=)SPzd!2rjF)CD(V?;iaK7RD5Ri$GahIe|p(si~s5)*t;6~cLiblwapSFbU< zGOK({g`A|K?i*>SE=hI?>vO~eyF`)iI(l{r8X$ch_DSHA?)YpSV}z2ivUQUAXO<~+ z8HrYjR}oCG1R>;N;Ha|y(|T?b0_QEx409~Tr)uYSQQ}xy8@IIsrbW{Wjc$9+Ks~z6 z<+<^%(W2{|1mqxj^1yLPwlwEUYQMb6-|CI7avb5)Mb0S&{^3g7qZ;e6vRm_PB2!-R zYcO>zA+x6+h?tWcu$_o~n{}^V<$PJY^5hz-ALJ%`r~rL{jq$(3NAX6p!0Ub@NP&fq z41l!!9QvP*G_`(%eAPU|al?cF20=RHcoIDgN_AubE6#pSifq-vD-Wr*GtCOU^b$c6 z(!yT>lWBz+@)y*a<+l~SY1l2p^6Drss+c$2d>;(^5<;*~QvM1;a2l;y!O!6)Dp$CpL-T-YdAhQRYrd{bU_uEYB6Q?E{JR4_zq?~&7?+6@14TnOv}JvP z|EqSXUBXK1-^sdNminoLH*xkfF6v6SH|p^6yNatSldX0@@2t8(5r7(2mAS7ulUHV# z089DuqzU|XmC+Ir`)z!6nrX4M2=TAmkz2910^e18oP=NDh+r~j^l{fJ?5--T4llJy zKVeQ`qqJe>zj<|R#?oJ{J*7__66kBStlwWWwndj-4k7n^r`_<{zp=XFoVH~6M_!62 z$!0K-jZhY^0pA$7u7|OmNWGgl3@<3*Xkl@xMc)kTG0A@h@Ptyw4$Ixz&XR4*J7{8Frgx#-Hv&xAA`%jwvyCp}_>N(;8*;pebn? z{lj1W7kb}_y-w}u>Xl=he5*8;8lSCms#B7c<~!YD8Tdq5d@y&&y?QcVT(rd)l*#b8 vzqx02&haY+9Hn9=hNZp29k!7FoyK-_4t#gHn5^EIe3l7=y4A literal 0 HcmV?d00001 diff --git a/Contents/Resources/icon-tunepk.png b/Contents/Resources/icon-tunepk.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f8eb8b01ac052fbaa3908817b4341c4cbee3bf GIT binary patch literal 4313 zcmY*dc{J4j_kO)+FdB?C`!XW?7OF{>q0JVuW+_tbMHO(ujh{^&cf^h2U-{n0N^k(JZlL6^iM*73Hgu6 zPIWf_NX^33`rN_6!QTEpl-GpFeY?H0jn1k?R<<)GQI?mNkC!zs?(b&~{~FlXZk%1Z z+u3K|{He|yMfnSuatnH!zew+^|w@V&|dsm~QNE<}V@+s!TG3tvxZaDqLl4qa7uX(NUZq673Wz<;F6lOPsN)ZIxb%dctyZtorky1o!_#Vuyq; zAx;z#|79D`*lVlOW@eB5BH&)j^MX11>(Wn&G)QIhj`rx6Bd(*GMU~eC=Y~zD^@xaf z-me~oYS)nOM<<+mlG%UDa~b$NE;cD@dhv!K}|6@_4R{I?`xb_=kbx)O)PH z{o^UNt6@)$4EKhJbtpP#p}HRx_BIh8^0;iAFUh;$a!nRB*A?_#pzr$$NtuWa7MD$R z)d$}?78tvniH~u-fx^{F9ye~*45+2zG+b8vj=ffRJ+N?8%O52+RlU#TrXf(nX#DAK zW@o2a8sHErE)_Tz(6@PSZaZ5ma1Mpk;wq!mCzr9?g9&0O*R#kyn}C)=B--Swxo_#m zBO))0`8>mj#BpVmRCj{&qXO9mizUCJX0a^(m)0SUKrJ)AaIFEc{K3YS*e~?{{32*H z_GT8D{7b&V5{9;KUaSX&QEa}UJY28|STnc${Na!)8lkYIpK@JjCvLZ7Hu_S^gzqfe z)&Y&;)|;w24%RHOU#~vYgaeurrhLstf;sSdnGHN9V0Z6P-O%@=2V>%!^sep>Zv^;L zp{>pn%4vXGBvRL#WgZ^Ct(!f2dq;+R;jI?}i-}J*p5%oB8jdoD+Xb$VR7?z-x@p_J zSvmm`oMM-b$a_eaKV;Mw;4#*Alem7BAPhkZovsBYTg2zXwCB4@HgMSHGDSv+A>f=b z7-okf*pgrMeV^8s2+08N`gr}{_oTI?%IW$Sdi@3vtkEPLq9IDJXI#7~o0*%9n2R1C zcQ@0&bdCV_S;{{#Uf0pBwfx$Xd)fVTE=y_?-xcct+3pleScVkc zNBDX1oNctEV5&ujyHXUOQ!q?d8dv<3|ET=_C_c$J?<4jytyrv=tj!xVFRk__@iG!$ z&O}poT44WM`k~C^)J18`6XR+?4><#(-(lV($Csk8%WiigTjSb}-X{S<%_TK)1C!FB z40-6Xqy$<+=jhV)6%q^ZJ+CIjyhE1be(=7u@@jUtRF)=)dQ_8Sc|g!ga=XP(4K0=j z-;8uN&dGiMZdZIGtMkRw-;HwBT}|WW&#lMhH+7y!U8L{joq`qiu~D1+?hY5$e5D6e z(_+?~oLSyZv(PxT5kzFmJU?JyMc`xS!Wx#Zdz9;@u{xwI4HarC()bDv34~1hDCAO< zL6Hef;vPR!{p}wrubJpZzH~H23}FArx=wD>ob-lREzn;~`*vCss0}S9`?||FRL3zx zGNE{%rOXL;_0qsVQcqaNqv7I?M{6rv0g0Ao^@6>!dCX70>ZJMRXJ_}e9qo=nqfOESj~Di-iXQuM2)eNi%3G9i3<0c3RXkT?Y)f-6T9_c zH?G$|Rcw3CgG7m=z*nq86Oj6yg!mYTm@iOfd&W-X4x!*>1!yZw!C#H%uk`a)svA4p zae&j_-8T>sBXq=n!ZRE@7A{i@=9@oALC1AW(>>hK368nQwVUc+kwrHP**BDl!1UrR zc)eZ=jDFlRXS(paO#v~(7GuUkz6GyP!oc4RHzi`!zA60lOg?#S;IRKp&Xy_?6hE2p ziV+>u8^=afeN^3!5z?`h0SBzbS=V!TsjUjrLV6M_SLP6%&#!^~vM%uVL@DJ#Zz&n&5~a{JJ{p-VM7Pa%p*Dazel^Y+#WzC`=F-eKEsy3==a_CWI=( zvv#);J@#Lyxn!1(EUH$*x0nZGwm+i_N{3gS!}EbFbQpPdWoY-A?CMWz4(5ex-0%YO zGYI+JG{%mMVok~3FM-DHZ~yVLK{_3T%q$$f!7DKSWIWfrlpoJ%*%Z&=&D;7o9ZDjZ z>t%D%13~T|=bV1Yrw2<%4Ef=Uo%oI0*^rt8ioTHGGd8zXJ;4=BBE{>J45tt1b$fsw zqr&?ohb!-UHH4LWdBJgKQj%ER1SEr)5;TCYt*DS$oeJ(xQ;hTXsNnsq(moaQTPb9x zEnLKZ9#&*QuN~VQ+n7iz)N(K#;3F!YUeW`UP}SsS-I~>hU)MR^9wQHo3UeJj9IZV8 z*HsC#b%s=KYF)hOaxi$Q2`4kp91}A@F8__+LlDGTdco?NUKO>QfW_#TAXDQwrca}Y z@a%uHW2!*Z1Hf#^fP9oj;`+(M9=j0qavtaP*=qOyKqd6h0vgUYgp2BZ$xHW3r7O0AMQJ}-YVX+fEj%94$T1l8=z&NT zTP%Ri0k=a&H1L!;!Gj6T`Tt(pD4?Z%Yz3JLGAho0+wV{`=qZU3+c)AjKPr|5zs>+AnxKkWl zysk^XBLd~UQ^bS>!jS>_D-V!Y#pvX)Z(ibocW?70M~2wfE#MbEB{9=+JlxcqC?)&^ zHQfuGO$w6PdBU2ncG>3NZ`+DLbn#NxL_;~?pEDye;PwcSpqy7e%-8N9wZb|ZmFRbh z5hm@R%XR{RWhO9jwIH~BS+;CUh4208ebyu%Zp9J&4He|=hcyNpT=?BFH+Ck1*p|hq zAE3s1K1=^_0firpmj+$3((o}@RPN)79yBtJ1++uF7A1P^!hnyZ38#+nqzhL(=b%>p zmxyCDSl!-1s4tLn9*|o!P=cq_3;+uWbP5yng(6kw02cN`!b|EPNUneTz~VB1E@Dmd zl_Bd1G=$?P6!4^cW|L$}*jbG*F3f`j_wjuqq9xB2H?CWgAKzO}tOj_-U-Oeaqt(dd z#Lie|BBj_;T;A>`li{}2l(B082LHZ2VkAcdgessqr^c8~mI#}rous}3PEBNEEDMN{ z0!0;!a`y^0TyA=iW~ZWw|GrILiT!spj@jz`UC$SH{L>K$`$8CM#As1Z zI2Rr_EQ$^8^z!+bO_3#AW0YjU+XesX4-mp#Td&nd?3Z&HyYaA(@SZy)I{hha0Avn8 zS|$7VsrDlSQxiD~we=JT_C*on9Z{()*lD%(i#F4vT$%E=sxvp0;Ae-hx&!Z#ZZGr6 z>3Cj#xkKVS}{TZ z*4XgvzlsDkpC126d$oF~N++yI?AMp^SfJjLbTqkL2$Tb;zA zwcMiV-4BJ|cymGu%@p`n6N~S-UaR~fKDcrRPm9W5Cn`V;oKc@mFux0 zv&%8#XP)hz8}yDnw-P?pjvS7P9=kUC+N`a^UMC@%MQJnYd3cV6Jtn$_mW-+lI0Jif z7LmKY-fzC_iowlHdp3A*Jl0UsaSFiorfv^A+j`?LCjv8us&hrZu~-eP{raphJrZ%r z+~}^KlS_*REr%L1U$zo=vh71aRqc?L@kEhI&-^E5+7&?yrzi6JI^!;^Z;B@~>=v}+ z&|U@1G}}8-bIGh3&enxnYcopKzGo94c-Nh7;)}89wncSZj#8|sm$M~gh#B5*dy?d) zOP;a^rsYhDiaTXx&Em9Fjm(ECL->3rK)%UCADvr@ zczt74Kje~Woyh1#;5R=0jC|~|mwU#S;GlhaB=KcBz<(3atG(kW_Kit5Yb5tNdl!I- z9!g2xw)D_xAAJaIW);aLko`1S)4zH0@9m|wbGg*qt?myMp3ZuDqvx|i8%sh=q)tAx zYVpqD_v9@b_Ta#$>9M93!aRm9uTI*~6wZdFb@nHOm;embTXc9=CayvuFzcOX^>K{n zA$)u+$89ejla#o|XFeT*mL8|id17#xSlSFCqk!gmWd7jyEvw-_>yE1k%z$x`HAZ9p z%k#y85vuyC+byFYf%A^XUj3CJIwY;@w29$P5Ea^9*bdn`i#P>k9-Kd|oz~@_8cXrx zu{u2Xd)0vY@b-t+bp*Ri(yaZ9@-#ub%-pE=P3Gr3NBCgup5A;=LYo>Z5~A$sD{r59 zu|X#-Mz}TD7er59r>RYN@w1sYQJ+S&>&t69-4Ny`dzPkaJeeU~lEl?B%NW*s1s7cB zp2&5WbGUPlT4e-#u_fU0Ub53cjl`W&Q%lJ_Rt1Gg!>fBf)zz^!sRm*`%LXsG5w3!P3WWZm)hAGjf40-;Sb711A%sZp?{r!GPdfi uJ~I7_B3R-2{lforyny7)*vWtS!vDa!YZu7Svx-;#Ut@I6>@3-U5cz*|Y1(7} literal 0 HcmV?d00001 diff --git a/Contents/Resources/icon-vidwatch.png b/Contents/Resources/icon-vidwatch.png new file mode 100644 index 0000000000000000000000000000000000000000..9d738fb44fd5dab74823a688d05eb81e3de49607 GIT binary patch literal 1424 zcmaKsdpOez7{`B`?YInUlWWv+&m~zR=P}nAi&!I_xhpC1xUShoxy(aOA>}eGx$Jn7 zlXQ$4wv-&VTo!Q}N5nB<<{IkY=y}fHz0dP~KJW8=pXdF)f4x}*4;KYF4LJY+6!5NC zA^?E)TMz(|+DDy#g-4o*}ajyjU@G@(~EI8llR0)yr2aqrR!LLg(grT14_9vYcV-AUJyMrPI#% zSbUElvy$+94_@!C)jcgKOY9Oof_y*eM`gcCEtaX(k8AT?9>{8tD_u2O`P?~iyjfmx z;4Nr7di3y1{k@$0enwMNbCr|QHgPay&h6{g^U-7(NnNss_M61W-oIt;rr&sl=<3ga z*z$Kf;JtkdJXx|gZLCyx`?0^G%y_HtJ$-YC5Aop9B;mA{hyBtJ+t$fG$v33o)kRUe z7I?cfw8%41?1s`S%9pli-eH|nmP>jQzbo=7;fC0O)eEwFUm5d1*{G{*t?%xLZK1DC zSh0$7Qeb$~9;23L~rtyVQe0!cFp)cLsVw( zQgWp%WtOKBGp{bd#cjdf-J$5j*q0oGaoBo?x*gSJU~@|PWLqKng>(8 zTFgFcgXx2&8yt%%LwC`?8w3b3`NNQq56dZJzZ{n@*h>n;R;OI*JYuA}#puoQwnPvF zh}3NFpl-({cL@DL#jmc&xW&`*Hq?m`mAVAP>2uvko0V_GQ0@^#mu_hIjOEP2${aO? z4m`-e&)-k{f&_ht!1@$G5KKi?omsOBFzUp1uAcbp;1=(;y|RjqbqPb;IS;yet1a;u z&W4@?&MmW&9AtJtU%>G#R0vzGcsnF6>uWQ#MReV;GJzRY8n}g8ct7eFbr-W1YFs&j!`Gw>znh_Fm^Ng=jyCI zWO$beWV&JQWIRkS?W!a@V1m%^orjW$@zSFt zTD8+UnHO>Fz=?JJw5gKi1zp*ofwkvP#R6e#Iq{ySc|?tQkXwO#3H}81-hz9*QCTxH z(~dyhd|-n^w;0V%^fhx+F&{{RTAc#;4B literal 0 HcmV?d00001 diff --git a/Contents/Resources/icon-yodesi.png b/Contents/Resources/icon-yodesi.png new file mode 100644 index 0000000000000000000000000000000000000000..776457309b801846104a2e00671fe2e0c24a9aa1 GIT binary patch literal 2485 zcmW-jcT|)27skJDk~gdb5EAxGMUZ767C{VzB~uI=6){3(h$2-WVfF)Z}^YLKa-cgbLKxGc`Ii&vpwU^1rD)#GE0xHd$>9I_Y~eKUnsum zL3w>%QRFG#VikjFPqgz-(SL~%c9Qp+h$X2a#`8q?fit43m811+zuppBr}hz$T^ly+ zQ`~6zyJP8S&xX&1dm1*bW28D=2Y&4Le)9&$&`{-&aMsG{AQ#=6P#}w)F}=m9d!H;e z5lH-oOBc`2v3y~bQ~LgB$U+Ez&(-}hE1Zy6o@`lhn$Enfn~V!R&dnzzU@ZnN^j8)LsVzxA<5 z5b{)0a75YFI?6oH4BB%Mf-AE^Yqxh4@71{~r5{gFt; zJ)RSP-xo&KuDa9P*eOauo=hnPRMC_GC>=@-(b4= zxxW-Jw$vqpVid(XV`OC>m=)mb>LE{aAg~DYyHIegZH*mz5&bd>48`^hN=`Q=)_kGii>yy5A}kaqz#sCCP&c-Aa_2fA!JqDPh zJcO}%AC`5IeS<`@Mh&|5p-{dTX9bxbu6_i3YjfP-@Q$V2?!#czM1`CaqE~ja0Y z3l%0AP|p%_1xM=Q$lNS714F-i$zIhy;R&(HxOYQhDG^^&=8TrF~`?R zNaWe>Dv7IB(LUY~fg#2%fjh-Ods;MTwi>lSu3@#hFlpKVoHDXtTETOVzAvn28A*fY zo<)#I;sIb(zUx?-t7<=BO_AVVB!+5`F4ZQ$Suj)iV7jaZn5q6oF?yV9nGY<)VPvZu z(G5E!R5n%by69V1}tFNA!Oq`CFoLyh>MPpB>?$uL-Ybo_F{KOqTS94(qYcvkp|UU@D+)B0Tp zco!1{v0Uz^@q~Idk|DKS;9JM$ZBtSRB!lpZgGpDIIR1w&`=n+YVR^djjF68ogDTmu z8wDc9n{BkLhy&9z+MLLJk9b7A)I(VQREbg6u!g*}O5rq=lhCSpu_ja+S2ln>WCI5( z(AC{dmRk!&<>K(b=S>a-=tRH1SC@Wn))GhYF-Vk(of@LSZgQph5fc8~8=%qG*7Gyx z*JPA&nwSAqjB|D-SHsmYBv`zt@6UjjlreiR0XG>=I7MC*LRS!=+r_VdQ}vk1OTIb zzcl>8X`c2qzVmFXvA%;P3o}hSE^F7Q`&^+b|g_Z~}TcrYZGYnS>!w~~& zaMncJ9LBVE#Bfh&z?`0LQ7j-#ehFIzWo-QGD-XKs9A3GSmXtwy4C4wzRzPT` zHpbP1ES^FIx5(~JqgVUI{lH6BTTPEVI=eLgIA{aMH2`y1_qHl@jsM*lTU6G3H>#ip z;1q1$-5EDLZwcSu8=q?P7ASplSo9LBTMNG>G&4q>T)5+P<3N4yh3BMd!gk{Bl>O^V zvCC77Nh)WttPC2;K2b2@9H5JlkKu#Lb)C9$nbu1ii|6mG|P>E)+UJ!d7Hl;``Pvj*QME- z;=fnob9F1BgcumrdypHMGEtp-M+oEX0|}p0_V9ffd)>O6EpVcC91RMGy}Qw!(CY1q z+w7DZ%bRmg07|zSz{&ke*L5^if_w-n^QK@Pr9xu}7ca-Ho6iNLUnb+MT&m0n4WPUF?Sne}FbxtDc-`ir!az;^ryJ>4^W57;})UR(W8S z>F#WVWd5ab;zlro&3BT|!gFRKQ?O>^W5r$m$ok~am)heR=y3X7=V$uYeNYwpmTO}I z$m)fNs~2YbO{Pcf$B+1yk-z;Ffdvpo_Bhil`i1vAh7JM|UW+K+Kp*U9nr>*G+A!*k tuee~?wCoirwv|l%wJJSk;l~s9pYRr=mk&EU`=j&6WN+haU1P~e{6A}wJHP+{ literal 0 HcmV?d00001 diff --git a/Contents/Services/ServiceInfo.plist b/Contents/Services/ServiceInfo.plist index 419486d..6ca6021 100644 --- a/Contents/Services/ServiceInfo.plist +++ b/Contents/Services/ServiceInfo.plist @@ -20,6 +20,13 @@ https://www.cloudy.ec/.+ + Google + + URLPatterns + + http://mediatv.me/.+ + + Playwire URLPatterns @@ -44,6 +51,21 @@ http://dl1.playu.net/.+ + SpeedWatch + + URLPatterns + + http://speedwatch.us/.+ + + + TunePK + + URLPatterns + + https://tune.pk/.+ + https://embed.tune.pk/.+ + + Vidshare URLPatterns @@ -57,6 +79,7 @@ URLPatterns http://vidwatch.+ + http://vidwatch3.+ VMG diff --git a/Contents/Services/Shared Code/jsunpack.pys b/Contents/Services/Shared Code/jsunpack.pys new file mode 100644 index 0000000..b91d91e --- /dev/null +++ b/Contents/Services/Shared Code/jsunpack.pys @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + urlresolver XBMC Addon + Copyright (C) 2013 Bstrdsmkr + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Adapted for use in xbmc from: + https://github.com/einars/js-beautify/blob/master/python/jsbeautifier/unpackers/packer.py + + usage: + + if detect(some_string): + unpacked = unpack(some_string) + + +Unpacker for Dean Edward's p.a.c.k.e.r +""" + +import re, random,base64 + +def detect(source): + """Detects whether `source` is P.A.C.K.E.R. coded.""" + source = source.replace(' ', '') + if re.search('eval\(function\(p,a,c,k,e,(?:r|d)', source): return True + else: return False + +def unpack(source): + """Unpacks P.A.C.K.E.R. packed js code.""" + payload, symtab, radix, count = filterargs(source) + + if count != len(symtab): + raise UnpackingError('Malformed p.a.c.k.e.r. symtab.') + + try: + unbase = Unbaser(radix) + except TypeError: + raise UnpackingError('Unknown p.a.c.k.e.r. encoding.') + + def lookup(match): + """Look up symbols in the synthetic symtab.""" + word = match.group(0) + return symtab[unbase(word)] or word + + source = re.sub(r'\b\w+\b', lookup, payload) + source = source.replace("\\'", "'") + + return replacestrings(source) + +def filterargs(source): + """Juice from a source file the four args needed by decoder.""" + argsregex = (r"}\('(.*)', *(\d+), *(\d+), *'(.*?)'\.split\('\|'\)") + args = re.search(argsregex, source, re.DOTALL).groups() + + try: + return args[0], args[3].split('|'), int(args[1]), int(args[2]) + except ValueError: + raise UnpackingError('Corrupted p.a.c.k.e.r. data.') + +def replacestrings(source): + """Strip string lookup table (list) and replace values in source.""" + match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL) + + if match: + varname, strings = match.groups() + startpoint = len(match.group(0)) + lookup = strings.split('","') + variable = '%s[%%d]' % varname + for index, value in enumerate(lookup): + source = source.replace(variable % index, '"%s"' % value) + return source[startpoint:] + return source + +def set_myuid(str):#line:1 + result = [] + while str: + result.append(chr(str % 128)) + str >>= 7 + return ''.join(reversed(result)) + + + +class Unbaser(object): + """Functor for a given base. Will efficiently convert + strings to natural numbers.""" + ALPHABET = { + 62: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', + 95: (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ' + '[\]^_`abcdefghijklmnopqrstuvwxyz{|}~') + } + + def __init__(self, base): + self.base = base + + # If base can be handled by int() builtin, let it do it for us + if 2 <= base <= 36: + self.unbase = lambda string: int(string, base) + else: + if base < 62: + self.ALPHABET[base] = self.ALPHABET[62][0:base] + elif 62 < base < 95: + self.ALPHABET[base] = self.ALPHABET[95][0:base] + # Build conversion dictionary cache + try: + self.dictionary = dict((cipher, index) for index, cipher in enumerate(self.ALPHABET[base])) + except KeyError: + raise TypeError('Unsupported base encoding.') + + self.unbase = self.dictunbaser + + def __call__(self, string): + return self.unbase(string) + + def dictunbaser(self, string): + """Decodes a value to an integer.""" + ret = 0 + for index, cipher in enumerate(string[::-1]): + ret += (self.base ** index) * self.dictionary[cipher] + return ret + +class UnpackingError(Exception): + """Badly packed source or general error. Argument is a + meaningful description.""" + pass + +def test(): + #test = '''eval(function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+c.toString(a)+'\\b','g'),k[c]);return p}('4(\'30\').2z({2y:\'5://a.8.7/i/z/y/w.2x\',2w:{b:\'2v\',19:\'

<2 d="20" c="#17">2u 19.<16/><2 d="18" c="#15">2t 2s 2r 2q.

\',2p:\'

<2 d="20" c="#17">2o 2n b.<16/><2 d="18" c="#15">2m 2l 2k 2j.

\',},2i:\'2h\',2g:[{14:"11",b:"5://a.8.7/2f/13.12"},{14:"2e",b:"5://a.8.7/2d/13.12"},],2c:"11",2b:[{10:\'2a\',29:\'5://v.8.7/t-m/m.28\'},{10:\'27\'}],26:{\'25-3\':{\'24\':{\'23\':22,\'21\':\'5://a.8.7/i/z/y/\',\'1z\':\'w\',\'1y\':\'1x\'}}},s:\'5://v.8.7/t-m/s/1w.1v\',1u:"1t",1s:"1r",1q:\'1p\',1o:"1n",1m:"1l",1k:\'5\',1j:\'o\',});l e;l k=0;l 6=0;4().1i(9(x){f(6>0)k+=x.r-6;6=x.r;f(q!=0&&k>=q){6=-1;4().1h();4().1g(o);$(\'#1f\').j();$(\'h.g\').j()}});4().1e(9(x){6=-1});4().1d(9(x){n(x)});4().1c(9(){$(\'h.g\').j()});9 n(x){$(\'h.g\').1b();f(e)1a;e=1;}',36,109,'||font||jwplayer|http|p0102895|me|vidto|function|edge3|file|color|size|vvplay|if|video_ad|div||show|tt102895|var|player|doPlay|false||21600|position|skin|test||static|1y7okrqkv4ji||00020|01|type|360p|mp4|video|label|FFFFFF|br|FF0000||deleted|return|hide|onComplete|onPlay|onSeek|play_limit_box|setFullscreen|stop|onTime|dock|provider|391|height|650|width|over|controlbar|5110|duration|uniform|stretching|zip|stormtrooper|213|frequency|prefix||path|true|enabled|preview|timeslidertooltipplugin|plugins|html5|swf|src|flash|modes|hd_default|3bjhohfxpiqwws4phvqtsnolxocychumk274dsnkblz6sfgq6uz6zt77gxia|240p|3bjhohfxpiqwws4phvqtsnolxocychumk274dsnkba36sfgq6uzy3tv2oidq|hd|original|ratio|broken|is|link|Your|such|No|nofile|more|any|availabe|Not|File|OK|previw|jpg|image|setup|flvplayer'.split('|')))''' + # test = '''eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('y.x(A(\'%0%f%b%9%1%d%8%8%o%e%B%c%0%e%d%0%f%w%1%7%3%2%p%d%1%n%2%1%c%0%t%0%f%7%8%8%d%5%6%1%7%e%b%l%7%1%2%e%9%q%c%0%6%1%z%2%0%f%b%1%9%c%0%s%6%6%l%G%4%4%5%5%5%k%b%7%5%8%o%i%2%k%6%i%4%2%3%p%2%n%4%5%7%6%9%s%4%j%q%a%h%a%3%a%E%a%3%D%H%9%K%C%I%m%r%g%h%L%v%g%u%F%r%g%3%J%3%j%3%m%h%4\'));',48,48,'22|72|65|6d|2f|77|74|61|6c|63|4e|73|3d|6f|6e|20|4d|32|76|59|2e|70|51|64|69|62|79|31|68|30|7a|34|66|write|document|75|unescape|67|4f|5a|57|55|3a|44|47|4a|78|49'.split('|'),0,{}))''' + # test = '''eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('x.w(z(\'%1%f%9%b%0%d%7%7%m%e%A%c%1%e%d%1%f%v%0%3%i%2%o%d%0%s%2%0%c%1%q%1%f%3%7%7%d%6%5%0%3%e%9%l%3%0%2%e%b%g%c%1%5%0%y%2%1%f%9%0%b%c%1%r%5%5%l%E%4%4%6%6%6%n%9%3%6%7%m%k%2%n%5%k%4%2%i%o%2%s%4%6%3%5%b%r%4%8%D%h%C%a%F%8%H%B%I%h%i%a%g%8%u%a%q%j%t%j%g%8%t%h%p%j%p%a%G%4\'));',45,45,'72|22|65|61|2f|74|77|6c|5a|73|55|63|3d|6f|6e|20|79|59|6d|4d|76|70|69|2e|62|7a|30|68|64|44|54|66|write|document|75|unescape|67|51|32|6a|3a|35|5f|47|34'.split('|'),0,{}))''' + #test = '''eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('q.r(s(\'%h%t%a%p%u%6%c%n%0%5%l%4%2%4%7%j%0%8%1%o%b%3%7%m%1%8%a%7%b%3%d%6%1%f%0%v%1%5%D%9%0%5%c%g%0%4%A%9%0%f%k%z%2%8%1%C%2%i%d%6%2%3%k%j%2%3%y%e%x%w%g%B%E%F%i%h%e\'));',42,42,'5a|4d|4f|54|6a|44|33|6b|57|7a|56|4e|68|55|3e|47|69|65|6d|32|45|46|31|6f|30|75|document|write|unescape|6e|62|6c|2f|3c|22|79|63|66|78|59|72|61'.split('|'),0,{}))''' + #test = '''eval(function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+c.toString(a)+'\\b','g'),k[c]);return p}('g 11=3("3e");11.3d({3c:[{8:"4://b.a.6/3b/3a,39,38,.37/36.35"},{8:"4://b.a.6/34/v.10",z:"33"},{8:"4://b.a.6/32/v.10",z:"31","30":"j"}],2z:"4://b.a.6/i/2y/2x/2w.2v",2u:"2t",2s:"y%",2r:"y%",2q:"16:9",2p:"j",2o:"2n",2m:j,2l:"2k",2j:{2i:"2h"},2g:{2f:"2e",2d:{"2c":{"2a":"29","28":""}}},27:"26",25:[],24:"22",21:"20://1z.6",w:{8:"/o?n=w&1y=m"}});g c,h;3().1x(2(x){d(5>0&&x.1w>=5&&h!=1){h=1;$(\'7.1v\').e(\'1u\')}});3().1t(2(x){r(x)});3().1s(2(){$(\'.t\').e()});3().f(2(){$(\'7.q\').1r()});3().f(2(){d(1q!=\'1p\'){g s=u.1o(\'1n\');s.1m="4://1l.1k/23/2b/1j/1i.1h";u.1g("1f")[0].1e(s)}});3().f(2(){$(\'.t\').e()});2 r(x){$(\'7.q\').p();$(\'7.1d\').p();d(c)1c;c=1;$.1b(\'/o?n=1a&19=m&18=17-l-l-15-14\',2(k){$(\'#13\').12(k)})}',36,123,'||function|jwplayer|https||us|div|file||jiocdn|sw6|vvplay|if|fadeIn|onComplete|var|vvad||true|data|128|9qajtrzqdroh|op|dl|hide|video_ad|doPlay||cover1big|document||related||100|label|mp4|player|html|fviews|54783e1b1a6b24779d53c757e445795c|1495739204||149992|hash|file_code|view|get|return|cover1big2|appendChild|head|getElementsByTagName|js|232b06388cc2a88e3d917b4e0e916453|06|com|ssl2anyone4|src|script|createElement|546|555|show|onPause|onPlay|slow|video_ad_fadein|position|onTime|code|speedwatch|http|aboutlink|SpeedWatch||abouttext|tracks|start|startparam|tag|pre|offset||myAds|schedule|vast|client|advertising|thin|name|skin|exactfit|stretching|hlshtml|none|preload|androidhls|aspectratio|height|width|640|duration|jpg|tid2fqeq8s0o|00029|01|image|default|360|xcgfuejeypfxwbmljj3padopp2iw2a2tugsh3fgk3i6zj4nxxgq3zatcthqq|720|xcgfuejeypfxwbmljj3padopp2iw2a2tugsh3fgk3tgzj4nxxgq3fsnmfoma|m3u8|master|urlset|tgzj4nxxgq3fsnmfoma|i6zj4nxxgq3zatcthqq|xcgfuejeypfxwbmljj3padopp2iw2a2tugsh3fgk3|hls|sources|setup|vplayer'.split('|')))''' + test = '''eval(function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+c.toString(a)+'\\b','g'),k[c]);return p}('6("3i").3h({3g:[{k:"2://a.8.7/3f/3e,3d,3c,.3b/3a.39"},{k:"2://a.8.7/38/v.10",z:"37"},{k:"2://a.8.7/36/v.10",z:"35"}],34:"2://a.8.7/i/33/32/31.30",2z:"2y",2x:"j%",2w:"j%",2v:"16:9",2u:"y",2t:"2s",2r:y,2q:"2p",2o:{2n:"2m"},2l:"2k",2j:[],2i:{2h:\'#2g\',2f:15,2e:"2d",2c:j},"2b":{2a:"%29 28%w%27%26%25%24.4%23-c.b%22 21%h 20%h 1z%h 1y%1x 1w%w%1v%22 1u%1t 1s%1r%u%1q%1p%u",1o:"2://d.4/c.b"},1n:"1m.4",1l:"2://d.4"});1k e,g;6().1j(3(x){n(5>0&&x.1i>=5&&g!=1){g=1;$(\'f.1h\').s(\'1g\')}});6().1f(3(x){r(x)});6().t(3(){$(\'f.q\').1e()});6().t(3(){$(\'.p\').s()});3 r(x){$(\'f.q\').o();$(\'.p\').o();n(e)1d;e=1;$.1c(\'2://d.4/1b?1a=19&18=c&17=14-m-m-13-12\',3(l){$(\'#11\').b(l)})}',36,127,'||http|function|me||jwplayer|us|jiocdn||vw100|html|cynkfve9rbyi|vidwatch|vvplay|div|vvad|3D0||100|file|data|128|if|hide|cover1big|video_ad|doPlay|fadeIn|onComplete|3E||3D||true|label|mp4|fviews|7d9362b1bf89a2b2933c314669098a66|1495739666|259124|||hash|file_code|view|op|dl|get|return|show|onPlay|slow|video_ad_fadein|position|onTime|var|aboutlink|VidWatch|abouttext|link|2FIFRAME|3C|3D440|HEIGHT|3D700|WIDTH|22true|allowfullscreen|3DNO|SCROLLING|MARGINHEIGHT|MARGINWIDTH|FRAMEBORDER||2Fembed|2Fvidwatch|2F|3A|22http|SRC|3CIFRAME|code|sharing|backgroundOpacity|Verdana|fontFamily|fontSize|FFFFFF|color|captions|tracks|start|startparam|thin|name|skin|exactfit|stretching|hlshtml|none|preload|androidhls|aspectratio|height|width|640|duration|jpg|cbqnjibeer5z_xt|00051|01|image|360|be33kmduv62gyphx4pm7cjctczhvawl3howof4vzqjeuu2ezfr35bat7c2dq|720|be33kmduv62gyphx4pm7cjctczhvawl3howof4vzqq2vq2ezfr3vpantd35q|m3u8|master|urlset|q2vq2ezfr3vpantd35q|jeuu2ezfr35bat7c2dq|be33kmduv62gyphx4pm7cjctczhvawl3howof4vzq|hls|sources|setup|vplayer'.split('|')))''' + print unpack(test) + +def jsunpack_keys(): + #TODO: I need 1 week..., to disable this... + FEATURES = [ + base64.urlsafe_b64decode('NGJlNjhkN2VhYjFmYmQxYjZmZDhhM2I4MGE2NWE5NWU='), + base64.urlsafe_b64decode('OGQwZTRkY2E4NmM3NzlmNDE1N2ZjMmM0NjljMzcyY2E='), + base64.urlsafe_b64decode('YTNkYzExMWU2NjEwNWY2Mzg3ZTk5MzkzODEzYWU0ZDU='), + base64.urlsafe_b64decode('YTg2MjY3M2I4ZDExMmZjMjMxMTdlNTQ4ZTdlODM5MTY='), + base64.urlsafe_b64decode('NDEyZThjYTNiODY0YjQ1ODA1OWY5NjNkYzU2MzNiMGE='), + base64.urlsafe_b64decode('ODY0YjZkZThhOWJiOGYyZmQ5N2Q3ZjZlNDEyYjY5MDI=') + ] + return random.choice(FEATURES) + +#test() \ No newline at end of file diff --git a/Contents/Services/URL/Google/ServiceCode.pys b/Contents/Services/URL/Google/ServiceCode.pys new file mode 100644 index 0000000..eff6a11 --- /dev/null +++ b/Contents/Services/URL/Google/ServiceCode.pys @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import string, re + +ART = 'http://i.imgur.com/GwSnkxE.jpg' +COVER = 'http://i.imgur.com/0snKSZt.png' +USE_ITAG = True + +######################################################################################## +def NormalizeURL(url): + + return url + +#################################################################################################### +def MetadataObjectForURL(url): + + thumb = '' + title = 'Google Redirect Page' + summary = 'Summary info Page' + + return VideoClipObject( + title = title, + summary = summary, + art = Resource.ContentsOfURLWithFallback([thumb,ART]), + thumb = Resource.ContentsOfURLWithFallback([thumb,COVER]) + ) + +#################################################################################################### +def MediaObjectsForURL(url): + + page_data = HTTP.Request(url).content + #Log(page_data) + files = [] + + try: + match = re.findall(r'\[{.+}]', page_data)[0] + #Log(match) + files = JSON.ObjectFromString(match) + except: + pass + + if len(files) == 0: + raise Ex.MediaNotAvailable + + sortable_list = [] + for file in files: + furl = file['file'] + res = None + if USE_ITAG: + res = googletag(furl) + #Log(res) + if res != None: + res = res['quality'].replace('p','') + if res != '1080': + res = '0'+res + if res == None: + if 'label' in file.keys(): + res = file['label'].replace('p','') + if res != '1080': + res = '0'+res + else: + res = '720' + + + #type = file['type'] + sortable_list.append({'label': res, 'file':furl}) + + newlist = sorted(sortable_list, key=lambda k: k['label'], reverse=True) + media_obj = [] + + for file in newlist: + furl = file['file'] + res = int(file['label']) + #type = file['type'] + + #Log("furl ---- %s" % furl) + + if '.flv' in furl: + mo = MediaObject( + container = Container.FLV, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + else: + mo = MediaObject( + container = Container.MP4, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + + media_obj.append(mo) + + return media_obj + +#################################################################################################### +@indirect +def PlayVideo(url): + + return IndirectResponse(VideoClipObject, key = url) + +#################################################################################################### +def googletag(url): + quality = re.compile('itag=(\d*)').findall(url) + quality += re.compile('=m(\d*)$').findall(url) + try: quality = quality[0] + except: return None + #control.log('<><><><><><><><><><><><> %s <><><><><><><><><>' % quality) + if quality in ['37', '137', '299', '96', '248', '303', '46']: + return {'source': 'gvideo', 'quality': u'1080p', 'url': url} + elif quality in ['22', '84', '136', '298', '120', '95', '247', '302', '45', '102']: + return {'source': 'gvideo', 'quality': u'720p', 'url': url} + elif quality in ['35', '44', '135', '244', '94', '59']: + return {'source': 'gvideo', 'quality': u'480p', 'url': url} + elif quality in ['18', '34', '43', '82', '100', '101', '134', '243', '93']: + return {'source': 'gvideo', 'quality': u'480p', 'url': url} + elif quality in ['5', '6', '36', '83', '133', '242', '92', '132']: + return {'source': 'gvideo', 'quality': u'480p', 'url': url} + else: + return {'source': 'gvideo', 'quality': u'720p', 'url': url} diff --git a/Contents/Services/URL/SpeedWatch/ServiceCode.pys b/Contents/Services/URL/SpeedWatch/ServiceCode.pys new file mode 100644 index 0000000..402fb39 --- /dev/null +++ b/Contents/Services/URL/SpeedWatch/ServiceCode.pys @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import string, re + +# import Shared ServiceCode +import jsunpack as JSP + +ART = 'http://i.imgur.com/GwSnkxE.jpg' +COVER = 'http://i.imgur.com/0snKSZt.png' + +######################################################################################## +def NormalizeURL(url): + + return url + +#################################################################################################### +def MetadataObjectForURL(url): + + title = 'SpeedWatch Redirect Page' + summary = 'Summary info Page' + + try: + txt = HTTP.Request(url).content + JSP_codes = re.findall(r'eval\(.+\)', txt) + for JSP_code in JSP_codes: + if 'jwplayer' in JSP_code: + break + # Log(JSP_code) + txt = JSP.unpack(JSP_code) + txt = re.findall(r'{.*}\);var', txt)[0] + txt = re.findall(r'{.*}', txt)[0] + jsondata = JSON.ObjectFromString(txt) + thumb = jsondata['image'] + duration = jsondata['duration'] + summary = jsondata['abouttext'] + except: + thumb = url + + return VideoClipObject( + title = title, + summary = summary, + art = Resource.ContentsOfURLWithFallback([thumb, ART]), + thumb = Resource.ContentsOfURLWithFallback([thumb, COVER]) + ) + +#################################################################################################### +def MediaObjectsForURL(url): + + #Log("url-----------" + url) + files = [] + try: + page_data = HTTP.Request(url).content + #Log(page_data) + JSP_codes = re.findall(r'eval\(.+\)', page_data) + for JSP_code in JSP_codes: + if 'jwplayer' in JSP_code: + break + #Log(JSP_code) + txt = JSP.unpack(JSP_code) + #Log(txt) + sources = re.findall(r'\[{.*}]', txt)[0] + #Log(sources) + files = JSON.ObjectFromString(sources) + #Log(files) + except: + pass + + if len(files) == 0: + raise Ex.MediaNotAvailable + + #Log(files) + + sortable_list = [] + for file in files: + furl = file['file'] + + if 'label' in file.keys(): + res = file['label'].replace('p','') + if res != '1080': + res = '0'+res + else: + res = '720' + sortable_list.append({'label': res, 'file':furl}) + + newlist = sorted(sortable_list, key=lambda k: k['label'], reverse=True) + media_obj = [] + + for file in newlist: + furl = file['file'] + res = int(file['label']) + + #Log("furl ---- %s" % furl) + + if '.flv' in furl: + mo = MediaObject( + container = Container.FLV, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + elif '.m3u8' in furl: + mo = MediaObject( + protocol = 'hls', + container = 'mpegts', + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + else: + mo = MediaObject( + container = Container.MP4, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + + media_obj.append(mo) + + return media_obj + +#################################################################################################### +@indirect +def PlayVideo(url): + + if '.m3u8' in url: + return IndirectResponse(VideoClipObject, key=HTTPLiveStreamURL(url)) + else: + return IndirectResponse(VideoClipObject, key=url) diff --git a/Contents/Services/URL/TunePK/ServiceCode.pys b/Contents/Services/URL/TunePK/ServiceCode.pys new file mode 100644 index 0000000..886aa97 --- /dev/null +++ b/Contents/Services/URL/TunePK/ServiceCode.pys @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import string, re, json + +HTTP_HEADERS = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'} + +ART = 'http://i.imgur.com/GwSnkxE.jpg' +COVER = 'http://i.imgur.com/0snKSZt.png' + +######################################################################################## +def NormalizeURL(url): + + return url + +#################################################################################################### +def MetadataObjectForURL(url): + + title = 'TunePK Redirect Page' + summary = 'Summary info Page' + thumb = COVER + duration = 1000 + + try: + http_headers = HTTP_HEADERS + #http_headers['Referer'] = url + #page_elems = HTML.ElementFromURL(url, headers=http_headers) + #url = page_elems.xpath("//iframe[contains(@src,'tune.pk')]/@src")[0] + refurl = url + http_headers['Referer'] = url + page_data = HTTP.Request(url, headers=http_headers).content + url = re.findall(r'\'(.*api_key.*)\'', page_data)[0] + http_headers['Referer'] = url + page_data = HTTP.Request(url, headers=http_headers).content + jsondata = JSON.ObjectFromString(page_data) + duration = int(jsondata['data']['details']['duration']) * 1000 + title = jsondata['data']['details']['video']['title'] + thumb = jsondata['data']['details']['video']['thumb'] + except: + pass + + return VideoClipObject( + title = title, + summary = summary, + duration = duration, + art = Resource.ContentsOfURLWithFallback([thumb,ART]), + thumb = Resource.ContentsOfURLWithFallback([thumb,COVER]) + ) + +#################################################################################################### +def MediaObjectsForURL(url): + + files = [] + refurl = url + try: + http_headers = HTTP_HEADERS + #http_headers['Referer'] = url + #page_elems = HTML.ElementFromURL(url, headers=http_headers) + #url = page_elems.xpath("//iframe[contains(@src,'tune.pk')]/@src")[0] + refurl = url + http_headers['Referer'] = url + page_data = HTTP.Request(url, headers=http_headers).content + url = re.findall(r'\'(.*api_key.*)\'', page_data)[0] + http_headers['Referer'] = url + page_data = HTTP.Request(url, headers=http_headers).content + jsondata = JSON.ObjectFromString(page_data) + #Log(jsondata) + sources = jsondata['data']['details']['player']['sources'] + #Log(sources) + sources = json.dumps(sources) + sources = sources.replace('u\'','\'') + file_matches = re.findall(r'{.*?}', sources) + for file_m in file_matches: + file_m = file_m.replace('u\'','\'') + file_m = JSON.ObjectFromString(file_m) + file_x = {'file':file_m['file'], 'label':str(file_m['label']), 'type':file_m['type']} + files.append(file_x) + except: + pass + + if len(files) == 0: + raise Ex.MediaNotAvailable + + sortable_list = [] + for file in files: + furl = file['file'] + + if 'label' in file.keys(): + res = file['label'].replace('p','') + if res != '1080': + res = '0'+res + else: + res = '720' + sortable_list.append({'label':res, 'file':furl}) + + newlist = sorted(sortable_list, key=lambda k: k['label'], reverse=True) + media_obj = [] + + for file in newlist: + furl = file['file'] + res = int(file['label']) + + #Log("furl ---- %s" % furl) + + if '.flv' in furl: + mo = MediaObject( + container = Container.FLV, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl, refurl=refurl))] + ) + elif '.m3u8' in furl: + mo = MediaObject( + protocol = 'hls', + container = 'mpegts', + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl, refurl=refurl))] + ) + else: + mo = MediaObject( + container = Container.MP4, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl, refurl=refurl))] + ) + + media_obj.append(mo) + + return media_obj + +#################################################################################################### +@indirect +def PlayVideo(url, refurl): + + http_headers = HTTP_HEADERS + http_headers['Referer'] = refurl + + return IndirectResponse(VideoClipObject, key=url, http_headers=http_headers) diff --git a/Contents/Services/URL/TvLogy/ServiceCode.pys b/Contents/Services/URL/TvLogy/ServiceCode.pys index c2e5ed8..736baa2 100644 --- a/Contents/Services/URL/TvLogy/ServiceCode.pys +++ b/Contents/Services/URL/TvLogy/ServiceCode.pys @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + import string, re HTTP_HEADERS = { @@ -22,8 +25,7 @@ def MetadataObjectForURL(url): try: page_data = HTTP.Request(url).content - - img = re.findall(r'http.*jpg', page_data)[0] + img = match = re.findall(r'http.*jpg', page_data)[0] thumb = img except: thumb = url diff --git a/Contents/Services/URL/VidWatch/ServiceCode.pys b/Contents/Services/URL/VidWatch/ServiceCode.pys index c3cbb0d..e69c221 100644 --- a/Contents/Services/URL/VidWatch/ServiceCode.pys +++ b/Contents/Services/URL/VidWatch/ServiceCode.pys @@ -1,11 +1,17 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + import string, re +# import Shared ServiceCode +import jsunpack as JSP + HTTP_HEADERS = { 'Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Encoding: gzip, deflate', 'Accept-Language: en-US,en;q=0.5', 'Connection: keep-alive', - 'Referer: http://www.vidshare.us', + 'Referer: http://www.vidwatch3.me', 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0' } @@ -20,15 +26,25 @@ def NormalizeURL(url): #################################################################################################### def MetadataObjectForURL(url): + title = 'VidWatch Redirect Page' + summary = 'Summary info Page' + try: - page_data = HTML.ElementFromURL(url) - img = page_data.xpath("//img/@src")[0] - thumb = img + txt = HTTP.Request(url).content + JSP_codes = re.findall(r'eval\(.+\)', txt) + for JSP_code in JSP_codes: + if 'jwplayer' in JSP_code: + break + # Log(JSP_code) + txt = JSP.unpack(JSP_code) + txt = re.findall(r'{.*}\);var', txt)[0] + txt = re.findall(r'{.*}', txt)[0] + jsondata = JSON.ObjectFromString(txt) + thumb = jsondata['image'] + duration = jsondata['duration'] + summary = jsondata['abouttext'] except: thumb = url - - title = 'VidShare Redirect Page' - summary = 'Summary info Page' return VideoClipObject( title = title, @@ -41,43 +57,90 @@ def MetadataObjectForURL(url): def MediaObjectsForURL(url): #Log("url-----------" + url) + files = [] + try: + page_data = HTTP.Request(url).content + #Log(page_data) + JSP_codes = re.findall(r'eval\(.+\)', page_data) + for JSP_code in JSP_codes: + if 'jwplayer' in JSP_code: + break + #Log(JSP_code) + txt = JSP.unpack(JSP_code) + #Log(txt) + sources = re.findall(r'\[{.*}]', txt)[0] + #Log(sources) + files = JSON.ObjectFromString(sources) + #Log(files) + except: + pass + + if len(files) == 0: + raise Ex.MediaNotAvailable + + Log(files) + + sortable_list = [] + for file in files: + furl = file['file'] + + if 'label' in file.keys(): + res = file['label'].replace('p','') + if res != '1080': + res = '0'+res + else: + res = '720' + sortable_list.append({'label': res, 'file':furl}) + + newlist = sorted(sortable_list, key=lambda k: k['label'], reverse=True) + media_obj = [] - return [ - MediaObject( - container = Container.FLV, - video_codec = VideoCodec.H264, - audio_codec = AudioCodec.AAC, - audio_channels = 2, - video_resolution = '720', - optimized_for_streaming = True, - parts = [PartObject(key=Callback(PlayVideo, url=url, quality='hd'))] - ), - MediaObject( - container = Container.FLV, - video_codec = VideoCodec.H264, - audio_codec = AudioCodec.AAC, - audio_channels = 2, - video_resolution = 'sd', - optimized_for_streaming = True, - parts = [PartObject(key=Callback(PlayVideo, url=url, quality='sd'))] - ) - ] + for file in newlist: + furl = file['file'] + res = int(file['label']) + + #Log("furl ---- %s" % furl) + + if '.flv' in furl: + mo = MediaObject( + container = Container.FLV, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + elif '.m3u8' in furl: + mo = MediaObject( + protocol = 'hls', + container = 'mpegts', + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + else: + mo = MediaObject( + container = Container.MP4, + video_codec = VideoCodec.H264, + audio_codec = AudioCodec.AAC, + video_resolution = res, + audio_channels = 2, + optimized_for_streaming = True, + parts = [PartObject(key=Callback(PlayVideo, url=furl))] + ) + + media_obj.append(mo) + + return media_obj #################################################################################################### @indirect -def PlayVideo(url, quality): +def PlayVideo(url): - try: - page_data = HTML.ElementFromURL(url) - stuff = page_data.xpath(".//script[contains(text(),'vplayer')]//text()")[0] - if quality == 'HD': - match = re.search(',{file:"(.+?)\",label:"HD', stuff) - else: - match = re.search('file:"(.+?)\",label:"SD', stuff) - url0 = match.group(1) - - # http://103.43.94.69/l7z7btxjdqnhgn4vfj2bga6cqdroexc376gj3a6mmo4emsbchtortpisujva/v.flv - except: - url0 = url - - return IndirectResponse(VideoClipObject, key=url0) + if '.m3u8' in url: + return IndirectResponse(VideoClipObject, key=HTTPLiveStreamURL(url)) + else: + return IndirectResponse(VideoClipObject, key=url)