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/mythtv/bindings/python/MythTV/ttvdb/XSLT/tvdbVideo.xsl
+++ b/mythtv/bindings/python/MythTV/ttvdb/XSLT/tvdbVideo.xsl
@@ -18,27 +18,28 @@
     <xsl:template match="/">
         <xsl:if test="//Data/Series">
             <metadata>
-                <xsl:call-template name='tvdbVideoData'/>
+                <xsl:call-template name="tvdbEpisodes"/>
             </metadata>
         </xsl:if>
     </xsl:template>

-    <xsl:template name="tvdbVideoData">
-        <xsl:for-each select="//Data/Series">
+    <xsl:template name="tvdbEpisodes">
+        <xsl:for-each select="//requestDetails">
+            <xsl:variable name="episode_cnt" select="tvdbXpath:episode_counter()" />
             <item>
-                <title><xsl:value-of select="normalize-space(SeriesName)"/></title>
+                <title><xsl:value-of select="normalize-space(//Data/Series/SeriesName)"/></title>
                 <xsl:if test="tvdbXpath:getValue(//requestDetails, //Data, 'subtitle') != ''">
                     <subtitle><xsl:value-of select="normalize-space(tvdbXpath:getResult())"/></subtitle>
                 </xsl:if>
-                <language><xsl:value-of select="normalize-space(Language)"/></language>
+                <language><xsl:value-of select="normalize-space(//Data/Series/Language)"/></language>
                 <xsl:if test="tvdbXpath:getValue(//requestDetails, //Data, 'description') != ''">
                     <description><xsl:value-of select="normalize-space(tvdbXpath:htmlToString(tvdbXpath:getResult()))"/></description>
                 </xsl:if>
-                <season><xsl:value-of select="normalize-space(//requestDetails/@season)"/></season>
-                <episode><xsl:value-of select="normalize-space(//requestDetails/@episode)"/></episode>
-                <xsl:if test="./ContentRating/text() != ''">
+                <season><xsl:value-of select="normalize-space(./@season)"/></season>
+                <episode><xsl:value-of select="normalize-space(./@episode)"/></episode>
+                <xsl:if test="//Data/Series/ContentRating/text() != ''">
                     <certifications>
-                        <xsl:for-each select=".//ContentRating">
+                        <xsl:for-each select="//Data/Series//ContentRating">
                             <xsl:element name="certification">
                                 <xsl:attribute name="locale">us</xsl:attribute>
                                 <xsl:attribute name="name"><xsl:value-of select="normalize-space(.)"/></xsl:attribute>
@@ -46,9 +47,9 @@
                         </xsl:for-each>
                     </certifications>
                 </xsl:if>
-                <xsl:if test="./Genre/text() != ''">
+                <xsl:if test="//Data/Series/Genre/text() != ''">
                     <categories>
-                        <xsl:for-each select="tvdbXpath:stringToList(string(./Genre))">
+                        <xsl:for-each select="tvdbXpath:stringToList(string(//Data/Series/Genre))">
                             <xsl:element name="category">
                                 <xsl:attribute name="type">genre</xsl:attribute>
                                 <xsl:attribute name="name"><xsl:value-of select="normalize-space(.)"/></xsl:attribute>
@@ -56,29 +57,29 @@
                         </xsl:for-each>
                     </categories>
                 </xsl:if>
-                <xsl:if test="./Network/text() != ''">
+                <xsl:if test="//Data/Series/Network/text() != ''">
                     <studios>
-                        <xsl:for-each select="./Network">
+                        <xsl:for-each select="//Data/Series/Network">
                             <xsl:element name="studio">
                                 <xsl:attribute name="name"><xsl:value-of select="normalize-space(.)"/></xsl:attribute>
                             </xsl:element>
                         </xsl:for-each>
                     </studios>
                 </xsl:if>
-                <xsl:if test="./Runtime/text() != ''">
-                    <runtime><xsl:value-of select="normalize-space(Runtime)"/></runtime>
+                <xsl:if test="//Data/Series/Runtime/text() != ''">
+                    <runtime><xsl:value-of select="normalize-space(//Data/Series/Runtime)"/></runtime>
                 </xsl:if>
-                <inetref><xsl:value-of select="normalize-space(id)"/></inetref>
-                <collectionref><xsl:value-of select="normalize-space(id)"/></collectionref>
-                <xsl:if test="./IMDB_ID/text() != '' and tvdbXpath:getValue(//requestDetails, //Data, 'IMDB') = ''">
-                    <imdb><xsl:value-of select="normalize-space(substring-after(string(IMDB_ID), 'tt'))"/></imdb>
+                <inetref><xsl:value-of select="normalize-space(//Data/Series/id)"/></inetref>
+                <collectionref><xsl:value-of select="normalize-space(//Data/Series/id)"/></collectionref>
+                <xsl:if test="//Data/Series/IMDB_ID/text() != '' and tvdbXpath:getValue(//requestDetails, //Data, 'IMDB') = ''">
+                    <imdb><xsl:value-of select="normalize-space(substring-after(string(//Data/Series/IMDB_ID), 'tt'))"/></imdb>
                 </xsl:if>
