home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / xbmc-9.11.exe / scripts / AppleMovieTrailers / resources / plugins / Apple Movie Trailers / amtAPI / xbmcplugin_videos.py < prev   
Encoding:
Python Source  |  2009-04-04  |  18.4 KB  |  344 lines

  1. """
  2.     Videos module: fetches a list of playable streams for a specific category
  3. """
  4.  
  5. # main imports
  6. import sys
  7. import os
  8. import xbmc
  9. import xbmcgui
  10. import xbmcplugin
  11.  
  12. import re
  13. from random import randrange
  14.  
  15. from pysqlite2 import dbapi2 as sqlite
  16.  
  17.  
  18. class _Info:
  19.     def __init__( self, *args, **kwargs ):
  20.         self.__dict__.update( kwargs )
  21.  
  22.  
  23. class Main:
  24.     # base paths
  25.     BASE_DATA_PATH = os.path.join( xbmc.translatePath( "special://masterprofile/" ), "script_data", sys.modules[ "__main__" ].__script__ )
  26.  
  27.     def __init__( self ):
  28.         self._get_settings()
  29.         self._parse_argv()
  30.         self.get_videos()
  31.  
  32.     def _parse_argv( self ):
  33.         # call _Info() with our formatted argv to create the self.args object
  34.         exec "self.args = _Info(%s)" % ( sys.argv[ 2 ][ 1 : ].replace( "&", ", " ), )
  35.  
  36.     def _get_settings( self ):
  37.         self.settings = {}
  38.         self.settings[ "quality" ] = int( xbmcplugin.getSetting( "quality" ) )
  39.         self.settings[ "only_hd" ] = xbmcplugin.getSetting( "only_hd" ) == "true"
  40.         self.settings[ "play_all" ] = xbmcplugin.getSetting( "play_all" ) == "true"
  41.         self.settings[ "rating" ] = int( xbmcplugin.getSetting( "rating" ) )
  42.         self.settings[ "mode" ] = int( xbmcplugin.getSetting( "mode" ) )
  43.         self.settings[ "download_path" ] = xbmc.translatePath( xbmcplugin.getSetting( "download_path" ) )
  44.         self.settings[ "mark_watched" ] = xbmcplugin.getSetting( "mark_watched" ) == "true"
  45.         self.settings[ "whole_words" ] = xbmcplugin.getSetting( "whole_words" ) == "true"
  46.         #self.settings[ "player_core" ] = int( xbmcplugin.getSetting( "player_core" ) )
  47.         self.settings[ "fanart_genre" ] = xbmcplugin.getSetting( "fanart_genre" ) == "true"
  48.         self.settings[ "fanart_genre_path" ] = xbmcplugin.getSetting( "fanart_genre_path" )
  49.         self.settings[ "fanart_image" ] = xbmcplugin.getSetting( "fanart_image" )
  50.         self.settings[ "fanart_color1" ] = xbmcplugin.getSetting( "fanart_color1" )
  51.         self.settings[ "fanart_color2" ] = xbmcplugin.getSetting( "fanart_color2" )
  52.         self.settings[ "fanart_color3" ] = xbmcplugin.getSetting( "fanart_color3" )
  53.         self.settings[ "amt_db_path" ] = xbmc.translatePath( xbmcplugin.getSetting( "amt_db_path" ) )
  54.  
  55.     def get_videos( self ):
  56.         try:
  57.             # fetch trailers from database
  58.             if ( self.args.genre_id == -99 ):
  59.                 trailers = self._search_query()
  60.             else:
  61.                 trailers = self._genre_query()
  62.             # fill media list
  63.             ok = self._fill_media_list( trailers )
  64.         except:
  65.             # oops print error message
  66.             print "ERROR: %s::%s (%d) - %s" % ( self.__class__.__name__, sys.exc_info()[ 2 ].tb_frame.f_code.co_name, sys.exc_info()[ 2 ].tb_lineno, sys.exc_info()[ 1 ], )
  67.             ok = False
  68.         # send notification we're finished, successfully or unsuccessfully
  69.         xbmcplugin.endOfDirectory( handle=int( sys.argv[ 1 ] ), succeeded=ok )
  70.  
  71.     def _parse_extra_info( self, records ):
  72.         if ( not records ): return self.args.genre, "", []
  73.         genre = ""
  74.         studio = records[ 0 ][ 1 ]
  75.         cast = []
  76.         for record in records:
  77.             if ( record[ 0 ] not in genre ):
  78.                 genre += record[ 0 ] + " / "
  79.             if ( record[ 2 ] not in cast ):
  80.                 cast += [ record[ 2 ] ]
  81.         genre = genre[ : -3 ]
  82.         return genre, studio, cast
  83.  
  84.     def _fill_media_list( self, trailers ):
  85.         try:
  86.             records = Records( amt_db_path=self.settings[ "amt_db_path" ] )
  87.             ok = True
  88.             # enumerate through the list of trailers and add the item to the media list
  89.             for trailer in trailers:
  90.                 # select the correct trailer quality.
  91.                 url = self._get_trailer_url( trailer[ 0 ], eval( trailer[ 3 ] ), eval( trailer[ 13 ] ) )
  92.                 if ( url ):
  93.                     # check for a valid thumbnail
  94.                     thumbnail = ""
  95.                     if ( trailer[ 4 ] and trailer[ 4 ] is not None ):
  96.                         thumbnail = os.path.join( self.BASE_DATA_PATH, ".cache", trailer[ 4 ][ 0 ], trailer[ 4 ] )
  97.                     # set the default icon
  98.                     icon = "DefaultVideo.png"
  99.                     # if a rating exists format it
  100.                     rating = ( "", "[%s]" % trailer[ 7 ], )[ trailer[ 7 ] != "" ]
  101.                     # if a plot does not exist, use a default message
  102.                     plot = ( "No synopsis provided by the studio.", trailer[ 5 ], )[ trailer[ 5 ] != "" ]
  103.                     # only need to add label, icon and thumbnail, setInfo() and addSortMethod() takes care of label2
  104.                     listitem = xbmcgui.ListItem( trailer[ 1 ], rating, iconImage=icon, thumbnailImage=thumbnail )
  105.                     # fetch extra info
  106.                     result = records.fetch( Query()[ "extra" ], ( trailer[ 0 ], ) )
  107.                     # parse information
  108.                     genre, studio, cast = self._parse_extra_info( result )
  109.                     # set watched status
  110.                     watched = trailer[ 10 ] > 0
  111.                     # set an overlay if one is practical
  112.                     overlay = ( xbmcgui.ICON_OVERLAY_NONE, xbmcgui.ICON_OVERLAY_HD, )[ "720p.mov" in url or "1080p.mov" in url ]
  113.                     overlay = ( overlay, xbmcgui.ICON_OVERLAY_WATCHED, )[ watched ]
  114.                     # release date and year
  115.                     try:
  116.                         parts = trailer[ 9 ].split( " " )
  117.                         year = int( parts[ 2 ] )
  118.                         day = int( parts[ 1 ][ : -3 ] )
  119.                         month = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ].index( parts[ 0 ] ) + 1
  120.                         release_date = "%02d-%02d-%04d" % ( day, month, year, )
  121.                     except:
  122.                         release_date = ""
  123.                         year = 0
  124.                     # add the different infolabels we want to sort by
  125.                     listitem.setInfo( type="Video", infoLabels={ "Watched": watched, "Date": release_date, "Overlay": overlay, "Duration": trailer[ 6 ], "MPAA": rating, "Plot": plot, "Plotoutline": plot, "Title": trailer[ 1 ], "Year": year, "Genre": genre, "Studio": studio, "Cast": cast } )
  126.                     # set release date property
  127.                     listitem.setProperty( "releasedate", trailer[ 9 ] )
  128.                     # add the item to the media list
  129.                     ok = xbmcplugin.addDirectoryItem( handle=int( sys.argv[ 1 ] ), url=url, listitem=listitem, totalItems=len(trailers) )
  130.                     # if user cancels, call raise to exit loop
  131.                     if ( not ok ): raise
  132.         except:
  133.             # user cancelled dialog or an error occurred
  134.             print "ERROR: %s::%s (%d) - %s" % ( self.__class__.__name__, sys.exc_info()[ 2 ].tb_frame.f_code.co_name, sys.exc_info()[ 2 ].tb_lineno, sys.exc_info()[ 1 ], )
  135.             ok = False
  136.         records.close()
  137.         # if successful and user did not cancel, set our sort orders, content, plugin category and fanart
  138.         if ( ok ):
  139.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_LABEL )
  140.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_DATE )
  141.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_MPAA_RATING )
  142.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_VIDEO_RUNTIME )
  143.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_VIDEO_YEAR )
  144.             xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_STUDIO )
  145.             # set content
  146.             xbmcplugin.setContent( handle=int( sys.argv[ 1 ] ), content="movies" )
  147.             try:
  148.                 # set our plugin category
  149.                 xbmcplugin.setPluginCategory( handle=int( sys.argv[ 1 ] ), category=self.args.genre )
  150.                 # set our fanart from user setting
  151.                 if ( self.settings[ "fanart_genre" ] ):
  152.                     # use an image named after the current genre
  153.                     if ( self.settings[ "fanart_genre_path" ] ):
  154.                         image_path = os.path.join( self.settings[ "fanart_genre_path" ], self.args.genre + ".tbn" )
  155.                         if ( os.path.isfile ( image_path ) ):
  156.                             xbmcplugin.setPluginFanart( handle=int( sys.argv[ 1 ] ), image=image_path, color1=self.settings[ "fanart_color1" ], color2=self.settings[ "fanart_color2" ], color3=self.settings[ "fanart_color3" ] )
  157.                 elif ( self.settings[ "fanart_image" ] ):
  158.                     xbmcplugin.setPluginFanart( handle=int( sys.argv[ 1 ] ), image=self.settings[ "fanart_image" ], color1=self.settings[ "fanart_color1" ], color2=self.settings[ "fanart_color2" ], color3=self.settings[ "fanart_color3" ] )
  159.             except:
  160.                 pass
  161.         return ok
  162.  
  163.     def _get_trailer_url( self, idMovie, trailer_urls, saved_trailers ):
  164.         # pick a random url (only really applies to multiple urls)
  165.         rnd = randrange( len( trailer_urls ) )
  166.         total = rnd + 1
  167.         urls = []
  168.         # if play_all is enabled we want to cycle through all the videos
  169.         if ( self.settings[ "play_all" ] and len( trailer_urls ) > 1 ):
  170.             rnd = 0
  171.             total = len( trailer_urls )
  172.         for count in range( rnd, total ):
  173.             # get intial choice
  174.             choice = ( self.settings[ "quality" ], len( trailer_urls[ count ] ) - 1, )[ self.settings[ "quality" ] >= len( trailer_urls[ count ] ) ]
  175.             # if quality is non progressive
  176.             if ( self.settings[ "quality" ] <= 2 ):
  177.                 # select the correct non progressive trailer
  178.                 while ( trailer_urls[ count ][ choice ].endswith( "p.mov" ) and choice != -1 ): choice -= 1
  179.             # quality is progressive
  180.             else:
  181.                 # select the proper progressive quality
  182.                 quality = ( "480p", "720p", "1080p", )[ self.settings[ "quality" ] - 3 ]
  183.                 # select the correct progressive trailer
  184.                 while ( quality not in trailer_urls[ count ][ choice ] and trailer_urls[ count ][ choice ].endswith( "p.mov" ) and choice != -1 ): choice -= 1
  185.             # if there was a valid trailer set it
  186.             if ( choice >= 0 and ( not self.settings[ "only_hd" ] or self.settings[ "quality" ] < 4 or ( self.settings[ "only_hd" ] and self.settings[ "quality" ] > 3 and ( "720p.mov" in trailer_urls[ count ][ choice ] or "1080p.mov" in trailer_urls[ count ][ choice ] ) ) ) ):
  187.                 urls += [ trailer_urls[ count ][ choice ] ]
  188.         # sort the urls, same as in main script
  189.         urls.sort()
  190.         # initialize our new list
  191.         url_list = []
  192.         # enumerate through the urls and check if a saved trailer exists
  193.         for url in urls:
  194.             for trailer in saved_trailers:
  195.                 # if a svaed trailer with the exact http address exists, use the saved trailer
  196.                 if ( url == trailer[ 1 ] ):
  197.                     url = trailer[ 0 ]
  198.                     break
  199.             # add our url to the new list
  200.             url_list += [ url ]
  201.         # we now join multiple urls together and create a stack:// url to pass to the player module
  202.         url = " , ".join( url_list )
  203.         if ( " , " in url ):
  204.             url = "stack://" + url
  205.         # TODO: fix player core when XBMC supports it
  206.         # if it is a stack:// url (multiple trailers), we want to download the trailer or mark it as watched, set the new url callback to the plugin
  207.         if ( url and ( url.startswith( "stack://" ) or self.settings[ "mode" ] > 0 or self.settings[ "mark_watched" ] ) ):#or self.settings[ "player_core" ] > 0 ) ):
  208.             url = "%s?idMovie=%d&trailer_url=%s" % ( sys.argv[ 0 ], idMovie, repr( url ), )
  209.         return url
  210.  
  211.     def _fetch_records( self, query, params=None ):
  212.         records = Records( amt_db_path=self.settings[ "amt_db_path" ] )
  213.         result = records.fetch( query, params )
  214.         records.close()
  215.         return result
  216.  
  217.     def _genre_query( self ):
  218.         trailers = self._fetch_records( Query()[ "movies" ] % self._get_limits(), ( self.args.genre_id, ) )
  219.         return trailers
  220.  
  221.     def _get_limits( self ):
  222.         # HD sql statement
  223.         hd_sql = ( "", "AND (movies.trailer_urls LIKE '%720p.mov%' OR movies.trailer_urls LIKE '%1080p.mov%')", )[ self.settings[ "only_hd" ] and ( self.settings[ "quality" ] > 3 ) ]
  224.         # mpaa ratings
  225.         mpaa_ratings = [ "G", "PG", "PG-13", "R", "NC-17" ]
  226.         rating_sql = ""
  227.         # if the user set a valid rating add all up to the selection
  228.         if ( self.settings[ "rating" ] < len( mpaa_ratings ) ):
  229.             user_rating = mpaa_ratings[ self.settings[ "rating" ] ]
  230.             rating_sql = "AND ("
  231.             # enumerate through mpaa ratings and add the selected ones to our sql statement
  232.             for rating in mpaa_ratings:
  233.                 rating_sql += "rating='%s' OR " % ( rating, )
  234.                 # if we found the users choice, we're finished
  235.                 if ( rating == user_rating ): break
  236.             # fix the sql statement
  237.             rating_sql = rating_sql[ : -4 ] + ") "
  238.         return ( hd_sql, rating_sql, )
  239.  
  240.     def _search_query( self ):
  241.         trailers = []
  242.         qv = self.get_keyboard( heading=xbmc.getLocalizedString( 30501 ) )
  243.         xbmc.sleep(10)
  244.         if ( qv ):
  245.             keywords = qv.split()
  246.             where = ""
  247.             compare = False
  248.             pattern = ( "LIKE '%%%s%%'", "regexp('\\b%s\\b')", )[ self.settings[ "whole_words" ] ]
  249.             for word in keywords:
  250.                 if ( word.upper() == "AND" or word.upper() == "OR" ):
  251.                     where += " %s " % word.upper()
  252.                     compare = False
  253.                     continue
  254.                 elif ( word.upper() == "NOT" ):
  255.                     where += "NOT "
  256.                     continue
  257.                 elif ( compare ):
  258.                     where += " AND "
  259.                     compare = False
  260.                 where += "(title %s OR " % ( pattern % ( word, ), )
  261.                 where += "plot %s OR " % ( pattern % ( word, ), )
  262.                 where += "actor %s OR " % ( pattern % ( word, ), )
  263.                 where += "studio %s OR " % ( pattern % ( word, ), )
  264.                 where += "genre %s)" % ( pattern % ( word, ), )
  265.                 compare = True
  266.             trailers = self._fetch_records( Query()[ "search" ] % ( ( where, ) + self._get_limits() ), )
  267.         return trailers
  268.  
  269.     def get_keyboard( self, default="", heading="", hidden=False ):
  270.         """ shows a keyboard and returns a value """
  271.         keyboard = xbmc.Keyboard( default, heading, hidden )
  272.         keyboard.doModal()
  273.         if ( keyboard.isConfirmed() ):
  274.             return keyboard.getText()
  275.         return default
  276.  
  277. class Records:
  278.     def __init__( self, *args, **kwargs ):
  279.         self.connect( kwargs[ "amt_db_path" ] )
  280.  
  281.     def connect( self, db ):
  282.         self.db = sqlite.connect( db )
  283.         self.db.create_function( "regexp", 2, self.regexp )
  284.         self.cursor = self.db.cursor()
  285.     
  286.     def regexp( self, pattern, item ):
  287.         return re.search( pattern, item, re.IGNORECASE ) is not None
  288.  
  289.     def close( self ):
  290.         self.db.close()
  291.     
  292.     def fetch( self, sql, params=None ):
  293.         try:
  294.             if ( params is not None ): self.cursor.execute( sql, params )
  295.             else: self.cursor.execute( sql )
  296.             retval = self.cursor.fetchall()
  297.         except:
  298.             # oops print error message
  299.             print "ERROR: %s::%s (%d) - %s" % ( self.__class__.__name__, sys.exc_info()[ 2 ].tb_frame.f_code.co_name, sys.exc_info()[ 2 ].tb_lineno, sys.exc_info()[ 1 ], )
  300.             retval = []
  301.         return retval
  302.  
  303.  
  304. class Query( dict ):
  305.     def __init__( self ):
  306.         self[ "extra" ] = """
  307.                                     SELECT genres.genre, studios.studio, actors.actor 
  308.                                     FROM movies, genres, genre_link_movie, studios, studio_link_movie, actors, actor_link_movie 
  309.                                     WHERE movies.idMovie=? 
  310.                                     AND studio_link_movie.idMovie=movies.idMovie 
  311.                                     AND studio_link_movie.idStudio=studios.idStudio 
  312.                                     AND actor_link_movie.idMovie=movies.idMovie 
  313.                                     AND actor_link_movie.idActor=actors.idActor 
  314.                                     AND genre_link_movie.idMovie=movies.idMovie 
  315.                                     AND genre_link_movie.idGenre=genres.idGenre;
  316.                                 """
  317.  
  318.         self[ "movies" ] = """
  319.                                     SELECT movies.* 
  320.                                     FROM movies, genre_link_movie
  321.                                     WHERE genre_link_movie.idMovie=movies.idMovie 
  322.                                     AND genre_link_movie.idGenre=? 
  323.                                     AND movies.trailer_urls IS NOT NULL 
  324.                                     AND movies.trailer_urls!='[]' 
  325.                                     %s
  326.                                     %s;
  327.                                 """
  328.  
  329.         self[ "search" ] = """
  330.                                     SELECT DISTINCT movies.*
  331.                                     FROM movies, genres, genre_link_movie, studios, studio_link_movie, actors, actor_link_movie 
  332.                                     WHERE %s 
  333.                                     AND movies.trailer_urls IS NOT NULL 
  334.                                     AND movies.trailer_urls!='[]' 
  335.                                     %s
  336.                                     %s
  337.                                     AND studio_link_movie.idMovie=movies.idMovie 
  338.                                     AND studio_link_movie.idStudio=studios.idStudio 
  339.                                     AND actor_link_movie.idMovie=movies.idMovie 
  340.                                     AND actor_link_movie.idActor=actors.idActor 
  341.                                     AND genre_link_movie.idMovie=movies.idMovie 
  342.                                     AND genre_link_movie.idGenre=genres.idGenre;
  343.                                 """ 
  344.