2020import re
2121import sys
2222import datetime
23- import json
2423import threading
2524from kodi_six import xbmc , xbmcgui , xbmcplugin , xbmcaddon , xbmcvfs
2625from bs4 import BeautifulSoup , SoupStrainer
5756if not xbmcvfs .exists (_addonpath ):
5857 xbmcvfs .mkdir (_addonpath )
5958
60- SHOWING_URL = 'https://www.imdb.com/showtimes/_ajax/location/ '
59+ SHOWING_URL = 'https://www.imdb.com/showtimes/'
6160COMING_URL = 'https://www.imdb.com/calendar/?type=MOVIE'
6261DETAILS_PAGE = "https://www.imdb.com/video/{0}/"
6362quality = int (_settings ("video_quality" )[:- 1 ])
@@ -451,10 +450,10 @@ def process_imdbid(self, imdbID):
451450 def get_contents1 (self , key ):
452451 if key == 'showing' :
453452 page_data = client .request (SHOWING_URL , headers = self .headers )
454- tlink = SoupStrainer ('div ' , {'class' : 'lister- list' })
453+ tlink = SoupStrainer ('ul ' , {'class' : re . compile ( '^ipc-metadata- list') })
455454 mdiv = BeautifulSoup (page_data , "html.parser" , parse_only = tlink )
456- videos = mdiv .find_all ('div ' , {'class' : 'lister -item' })
457- imdbIDs = [x .find ('div' , { 'class' : 'lister-item-image' }). get ( 'data-tconst' ) for x in videos ]
455+ videos = mdiv .find_all ('li ' , {'class' : 'ipc-metadata-list-summary -item' })
456+ imdbIDs = [x .find ('a' ). get ( 'href' ). split ( '/' )[ - 2 ] for x in videos ]
458457 else :
459458 page_data = client .request (COMING_URL , headers = self .headers )
460459 imdbIDs = re .findall (r'<a class="ipc-metadata-list-summary-item__t".+?href="/title/([^/]+)' , page_data , re .DOTALL )
@@ -485,22 +484,25 @@ def list_contents1(self):
485484 self .log ('list_contents1({0})' .format (key ))
486485
487486 items = cache .get (self .get_contents1 , cache_duration , key )
488- for litem in items :
489- listitem = self .make_listitem (litem .get ('labels' ), litem .get ('cast2' ))
490- listitem .setArt (litem .get ('art' ))
491- listitem .setProperty ('IsPlayable' , 'true' )
492- url = sys .argv [0 ] + '?' + urllib_parse .urlencode ({'action' : 'play' ,
493- 'videoid' : litem .get ('videoId' )})
494- xbmcplugin .addDirectoryItem (int (sys .argv [1 ]), url , listitem , False )
487+ if items :
488+ for litem in items :
489+ listitem = self .make_listitem (litem .get ('labels' ), litem .get ('cast2' ))
490+ listitem .setArt (litem .get ('art' ))
491+ listitem .setProperty ('IsPlayable' , 'true' )
492+ url = sys .argv [0 ] + '?' + urllib_parse .urlencode ({
493+ 'action' : 'play' ,
494+ 'videoid' : litem .get ('videoId' )
495+ })
496+ xbmcplugin .addDirectoryItem (int (sys .argv [1 ]), url , listitem , False )
495497
496- # Sort methods and content type...
497- xbmcplugin .setContent (int (sys .argv [1 ]), 'movies' )
498- xbmcplugin .addSortMethod (int (sys .argv [1 ]), xbmcplugin .SORT_METHOD_UNSORTED )
499- xbmcplugin .addSortMethod (int (sys .argv [1 ]), xbmcplugin .SORT_METHOD_VIDEO_TITLE )
500- if force_mode :
501- xbmc .executebuiltin ('Container.SetViewMode({})' .format (view_mode ))
502- # End of directory...
503- xbmcplugin .endOfDirectory (int (sys .argv [1 ]), cacheToDisc = True )
498+ # Sort methods and content type...
499+ xbmcplugin .setContent (int (sys .argv [1 ]), 'movies' )
500+ xbmcplugin .addSortMethod (int (sys .argv [1 ]), xbmcplugin .SORT_METHOD_UNSORTED )
501+ xbmcplugin .addSortMethod (int (sys .argv [1 ]), xbmcplugin .SORT_METHOD_VIDEO_TITLE )
502+ if force_mode :
503+ xbmc .executebuiltin ('Container.SetViewMode({})' .format (view_mode ))
504+ # End of directory...
505+ xbmcplugin .endOfDirectory (int (sys .argv [1 ]), cacheToDisc = True )
504506
505507 def get_contents2 (self , key ):
506508 videos = self .fetchdata (key )
@@ -720,24 +722,42 @@ def list_contents2(self):
720722 def fetch_video_url (self , video_id ):
721723 if DEBUG :
722724 self .log ('fetch_video_url("{0}")' .format (video_id ))
723- vidurl = DETAILS_PAGE .format (video_id )
724- pagedata = client .request (vidurl , headers = self .headers )
725- r = re .search (r'application/json">([^<]+)' , pagedata )
726- if r :
727- details = json .loads (r .group (1 )).get ('props' , {}).get ('pageProps' , {}).get ('videoPlaybackData' , {}).get ('video' )
728- if details :
729- details = {i .get ('displayName' ).get ('value' ): i .get ('url' ) for i in details .get ('playbackURLs' ) if i .get ('videoMimeType' ) == 'MP4' }
730- vids = [(x [:- 1 ], details [x ]) for x in details .keys () if 'p' in x ]
731- vids .sort (key = lambda x : int (x [0 ]), reverse = True )
725+ query = '''query VideoPlayback(
726+ $viconst: ID!
727+ ) {
728+ video(id: $viconst) {
729+ ...SharedVideoAllPlaybackUrls
730+ }
731+ }
732+ '''
733+ fragment = '''fragment SharedVideoAllPlaybackUrls on Video {
734+ playbackURLs {
735+ displayName {
736+ value
737+ }
738+ videoMimeType
739+ url
740+ }
741+ }
742+ '''
743+ pdata = {
744+ 'operationName' : "VideoPlayback" ,
745+ 'query' : self .gqlmin (query + fragment ),
746+ 'variables' : {"viconst" : video_id }
747+ }
748+ data = client .request (self .api_url , headers = self .headers , post = pdata )
749+ vids = data .get ('data' ).get ('video' ).get ('playbackURLs' )
750+ vids = {i .get ('displayName' ).get ('value' ): i .get ('url' ) for i in vids
751+ if i .get ('videoMimeType' ) == 'MP4' }
752+ vids = [(x [:- 1 ], y ) for x , y in vids .items () if 'p' in x ]
753+ vids .sort (key = lambda x : int (x [0 ]), reverse = True )
754+ if DEBUG :
755+ self .log ('Found %s videos' % len (vids ))
756+ for qual , vid in vids :
757+ if int (qual ) <= quality :
732758 if DEBUG :
733- self .log ('Found %s videos' % len (vids ))
734- for qual , vid in vids :
735- if int (qual ) <= quality :
736- if DEBUG :
737- self .log ('videoURL: %s' % vid )
738- return vid
739-
740- return None
759+ self .log ('videoURL: %s' % vid )
760+ return vid
741761
742762 def play (self ):
743763 if DEBUG :
@@ -758,38 +778,55 @@ def play(self):
758778
759779 def play_id (self ):
760780 imdb_id = self .parameters ('imdb' )
781+ season = self .parameters ('season' )
761782 if DEBUG :
762783 self .log ('play_id("{0}")' .format (imdb_id ))
763-
764- video = self .fetchdata_id (imdb_id )
765- if 'errors' in video .keys ():
766- msg = 'Invalid IMDb ID'
767- xbmcgui .Dialog ().notification (_plugin , msg , _icon , 3000 , False )
768- return
769-
770- video = video .get ('data' ).get ('title' )
771- if video .get ('latestTrailer' ):
772- videoid = video .get ('latestTrailer' ).get ('id' )
773- title = video .get ('titleText' ).get ('text' )
774- try :
775- year = video .get ('releaseDate' ).get ('year' )
776- except AttributeError :
777- year = ''
778- plot = video .get ('plot' )
779- if plot :
780- plot = plot .get ('plotText' ).get ('plainText' )
781- thumbnail = video .get ('latestTrailer' ).get ('thumbnail' ).get ('url' )
782- poster = video .get ('primaryImage' ).get ('url' )
783- listitem = self .make_plistitem (title + ' (Trailer)' , plot , year )
784- listitem .setArt ({'thumb' : poster ,
785- 'icon' : poster ,
786- 'poster' : poster ,
787- 'fanart' : thumbnail })
788- listitem .setPath (cache .get (self .fetch_video_url , cache_duration , videoid ))
789- xbmcplugin .setResolvedUrl (int (sys .argv [1 ]), True , listitem = listitem )
784+ if season :
785+ self .log ('play_id_season("{0}")' .format (season ))
786+
787+ if season :
788+ video = self .fetchdata_id_season (imdb_id , season )
789+ if video :
790+ videoid = video .get ('id' )
791+ title = video .get ('name' )
792+ listitem = self .make_plistitem (title )
793+ listitem .setPath (cache .get (self .fetch_video_url , cache_duration , videoid ))
794+ xbmcplugin .setResolvedUrl (int (sys .argv [1 ]), True , listitem = listitem )
795+ else :
796+ msg = 'No Trailers available'
797+ xbmcgui .Dialog ().notification (_plugin , msg , _icon , 3000 , False )
790798 else :
791- msg = 'No Trailers available'
792- xbmcgui .Dialog ().notification (_plugin , msg , _icon , 3000 , False )
799+ video = self .fetchdata_id (imdb_id )
800+ if 'errors' in video .keys ():
801+ msg = 'Invalid IMDb ID'
802+ xbmcgui .Dialog ().notification (_plugin , msg , _icon , 3000 , False )
803+ return
804+
805+ video = video .get ('data' ).get ('title' )
806+ if video .get ('latestTrailer' ):
807+ videoid = video .get ('latestTrailer' ).get ('id' )
808+ title = video .get ('titleText' ).get ('text' )
809+ try :
810+ year = video .get ('releaseDate' ).get ('year' )
811+ except AttributeError :
812+ year = ''
813+ plot = video .get ('plot' )
814+ if plot :
815+ plot = plot .get ('plotText' ).get ('plainText' )
816+ thumbnail = video .get ('latestTrailer' ).get ('thumbnail' ).get ('url' )
817+ poster = video .get ('primaryImage' ).get ('url' )
818+ listitem = self .make_plistitem (title + ' (Trailer)' , plot , year )
819+ listitem .setArt ({
820+ 'thumb' : poster ,
821+ 'icon' : poster ,
822+ 'poster' : poster ,
823+ 'fanart' : thumbnail
824+ })
825+ listitem .setPath (cache .get (self .fetch_video_url , cache_duration , videoid ))
826+ xbmcplugin .setResolvedUrl (int (sys .argv [1 ]), True , listitem = listitem )
827+ else :
828+ msg = 'No Trailers available'
829+ xbmcgui .Dialog ().notification (_plugin , msg , _icon , 3000 , False )
793830
794831 def parameters (self , arg ):
795832 _parameters = urllib_parse .parse_qs (urllib_parse .urlparse (sys .argv [2 ]).query )
@@ -798,7 +835,7 @@ def parameters(self, arg):
798835 val = val [0 ]
799836 return val
800837
801- def make_plistitem (self , title , plot , year = 0 ):
838+ def make_plistitem (self , title , plot = '' , year = 0 ):
802839 li = xbmcgui .ListItem (title )
803840 if _kodiver > 19.8 :
804841 vtag = li .getVideoInfoTag ()
@@ -942,6 +979,117 @@ def fetchdata_id(self, imdb_id):
942979 data = client .request (self .api_url , headers = self .headers , post = pdata )
943980 return data
944981
982+ def fetchdata_id_season (self , imdb_id , season ):
983+ video = {}
984+ query = [
985+ '''
986+ query TitleVideoGallerySubPage(
987+ $const: ID!
988+ $first: Int!
989+ $filter: VideosQueryFilter
990+ $sort: VideoSort
991+ ) {
992+ title(id: $const) {
993+ videoStrip(first: $first, filter: $filter, sort: $sort) {
994+ ...VideoGalleryItems
995+ }
996+ }
997+ }
998+ ''' ,
999+ '''
1000+ query TitleVideoGalleryPagination(
1001+ $const: ID!
1002+ $first: Int!
1003+ $after: ID !
1004+ $filter: VideosQueryFilter
1005+ $sort: VideoSort
1006+ ) {
1007+ title(id: $const) {
1008+ videoStrip(first: $first, after: $after, filter: $filter, sort: $sort) {
1009+ ...VideoGalleryItems
1010+ }
1011+ }
1012+ }
1013+ '''
1014+ ]
1015+
1016+ fragment = '''
1017+ fragment VideoGalleryItems on VideoConnection {
1018+ pageInfo {
1019+ endCursor
1020+ hasNextPage
1021+ }
1022+ edges {
1023+ position
1024+ node {
1025+ id
1026+ contentType {
1027+ displayName {
1028+ value
1029+ }
1030+ id
1031+ }
1032+ name {
1033+ value
1034+ }
1035+ }
1036+ }
1037+ }
1038+ '''
1039+
1040+ opname = ['TitleVideoGallerySubPage' , 'TitleVideoGalleryPagination' ]
1041+ page_size = 100
1042+ vpar = {
1043+ "const" : imdb_id ,
1044+ "first" : page_size ,
1045+ "filter" : {
1046+ "maturityLevel" : "INCLUDE_MATURE" ,
1047+ "types" : ["TRAILER" ]
1048+ },
1049+ "sort" : {
1050+ "by" : "DATE" ,
1051+ "order" : "DESC"
1052+ }
1053+ }
1054+
1055+ pdata = {
1056+ 'operationName' : opname [0 ],
1057+ 'query' : self .gqlmin (query [0 ] + fragment ),
1058+ 'variables' : vpar
1059+ }
1060+
1061+ data = client .request (self .api_url , headers = self .headers , post = pdata )
1062+ try :
1063+ data = data .get ('data' ).get ('title' ).get ('videoStrip' )
1064+ trailers = [{'id' : x .get ('node' ).get ('id' ), 'name' : x .get ('node' ).get ('name' ).get ('value' )} for x in data .get ('edges' )]
1065+ next_page = data .get ('pageInfo' ).get ('hasNextPage' )
1066+ while next_page :
1067+ vpar .update ({"after" : data .get ('pageInfo' ).get ('endCursor' )})
1068+ pdata = {
1069+ 'operationName' : opname [1 ],
1070+ 'query' : self .gqlmin (query [1 ] + fragment ),
1071+ 'variables' : vpar
1072+ }
1073+ data = client .request (self .api_url , headers = self .headers , post = pdata )
1074+ data = data .get ('data' ).get ('title' ).get ('videoStrip' )
1075+ trailers += [{'id' : x .get ('node' ).get ('id' ), 'name' : x .get ('node' ).get ('name' ).get ('value' )} for x in data .get ('edges' )]
1076+ next_page = data .get ('pageInfo' ).get ('hasNextPage' )
1077+ if trailers :
1078+ rel_trailers = [x for x in trailers if f'season { season } ' in x .get ('name' ).lower ()]
1079+ if rel_trailers :
1080+ if len (rel_trailers ) > 1 :
1081+ rel_trailers = [
1082+ x for x in rel_trailers
1083+ if all (y not in x .get ('name' ).lower () for y in ['teaser' , 'preview' , 'episode' ])
1084+ ]
1085+ video = rel_trailers [0 ]
1086+ else :
1087+ video = trailers [0 ]
1088+ except :
1089+ pass
1090+
1091+ return video
1092+
9451093 def fetchdata (self , key ):
9461094 vpar = {'limit' : 100 }
9471095 if key == 'trending' :
0 commit comments