-                <xsl:if test="./zap2it_id/text() != ''">
-                    <tmsref><xsl:value-of select="normalize-space(zap2it_id)"/></tmsref>
+                <xsl:if test="//Data/Series/zap2it_id/text() != ''">
+                    <tmsref><xsl:value-of select="normalize-space(//Data/Series/zap2it_id)"/></tmsref>
                 </xsl:if>
                 <xsl:for-each select="tvdbXpath:getValue(//requestDetails, //Data, 'allEpisodes', 'allresults')">
                     <xsl:if test="./IMDB_ID/text() != ''">
-                        <imdb><xsl:value-of select="normalize-space(substring-after(string(IMDB_ID), 'tt'))"/></imdb>
+                        <imdb><xsl:value-of select="normalize-space(substring-after(string(./IMDB_ID), 'tt'))"/></imdb>
                     </xsl:if>
                     <xsl:if test="./Rating/text() != ''">
                         <userrating><xsl:value-of select="normalize-space(./Rating)"/></userrating>
@@ -186,4 +187,5 @@
             </item>
         </xsl:for-each>
     </xsl:template>
+
 </xsl:stylesheet>
diff --git a/mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py b/mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py
index ff5dc68..670d9bf 100755
--- a/mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py
+++ b/mythtv/bindings/python/MythTV/ttvdb/tvdbXslt.py
@@ -21,10 +21,11 @@ See this link for the specifications:
 http://www.mythtv.org/wiki/MythTV_Universal_Metadata_Format
 '''

-__version__="v0.1.2"
+__version__="v0.1.3"
 # 0.1.0 Initial development
 # 0.1.1 Converted categories, genre, ... etc text characters to be XML compliant
 # 0.1.2 Performance improvements by removing complex data searches from the XLST stylesheet
+# 0.1.3 Change to handle multiple episodes with matching names


 # Specify the class names that have XPath extention functions
@@ -93,6 +94,9 @@ class xpathFunctions(object):
     """Functions specific extending XPath
     """
     def __init__(self):
+        #
+        self.episode_cnt = None
+        #
         self.filters = {
             '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"]'],
             '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"]'],
@@ -124,6 +128,7 @@ class xpathFunctions(object):
             'imageElements': self.imageElements,
             'getValue': self.getValue,
             'getResult': self.getResult,
+            'episode_counter': self.episode_counter,
             }
         return
     # end buildFuncDict()
@@ -179,12 +184,11 @@ class xpathFunctions(object):
         if not len(args[0]):
             return []
         elementList = []
-
         parmDict = {
             'type': args[1],
-            'language': args[2][0].attrib['lang'],
-            'season': args[2][0].attrib['season'],
-            'episode': args[2][0].attrib['episode'],
+            'language': args[2][self.episode_cnt].attrib['lang'],
+            'season': args[2][self.episode_cnt].attrib['season'],
+            'episode': args[2][self.episode_cnt].attrib['episode'],
             }
         filters = []
         for index in range(len(self.filters[args[1]])):
@@ -276,12 +280,11 @@ class xpathFunctions(object):
         else:
             allValues = True
         parmDict = {
-            'season': args[0][0].attrib['season'],
-            'episode': args[0][0].attrib['episode'],
+            'season': args[0][self.episode_cnt].attrib['season'],
+            'episode': args[0][self.episode_cnt].attrib['episode'],
             }
         xpathFilter = etree.XPath(self.dataFilters[args[2]] % parmDict)
         results = xpathFilter(args[1][0])
-
         # Sometimes all the results are required
         if allValues == True:
             self.persistentResult = results
@@ -300,6 +303,18 @@ class xpathFunctions(object):
         return self.persistentResult
     # end getResult()

+    def episode_counter(self, *args):
+        ''' Add one or set the index value for the episode array
+        return current index value
+        '''
+        if self.episode_cnt == None:
+            self.episode_cnt = 0
+        else:
+            self.episode_cnt += 1
+        #
+        return self.episode_cnt
+    # end episode_counter()
+
 ######################################################################################################
 #
 # End of XPath extension functions
diff --git a/mythtv/programs/scripts/metadata/Television/ttvdb.py b/mythtv/programs/scripts/metadata/Television/ttvdb.py
index 77517c0..3da5a15 100755
--- a/mythtv/programs/scripts/metadata/Television/ttvdb.py
+++ b/mythtv/programs/scripts/metadata/Television/ttvdb.py
@@ -37,7 +37,7 @@
 #-------------------------------------
 __title__ ="TheTVDB.com";
 __author__="R.D.Vaughan"
-__version__="1.1.5"
+__version__="1.1.6"
 # Version .1    Initial development
 # Version .2    Add an option to get season and episode numbers from ep name
 # Version .3    Cleaned up the documentation and added a usage display option
@@ -136,6 +136,12 @@ __version__="1.1.5"
 # Version 1.1.4 Add test mode (replaces --toprated)
 # Version 1.1.5 Add the -C (collection option) with corresponding XML output
 #               and add a <collectionref> XML tag to Search and Query XML output
+# Version 1.1.6 Fixed -N season and episode returned values when there is a
+#               multi-part episode with the same name e.g. "Haven" "Magic Hour".
+#               Now both the episodes details are returned. MythTV can use the
+#               original air date to match with the releasedate to determine
+#               which episode is the correct one to select.
+#

 usage_txt='''
 Usage: ttvdb.py usage: ttvdb -hdruviomMPFBDSC [parameters]
