Ticket #11264: ttvdb_py_v025_and_higher_multiple_episodes.patch
File ttvdb_py_v025_and_higher_multiple_episodes.patch, 21.2 KB (added by , 11 years ago) |
---|
-
mythtv/bindings/python/MythTV/ttvdb/XSLT/tvdbVideo.xsl
From d606669339fe6e2f4557b5f9d491c7fadfa53061 Mon Sep 17 00:00:00 2001 From: "R.D. Vaughan" <r.d.vaughan@rogers.com> Date: Thu, 29 Nov 2012 13:32:29 -0500 Subject: [PATCH 42/42] Log: Changed ttvdb.py to return each episode's metadata for a TV series which matches the passed episode name when the "-N" option is used. --- .../python/MythTV/ttvdb/XSLT/tvdbVideo.xsl | 46 +++---- mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py | 31 +++-- .../programs/scripts/metadata/Television/ttvdb.py | 133 ++++++++++++++------ 3 files changed, 141 insertions(+), 69 deletions(-) diff --git a/mythtv/bindings/python/MythTV/ttvdb/XSLT/tvdbVideo.xsl b/mythtv/bindings/python/MythTV/ttvdb/XSLT/tvdbVideo.xsl index 3eb3c8e..4f2ab79 100755
a b 18 18 <xsl:template match="/"> 19 19 <xsl:if test="//Data/Series"> 20 20 <metadata> 21 <xsl:call-template name= 'tvdbVideoData'/>21 <xsl:call-template name="tvdbEpisodes"/> 22 22 </metadata> 23 23 </xsl:if> 24 24 </xsl:template> 25 25 26 <xsl:template name="tvdbVideoData"> 27 <xsl:for-each select="//Data/Series"> 26 <xsl:template name="tvdbEpisodes"> 27 <xsl:for-each select="//requestDetails"> 28 <xsl:variable name="episode_cnt" select="tvdbXpath:episode_counter()" /> 28 29 <item> 29 <title><xsl:value-of select="normalize-space( SeriesName)"/></title>30 <title><xsl:value-of select="normalize-space(//Data/Series/SeriesName)"/></title> 30 31 <xsl:if test="tvdbXpath:getValue(//requestDetails, //Data, 'subtitle') != ''"> 31 32 <subtitle><xsl:value-of select="normalize-space(tvdbXpath:getResult())"/></subtitle> 32 33 </xsl:if> 33 <language><xsl:value-of select="normalize-space( Language)"/></language>34 <language><xsl:value-of select="normalize-space(//Data/Series/Language)"/></language> 34 35 <xsl:if test="tvdbXpath:getValue(//requestDetails, //Data, 'description') != ''"> 35 36 <description><xsl:value-of select="normalize-space(tvdbXpath:htmlToString(tvdbXpath:getResult()))"/></description> 36 37 </xsl:if> 37 <season><xsl:value-of select="normalize-space( //requestDetails/@season)"/></season>38 <episode><xsl:value-of select="normalize-space( //requestDetails/@episode)"/></episode>39 <xsl:if test=" ./ContentRating/text() != ''">38 <season><xsl:value-of select="normalize-space(./@season)"/></season> 39 <episode><xsl:value-of select="normalize-space(./@episode)"/></episode> 40 <xsl:if test="//Data/Series/ContentRating/text() != ''"> 40 41 <certifications> 41 <xsl:for-each select=" .//ContentRating">42 <xsl:for-each select="//Data/Series//ContentRating"> 42 43 <xsl:element name="certification"> 43 44 <xsl:attribute name="locale">us</xsl:attribute> 44 45 <xsl:attribute name="name"><xsl:value-of select="normalize-space(.)"/></xsl:attribute> … … 46 47 </xsl:for-each> 47 48 </certifications> 48 49 </xsl:if> 49 <xsl:if test=" ./Genre/text() != ''">50 <xsl:if test="//Data/Series/Genre/text() != ''"> 50 51 <categories> 51 <xsl:for-each select="tvdbXpath:stringToList(string( ./Genre))">52 <xsl:for-each select="tvdbXpath:stringToList(string(//Data/Series/Genre))"> 52 53 <xsl:element name="category"> 53 54 <xsl:attribute name="type">genre</xsl:attribute> 54 55 <xsl:attribute name="name"><xsl:value-of select="normalize-space(.)"/></xsl:attribute> … … 56 57 </xsl:for-each> 57 58 </categories> 58 59 </xsl:if> 59 <xsl:if test=" ./Network/text() != ''">60 <xsl:if test="//Data/Series/Network/text() != ''"> 60 61 <studios> 61 <xsl:for-each select=" ./Network">62 <xsl:for-each select="//Data/Series/Network"> 62 63 <xsl:element name="studio"> 63 64 <xsl:attribute name="name"><xsl:value-of select="normalize-space(.)"/></xsl:attribute> 64 65 </xsl:element> 65 66 </xsl:for-each> 66 67 </studios> 67 68 </xsl:if> 68 <xsl:if test=" ./Runtime/text() != ''">69 <runtime><xsl:value-of select="normalize-space( Runtime)"/></runtime>69 <xsl:if test="//Data/Series/Runtime/text() != ''"> 70 <runtime><xsl:value-of select="normalize-space(//Data/Series/Runtime)"/></runtime> 70 71 </xsl:if> 71 <inetref><xsl:value-of select="normalize-space( id)"/></inetref>72 <collectionref><xsl:value-of select="normalize-space( id)"/></collectionref>73 <xsl:if test=" ./IMDB_ID/text() != '' and tvdbXpath:getValue(//requestDetails, //Data, 'IMDB') = ''">74 <imdb><xsl:value-of select="normalize-space(substring-after(string( IMDB_ID), 'tt'))"/></imdb>72 <inetref><xsl:value-of select="normalize-space(//Data/Series/id)"/></inetref> 73 <collectionref><xsl:value-of select="normalize-space(//Data/Series/id)"/></collectionref> 74 <xsl:if test="//Data/Series/IMDB_ID/text() != '' and tvdbXpath:getValue(//requestDetails, //Data, 'IMDB') = ''"> 75 <imdb><xsl:value-of select="normalize-space(substring-after(string(//Data/Series/IMDB_ID), 'tt'))"/></imdb> 75 76 </xsl:if> 76 <xsl:if test=" ./zap2it_id/text() != ''">77 <tmsref><xsl:value-of select="normalize-space( zap2it_id)"/></tmsref>77 <xsl:if test="//Data/Series/zap2it_id/text() != ''"> 78 <tmsref><xsl:value-of select="normalize-space(//Data/Series/zap2it_id)"/></tmsref> 78 79 </xsl:if> 79 80 <xsl:for-each select="tvdbXpath:getValue(//requestDetails, //Data, 'allEpisodes', 'allresults')"> 80 81 <xsl:if test="./IMDB_ID/text() != ''"> 81 <imdb><xsl:value-of select="normalize-space(substring-after(string( IMDB_ID), 'tt'))"/></imdb>82 <imdb><xsl:value-of select="normalize-space(substring-after(string(./IMDB_ID), 'tt'))"/></imdb> 82 83 </xsl:if> 83 84 <xsl:if test="./Rating/text() != ''"> 84 85 <userrating><xsl:value-of select="normalize-space(./Rating)"/></userrating> … … 186 187 </item> 187 188 </xsl:for-each> 188 189 </xsl:template> 190 189 191 </xsl:stylesheet> -
mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py
diff --git a/mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py b/mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py index ff5dc68..670d9bf 100755
a b See this link for the specifications: 21 21 http://www.mythtv.org/wiki/MythTV_Universal_Metadata_Format 22 22 ''' 23 23 24 __version__="v0.1. 2"24 __version__="v0.1.3" 25 25 # 0.1.0 Initial development 26 26 # 0.1.1 Converted categories, genre, ... etc text characters to be XML compliant 27 27 # 0.1.2 Performance improvements by removing complex data searches from the XLST stylesheet 28 # 0.1.3 Change to handle multiple episodes with matching names 28 29 29 30 30 31 # Specify the class names that have XPath extention functions … … class xpathFunctions(object): 93 94 """Functions specific extending XPath 94 95 """ 95 96 def __init__(self): 97 # 98 self.episode_cnt = None 99 # 96 100 self.filters = { 97 101 'fanart': [u'//Banner[BannerType/text()="%(type)s" and Language/text()="%(language)s"]', u'//Banner[BannerType/text()="%(type)s" and Language/text()="en"]', u'//Banner[BannerType/text()="%(type)s"]'], 98 102 'poster': [u'//Banner[BannerType/text()="season" and Language/text()="%(language)s" and Season/text()="%(season)s" and BannerType2/text()="season"]', u'//Banner[BannerType/text()="%(type)s" and Language/text()="%(language)s"]', u'//Banner[BannerType/text()="season" and Language/text()="en" and Season/text()="%(season)s" and BannerType2/text()="season"]', u'//Banner[BannerType/text()="season" and Season/text()="%(season)s" and BannerType2/text()="season"]', u'//Banner[BannerType/text()="%(type)s" and Language/text()="en"]', u'//Banner[BannerType/text()="%(type)s"]'], … … class xpathFunctions(object): 124 128 'imageElements': self.imageElements, 125 129 'getValue': self.getValue, 126 130 'getResult': self.getResult, 131 'episode_counter': self.episode_counter, 127 132 } 128 133 return 129 134 # end buildFuncDict() … … class xpathFunctions(object): 179 184 if not len(args[0]): 180 185 return [] 181 186 elementList = [] 182 183 187 parmDict = { 184 188 'type': args[1], 185 'language': args[2][ 0].attrib['lang'],186 'season': args[2][ 0].attrib['season'],187 'episode': args[2][ 0].attrib['episode'],189 'language': args[2][self.episode_cnt].attrib['lang'], 190 'season': args[2][self.episode_cnt].attrib['season'], 191 'episode': args[2][self.episode_cnt].attrib['episode'], 188 192 } 189 193 filters = [] 190 194 for index in range(len(self.filters[args[1]])): … … class xpathFunctions(object): 276 280 else: 277 281 allValues = True 278 282 parmDict = { 279 'season': args[0][ 0].attrib['season'],280 'episode': args[0][ 0].attrib['episode'],283 'season': args[0][self.episode_cnt].attrib['season'], 284 'episode': args[0][self.episode_cnt].attrib['episode'], 281 285 } 282 286 xpathFilter = etree.XPath(self.dataFilters[args[2]] % parmDict) 283 287 results = xpathFilter(args[1][0]) 284 285 288 # Sometimes all the results are required 286 289 if allValues == True: 287 290 self.persistentResult = results … … class xpathFunctions(object): 300 303 return self.persistentResult 301 304 # end getResult() 302 305 306 def episode_counter(self, *args): 307 ''' Add one or set the index value for the episode array 308 return current index value 309 ''' 310 if self.episode_cnt == None: 311 self.episode_cnt = 0 312 else: 313 self.episode_cnt += 1 314 # 315 return self.episode_cnt 316 # end episode_counter() 317 303 318 ###################################################################################################### 304 319 # 305 320 # End of XPath extension functions -
mythtv/programs/scripts/metadata/Television/ttvdb.py
diff --git a/mythtv/programs/scripts/metadata/Television/ttvdb.py b/mythtv/programs/scripts/metadata/Television/ttvdb.py index 77517c0..3da5a15 100755
a b 37 37 #------------------------------------- 38 38 __title__ ="TheTVDB.com"; 39 39 __author__="R.D.Vaughan" 40 __version__="1.1. 5"40 __version__="1.1.6" 41 41 # Version .1 Initial development 42 42 # Version .2 Add an option to get season and episode numbers from ep name 43 43 # Version .3 Cleaned up the documentation and added a usage display option … … __version__="1.1.5" 136 136 # Version 1.1.4 Add test mode (replaces --toprated) 137 137 # Version 1.1.5 Add the -C (collection option) with corresponding XML output 138 138 # and add a <collectionref> XML tag to Search and Query XML output 139 # Version 1.1.6 Fixed -N season and episode returned values when there is a 140 # multi-part episode with the same name e.g. "Haven" "Magic Hour". 141 # Now both the episodes details are returned. MythTV can use the 142 # original air date to match with the releasedate to determine 143 # which episode is the correct one to select. 144 # 139 145 140 146 usage_txt=''' 141 147 Usage: ttvdb.py usage: ttvdb -hdruviomMPFBDSC [parameters] … … http://www.thetvdb.com/banners/blank/70382.jpg 213 219 > ttvdb -Bl en "Lost" 214 220 Banner:http://www.thetvdb.com/banners/graphical/73739-g4.jpg,http://www.thetvdb.com/banners/graphical/73739-g.jpg,http://www.thetvdb.com/banners/graphical/73739-g6.jpg,http://www.thetvdb.com/banners/graphical/73739-g8.jpg,http://www.thetvdb.com/banners/graphical/73739-g3.jpg,http://www.thetvdb.com/banners/graphical/73739-g7.jpg,http://www.thetvdb.com/banners/graphical/73739-g5.jpg,http://www.thetvdb.com/banners/graphical/24313-g2.jpg,http://www.thetvdb.com/banners/graphical/24313-g.jpg,http://www.thetvdb.com/banners/graphical/73739-g10.jpg,http://www.thetvdb.com/banners/graphical/73739-g2.jpg 215 221 216 (Return a season and episode numbers using the override file to identify the series as the US version)217 > ttvdb -N --configure="/home/user/.tvdb/tvdb.conf" "Eleventh Hour" "H2O"222 (Return multiple episodes when the episode name returns multiple values) 223 > ttvdb.py -N "Haven" "Magic Hour" 218 224 <?xml version='1.0' encoding='UTF-8'?> 219 225 <metadata> 220 226 <item> 221 <title> Eleventh Hour (US)</title>222 <subtitle> H2O</subtitle>227 <title>Haven</title> 228 <subtitle>Magic Hour (2)</subtitle> 223 229 <language>en</language> 224 <description>An epidemic of sudden, violent outbursts by law-abiding citizens draws Dr. Jacob Hood to a quiet Texas community to investigate - but he soon succumbs to the same erratic behavior.</description> 225 <season>1</season> 226 <episode>10</episode> 227 ... 228 <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/83066-4.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/83066-4.jpg" width="1280" height="720"/> 229 <image type="banner" url="http://www.thetvdb.com/banners/graphical/83066-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/83066-g.jpg"/> 230 </images> 230 <description>Audrey and Duke return from Colorado; Audrey, Duke and Tommy tries to find the woman with the resurrection touch.</description> 231 <season>3</season> 232 <episode>8</episode> 233 <certifications> 234 <certification locale="us" name="TV-PG"/> 235 </certifications> 236 <categories> 237 <category type="genre" name="Drama"/> 238 <category type="genre" name="Science-Fiction"/> 239 </categories> 240 <studios> 241 <studio name="Syfy"/> 242 </studios> 243 <runtime>60</runtime> 244 <inetref>158661</inetref> 245 <collectionref>158661</collectionref> 246 <imdb>1519931</imdb> 247 <tmsref>SH01281487</tmsref> 248 <userrating>7.9</userrating> 249 <year>2012</year> 250 <releasedate>2012-11-09</releasedate> 251 ... 252 </item> 253 <item> 254 <title>Haven</title> 255 <subtitle>Magic Hour (1)</subtitle> 256 <language>en</language> 257 <description>Audrey and Duke follow a lead on the Colorado Kid; Nathan and Tommy investigate a series of resurrections.</description> 258 <season>3</season> 259 <episode>7</episode> 260 <certifications> 261 <certification locale="us" name="TV-PG"/> 262 </certifications> 263 <categories> 264 <category type="genre" name="Drama"/> 265 <category type="genre" name="Science-Fiction"/> 266 </categories> 267 <studios> 268 <studio name="Syfy"/> 269 </studios> 270 <runtime>60</runtime> 271 <inetref>158661</inetref> 272 <collectionref>158661</collectionref> 273 <imdb>1519931</imdb> 274 <tmsref>SH01281487</tmsref> 275 <userrating>7.9</userrating> 276 <year>2012</year> 277 <releasedate>2012-11-02</releasedate> 278 <lastupdated>Thu, 15 Nov 2012 10:59:08 GMT</lastupdated> 279 <homepage>http://thetvdb.com/?tab=episode&seriesid=158661&seasonid=491933&id=4361567</homepage> 280 ... 231 281 </item> 232 282 </metadata> 233 283 … … def Getseries_episode_numbers(t, opts, series_season_ep): 926 976 ep_name=series_season_ep[1] # Leave the episode name alone 927 977 928 978 season_ep_num=search_for_series(t, series_name).fuzzysearch(ep_name, 'episodename') 929 if len(season_ep_num) != 0: 930 for episode in sorted(season_ep_num, key=lambda ep: _episode_sort(ep), reverse=True): 931 # if episode.distance == 0: # exact match 932 if xmlFlag: 933 displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']]) 934 sys.exit(0) 935 print season_and_episode_num.replace('\\n', '\n') % (int(episode['seasonnumber']), int(episode['episodenumber'])) 936 # elif (episode['episodename'].lower()).startswith(ep_name.lower()): 937 # if len(episode['episodename']) > (len(ep_name)+1): 938 # if episode['episodename'][len(ep_name):len(ep_name)+2] != ' (': 939 # continue # Skip episodes the are not part of a set of (1), (2) ... etc 940 # if xmlFlag: 941 # displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']]) 942 # sys.exit(0) 943 # print season_and_episode_num.replace('\\n', '\n') % (int(episode['seasonnumber']), int(episode['episodenumber'])) 979 all_matching_episodes = sorted(season_ep_num, key=lambda ep: _episode_sort(ep), reverse=True) 980 all_matching_episodes_cnt = len(all_matching_episodes) 981 # 982 all_matching_episodes_array = [] 983 for episode in all_matching_episodes: 984 all_matching_episodes_array.append([series_name, 985 episode['seasonnumber'], 986 episode['episodenumber']]) 987 if all_matching_episodes_array: 988 if xmlFlag: 989 displaySeriesXML(t, all_matching_episodes_array) 990 else: 991 for episode in all_matching_episodes: 992 print season_and_episode_num.replace('\\n', '\n') % ( 993 int(episode['seasonnumber']), 994 int(episode['episodenumber'])) 995 # 996 sys.exit(0) 944 997 # end Getseries_episode_numbers 945 998 946 999 # Set up a custom interface to get all series matching a partial series name … … def displaySearchXML(tvdb_api): 1072 1125 sys.exit(0) 1073 1126 # end displaySearchXML() 1074 1127 1075 def displaySeriesXML(tvdb_api, series_season_ep ):1128 def displaySeriesXML(tvdb_api, series_season_ep_array): 1076 1129 '''Using a XSLT style sheet translate TVDB Series data results into the 1077 1130 MythTV Universal Query format 1078 1131 return nothing … … def displaySeriesXML(tvdb_api, series_season_ep): 1080 1133 global xslt, tvdbXpath 1081 1134 allDataElement = etree.XML(u'<allData></allData>') 1082 1135 requestDetails = etree.XML(u'<requestDetails></requestDetails>') 1083 requestDetails.attrib['lang'] = xslt.language 1084 requestDetails.attrib['series'] = series_season_ep[0] 1085 requestDetails.attrib['season'] = series_season_ep[1] 1086 requestDetails.attrib['episode'] = series_season_ep[2] 1087 allDataElement.append(requestDetails) 1088 1136 for series_season_ep in series_season_ep_array: 1137 requestDetails_copy = deepcopy(requestDetails) 1138 requestDetails_copy.attrib['lang'] = xslt.language 1139 requestDetails_copy.attrib['series'] = series_season_ep[0] 1140 requestDetails_copy.attrib['season'] = series_season_ep[1] 1141 requestDetails_copy.attrib['episode'] = series_season_ep[2] 1142 allDataElement.append(requestDetails_copy) 1143 # 1089 1144 # Combine the various XML inputs into a single XML element and send to the XSLT stylesheet 1090 1145 if tvdb_api.epInfoTree != None: 1091 1146 allDataElement.append(tvdb_api.epInfoTree) … … def displaySeriesXML(tvdb_api, series_season_ep): 1099 1154 allDataElement.append(tvdb_api.imagesInfoTree) 1100 1155 else: 1101 1156 allDataElement.append(etree.XML(u'<Banners></Banners>')) 1102 1157 # 1103 1158 tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbVideo.xsl'))) 1104 1159 items = tvdbQueryXslt(allDataElement) 1105 1160 if items.getroot() != None: … … def main(): 1228 1283 if len(series_season_ep) < 2: 1229 1284 parser.error("! An Episode name must be included") 1230 1285 sys.exit(1) 1231 if len(series_season_ep) == 3:1286 elif len(series_season_ep) == 3 and not opts.numbers: 1232 1287 season_and_episode_num = series_season_ep[2] # Override default output format 1233 1288 # 1234 1289 if opts.screenshot: 1235 1290 if len(series_season_ep) > 1: 1236 1291 if not _can_int(series_season_ep[1]): … … def main(): 1430 1485 print u"Season and Episode numbers required." 1431 1486 else: 1432 1487 if opts.xml: 1433 displaySeriesXML(t, series_season_ep)1488 displaySeriesXML(t, [series_season_ep]) 1434 1489 sys.exit(0) 1435 1490 Getseries_episode_data(t, opts, series_season_ep, language=opts.language) 1436 1491 else: 1437 1492 if opts.xml and len(series_season_ep) == 3: 1438 displaySeriesXML(t, series_season_ep)1493 displaySeriesXML(t, [series_season_ep]) 1439 1494 sys.exit(0) 1440 1495 Getseries_episode_data(t, opts, series_season_ep, language=opts.language) 1441 1496