@@ -213,21 +219,65 @@ http://www.thetvdb.com/banners/blank/70382.jpg
 > ttvdb -Bl en "Lost"
 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

-(Return a season and episode numbers using the override file to identify the series as the US version)
-> ttvdb -N --configure="/home/user/.tvdb/tvdb.conf" "Eleventh Hour" "H2O"
+(Return multiple episodes when the episode name returns multiple values)
+> ttvdb.py -N "Haven" "Magic Hour"
 <?xml version='1.0' encoding='UTF-8'?>
 <metadata>
   <item>
-    <title>Eleventh Hour (US)</title>
-    <subtitle>H2O</subtitle>
+    <title>Haven</title>
+    <subtitle>Magic Hour (2)</subtitle>
     <language>en</language>
-    <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>
-    <season>1</season>
-    <episode>10</episode>
-...
-      <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"/>
-      <image type="banner" url="http://www.thetvdb.com/banners/graphical/83066-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/83066-g.jpg"/>
-    </images>
+    <description>Audrey and Duke return from Colorado; Audrey, Duke and Tommy tries to find the woman with the resurrection touch.</description>
+    <season>3</season>
+    <episode>8</episode>
+    <certifications>
+      <certification locale="us" name="TV-PG"/>
+    </certifications>
+    <categories>
+      <category type="genre" name="Drama"/>
+      <category type="genre" name="Science-Fiction"/>
+    </categories>
+    <studios>
+      <studio name="Syfy"/>
+    </studios>
+    <runtime>60</runtime>
+    <inetref>158661</inetref>
+    <collectionref>158661</collectionref>
+    <imdb>1519931</imdb>
+    <tmsref>SH01281487</tmsref>
+    <userrating>7.9</userrating>
+    <year>2012</year>
+    <releasedate>2012-11-09</releasedate>
+    ...
+  </item>
+  <item>
+    <title>Haven</title>
+    <subtitle>Magic Hour (1)</subtitle>
+    <language>en</language>
+    <description>Audrey and Duke follow a lead on the Colorado Kid; Nathan and Tommy investigate a series of resurrections.</description>
+    <season>3</season>
+    <episode>7</episode>
+    <certifications>
+      <certification locale="us" name="TV-PG"/>
+    </certifications>
+    <categories>
+      <category type="genre" name="Drama"/>
+      <category type="genre" name="Science-Fiction"/>
+    </categories>
+    <studios>
+      <studio name="Syfy"/>
+    </studios>
+    <runtime>60</runtime>
+    <inetref>158661</inetref>
+    <collectionref>158661</collectionref>
+    <imdb>1519931</imdb>
+    <tmsref>SH01281487</tmsref>
+    <userrating>7.9</userrating>
+    <year>2012</year>
+    <releasedate>2012-11-02</releasedate>
+    <lastupdated>Thu, 15 Nov 2012 10:59:08 GMT</lastupdated>
+    <homepage>http://thetvdb.com/?tab=episode&amp;seriesid=158661&amp;seasonid=491933&amp;id=4361567</homepage>
+    ...
   </item>
 </metadata>

@@ -926,21 +976,24 @@ def Getseries_episode_numbers(t, opts, series_season_ep):
         ep_name=series_season_ep[1] # Leave the episode name alone

     season_ep_num=search_for_series(t, series_name).fuzzysearch(ep_name, 'episodename')
-    if len(season_ep_num) != 0:
-        for episode in sorted(season_ep_num, key=lambda ep: _episode_sort(ep), reverse=True):
-#            if episode.distance == 0: # exact match
-                if xmlFlag:
-                    displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']])
-                    sys.exit(0)
-                print season_and_episode_num.replace('\\n', '\n') % (int(episode['seasonnumber']), int(episode['episodenumber']))
-#            elif (episode['episodename'].lower()).startswith(ep_name.lower()):
-#                if len(episode['episodename']) > (len(ep_name)+1):
-#                    if episode['episodename'][len(ep_name):len(ep_name)+2] != ' (':
-#                        continue # Skip episodes the are not part of a set of (1), (2) ... etc
-#                    if xmlFlag:
-#                        displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']])
-#                        sys.exit(0)
-#                    print season_and_episode_num.replace('\\n', '\n') % (int(episode['seasonnumber']), int(episode['episodenumber']))
+    all_matching_episodes = sorted(season_ep_num, key=lambda ep: _episode_sort(ep), reverse=True)
+    all_matching_episodes_cnt = len(all_matching_episodes)
+    #
+    all_matching_episodes_array = []
+    for episode in all_matching_episodes:
+        all_matching_episodes_array.append([series_name,
+                                            episode['seasonnumber'],
+                                            episode['episodenumber']])
+    if all_matching_episodes_array:
+        if xmlFlag:
+            displaySeriesXML(t, all_matching_episodes_array)
+        else:
+            for episode in all_matching_episodes:
+                print season_and_episode_num.replace('\\n', '\n') % (
+                                        int(episode['seasonnumber']),
+                                        int(episode['episodenumber']))
+    #
+    sys.exit(0)
 # end Getseries_episode_numbers

 # Set up a custom interface to get all series matching a partial series name
@@ -1072,7 +1125,7 @@ def displaySearchXML(tvdb_api):
     sys.exit(0)
 # end displaySearchXML()

-def displaySeriesXML(tvdb_api, series_season_ep):
+def displaySeriesXML(tvdb_api, series_season_ep_array):
     '''Using a XSLT style sheet translate TVDB Series data results into the
     MythTV Universal Query format
     return nothing
@@ -1080,12 +1133,14 @@ def displaySeriesXML(tvdb_api, series_season_ep):
     global xslt, tvdbXpath
     allDataElement = etree.XML(u'<allData></allData>')
     requestDetails = etree.XML(u'<requestDetails></requestDetails>')
-    requestDetails.attrib['lang'] = xslt.language
-    requestDetails.attrib['series'] = series_season_ep[0]
-    requestDetails.attrib['season'] = series_season_ep[1]
-    requestDetails.attrib['episode'] = series_season_ep[2]
-    allDataElement.append(requestDetails)
-
+    for series_season_ep in series_season_ep_array:
+        requestDetails_copy = deepcopy(requestDetails)
+        requestDetails_copy.attrib['lang'] = xslt.language
+        requestDetails_copy.attrib['series'] = series_season_ep[0]
+        requestDetails_copy.attrib['season'] = series_season_ep[1]
+        requestDetails_copy.attrib['episode'] = series_season_ep[2]
+        allDataElement.append(requestDetails_copy)
+    #
     # Combine the various XML inputs into a single XML element and send to the XSLT stylesheet
     if tvdb_api.epInfoTree != None:
         allDataElement.append(tvdb_api.epInfoTree)
@@ -1099,7 +1154,7 @@ def displaySeriesXML(tvdb_api, series_season_ep):
         allDataElement.append(tvdb_api.imagesInfoTree)
     else:
         allDataElement.append(etree.XML(u'<Banners></Banners>'))
-
+    #
     tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbVideo.xsl')))
     items = tvdbQueryXslt(allDataElement)
     if items.getroot() != None:
@@ -1228,9 +1283,9 @@ def main():
         if len(series_season_ep) < 2:
             parser.error("! An Episode name must be included")
             sys.exit(1)
-        if len(series_season_ep) == 3:
+        elif len(series_season_ep) == 3 and not opts.numbers:
             season_and_episode_num = series_season_ep[2] # Override default output format
-
+    #
     if opts.screenshot:
         if len(series_season_ep) > 1:
             if not _can_int(series_season_ep[1]):
@@ -1430,12 +1485,12 @@ def main():
                 print u"Season and Episode numbers required."
             else:
                 if opts.xml:
-                    displaySeriesXML(t, series_season_ep)
+                    displaySeriesXML(t, [series_season_ep])
                     sys.exit(0)
                 Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
         else:
             if opts.xml and len(series_season_ep) == 3:
-                displaySeriesXML(t, series_season_ep)
+                displaySeriesXML(t, [series_season_ep])
                 sys.exit(0)
             Getseries_episode_data(t, opts, series_season_ep, language=opts.language)

--
1.7.9.5
