MythTV  master
ttvdb.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3 # ----------------------
4 # Name: ttvdb.py
5 # Python Script
6 # Author: R.D. Vaughan
7 # Purpose:
8 # This python script is intended to perform TV series data lookups
9 # based on information found on the http://thetvdb.com/ website. It
10 # follows the MythTV standards set for the movie data
11 # lookups. e.g. the perl script "tmdb.pl" used to access themoviedb.com
12 # This script uses the python module tvdb_api.py (v1.0 or higher) found at
13 # http://pypi.python.org/pypi?%3Aaction=search&term=tvnamer&submit=search
14 # thanks to the authors of this excellant module.
15 # The tvdb_api.py module uses the full access XML api published by
16 # thetvdb.com see:
17 # http://thetvdb.com/wiki/index.php?title=Programmers_API
18 # Users of this script are encouraged to populate thetvdb.com with TV show
19 # information, posters, fan art and banners. The richer the source the more
20 # valuable the script.
21 # This python script was modified based on the "tvnamer.py" created by
22 # "dbr/Ben" who is also
23 # the author of the "tvdb_api.py" module. "tvnamer.py" is used to rename avi
24 # files with series/episode information found at thetvdb.com
25 # Command example:
26 # See help (-u and -h) options
27 #
28 # Design:
29 # 1) Verify the command line options (display help or version and exit)
30 # 2) Verify that thetvdb.com has the series or series_season_ep being
31 # requested exit if does not exit
32 # 3) Find the requested information and send to stdout if any found
33 #
34 #
35 # License:Creative Commons GNU GPL v2
36 # (http://creativecommons.org/licenses/GPL/2.0/)
37 # -------------------------------------
38 """
39 Doctests
40 
41 >>> sys.argv = shlex.split('./ttvdb.py -B Sanctuary')
42 >>> main()
43 Banner:http://thetvdb.com/banners/graphical/80159-g4.jpg,http://thetvdb.com/banners/graphical/80159-g5.jpg,http://thetvdb.com/banners/graphical/80159-g3.jpg,http://thetvdb.com/banners/graphical/80159-g6.jpg,http://thetvdb.com/banners/graphical/80159-g2.jpg,http://thetvdb.com/banners/graphical/80159-g.jpg,http://thetvdb.com/banners/graphical/80159-g8.jpg
44 0
45 >>> sys.argv = shlex.split('./ttvdb.py -S SG-1 1 10')
46 >>> main()
47 <?xml version='1.0' encoding='UTF-8'?>
48 <metadata>
49  <item>
50  <title>Stargate SG-1</title>
51  <subtitle>Thor's Hammer</subtitle>
52  <description>Teal'c and O'Neill are transported to an underground cage designed by the Asgard to protect an alien world from the Goa'uld.</description>
53  <season>1</season>
54  <episode>10</episode>
55  <certifications>
56  <certification locale="us" name="TV-PG"/>
57  </certifications>
58  <categories>
59  <category type="genre" name="Action"/>
60  <category type="genre" name="Adventure"/>
61  <category type="genre" name="Fantasy"/>
62  <category type="genre" name="Science-Fiction"/>
63  </categories>
64  <studios>
65  <studio name="Syfy"/>
66  </studios>
67  <runtime>45</runtime>
68  <inetref>72449</inetref>
69  <collectionref>72449</collectionref>
70  <imdb>0118480</imdb>
71  <tmsref>EP00225421</tmsref>
72  <language>en</language>
73  <year>1997</year>
74  <releasedate>1997-09-26</releasedate>
75  <people>
76  <person job="Actor" name="Richard Dean Anderson" character="Jack O'Neill" url="http://thetvdb.com/banners/actors/17720.jpg" thumb="http://thetvdb.com/banners/actors/17720.jpg"/>
77 ...
78  <person job="Guest Star" name="James Earl Jones"/>
79  <person job="Guest Star" name="Galyn Gorg"/>
80  <person job="Guest Star" name="Tamsin Kelsey"/>
81  <person job="Guest Star" name="Vincent Hammond"/>
82  <person job="Guest Star" name="Mark Gibbon"/>
83  <person job="Director" name="Brad Turner"/>
84  <person job="Author" name="Katharyn Michaelian Powers"/>
85  </people>
86  <images>
87  <image type="screenshot" url="http://thetvdb.com/banners/episodes/72449/85759.jpg" thumb="http://thetvdb.com/banners/_cache/episodes/72449/85759.jpg"/>
88  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/72449-1-9.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/72449-1-9.jpg"/>
89  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/72449-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/72449-1.jpg"/>
90  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/72449-1-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/72449-1-2.jpg"/>
91  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/72449-1-8.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/72449-1-8.jpg"/>
92  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/185-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/185-1.jpg"/>
93  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72449-55.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72449-55.jpg" width="1920" height="1080"/>
94  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72449-34.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72449-34.jpg" width="1280" height="720"/>
95 ...
96  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72449-75.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72449-75.jpg" width="1280" height="720"/>
97  </images>
98  </item>
99 </metadata>
100 0
101 >>> sys.argv = shlex.split('ttvdb -PFB "Stargate SG-1"')
102 >>> main()
103 Coverart:http://thetvdb.com/banners/posters/72449....jpg
104 Fanart:http://thetvdb.com/banners/fanart/original/72449-....jpg
105 Banner:http://thetvdb.com/banners/graphical/72449-....jpg
106 0
107 
108 # Coverart:http://www.thetvdb.com/banners/posters/72449-1.jpg
109 # Fanart:http://www.thetvdb.com/banners/fanart/original/72449-1.jpg
110 # Banner:http://www.thetvdb.com/banners/graphical/185-g3.jpg
111 >>> sys.argv = shlex.split('ttvdb -B "Night Gallery"')
112 >>> main()
113 Banner:http://thetvdb.com/banners/graphical/70382-g4.jpg,http://thetvdb.com/banners/graphical/1013-g.jpg,http://thetvdb.com/banners/blank/70382.jpg,http://thetvdb.com/banners/graphical/70382-g.jpg,http://thetvdb.com/banners/graphical/70382-g2.jpg,http://thetvdb.com/banners/graphical/70382-g3.jpg
114 0
115 
116 # http://www.thetvdb.com/banners/blank/70382.jpg
117 >>> sys.argv = shlex.split('ttvdb -Bl en Lost')
118 >>> main()
119 Banner:http://thetvdb.com/banners/graphical/73739-g4.jpg,http://thetvdb.com/banners/graphical/73739-g13.jpg,http://thetvdb.com/banners/graphical/73739-g18.jpg,http://thetvdb.com/banners/graphical/73739-g6.jpg,http://thetvdb.com/banners/graphical/73739-g12.jpg,http://thetvdb.com/banners/graphical/73739-g3.jpg,http://thetvdb.com/banners/graphical/24313-g2.jpg,http://thetvdb.com/banners/graphical/73739-g8.jpg,http://thetvdb.com/banners/graphical/73739-g.jpg,http://thetvdb.com/banners/graphical/73739-g5.jpg,http://thetvdb.com/banners/graphical/73739-g7.jpg,http://thetvdb.com/banners/graphical/73739-g10.jpg,http://thetvdb.com/banners/graphical/73739-g11.jpg,http://thetvdb.com/banners/graphical/24313-g.jpg,http://thetvdb.com/banners/graphical/73739-g2.jpg,http://thetvdb.com/banners/blank/73739.jpg
120 0
121 
122 # 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
123 > ttvdb -N --configure="/home/user/.tvdb/tvdb.conf" "Eleventh Hour" "H2O"
124 >>> sys.argv = shlex.split('ttvdb -N --configure=./tvdb_test.conf "Eleventh Hour" H2O')
125 >>> main()
126 <?xml version='1.0' encoding='UTF-8'?>
127 <metadata>
128  <item>
129  <title>Eleventh Hour (US)</title>
130  <subtitle>H2O</subtitle>
131  <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>
132  <season>1</season>
133  <episode>10</episode>
134  <certifications>
135  <certification locale="us" name="TV-14"/>
136  </certifications>
137  <categories>
138  <category type="genre" name="Drama"/>
139  <category type="genre" name="Science-Fiction"/>
140  <category type="genre" name="Thriller"/>
141  </categories>
142  <studios>
143  <studio name="CBS"/>
144  </studios>
145  <runtime>45</runtime>
146  <inetref>83066</inetref>
147  <collectionref>83066</collectionref>
148  <imdb>1118697</imdb>
149  <language>en</language>
150  <year>2009</year>
151  <releasedate>2009-01-15</releasedate>
152  <people>
153  <person job="Actor" name="Rufus Sewell" character="Jacob Hood" url="http://thetvdb.com/banners/actors/78899.jpg" thumb="http://thetvdb.com/banners/actors/78899.jpg"/>
154  <person job="Actor" name="Marley Shelton" character="Rachel Young" url="http://thetvdb.com/banners/actors/78898.jpg" thumb="http://thetvdb.com/banners/actors/78898.jpg"/>
155  <person job="Actor" name="Omar Benson Miller" character="Felix Lee" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
156  <person job="Actor" name="Chris Krauser" character="EMT" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
157  <person job="Actor" name="Erica Frene" character="Receptionist" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
158  <person job="Actor" name="Lei'lah Star" character="Sick Kid" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
159  <person job="Actor" name="Mark C. Baldwin" character="Infomercial Announcer" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
160  <person job="Director" name="McDonough"/>
161  <person job="Author" name="Kim Newton"/>
162  </people>
163  <images>
164  <image type="screenshot" url="http://thetvdb.com/banners/episodes/83066/416216.jpg" thumb="http://thetvdb.com/banners/_cache/episodes/83066/416216.jpg"/>
165  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/83066-1-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/83066-1-2.jpg"/>
166  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/83066-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/83066-1.jpg"/>
167  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/83066-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/83066-1.jpg" width="1280" height="720"/>
168  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/83066-3.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/83066-3.jpg" width="1280" height="720"/>
169  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/83066-5.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/83066-5.jpg" width="1920" height="1080"/>
170  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/83066-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/83066-2.jpg" width="1280" height="720"/>
171  <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"/>
172  </images>
173  </item>
174 </metadata>
175 0
176 
177 # <language>en</language>
178 # <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"/>
179 # <image type="banner" url="http://www.thetvdb.com/banners/graphical/83066-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/83066-g.jpg"/>
180 (Return the season numbers for a series)
181 > ttvdb --configure="./tvdb_test.conf" -n "SG-1"
182 >>> sys.argv = shlex.split('ttvdb --configure=./tvdb_test.conf -n SG-1')
183 >>> main()
184 0,1,2,3,4,5,6,7,8,9,10
185 0
186 
187 (Return the meta data for a specific series/season/episode)
188 > ttvdb.py -D 80159 2 2
189 >>> sys.argv = shlex.split('ttvdb -D 80159 2 2')
190 >>> main()
191 <?xml version='1.0' encoding='UTF-8'?>
192 <metadata>
193  <item>
194  <title>Sanctuary</title>
195  <subtitle>End of Nights (2)</subtitle>
196  <description>Furious at being duped into a trap, Magnus takes on Kate, demanding information and complete access to her Cabal contacts. The Cabal’s true agenda is revealed and Magnus realizes that they are not only holding Ashley as ransom to obtain complete control of the Sanctuary Network, but turning her into the ultimate weapon. Now transformed into a Super Abnormal with devastating powers, Ashley and her newly cloned fighters begin their onslaught, destroying Sanctuaries in cities around the world. Tesla and Henry attempt to create a weapon that can stop the attacks…without killing Ashley. As the team prepares to defend the Sanctuary with Tesla’s new weapon, Magnus must come to the realization that they may not be able to stop the Cabal’s attacks without harming Ashley. She realizes she might have to choose between saving her only daughter, or losing the Sanctuary and all the lives and secrets within it.</description>
197  <season>2</season>
198  <episode>2</episode>
199  <certifications>
200  <certification locale="us" name="TV-PG"/>
201  </certifications>
202  <categories>
203  <category type="genre" name="Action"/>
204  <category type="genre" name="Adventure"/>
205  <category type="genre" name="Crime"/>
206  <category type="genre" name="Mystery"/>
207  <category type="genre" name="Science-Fiction"/>
208  </categories>
209  <studios>
210  <studio name="Space"/>
211  </studios>
212  <runtime>60</runtime>
213  <inetref>80159</inetref>
214  <collectionref>80159</collectionref>
215  <imdb>0965394</imdb>
216  <tmsref>EP01085421</tmsref>
217  <language>en</language>
218  <year>2009</year>
219  <releasedate>2009-10-16</releasedate>
220  <people>
221  <person job="Actor" name="Amanda Tapping" character="Dr. Helen Magnus" url="http://thetvdb.com/banners/actors/73053.jpg" thumb="http://thetvdb.com/banners/actors/73053.jpg"/>
222  <person job="Actor" name="Robin Dunne" character="Will Zimmerman" url="http://thetvdb.com/banners/actors/73054.jpg" thumb="http://thetvdb.com/banners/actors/73054.jpg"/>
223  <person job="Actor" name="Emilie Ullerup" character="Ashley Magnus" url="http://thetvdb.com/banners/actors/73055.jpg" thumb="http://thetvdb.com/banners/actors/73055.jpg"/>
224  <person job="Actor" name="Christopher Heyerdahl" character="John Druitt" url="http://thetvdb.com/banners/actors/73056.jpg" thumb="http://thetvdb.com/banners/actors/73056.jpg"/>
225  <person job="Actor" name="Christopher Heyerdahl" character="Bigfoot" url="http://thetvdb.com/banners/actors/309797.jpg" thumb="http://thetvdb.com/banners/actors/309797.jpg"/>
226  <person job="Actor" name="Ryan Robbins" character="Henry Foss" url="http://thetvdb.com/banners/actors/80072.jpg" thumb="http://thetvdb.com/banners/actors/80072.jpg"/>
227  <person job="Actor" name="Agam Darshi" character="Kate Freelander" url="http://thetvdb.com/banners/actors/118211.jpg" thumb="http://thetvdb.com/banners/actors/118211.jpg"/>
228  <person job="Actor" name="Vincent Gale" character="Nigel Griffin" url="http://thetvdb.com/banners/actors/372548.jpg" thumb="http://thetvdb.com/banners/actors/372548.jpg"/>
229  <person job="Actor" name="Peter Wingfield" character="James Watson" url="http://thetvdb.com/banners/actors/372549.jpg" thumb="http://thetvdb.com/banners/actors/372549.jpg"/>
230  <person job="Actor" name="Jonathon Young" character="Nikola Tesla" url="http://thetvdb.com/banners/actors/372550.jpg" thumb="http://thetvdb.com/banners/actors/372550.jpg"/>
231  <person job="Actor" name="Ian Tracey" character="Adam Worth" url="http://thetvdb.com/banners/actors/372551.jpg" thumb="http://thetvdb.com/banners/actors/372551.jpg"/>
232  <person job="Actor" name="Jim Byrnes" character="Gregory Magnus" url="http://thetvdb.com/banners/actors/372552.jpg" thumb="http://thetvdb.com/banners/actors/372552.jpg"/>
233  <person job="Actor" name="Polly Walker" character="Ranna Seneschal" url="http://thetvdb.com/banners/actors/372553.jpg" thumb="http://thetvdb.com/banners/actors/372553.jpg"/>
234  <person job="Actor" name="Robert Lawrenson" character="Declan Macrae" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
235  <person job="Actor" name="Pascale Hutton" character="Abby Corrigan" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
236  <person job="Actor" name="Lynda Boyd" character="Dana Whitcomb" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
237  <person job="Actor" name="Shekhar Paleja" character="Ravi" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
238  <person job="Actor" name="Chuck Campbell" character="Two Faced Guy" url="http://thetvdb.com/banners/" thumb="http://thetvdb.com/banners/"/>
239  <person job="Guest Star" name="Jonathon Young"/>
240  <person job="Guest Star" name="Christine Chatelain"/>
241  <person job="Guest Star" name="Robert Lawrenson"/>
242  <person job="Guest Star" name="Maiko Yamamoto"/>
243  <person job="Guest Star" name="Stanley Tsang"/>
244  <person job="Guest Star" name="Darren A. Hebert"/>
245  <person job="Guest Star" name="Lynda Boyd"/>
246  <person job="Director" name="Martin Wood"/>
247  <person job="Author" name="Damian Kindler"/>
248  </people>
249  <images>
250  <image type="screenshot" url="http://thetvdb.com/banners/episodes/80159/998441.jpg" thumb="http://thetvdb.com/banners/_cache/episodes/80159/998441.jpg"/>
251  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/80159-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/80159-2.jpg"/>
252  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/80159-2-3.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/80159-2-3.jpg"/>
253  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/80159-2-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/80159-2-2.jpg"/>
254  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-10.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-10.jpg" width="1280" height="720"/>
255  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-6.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-6.jpg" width="1920" height="1080"/>
256  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-3.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-3.jpg" width="1920" height="1080"/>
257  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-9.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-9.jpg" width="1920" height="1080"/>
258  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-7.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-7.jpg" width="1920" height="1080"/>
259  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-8.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-8.jpg" width="1920" height="1080"/>
260  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-2.jpg" width="1920" height="1080"/>
261  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-4.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-4.jpg" width="1280" height="720"/>
262  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-5.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-5.jpg" width="1920" height="1080"/>
263  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-21.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-21.jpg" width="1920" height="1080"/>
264  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-16.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-16.jpg" width="1280" height="720"/>
265  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-1.jpg" width="1280" height="720"/>
266  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-15.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-15.jpg" width="1920" height="1080"/>
267  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-17.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-17.jpg" width="1920" height="1080"/>
268  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-18.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-18.jpg" width="1920" height="1080"/>
269  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-19.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-19.jpg" width="1920" height="1080"/>
270  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-20.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-20.jpg" width="1920" height="1080"/>
271  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-22.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-22.jpg" width="1920" height="1080"/>
272  </images>
273  </item>
274 </metadata>
275 0
276 
277 (Return a list of "thetv.com series id and series name" that contain specific search word(s) )
278 (!! Be careful with this option as poorly defined search words can result in large lists being returned !!)
279 > ttvdb.py -M "night a"
280 >>> sys.argv = shlex.split('ttvdb -M "night a"')
281 >>> main()
282 <?xml version='1.0' encoding='UTF-8'?>
283 <metadata>
284  <item>
285  <language>en</language>
286  <title>A Night of Numbers</title>
287  <inetref>249306</inetref>
288  <collectionref>249306</collectionref>
289  <description>BBC FOUR celebrates mathematics and the beauty of numbers with a series of programmes about this most precise and exacting of all intellectual disciplines. Throughout the night, the channel will show films that offer insights into the minds of great mathematicians, and reveal the stories behind some of the great mathematical breakthroughs.</description>
290  <releasedate>2005-12-06</releasedate>
291  <images>
292  <image type="banner" url="http://www.thetvdb.com/banners/graphical/249306-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/249306-g.jpg"/>
293  </images>
294  </item>
295  <item>
296  <language>en</language>
297  <title>A night at The Classic</title>
298  <inetref>224951</inetref>
299  <collectionref>224951</collectionref>
300  <description>Each episode of A Night at The Classic follows MC Brendhan Lovegrove and guest comedians as they perform for a different crowd on a different "night" at The Classic. Along with stand-up comedy recorded in front of a live audience, viewers are given a glimpse of what the comedians are like backstage, providing a rare insight into the rivalries and rituals of stand-up comedians.</description>
301  <releasedate>2010-11-03</releasedate>
302  <images>
303  <image type="banner" url="http://www.thetvdb.com/banners/graphical/224951-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/224951-g.jpg"/>
304  </images>
305  </item>
306  <item>
307  <language>en</language>
308  <title>A Night at the Rijksmuseum</title>
309  <inetref>268908</inetref>
310  <collectionref>268908</collectionref>
311  <releasedate>2013-04-18</releasedate>
312  </item>
313  <item>
314  <language>en</language>
315  <title>A Night of Heroes: The Sun Military Awards</title>
316  <inetref>270984</inetref>
317  <collectionref>270984</collectionref>
318  <description>Annual celebration, A Night of Heroes: Also known as The Millies, the awards recognize the excellence and sacrifice made by Britain's Armed Forces</description>
319  <images>
320  <image type="banner" url="http://www.thetvdb.com/banners/graphical/270984-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/270984-g.jpg"/>
321  </images>
322  </item>
323  <item>
324  <language>en</language>
325  <title>A Night of Exploration</title>
326  <inetref>271528</inetref>
327  <collectionref>271528</collectionref>
328  <description>For well over a century the National Geographic Society has been synonymous with pioneering expeditions, groundbreaking discoveries and breathtaking imagery of world cultures and exotic locations. In celebration of the iconic yellow border’s 125th anniversary, National Geographic Channel pays tribute to the hotshots, the mavericks and the best in their field who have devoted their lives to exploring the world around us and the groundbreaking discoveries that are making a difference.</description>
329  <images>
330  <image type="banner" url="http://www.thetvdb.com/banners/graphical/271528-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/271528-g.jpg"/>
331  </images>
332  </item>
333  <item>
334  <language>en</language>
335  <title>A Night at the Office</title>
336  <inetref>118511</inetref>
337  <collectionref>118511</collectionref>
338  <description>On August 11th 2009, it was announced that the cast of The Office would be reuniting for a special, called "A Night at The Office", available at BBC2 and online, it was the entire first series of the seminal BBC comedy 'The Office' with new comments from the writers and celebrity fans shown between each episode.</description>
339  <releasedate>2009-08-17</releasedate>
340  <images>
341  <image type="banner" url="http://www.thetvdb.com/banners/graphical/118511-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/118511-g.jpg"/>
342  </images>
343  </item>
344  <item>
345  <language>en</language>
346  <title>A Night With The Stars</title>
347  <inetref>256045</inetref>
348  <collectionref>256045</collectionref>
349  <description>For one night only, Professor Brian Cox goes unplugged in a specially recorded programme from the lecture theatre of the Royal Institution of Great Britain. In his own inimitable style, Brian takes an audience of famous faces, scientists and members of the public on a journey through some of the most challenging concepts in physics. With the help of Jonathan Ross, Simon Pegg, Sarah Millican and James May, Brian shows how diamonds - the hardest material in nature - are made up of nothingness; how things can be in an infinite number of places at once; why everything we see or touch in the universe exists; and how a diamond in the heart of London is in communication with the largest diamond in the cosmos.</description>
350  <releasedate>2011-12-18</releasedate>
351  <images>
352  <image type="banner" url="http://www.thetvdb.com/banners/graphical/256045-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/256045-g.jpg"/>
353  </images>
354  </item>
355  <item>
356  <language>en</language>
357  <title>A Night at the Festival Club</title>
358  <inetref>268969</inetref>
359  <collectionref>268969</collectionref>
360  <description>A Night at the Festival Club is an Australian stand-up comedy television event created and executive produced by the Comedy Channel programming director Darren Chau, produced by Ted Robinson and GNW TV Productions for the Comedy Channel as part of the Melbourne International Comedy Festival. The series centres around bottling the unique comedic live performances and moments that occur late night in the Festival Club during the Melbourne International Comedy Festival.</description>
361  <releasedate>2008-05-02</releasedate>
362  </item>
363  <item>
364  <language>en</language>
365  <title>A Clear Midsummer Night</title>
366  <inetref>286538</inetref>
367  <collectionref>286538</collectionref>
368  <description>The daughter of a real estate mogul Xia Wan Qing, has seemingly no way of retreating after a friend's betrayal and her boyfriend backing out of their wedding. Fortunately, she's saved by business genius Qiao Jin Fan. Jin Fan is a "playboy" and the future successor for Qiao corporation. He extends an offering hand and together they embark on a path of revenge. Each for reasons of their own, begin a love with "uncertain motives." After enduring circumstances because of their families' competing interests and a number of conspiracies the two find true love.</description>
369  </item>
370  <item>
371  <language>en</language>
372  <title>A Christmas Night with the Stars</title>
373  <inetref>248911</inetref>
374  <collectionref>248911</collectionref>
375  <description>Christmas Night with the Stars was a television show broadcast each Christmas night by the BBC from 1958 to 1972 (with the exception of 1961, 1965 and 1966) and also revived in 1994. The show was hosted each year by a leading star of BBC TV and featured specially made short seasonal editions (typically about 10 minutes long) of the previous year's most popular BBC sitcoms and light entertainment programs. The show was voted 24th in the Channel 4 100 Greatest Christmas Moments. Most of the variety segments no longer exist.</description>
376  <releasedate>1958-12-25</releasedate>
377  <images>
378  <image type="banner" url="http://www.thetvdb.com/banners/graphical/248911-g2.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/248911-g2.jpg"/>
379  </images>
380  </item>
381  <item>
382  <language>en</language>
383  <title>A Night With My Ex</title>
384  <inetref>331751</inetref>
385  <collectionref>331751</collectionref>
386  <description>Do you have unfinished business with a partner from a previous relationship? All of the onetime couples featured on ``A Night With My Ex'' do, and the show is letting them tie up loose ends from the past. In each episode, a pair of exes spend a night together in a one-bedroom apartment complete with a multiple-camera setup. They are left to their own devices -- with no producers and no interruptions -- to try to hash things out. The participants get things off their chests, ask hard-hitting questions and face accusations of infidelity with the ultimate goal of achieving closure on the relationship. Sometimes that closure means a clean break, and other times it leads to renewing the spark and rekindling the romance. Regardless of the outcome, anything goes on the road to reaching that point as the couples confront their pasts -- and their futures.</description>
387  <releasedate>2017-07-18</releasedate>
388  </item>
389  <item>
390  <language>en</language>
391  <title>On a Lustful Night Mingling with a Priest...</title>
392  <inetref>325375</inetref>
393  <collectionref>325375</collectionref>
394  <description>The reunion of Kujo with his old female classmate. He has inherited his parents' temple and became a priest. However, after the two became drunk, he does something unexpected of him to her!</description>
395  <releasedate>2017-04-03</releasedate>
396  </item>
397  <item>
398  <language>en</language>
399  <title>Love on a Saturday Night</title>
400  <inetref>74382</inetref>
401  <collectionref>74382</collectionref>
402  <releasedate>2004-02-01</releasedate>
403  </item>
404  <item>
405  <language>en</language>
406  <title>Britain's Tudor Treasure A Night At Hampton Court</title>
407  <inetref>332440</inetref>
408  <collectionref>332440</collectionref>
409  <description>Lucy Worsley and David Starkey celebrate the 500th anniversary of Britain's finest surviving Tudor building, Hampton Court. As Henry VIII's pleasure palace, Hampton Court was a showcase for royal magnificence and ceremony - and the most important event of all was the christening of Henry's long-awaited son, Prince Edward, on October 15th, 1537. Lucy and David explore how Tudor art, architecture and ritual came together for this momentous occasion. Drawing on historical records and with the help of a team of experts, they recreate key elements of the christening ceremony - including a magnificent set-piece procession through Hampton Court involving nearly 100 people in full Tudor costume.</description>
410  </item>
411 </metadata>
412 0
413 
414 (Return TV series collection data of "thetv.com series id" for a specified language)
415 >>> sys.argv = shlex.split('ttvdb -l de -C 80159')
416 >>> main()
417 <?xml version='1.0' encoding='UTF-8'?>
418 <metadata>
419  <item>
420  <language>de</language>
421  <title>Sanctuary</title>
422  <network>Space</network>
423  <airday>Friday</airday>
424  <airtime>10:00 PM</airtime>
425  <description>Dr. Helen Magnus ist eine so brillante wie geheimnisvolle Wissenschaftlerin die sich mit den Kreaturen der Nacht beschäftigt. In ihrem Unterschlupf - genannt "Sanctuary" - hat sie ein Team versammelt, das seltsame und furchteinflößende Ungeheuer untersucht, die mit den Menschen auf der Erde leben. Konfrontiert mit ihren düstersten Ängsten und ihren schlimmsten Alpträumen versucht das Sanctuary-Team, die Welt vor den Monstern - und die Monster vor der Welt zu schützen.</description>
426  <certifications>
427  <certification locale="us" name="TV-PG"/>
428  </certifications>
429  <categories>
430  <category type="genre" name="Action"/>
431  <category type="genre" name="Adventure"/>
432  <category type="genre" name="Crime"/>
433  <category type="genre" name="Mystery"/>
434  <category type="genre" name="Science-Fiction"/>
435  </categories>
436  <studios>
437  <studio name="Space"/>
438  </studios>
439  <runtime>60</runtime>
440  <inetref>80159</inetref>
441  <imdb>0965394</imdb>
442  <userrating>8.1</userrating>
443  <ratingcount>168</ratingcount>
444  <year>2007</year>
445  <releasedate>2007-03-14</releasedate>
446  <lastupdated>...</lastupdated>
447  <status>Ended</status>
448  <images>
449  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-11.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-11.jpg"/>
450  <image type="banner" url="http://thetvdb.com/banners/graphical/80159-g4.jpg" thumb="http://thetvdb.com/banners/_cache/graphical/80159-g4.jpg"/>
451  </images>
452  </item>
453 </metadata>
454 0
455 
456 # test match is loose due ordering differences between py2 and 3
457 # i.e. dict key ordering
458 # key ordering is not sorted so that Title is first for existing client
459 # compatability
460 >>> sys.argv = shlex.split('ttvdb -l en -a US -D 281053')
461 >>> main()
462 Title:Fixer Upper
463 Season:0
464 Episode:1
465 Subtitle:The Waco Way of Life
466 Year:2014
467 ReleaseDate:2014-07-16
468 Director:
469 Plot:Chip and Joanna Gaines tell why they love raising a family in Waco, Texas.
470 UserRating:
471 Writers:
472 Screenshot:
473 Language:en
474 Airedseasonid:583817
475 Dvddiscid:
476 Id:5463514
477 Imdbid:
478 Lastupdated:...
479 Lastupdatedby:447800
480 Productioncode:
481 Seriesid:281053
482 Showurl:
483 Siterating:0
484 Siteratingcount:0
485 Thumbadded:
486 Thumbauthor:...
487 Cast:Chip Gaines, Joanna Gaines
488 Runtime:45
489 Title:Fixer Upper
490 ...
491 Coverart:http://thetvdb.com/banners/posters/281053-....jpg
492 Fanart:http://thetvdb.com/banners/fanart/original/281053-....jpg
493 Banner:http://thetvdb.com/banners/graphical/281053-....jpg
494 0
495 
496 >>> sys.argv = shlex.split('ttvdb.py -l en -a US -N 72108 Pyramid')
497 >>> main()
498 <?xml version='1.0' encoding='UTF-8'?>
499 <metadata>
500  <item>
501  <title>NCIS</title>
502  <subtitle>Pyramid</subtitle>
503  <description>The lives of NCIS members are in jeopardy when they come face-to-face with the infamous Port-to-Port killer, on the eighth season finale of NCIS.</description>
504  <season>8</season>
505  <episode>24</episode>
506  <certifications>
507  <certification locale="us" name="TV-14"/>
508  </certifications>
509  <categories>
510  <category type="genre" name="Action"/>
511  <category type="genre" name="Adventure"/>
512  <category type="genre" name="Crime"/>
513  <category type="genre" name="Drama"/>
514  </categories>
515  <studios>
516  <studio name="CBS"/>
517  </studios>
518  <runtime>45</runtime>
519  <inetref>72108</inetref>
520  <collectionref>72108</collectionref>
521  <tmsref>EP00681911</tmsref>
522  <imdb/>
523  <language>en</language>
524  <year>2011</year>
525  <releasedate>2011-05-17</releasedate>
526  <people>
527  <person job="Actor" name="Mark Harmon" character="Leroy Jethro Gibbs" url="http://thetvdb.com/banners/actors/70164.jpg" thumb="http://thetvdb.com/banners/actors/70164.jpg"/>
528  <person job="Actor" name="Sean Murray" character="Timothy McGee" url="http://thetvdb.com/banners/actors/70163.jpg" thumb="http://thetvdb.com/banners/actors/70163.jpg"/>
529  <person job="Actor" name="David McCallum" character="Donald &quot;Ducky&quot; Mallard" url="http://thetvdb.com/banners/actors/70159.jpg" thumb="http://thetvdb.com/banners/actors/70159.jpg"/>
530  <person job="Actor" name="Pauley Perrette" character="Abby Sciuto" url="http://thetvdb.com/banners/actors/70161.jpg" thumb="http://thetvdb.com/banners/actors/70161.jpg"/>
531  <person job="Actor" name="Rocky Carroll" character="Leon Vance" url="http://thetvdb.com/banners/actors/127861.jpg" thumb="http://thetvdb.com/banners/actors/127861.jpg"/>
532  <person job="Actor" name="Brian Dietzen" character="Jimmy Palmer" url="http://thetvdb.com/banners/actors/219761.jpg" thumb="http://thetvdb.com/banners/actors/219761.jpg"/>
533  <person job="Actor" name="Wilmer Valderrama" character="Nicholas &quot;Nick&quot; Torres" url="http://thetvdb.com/banners/actors/394895.jpg" thumb="http://thetvdb.com/banners/actors/394895.jpg"/>
534  <person job="Actor" name="Michael Weatherly" character="Anthony &quot;Tony&quot; DiNozzo" url="http://thetvdb.com/banners/actors/70160.jpg" thumb="http://thetvdb.com/banners/actors/70160.jpg"/>
535  <person job="Actor" name="Sasha Alexander" character="Caitlin &quot;Kate&quot; Todd" url="http://thetvdb.com/banners/actors/70162.jpg" thumb="http://thetvdb.com/banners/actors/70162.jpg"/>
536  <person job="Actor" name="Cote de Pablo" character="Ziva David" url="http://thetvdb.com/banners/actors/70165.jpg" thumb="http://thetvdb.com/banners/actors/70165.jpg"/>
537  <person job="Actor" name="Lauren Holly" character="Jennifer &quot;Jenny&quot; Shepard" url="http://thetvdb.com/banners/actors/77850.jpg" thumb="http://thetvdb.com/banners/actors/77850.jpg"/>
538  <person job="Actor" name="Jennifer Esposito" character="Alex Quinn" url="http://thetvdb.com/banners/actors/402126.jpg" thumb="http://thetvdb.com/banners/actors/402126.jpg"/>
539  <person job="Actor" name="Emily Wickersham" character="Ellie Bishop" url="http://thetvdb.com/banners/actors/321201.jpg" thumb="http://thetvdb.com/banners/actors/321201.jpg"/>
540  <person job="Actor" name="Duane Henry" character="Clayton Reeves" url="http://thetvdb.com/banners/actors/418530.jpg" thumb="http://thetvdb.com/banners/actors/418530.jpg"/>
541  <person job="Actor" name="Maria Bello" character="Jacqueline “Jack” Sloane" url="http://thetvdb.com/banners/actors/461645.jpg" thumb="http://thetvdb.com/banners/actors/461645.jpg"/>
542  <person job="Guest Star" name="Kerr Smith"/>
543  <person job="Guest Star" name="Sarah Jane Morris"/>
544  <person job="Guest Star" name="Matt Craven"/>
545  <person job="Guest Star" name="David Dayan Fisher"/>
546  <person job="Guest Star" name="Muse Watson"/>
547  <person job="Guest Star" name="Alimi Ballard"/>
548  <person job="Guest Star" name="Matthew Willig"/>
549  <person job="Guest Star" name="Tehmina Sunny"/>
550  <person job="Guest Star" name="Enrique Murciano"/>
551  <person job="Guest Star" name="Jude Ciccolella"/>
552  <person job="Guest Star" name="Vera Miao"/>
553  <person job="Director" name="Dennis Smith"/>
554  <person job="Author" name="Gary Glasberg"/>
555  </people>
556  <images>
557  <image type="screenshot" url="http://thetvdb.com/banners/episodes/72108/4078484.jpg" thumb="http://thetvdb.com/banners/_cache/episodes/72108/4078484.jpg"/>
558  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/72108-8-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/72108-8-2.jpg"/>
559  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/72108-8.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/72108-8.jpg"/>
560  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/72108-8-7.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/72108-8-7.jpg"/>
561  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72108-20.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72108-20.jpg" width="1280" height="720"/>
562  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72108-28.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72108-28.jpg" width="1920" height="1080"/>
563  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72108-29.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72108-29.jpg" width="1920" height="1080"/>
564  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72108-31.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72108-31.jpg" width="1920" height="1080"/>
565 ...
566  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/72108-33.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/72108-33.jpg" width="1920" height="1080"/>
567  </images>
568  </item>
569 </metadata>
570 0
571 
572 >>> sys.argv = shlex.split('ttvdb.py -l en -a US -N 283661 "Egg Hunt"')
573 >>> main()
574 <?xml version='1.0' encoding='UTF-8'?>
575 <metadata>
576  <item>
577  <title>Jack Hanna's Wild Countdown</title>
578  <subtitle>Egg Hunt</subtitle>
579  <description>Jungle Jack takes off on a very special Egg Hunt, looking for creatures big and small that hatch from eggs! Crocodiles, Bald Eagles, Sea Turtles, Ostriches, Penguins, and more!</description>
580  <season>6</season>
581  <episode>17</episode>
582  <certifications>
583  <certification locale="us" name="TV-G"/>
584  </certifications>
585  <categories>
586  <category type="genre" name="Family"/>
587  <category type="genre" name="Special Interest"/>
588  </categories>
589  <studios>
590  <studio name="ABC (US)"/>
591  </studios>
592  <runtime>30</runtime>
593  <inetref>283661</inetref>
594  <collectionref>283661</collectionref>
595  <imdb>3062384</imdb>
596  <tmsref>EP01441760</tmsref>
597  <language>en</language>
598  <year>2017</year>
599  <releasedate>2017-04-15</releasedate>
600  <images>
601  <image type="screenshot" url="http://thetvdb.com/banners/episodes/283661/6050716.jpg" thumb="http://thetvdb.com/banners/_cache/episodes/283661/6050716.jpg"/>
602  <image type="coverart" url="http://www.thetvdb.com/banners/posters/283661-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/posters/283661-1.jpg" width="680" height="1000"/>
603  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/283661-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/283661-1.jpg" width="1920" height="1080"/>
604  </images>
605  </item>
606 </metadata>
607 0
608 
609 >>> sys.argv = shlex.split('ttvdb.py -l en -a US -N 76119 "Eclipse Over America"')
610 >>> main()
611 <?xml version='1.0' encoding='UTF-8'?>
612 <metadata>
613  <item>
614  <title>NOVA</title>
615  <subtitle>Eclipse Over America</subtitle>
616  <description>...</description>
617  <season>44</season>
618  <episode>11</episode>
619  <certifications>
620  <certification locale="us" name="TV-PG"/>
621  </certifications>
622  <categories>
623  <category type="genre" name="Documentary"/>
624  </categories>
625  <studios>
626  <studio name="PBS"/>
627  </studios>
628  <runtime>60</runtime>
629  <inetref>76119</inetref>
630  <collectionref>76119</collectionref>
631  <imdb>0206501</imdb>
632  <tmsref>EP00003163</tmsref>
633  <language>en</language>
634  <year>2017</year>
635  <releasedate>2017-08-21</releasedate>
636  <images>
637  <image type="screenshot" url="http://thetvdb.com/banners/episodes/76119/....jpg" thumb="http://thetvdb.com/banners/_cache/episodes/76119/....jpg"/>
638  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/76119-44.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/76119-44.jpg"/>
639  <image type="coverart" url="http://www.thetvdb.com/banners/seasons/76119-44-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/seasons/76119-44-2.jpg"/>
640  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/76119-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/76119-1.jpg" width="1920" height="1080"/>
641  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/76119-2.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/76119-2.jpg" width="1920" height="1080"/>
642  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/76119-3.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/76119-3.jpg" width="1280" height="720"/>
643  </images>
644  </item>
645 </metadata>
646 0
647 
648 >>> sys.argv = shlex.split('ttvdb.py -l en -a GB -C 330432')
649 >>> main()
650 <?xml version='1.0' encoding='UTF-8'?>
651 <metadata>
652  <item>
653  <language>en</language>
654  <title>Nine Minute Ninja</title>
655  <network>CBBC</network>
656  <description>Art Ninja spinoff. Ricky makes art in nine minutes.</description>
657  <categories>
658  <category type="genre" name="Children"/>
659  </categories>
660  <studios>
661  <studio name="CBBC"/>
662  </studios>
663  <runtime>10</runtime>
664  <inetref>330432</inetref>
665  <ratingcount>0</ratingcount>
666  <year>2015</year>
667  <releasedate>2015-06-24</releasedate>
668  <lastupdated>...</lastupdated>
669  <status>Continuing</status>
670  <images>
671  <image type="coverart" url="http://www.thetvdb.com/banners/posters/330432-1.jpg" thumb="http://www.thetvdb.com/banners/_cache/posters/330432-1.jpg"/>
672  </images>
673  </item>
674 </metadata>
675 0
676 
677 >>> sys.argv = shlex.split('ttvdb.py -l de -a DE -D 282022 1 1')
678 >>> main()
679 <?xml version='1.0' encoding='UTF-8'?>
680 <metadata>
681  <item>
682  <title>Wilder Westen</title>
683  <subtitle>Gebirge</subtitle>
684  <description>Der Wilde Westen. ...</description>
685  <season>1</season>
686  <episode>1</episode>
687  <categories>
688  <category type="genre" name="Documentary"/>
689  </categories>
690  <studios>
691  <studio name="BBC Four"/>
692  </studios>
693  <runtime>60</runtime>
694  <inetref>282022</inetref>
695  <collectionref>282022</collectionref>
696  <language>de</language>
697  <year>2014</year>
698  <releasedate>2014-05-22</releasedate>
699  <people>
700  <person job="Actor" name="Ray Mears" character="Presenter" url="http://thetvdb.com/banners/actors/330506.jpg" thumb="http://thetvdb.com/banners/actors/330506.jpg"/>
701  <person job="Director" name="Phil Coles"/>
702  </people>
703  <images>
704  <image type="screenshot" url="http://thetvdb.com/banners/episodes/282022/4890147.jpg" thumb="http://thetvdb.com/banners/_cache/episodes/282022/4890147.jpg"/>
705  </images>
706  </item>
707 </metadata>
708 0
709 
710 >>> sys.argv = shlex.split('ttvdb.py -l de -a DE -D 282022')
711 >>> main()
712 Title:Wilder Westen
713 Season:1
714 Episode:1
715 Subtitle:Gebirge
716 Year:2014
717 ReleaseDate:2014-05-22
718 Director:Phil Coles
719 Plot:Der Wilde Westen. Die Vorstellung ...
720 UserRating:
721 Writers:
722 Screenshot:http://thetvdb.com/banners/episodes/282022/4890147.jpg
723 Language:de
724 Absolutenumber:1
725 Airedseasonid:586419
726 Dvddiscid:
727 Id:4890147
728 Imdbid:
729 ...
730 Productioncode:b044jl70
731 Seriesid:282022
732 Showurl:
733 Siterating:0
734 Siteratingcount:0
735 ...
736 Cast:Ray Mears
737 Runtime:60
738 Title:Wilder Westen
739 Season:1
740 Episode:2
741 Subtitle:Great Plains
742 Year:2014
743 ReleaseDate:2014-05-29
744 Director:Phil Coles
745 Plot:Die Great Plains,...
746 UserRating:
747 Writers:
748 Screenshot:http://thetvdb.com/banners/episodes/282022/4890148.jpg
749 Language:de
750 Absolutenumber:2
751 Airedseasonid:586419
752 Dvddiscid:
753 Id:4890148
754 Imdbid:
755 ...
756 Productioncode:b044z1k0
757 Seriesid:282022
758 Showurl:
759 Siterating:0
760 Siteratingcount:0
761 ...
762 Cast:Ray Mears
763 Runtime:60
764 Title:Wilder Westen
765 Season:1
766 Episode:3
767 Subtitle:W...
768 Year:2014
769 ReleaseDate:2014-06-05
770 Director:
771 Plot:
772 UserRating:
773 Writers:
774 Screenshot:http://thetvdb.com/banners/episodes/282022/4890149.jpg
775 Language:de
776 Absolutenumber:3
777 Airedseasonid:586419
778 Dvddiscid:
779 Id:4890149
780 Imdbid:
781 ...
782 Productioncode:b045nz9q
783 Seriesid:282022
784 Showurl:
785 Siterating:0
786 Siteratingcount:0
787 ...
788 Cast:Ray Mears
789 Runtime:60
790 0
791 
792 >>> sys.argv = shlex.split('ttvdb.py -l de -a DE -N 78804 10 1')
793 >>> main()
794 <?xml version='1.0' encoding='UTF-8'?>
795 <metadata>
796  <item>
797  <title>Doctor Who (2005)</title>
798  <subtitle>42</subtitle>
799  <description>Der Doktor und Martha Jones betätigen sich als interplanetare Verkehrswacht - und eilen der Besatzung eines Raumfrachters zu Hilfe, der steuerlos auf einen Stern zutreibt. Den beiden bleiben nur 42 Minuten, bevor das Schiff in der Hitze der Sonne verglüht. Als wäre dieses Zeitlimit noch nicht schlimm genug, bekommen sie es auch mit einer mysteriösen Macht zu tun, die den Crewmitgliedern nach dem Leben trachtet...</description>
800  <season>3</season>
801  <episode>7</episode>
802  <certifications>
803  <certification locale="us" name="TV-PG"/>
804  </certifications>
805  <categories>
806  <category type="genre" name="Adventure"/>
807  <category type="genre" name="Drama"/>
808  <category type="genre" name="Science-Fiction"/>
809  </categories>
810  <studios>
811  <studio name="BBC One"/>
812  </studios>
813  <runtime>45</runtime>
814  <inetref>78804</inetref>
815  <collectionref>78804</collectionref>
816  <imdb>0436992</imdb>
817  <tmsref>EP00750178</tmsref>
818  <language>de</language>
819  <year>2007</year>
820  <releasedate>2007-05-19</releasedate>
821  <people>
822  <person job="Actor" name="Jodie Whitaker" character="Thirteenth Doctor" url="http://thetvdb.com/banners/actors/451936.jpg" thumb="http://thetvdb.com/banners/actors/451936.jpg"/>
823  <person job="Actor" name="Peter Capaldi" character="Twelfth Doctor" url="http://thetvdb.com/banners/actors/333133.jpg" thumb="http://thetvdb.com/banners/actors/333133.jpg"/>
824  <person job="Actor" name="Matt Smith" character="Eleventh Doctor" url="http://thetvdb.com/banners/actors/129621.jpg" thumb="http://thetvdb.com/banners/actors/129621.jpg"/>
825  <person job="Actor" name="David Tennant" character="Tenth Doctor" url="http://thetvdb.com/banners/actors/67922.jpg" thumb="http://thetvdb.com/banners/actors/67922.jpg"/>
826  <person job="Actor" name="Billie Piper" character="Rose Tyler" url="http://thetvdb.com/banners/actors/67923.jpg" thumb="http://thetvdb.com/banners/actors/67923.jpg"/>
827  <person job="Actor" name="Freema Agyeman" character="Martha Jones" url="http://thetvdb.com/banners/actors/67924.jpg" thumb="http://thetvdb.com/banners/actors/67924.jpg"/>
828  <person job="Actor" name="John Barrowman" character="Jack Harkness" url="http://thetvdb.com/banners/actors/67925.jpg" thumb="http://thetvdb.com/banners/actors/67925.jpg"/>
829  <person job="Actor" name="Dan Starkey" character="Strax" url="http://thetvdb.com/banners/actors/343990.jpg" thumb="http://thetvdb.com/banners/actors/343990.jpg"/>
830  <person job="Actor" name="Catrin Stewart" character="Jenny Flint" url="http://thetvdb.com/banners/actors/343989.jpg" thumb="http://thetvdb.com/banners/actors/343989.jpg"/>
831  <person job="Actor" name="Neve McIntosh" character="Madame Vastra" url="http://thetvdb.com/banners/actors/343988.jpg" thumb="http://thetvdb.com/banners/actors/343988.jpg"/>
832  <person job="Actor" name="Christopher Eccleston" character="Ninth Doctor" url="http://thetvdb.com/banners/actors/77724.jpg" thumb="http://thetvdb.com/banners/actors/77724.jpg"/>
833  <person job="Actor" name="Catherine Tate" character="Donna Noble" url="http://thetvdb.com/banners/actors/77751.jpg" thumb="http://thetvdb.com/banners/actors/77751.jpg"/>
834  <person job="Actor" name="John Simm" character="The Master" url="http://thetvdb.com/banners/actors/79737.jpg" thumb="http://thetvdb.com/banners/actors/79737.jpg"/>
835  <person job="Actor" name="Karen Gillan" character="Amy Pond" url="http://thetvdb.com/banners/actors/129611.jpg" thumb="http://thetvdb.com/banners/actors/129611.jpg"/>
836  <person job="Actor" name="Arthur Darvill" character="Rory Williams" url="http://thetvdb.com/banners/actors/200011.jpg" thumb="http://thetvdb.com/banners/actors/200011.jpg"/>
837  <person job="Actor" name="Alex Kingston" character="River Song" url="http://thetvdb.com/banners/actors/286535.jpg" thumb="http://thetvdb.com/banners/actors/286535.jpg"/>
838  <person job="Actor" name="Jenna Coleman" character="Clara Oswald" url="http://thetvdb.com/banners/actors/305854.jpg" thumb="http://thetvdb.com/banners/actors/305854.jpg"/>
839  <person job="Actor" name="Bernard Cribbins" character="Wilfred Mott" url="http://thetvdb.com/banners/actors/308282.jpg" thumb="http://thetvdb.com/banners/actors/308282.jpg"/>
840  <person job="Actor" name="Camille Coduri" character="Jackie Tyler" url="http://thetvdb.com/banners/actors/337609.jpg" thumb="http://thetvdb.com/banners/actors/337609.jpg"/>
841  <person job="Actor" name="Noel Clarke" character="Mickey Smith" url="http://thetvdb.com/banners/actors/337610.jpg" thumb="http://thetvdb.com/banners/actors/337610.jpg"/>
842  <person job="Actor" name="Samuel Anderson" character="Danny Pink" url="http://thetvdb.com/banners/actors/339332.jpg" thumb="http://thetvdb.com/banners/actors/339332.jpg"/>
843  <person job="Actor" name="Michelle Gomez" character="Missy" url="http://thetvdb.com/banners/actors/356014.jpg" thumb="http://thetvdb.com/banners/actors/356014.jpg"/>
844  <person job="Actor" name="Jemma Redgrave" character="Kate Stewart" url="http://thetvdb.com/banners/actors/385486.jpg" thumb="http://thetvdb.com/banners/actors/385486.jpg"/>
845  <person job="Actor" name="Matt Lucas" character="Nardole" url="http://thetvdb.com/banners/actors/418731.jpg" thumb="http://thetvdb.com/banners/actors/418731.jpg"/>
846  <person job="Actor" name="Pearl Mackie" character="Bill Potts" url="http://thetvdb.com/banners/actors/418730.jpg" thumb="http://thetvdb.com/banners/actors/418730.jpg"/>
847  <person job="Guest Star" name="Rebecca Oldfield"/>
848  <person job="Guest Star" name="Gary Powell"/>
849  <person job="Guest Star" name="Vinette Robinson"/>
850  <person job="Guest Star" name="Matthew Chambers"/>
851  <person job="Guest Star" name="William Ash"/>
852  <person job="Guest Star" name="Anthony Flanagan"/>
853  <person job="Guest Star" name="Michelle Collins"/>
854  <person job="Guest Star" name="Adjoa Andoh"/>
855  <person job="Guest Star" name="Elize Du Toit"/>
856  <person job="Director" name="Graeme Harper"/>
857  <person job="Author" name="Chris Chibnall"/>
858  </people>
859  <images>
860  <image type="screenshot" url="http://thetvdb.com/banners/episodes/78804/327376.jpg" thumb="http://thetvdb.com/banners/_cache/episodes/78804/327376.jpg"/>
861  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-54.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-54.jpg" width="1280" height="720"/>
862  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-61.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-61.jpg" width="1280" height="720"/>
863  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-155.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-155.jpg" width="1920" height="1080"/>
864  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-122.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-122.jpg" width="1920" height="1080"/>
865  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-60.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-60.jpg" width="1920" height="1080"/>
866  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-93.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-93.jpg" width="1920" height="1080"/>
867  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-59.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-59.jpg" width="1920" height="1080"/>
868  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-64.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-64.jpg" width="1280" height="720"/>
869  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-148.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-148.jpg" width="1280" height="720"/>
870  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-13.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-13.jpg" width="1920" height="1080"/>
871  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-125.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-125.jpg" width="1920" height="1080"/>
872  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-73.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-73.jpg" width="1920" height="1080"/>
873  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-85.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-85.jpg" width="1920" height="1080"/>
874  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-115.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-115.jpg" width="1920" height="1080"/>
875  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-113.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-113.jpg" width="1280" height="720"/>
876  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-143.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-143.jpg" width="1920" height="1080"/>
877  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-116.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-116.jpg" width="1920" height="1080"/>
878  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-62.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-62.jpg" width="1280" height="720"/>
879  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-114.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-114.jpg" width="1280" height="720"/>
880  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-117.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-117.jpg" width="1920" height="1080"/>
881  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-144.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-144.jpg" width="1920" height="1080"/>
882  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/78804-63.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/78804-63.jpg" width="1280" height="720"/>
883  </images>
884  </item>
885 </metadata>
886 0
887 
888 """
889 from __future__ import print_function
890 
891 __title__ ="TheTVDB.com";
892 __author__="R.D.Vaughan"
893 __version__="2.0.0"
894 
895 # Version .1 Initial development
896 # Version .2 Add an option to get season and episode numbers from ep name
897 # Version .3 Cleaned up the documentation and added a usage display option
898 # Version .4 Added override formating of the number option (-N)
899 # Version .5 Added a check that tvdb_api.py dependancies are installed
900 # Version .6 Added -M Series List functionality
901 # Version .7 Added -o Series name override file functionality
902 # Version .8 Removed a dependancy, fixed caching for multiple users and
903 # used better method of supporting the -M option with tvdb_api
904 # Version .8.1 Cleaned up documentation errors.
905 # Version .8.2 Added the series name to output of meta data -D
906 # Version .8.3 Patched tv_api so even episode image is fully qualified URL
907 # Version .8.4 Fixed bug in -N when multiple episodes are returned from
908 # a search on episode name.
909 # Version .8.5 Made option -N more flexible in finding a matching episode
910 # Version .8.6 Add option -t for top rated graphics (-P,B,F) for a series
911 # Add option -l to filter graphics on the local language
912 # Add season level granularity to Poster and Banner URLs
913 # Changed the override file location to be specified on the
914 # command line along with the option -o.
915 # Increased the amount of massaging of episode data to improve
916 # compatiblilty with data bases.
917 # Changed the default season episode number format to SxxExx
918 # Add an option (-n) to return the season numbers for a series
919 # Added passing either a thetvdb.com SID (series identifcation
920 # number) or series name for all functions except -M list.
921 # Now ALL available episode meta data is returned. This
922 # includes meta data that MythTv DB does not currently store.
923 # Meta data 'Year' now derived from year in release date.
924 # Version .8.7 Fixed a bug with unicode meta data handling
925 # Version .8.8 Replaced the old configuration file with a true conf file
926 # Version .8.9 Add option -m to better conform to mythvideo standards
927 # Version .9.0 Now when a season level Banner is not found then the
928 # top rated series level graphics is returned instead. This
929 # feature had previously been available for posters only.
930 # Add runtime to episode meta data (-D). It is always the
931 # same for each episode as the information is only available
932 # from the series itself.
933 # Added the TV Series cast members as part of episode
934 # meta data. Option (-D).
935 # Added TV Series series genres as part of episode
936 # meta data. Option (-D).
937 # Resync with tvdb_api getting bug fixes and new features.
938 # Add episode data download to a specific language see
939 # -l 'es' option. If there is no data for a languages episode
940 # then any engish episode data is returned. English is default.
941 # The -M is still only in English. Waiting on tvdb_api fix.
942 # Version .9.1 Bug fix stdio abort when no genre exists for TV series
943 # Version .9.2 Bug fix stdio abort when no cast exists for TV series
944 # Version .9.3 Changed option -N when episodes partially match each
945 # combination of season/episode numbers are returned. This was
946 # added to deal with episodes which have a "(1)" trailing
947 # the episode name. An episode in more than one part.
948 # Version .9.4 Option -M can now list multi-language TV Series
949 # Version .9.5 "Director" metadata field returned as "None" is changed to
950 # "Unknown".
951 # File name parsing was changed to use multi-language capable
952 # regex patterns
953 # Version .9.6 Synced up to the 1.0 release of tvdb_api
954 # Added a tvdb_api version check and abort if not at least v1.0
955 # Changed to new tvdb_api's method of assigning the tvdb api key
956 # Version .9.7 Account for TVDB increasing the number of digits in their
957 # SID number (now greater then 5
958 # e,g, "Defying Gravity" is SID 104581)
959 # Version .9.8 Added a (-S) option for requesting a thetvdb
960 # episode screen shot
961 # Version .9.9 Fixed the -S option when NO episode image exists
962 # Version 1.0. Removed LF and replace with a space for all TVDB metatdata
963 # fields
964 # Version 1.0.1 Return all graphics (series and season) in the order
965 # highest to lowest as rated by users
966 # Version 1.0.2 Added better error messages to config file checking. Updated to
967 # v1.0.2 tvdb_api which contained fixes for concurrent instances
968 # of ttvdb.py generated by MythVideo.
969 # Version 1.0.3 Conform to new -D standards which return all graphics URLs along
970 # with text meta data. Also Posters, Banners and Fan art have one or
971 # comma separated URLs as one continuous string.
972 # Version 1.0.4 Poster Should be Coverart instead.
973 # Version 1.0.5 Added the TVDB URL to the episode metadata
974 # Version 1.0.6 When the language is invalid ignore language and continue processing
975 # Changed all exit codes to be 0 for passed and 1 for failed
976 # Removed duplicates when using the -M option and the -l option
977 # Version 1.0.7 Change over to the installed TTVDB api library
978 # Fixed cast name bad data abort reported in ticket #7957
979 # Fixed ticket #7900 - There is now fall back graphics when a
980 # language is specified and there is no images for that language.
981 # New default location and name of the ttvdb.conf file is '~/.mythtv/ttvdb.conf'. The command
982 # line option "-c" can still override the default location and name.
983 # Version 1.0.8 Removed any stderr messages for non-critical events. They cause dual pop-ups in MythVideo.
984 # Version 1.0.9 Removed another stderr messages when a non-supported language code is passed.
985 # Version 1.1.0 Added support for XML output. See:
986 # http://www.mythtv.org/wiki/MythTV_Universal_Metadata_Format
987 # Version 1.1.1 Make XML output the default.
988 # Version 1.1.2 Convert version information to XML
989 # Version 1.1.3 Implement fuzzy matching for episode name lookup
990 # Version 1.1.4 Add test mode (replaces --toprated)
991 # Version 1.1.5 Add the -C (collection option) with corresponding XML output
992 # and add a <collectionref> XML tag to Search and Query XML output
993 # Version 1.1.6 Honor series name overrides during TV series search
994 # Version 2.0.0 Update to API V2
995 
996 usage_txt='''
997 Usage: ttvdb.py usage: ttvdb -hdruviomMPFBDSC [parameters]
998  <series name or 'series and season number' or 'series and season number and episode number'>
999 
1000 For details on using ttvdb with Mythvideo see the ttvdb wiki page at:
1001 http://www.mythtv.org/wiki/Ttvdb.py
1002 
1003 Options:
1004  -h, --help show this help message and exit
1005  -d, --debug Show debugging info
1006  -r, --raw Dump raw data only
1007  -u, --usage Display examples for executing the ttvdb script
1008  -v, --version Display version and author
1009  -t Test mode, to check for installed dependencies
1010  -i, --interactive Interaction mode (allows selection of a specific
1011  Series)
1012  -c FILE, --configure=FILE
1013  Use configuration settings
1014  -a COUNTRY, --area=COUNTRY
1015  Select data that matches the specified country
1016  -l LANGUAGE, --language=LANGUAGE
1017  Select data that matches the specified language fall
1018  back to english if nothing found (e.g. 'es' Español,
1019  'de' Deutsch ... etc)
1020  -n, --num_seasons Return the season numbers for a series
1021  -m, --mythvideo Conform to mythvideo standards when processing -M, -P,
1022  -F and -D
1023  -M, --list Get matching TV Series list
1024  -P, --poster Get Series Poster URL(s)
1025  -F, --fanart Get Series fan art URL(s)
1026  -B, --backdrop Get Series banner/backdrop URL(s)
1027  -S, --screenshot Get Series episode screenshot URL
1028  -D, --data Get Series episode data
1029  -N, --numbers Get Season and Episode numbers
1030  -C, --collection Get A TV Series (collection) series specific information
1031 
1032 Command examples:
1033 (Return the banner graphics for a series)
1034 > ttvdb -B "Sanctuary"
1035 Banner:http://www.thetvdb.com/banners/graphical/80159-g2.jpg,http://www.thetvdb.com/banners/graphical/80159-g3.jpg,http://www.thetvdb.com/banners/graphical/80159-g.jpg
1036 
1037 (Return the banner graphics for a Series specific to a season)
1038 > ttvdb -B "SG-1" 1
1039 Banner:http://www.thetvdb.com/banners/graphical/72449-g2.jpg,http://www.thetvdb.com/banners/graphical/185-g3.jpg,http://www.thetvdb.com/banners/graphical/185-g2.jpg,http://www.thetvdb.com/banners/graphical/185-g.jpg,http://www.thetvdb.com/banners/graphical/72449-g.jpg,http://www.thetvdb.com/banners/text/185.jpg
1040 
1041 (Return the screen shot graphic for a Series Episode)
1042 > ttvdb -S "SG-1" 1 10
1043 http://www.thetvdb.com/banners/episodes/72449/85759.jpg
1044 
1045 (Return the banner graphics for a SID (series ID) specific to a season)
1046 (SID "72449" is specific for the series "SG-1")
1047 > ttvdb -B 72449 1
1048 Banner:http://www.thetvdb.com/banners/graphical/72449-g2.jpg,http://www.thetvdb.com/banners/graphical/185-g3.jpg,http://www.thetvdb.com/banners/graphical/185-g2.jpg,http://www.thetvdb.com/banners/graphical/185-g.jpg,http://www.thetvdb.com/banners/graphical/72449-g.jpg,http://www.thetvdb.com/banners/text/185.jpg
1049 
1050 (Return the banner graphics for a file name)
1051 > ttvdb -B "Stargate SG-1 - S08E03 - Lockdown"
1052 Banner:http://www.thetvdb.com/banners/graphical/72449-g2.jpg,http://www.thetvdb.com/banners/graphical/185-g3.jpg,http://www.thetvdb.com/banners/graphical/185-g2.jpg,http://www.thetvdb.com/banners/graphical/185-g.jpg,http://www.thetvdb.com/banners/graphical/72449-g.jpg,http://www.thetvdb.com/banners/text/185.jpg
1053 
1054 (Return the posters, banners and fan art for a series)
1055 > ttvdb -PFB "Sanctuary"
1056 Coverart:http://www.thetvdb.com/banners/posters/80159-2.jpg,http://www.thetvdb.com/banners/posters/80159-1.jpg
1057 Fanart:http://www.thetvdb.com/banners/fanart/original/80159-2.jpg,http://www.thetvdb.com/banners/fanart/original/80159-1.jpg,http://www.thetvdb.com/banners/fanart/original/80159-8.jpg,http://www.thetvdb.com/banners/fanart/original/80159-6.jpg,http://www.thetvdb.com/banners/fanart/original/80159-5.jpg,http://www.thetvdb.com/banners/fanart/original/80159-9.jpg,http://www.thetvdb.com/banners/fanart/original/80159-3.jpg,http://www.thetvdb.com/banners/fanart/original/80159-7.jpg,http://www.thetvdb.com/banners/fanart/original/80159-4.jpg
1058 Banner:http://www.thetvdb.com/banners/graphical/80159-g2.jpg,http://www.thetvdb.com/banners/graphical/80159-g3.jpg,http://www.thetvdb.com/banners/graphical/80159-g.jpg
1059 
1060 (Return thetvdb.com's top rated poster, banner and fan art for a TV Series)
1061 (NOTE: If there is no graphic for a type or any graphics at all then those types are not returned)
1062 > ttvdb -tPFB "Stargate SG-1"
1063 Coverart:http://www.thetvdb.com/banners/posters/72449-1.jpg
1064 Fanart:http://www.thetvdb.com/banners/fanart/original/72449-1.jpg
1065 Banner:http://www.thetvdb.com/banners/graphical/185-g3.jpg
1066 > ttvdb -tB "Night Gallery"
1067 http://www.thetvdb.com/banners/blank/70382.jpg
1068 
1069 (Return graphics only matching the local language for a TV series)
1070 (In this case banner 73739-g9.jpg is not included because it does not match the language 'en')
1071 > ttvdb -Bl en "Lost"
1072 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
1073 
1074 (Return a season and episode numbers using the override file to identify the series as the US version)
1075 > ttvdb -N --configure="/home/user/.tvdb/tvdb.conf" "Eleventh Hour" "H2O"
1076 <?xml version='1.0' encoding='UTF-8'?>
1077 <metadata>
1078  <item>
1079  <title>Eleventh Hour (US)</title>
1080  <subtitle>H2O</subtitle>
1081  <language>en</language>
1082  <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>
1083  <season>1</season>
1084  <episode>10</episode>
1085 ...
1086  <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"/>
1087  <image type="banner" url="http://www.thetvdb.com/banners/graphical/83066-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/83066-g.jpg"/>
1088  </images>
1089  </item>
1090 </metadata>
1091 
1092 (Return the season numbers for a series)
1093 > ttvdb --configure="/home/user/.tvdb/tvdb.conf" -n "SG-1"
1094 0,1,2,3,4,5,6,7,8,9,10
1095 
1096 (Return the meta data for a specific series/season/episode)
1097 > ttvdb.py -D 80159 2 2
1098 <?xml version='1.0' encoding='UTF-8'?>
1099 <metadata>
1100  <item>
1101  <title>Sanctuary</title>
1102  <subtitle>End of Nights (2)</subtitle>
1103  <language>en</language>
1104  <description>Furious at being duped into a trap, Magnus (AMANDA TAPPING) takes on Kate (AGAM DARSHI), demanding information and complete access to her Cabal contacts. The Cabal’s true agenda is revealed and Magnus realizes that they are not only holding Ashley (EMILIE ULLERUP) as ransom to obtain complete control of the Sanctuary Network, but turning her into the ultimate weapon. Now transformed into a Super Abnormal with devastating powers, Ashley and her newly cloned fighters begin their onslaught, destroying Sanctuaries in cities around the world. Tesla (JONATHON YOUNG) and Henry (RYAN ROBBINS) attempt to create a weapon that can stop the attacks…without killing Ashley. As the team prepares to defend the Sanctuary with Tesla’s new weapon, Magnus must come to the realization that they may not be able to stop the Cabal’s attacks without harming Ashley. She realizes she might have to choose between saving her only daughter, or losing the Sanctuary and all the lives and secrets within it.</description>
1105  <season>2</season>
1106  <episode>2</episode>
1107  <certifications>
1108  <certification locale="us" name="TV-PG"/>
1109  </certifications>
1110  <categories>
1111  <category type="genre" name="Action and Adventure"/>
1112  <category type="genre" name="Science-Fiction"/>
1113  </categories>
1114  <studios>
1115  <studio name="SciFi"/>
1116  </studios>
1117 ...
1118  <image type="banner" url="http://www.thetvdb.com/banners/graphical/80159-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/80159-g.jpg"/>
1119  </images>
1120  </item>
1121 </metadata>
1122 
1123 (Return a list of "thetv.com series id and series name" that contain specific search word(s) )
1124 (!! Be careful with this option as poorly defined search words can result in large lists being returned !!)
1125 > ttvdb.py -M "night a"
1126 <?xml version='1.0' encoding='UTF-8'?>
1127 <metadata>
1128  <item>
1129  <language>en</language>
1130  <title>Love on a Saturday Night</title>
1131  <inetref>74382</inetref>
1132  <releasedate>2004-02-01</releasedate>
1133  </item>
1134  <item>
1135  <language>en</language>
1136  <title>A Night on Mount Edna</title>
1137  <inetref>108281</inetref>
1138  </item>
1139  <item>
1140  <language>en</language>
1141  <title>A Night at the Office</title>
1142  <inetref>118511</inetref>
1143  <description>On August 11th 2009, it was announced that the cast of The Office would be reuniting for a special, called "A Night at The Office", available at BBC2 and online.</description>
1144  <releasedate>2009-01-01</releasedate>
1145  <images>
1146  <image type="banner" url="http://www.thetvdb.com/banners/graphical/118511-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/118511-g.jpg"/>
1147  </images>
1148  </item>
1149  <item>
1150  <language>en</language>
1151  <title>Star For A Night</title>
1152  <inetref>71476</inetref>
1153  <releasedate>1999-01-01</releasedate>
1154  </item>
1155 </metadata>
1156 
1157 (Return TV series collection data of "thetv.com series id" for a specified language)
1158 > ttvdb.py -l de -C 80159
1159 <?xml version='1.0' encoding='UTF-8'?>
1160 <metadata>
1161  <item>
1162  <language>de</language>
1163  <title>Sanctuary</title>
1164  <network>Syfy</network>
1165  <airday>Friday</airday>
1166  <airtime>22:00</airtime>
1167  <description>Dr. Helen Magnus ist eine so brillante wie geheimnisvolle Wissenschaftlerin die sich mit den Kreaturen der Nacht beschäftigt. In ihrem Unterschlupf - genannt "Sanctuary" - hat sie ein Team versammelt, das seltsame und furchteinflößende Ungeheuer untersucht, die mit den Menschen auf der Erde leben. Konfrontiert mit ihren düstersten Ängsten und ihren schlimmsten Alpträumen versucht das Sanctuary-Team, die Welt vor den Monstern - und die Monster vor der Welt zu schützen.</description>
1168  <certifications>
1169  <certification locale="us" name="TV-PG"/>
1170  </certifications>
1171  <categories>
1172  <category type="genre" name="Action and Adventure"/>
1173  <category type="genre" name="Science-Fiction"/>
1174  </categories>
1175  <studios>
1176  <studio name="Syfy"/>
1177  </studios>
1178  <runtime>60</runtime>
1179  <inetref>80159</inetref>
1180  <imdb>0965394</imdb>
1181  <tmsref>EP01085421</tmsref>
1182  <userrating>8.0</userrating>
1183  <ratingcount>128</ratingcount>
1184  <year>2007</year>
1185  <releasedate>2007-05-14</releasedate>
1186  <lastupdated>Fri, 17 Feb 2012 16:57:02 GMT</lastupdated>
1187  <status>Continuing</status>
1188  <images>
1189  <image type="coverart" url="http://www.thetvdb.com/banners/posters/80159-4.jpg" thumb="http://www.thetvdb.com/banners/_cache/posters/80159-4.jpg"/>
1190  <image type="fanart" url="http://www.thetvdb.com/banners/fanart/original/80159-8.jpg" thumb="http://www.thetvdb.com/banners/_cache/fanart/original/80159-8.jpg"/>
1191  <image type="banner" url="http://www.thetvdb.com/banners/graphical/80159-g6.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/80159-g6.jpg"/>
1192  </images>
1193  </item>
1194 </metadata>
1195 '''
1196 
1197 # Episode keys that can be used in a episode data/information search.
1198 # All keys are currently being used.
1199 '''
1200 'episodenumber'
1201 'rating'
1202 'overview'
1203 'dvd_episodenumber'
1204 'dvd_discid'
1205 'combined_episodenumber'
1206 'epimgflag'
1207 'id'
1208 'seasonid'
1209 'seasonnumber'
1210 'writer'
1211 'lastupdated'
1212 'filename'
1213 'absolute_number'
1214 'combined_season'
1215 'imdb_id'
1216 'director'
1217 'dvd_chapter'
1218 'dvd_season'
1219 'gueststars'
1220 'seriesid'
1221 'language'
1222 'productioncode'
1223 'firstaired'
1224 'episodename'
1225 '''
1226 
1227 # System modules
1228 import sys, os, re
1229 from optparse import OptionParser
1230 from copy import deepcopy
1231 # shlex for doctest
1232 import shlex
1233 
1234 # import logging
1235 # logger = logging.getLogger()
1236 # ch = logging.StreamHandler()
1237 # fh = logging.FileHandler("ttvdb.log")
1238 # #ch.setLevel(logging.DEBUG)
1239 # fh.setLevel(logging.DEBUG)
1240 # logging.getLogger("dicttoxml").setLevel(logging.WARN)
1241 # logging.getLogger("tvdb_api").setLevel(logging.DEBUG)
1242 # logger.addHandler(ch)
1243 # logger.addHandler(fh)
1244 
1245 IS_PY2 = sys.version_info[0] == 2
1246 if IS_PY2:
1247  import ConfigParser
1248 else:
1249  import configparser as ConfigParser
1250 
1252  # explicit username and account id are not required
1253  # to use the API. API documentation is unclear in this regard
1254  username = ""
1255  account_identifier = ""
1256  apikey = '0BB856A59C51D607'
1257 
1258 # Verify that tvdb_api.py are available
1259 try:
1260  # thetvdb.com specific modules
1261  import MythTV.ttvdb.tvdb_api as tvdb_api
1262  from MythTV.ttvdb.tvdb_api import (tvdb_error, tvdb_shownotfound, tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_episodenotfound, tvdb_attributenotfound, tvdb_userabort)
1263 
1264  # verify version of tvdbapi to make sure it is at least 1.0
1265  if tvdb_api.__version__ < '2.0':
1266  print("\nYour current installed tvdb_api.py version is (%s)\n" % tvdb_api.__version__)
1267  raise
1268 except Exception as e:
1269  print('''
1270 The modules tvdb_api.py (v2.0 or greater).
1271 They should have been installed along with the MythTV python bindings.
1272 Error:(%s)
1273 ''' % e)
1274  sys.exit(1)
1275 finally:
1276  pass
1277 
1278 try:
1279  from MythTV.utility import levenshtein
1280 except Exception as e:
1281  print("""Could not import levenshtein string distance method from MythTV Python Bindings
1282 Error:(%s)
1283 """ % e)
1284  sys.exit(1)
1285 
1286 try:
1287  if IS_PY2:
1288  from StringIO import StringIO
1289  else:
1290  from io import StringIO
1291  from lxml import etree as etree
1292 except Exception as e:
1293  sys.stderr.write(u'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
1294  sys.exit(1)
1295 
1296 from MythTV.utility.dicttoxml import dicttoxml
1297 try:
1298  import json
1299  from lxml import etree as eTree
1300 except Exception as e:
1301  sys.stderr.write(u'\n! Error - Importing the "lxml" python library failed on error(%s)\n' % e)
1302  sys.exit(1)
1303 
1304 if IS_PY2:
1305  stdio_type = file
1306 else:
1307  import io
1308  stdio_type = io.TextIOWrapper
1309  unicode = str
1310 
1311 # disable the insecure request warning that we know we are going to get
1312 import urllib3
1313 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
1314 
1315 # Check that the lxml library is current enough
1316 # From the lxml documents it states: (http://codespeak.net/lxml/installation.html)
1317 # "If you want to use XPath, do not use libxml2 2.6.27. We recommend libxml2 2.7.2 or later"
1318 # Testing was performed with the Ubuntu 9.10 "python-lxml" version "2.1.5-1ubuntu2" repository package
1319 version = ''
1320 for digit in etree.LIBXML_VERSION:
1321  version+=str(digit)+'.'
1322 version = version[:-1]
1323 if version < '2.7.2':
1324  sys.stderr.write(u'''
1325 ! Error - The installed version of the "lxml" python library "libxml" version is too old.
1326  At least "libxml" version 2.7.2 must be installed. Your version is (%s).
1327 ''' % version)
1328  sys.exit(1)
1329 
1330 
1331 # Global variables
1332 http_find="http://www.thetvdb.com"
1333 http_replace="http://www.thetvdb.com" #Keep replace code "just in case"
1334 
1335 name_parse=[
1336  # foo_[s01]_[e01]
1337  re.compile('''^(.+?)[ \._\-]\[[Ss]([0-9]+?)\]_\[[Ee]([0-9]+?)\]?[^\\/]*$'''),
1338  # foo.1x09*
1339  re.compile('''^(.+?)[ \._\-]\[?([0-9]+)x([0-9]+)[^\\/]*$'''),
1340  # foo.s01.e01, foo.s01_e01
1341  re.compile('''^(.+?)[ \._\-][Ss]([0-9]+)[\.\- ]?[Ee]([0-9]+)[^\\/]*$'''),
1342  # foo.103*
1343  re.compile('''^(.+)[ \._\-]([0-9]{1})([0-9]{2})[\._ -][^\\/]*$'''),
1344  # foo.0103*
1345  re.compile('''^(.+)[ \._\-]([0-9]{2})([0-9]{2,3})[\._ -][^\\/]*$'''),
1346 ] # contains regex parsing filename parsing strings used to extract info from video filenames
1347 
1348 # Episode meta data that is massaged
1349 massage={'writer':'|','director':'|', 'overview':'&', 'gueststars':'|' }
1350 # Keys and titles used for episode data (option '-D')
1351 data_keys =['airedSeason','airedEpisodeNumber','episodeName','firstAired','directors','overview','rating','writers','filename','language' ]
1352 data_titles=['Season:','Episode:','Subtitle:','ReleaseDate:','Director:','Plot:','UserRating:','Writers:','Screenshot:','Language:' ]
1353 # High level dictionay keys for select graphics URL(s)
1354 fanart_key='fanart'
1355 banner_key='series'
1356 poster_key='poster'
1357 season_key='season'
1358 # Lower level dictionay keys for select graphics URL(s)
1359 poster_series_key='680x1000'
1360 poster_season_key='season'
1361 fanart_hires_key='1920x1080'
1362 fanart_lowres_key='1280x720'
1363 banner_series_key='graphical'
1364 banner_season_key='seasonwide'
1365 # Type of graphics being requested
1366 poster_type='Poster'
1367 fanart_type='Fanart'
1368 banner_type='Banner'
1369 screenshot_request = False
1370 
1371 # Cache directory name specific to the user. This avoids permission denied error with a common cache dirs
1372 confdir = os.environ.get('MYTHCONFDIR', '')
1373 if (not confdir) or (confdir == '/'):
1374  confdir = os.environ.get('HOME', '')
1375  if (not confdir) or (confdir == '/'):
1376  print("Unable to find MythTV directory for metadata cache.")
1377  sys.exit(1)
1378  confdir = os.path.join(confdir, '.mythtv')
1379 # different cache dirs due to different pickle protocols
1380 # TODO massage pickle so python3 generates python2 compatible pickles
1381 if IS_PY2:
1382  cache_dir=os.path.join(confdir, "cache/tvdb_api/")
1383 else:
1384  cache_dir=os.path.join(confdir, "cache/tvdb_api3/")
1385 if not os.path.exists(cache_dir):
1386  os.makedirs(cache_dir)
1387 
1388 def _can_int(x):
1389  """Takes a string, checks if it is numeric.
1390  >>> _can_int("2")
1391  True
1392  >>> _can_int("A test")
1393  False
1394  """
1395  try:
1396  int(x)
1397  except ValueError:
1398  return False
1399  else:
1400  return True
1401 # end _can_int
1402 
1403 class OutStreamEncoder(object):
1404  """Wraps a stream with an encoder"""
1405  def __init__(self, outstream, encoding=None):
1406  self.out = outstream
1407  if not encoding:
1408  self.encoding = sys.getfilesystemencoding()
1409  else:
1410  self.encoding = encoding
1411 
1412  def write(self, obj):
1413  """Wraps the output stream, encoding Unicode strings with the specified encoding"""
1414  if isinstance(obj, unicode):
1415  obj = obj.encode(self.encoding)
1416  if IS_PY2:
1417  self.out.write(obj)
1418  else:
1419  self.out.buffer.write(obj)
1420 
1421  def __getattr__(self, attr):
1422  """Delegate everything but write to the stream"""
1423  return getattr(self.out, attr)
1424 if isinstance(sys.stdout, stdio_type):
1425  sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
1426  sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
1427 
1428 # modified Show class implementing a fuzzy search
1429 class Show( tvdb_api.Show ):
1430  def fuzzysearch(self, term = None, key = None):
1431  results = []
1432  for cur_season in self.values():
1433  searchresult = cur_season.fuzzysearch(term = term, key = key)
1434  if len(searchresult) != 0:
1435  results.extend(searchresult)
1436  return results
1437 # end Show
1438 
1439 # modified Season class implementing a fuzzy search
1440 class Season( tvdb_api.Season ):
1441  def fuzzysearch(self, term = None, key = None):
1442  results = []
1443  for episode in self.values():
1444  searchresult = episode.fuzzysearch(term = term, key = key)
1445  if searchresult is not None:
1446  results.append(searchresult)
1447  return results
1448 # end Season
1449 
1450 # modified Episode class implementing a fuzzy search
1451 class Episode( tvdb_api.Episode ):
1452  _re_strippart = re.compile('(.*) \‍([0-9]+\‍)')
1453  def fuzzysearch(self, term = None, key = None):
1454  if term is None:
1455  raise TypeError("must supply string to search for (contents)")
1456 
1457  term = unicode(term).lower()
1458  for cur_key, cur_value in self.items():
1459  cur_key, cur_value = [unicode(a).lower() for a in [cur_key, cur_value]]
1460  if key is not None and cur_key != key:
1461  continue
1462  distance = levenshtein(cur_value, term)
1463  if distance <= 3:
1464  # handle most matches
1465  self.distance = distance
1466  return self
1467  if distance <= 5:
1468  # handle part numbers, 'subtitle (nn)'
1469  match = self._re_strippart.match(cur_value)
1470  if match:
1471  tmp = match.group(1)
1472  if levenshtein(tmp, term) <= 3:
1473  self.distance = distance
1474  return self
1475  return None
1476 #end Episode
1477 
1478 # modified Tvdb API class using modified show classes
1479 class Tvdb( tvdb_api.Tvdb ):
1480  def series_by_sid(self, sid, language):
1481  "Lookup a series via it's sid"
1482  seriesid = 'sid:' + sid
1483  sid = int(sid)
1484  if not seriesid in self.corrections:
1485  self._getShowData(sid, language=language)
1486  self.corrections[seriesid] = sid
1487  return self.shows[sid]
1488  #end series_by_sid
1489 
1490  # override the existing method, using modified show classes
1491  def _setItem(self, sid, seas, ep, attrib, value):
1492  if sid not in self.shows:
1493  self.shows[sid] = Show()
1494  if seas not in self.shows[sid]:
1495  self.shows[sid][seas] = Season()
1496  if ep not in self.shows[sid][seas]:
1497  self.shows[sid][seas][ep] = Episode()
1498  self.shows[sid][seas][ep][attrib] = value
1499  #end _setItem
1500 
1501  # override the existing method, using modified show class
1502  def _setShowData(self, sid, key, value):
1503  if sid not in self.shows:
1504  self.shows[sid] = Show()
1505  self.shows[sid].data[key] = value
1506  #end _setShowData
1507 #end Tvdb
1508 
1509 # Search for a series by SID or Series name
1510 def search_for_series(tvdb, sid_or_name, language):
1511  "Get series data by sid or series name of the Tv show"
1512  if SID == True:
1513  return tvdb.series_by_sid(sid_or_name, language)
1514  else:
1515  return tvdb[sid_or_name]
1516 # end search_for_series
1517 
1518 # Verify that a Series or Series and Season exists on thetvdb.com
1519 def searchseries(t, opts, series_season_ep):
1520  global SID
1521  series_name=''
1522  key = series_season_ep[0].lower()
1523  if opts.configure != "" and key in override:
1524  if override[key][2]:
1525  series_name=override[key][2] # Override series name
1526  SID = True
1527  else:
1528  series_name=override[key][0] # Override series name
1529  else:
1530  series_name=series_season_ep[0] # Leave the series name alone
1531  try:
1532  # Search for the series or series & season or series & season & episode
1533  series_data = search_for_series(t, series_name, opts.language)
1534  if len(series_season_ep)>1:
1535  if len(series_season_ep)>2: # series & season & episode
1536  seriesfound=series_data[ int(series_season_ep[1]) ][ int(series_season_ep[2]) ]
1537  else:
1538  seriesfound=series_data[ int(series_season_ep[1]) ] # series & season
1539  else:
1540  seriesfound=series_data # Series only
1541  except tvdb_shownotfound:
1542  # No such show found.
1543  # Use the show-name from the files name, and None as the ep name
1544  sys.exit(0)
1545  except (tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound):
1546  # The season, episode or name wasn't found, but the show was.
1547  # Use the corrected show-name, but no episode name.
1548  sys.exit(0)
1549  except tvdb_error as errormsg:
1550  # Error communicating with thetvdb.com
1551  if SID == True: # Maybe the digits were a series name (e.g. 90210)
1552  SID = False
1553  return searchseries(t, opts, series_season_ep)
1554  sys.exit(0)
1555  except tvdb_userabort as errormsg:
1556  # User aborted selection (q or ^c)
1557  print("\n", errormsg)
1558  sys.exit(0)
1559  else:
1560  if opts.raw==True:
1561  print("="*20)
1562  print("Raw Series Data:\n")
1563  if len(series_season_ep)>1:
1564  print(t[ series_name ][ int(series_season_ep[1]) ])
1565  else:
1566  print(t[ series_name ])
1567  print("="*20)
1568  return(seriesfound)
1569 # end searchseries
1570 
1571 # Retrieve Poster or Fan Art or Banner graphics URL(s)
1572 def get_graphics(t, opts, series_season_ep, graphics_type, single_option, language=False):
1573  banners='_banners'
1574  series_name=''
1575  graphics=[]
1576  key = series_season_ep[0].lower()
1577  if opts.configure != "" and key in override:
1578  series_name=override[key][0] # Override series name
1579  else:
1580  series_name=series_season_ep[0] # Leave the series name alone
1581 
1582  if SID == True:
1583  t._parseBanners(series_name)
1584  else:
1585  t._parseBanners(t._nameToSid(series_name))
1586  bid_order = {'fanart': [], 'poster': [], 'series': [], 'season': [], 'seasonwide': []}
1587  URLs = {'fanart': [], 'poster': [], 'series': [], 'season': [], 'seasonwide': []}
1588 
1589  # get the urls in presented order
1590  for key in t.shows.keys():
1591  banner = t.shows[key].data['_banners']
1592  for graphic_type_items in bid_order.keys():
1593  if graphic_type_items in banner:
1594  for graphic_item in banner[graphic_type_items]['raw']:
1595  url = banner[graphic_type_items][graphic_item['resolution']][graphic_item['id']]
1596  url['rating'] = graphic_item['ratingsInfo']['average']
1597  URLs[graphic_type_items].append(url)
1598 
1599  if graphics_type == fanart_type: # Series fanart graphics
1600  if not len(URLs[u'fanart']):
1601  return []
1602  for url in URLs[u'fanart']:
1603  graphics.append(url)
1604  elif len(series_season_ep) == 1:
1605  if not len(URLs[u'series']):
1606  return []
1607  if graphics_type == banner_type: # Series Banners
1608  for url in URLs[u'series']:
1609  graphics.append(url)
1610  else: # Series Posters
1611  for url in URLs[u'poster']:
1612  graphics.append(url)
1613  else:
1614  if not len(URLs[u'season']):
1615  return []
1616  if graphics_type == banner_type: # Season Banners
1617  season_banners=[]
1618  # seasonwide has blank resolution
1619  for url in URLs[u'seasonwide']:
1620  if url[u'resolution'] == u'' and url[u'subKey'] == series_season_ep[1]:
1621  season_banners.append(url)
1622  if not len(season_banners):
1623  return []
1624  graphics = season_banners
1625  else: # Season Posters
1626  # season has blank resolution
1627  season_posters=[]
1628  for url in URLs[u'season']:
1629  if url[u'resolution'] == u'' and url[u'subKey'] == series_season_ep[1]:
1630  season_posters.append(url)
1631  if not len(season_posters):
1632  return []
1633  graphics = season_posters
1634 
1635  graphicsURLs=[]
1636  if single_option==False:
1637  graphicsURLs.append(graphics_type+':')
1638 
1639  count = 0
1640  wasanythingadded = 0
1641  anyotherlanguagegraphics=[]
1642  englishlanguagegraphics=[]
1643  graphics = sorted(graphics, key=lambda k: k['rating'], reverse=True)
1644  for URL in graphics:
1645  if graphics_type == 'filename':
1646  if URL[graphics_type] is None:
1647  continue
1648  if language and 'language' in URL: # Is there a language to filter URLs on?
1649  if language == URL['language']:
1650  graphicsURLs.append((URL['_bannerpath']).replace(http_find, http_replace))
1651  else: # Check for fall back graphics in case there are no selected language graphics
1652  if u'en' == URL['language']:
1653  englishlanguagegraphics.append((URL['_bannerpath']).replace(http_find, http_replace))
1654  else:
1655  anyotherlanguagegraphics.append((URL['_bannerpath']).replace(http_find, http_replace))
1656  else:
1657  graphicsURLs.append((URL['_bannerpath']).replace(http_find, http_replace))
1658  if wasanythingadded == len(graphicsURLs):
1659  continue
1660  wasanythingadded = len(graphicsURLs)
1661 
1662  if not len(graphicsURLs):
1663  if len(englishlanguagegraphics): # Fall back to English graphics
1664  graphicsURLs = englishlanguagegraphics
1665  elif len(anyotherlanguagegraphics): # Fall back-back to any available graphics
1666  graphicsURLs = anyotherlanguagegraphics
1667 
1668  if opts.debug == True:
1669  print(u"\nGraphics:\n", graphicsURLs)
1670 
1671  if len(graphicsURLs) == 1 and graphicsURLs[0] == graphics_type+':':
1672  return [] # Due to the language filter there may not be any URLs
1673  return(graphicsURLs)
1674 # end get_graphics
1675 
1676 # Massage episode name to match those in thetvdb.com for this series
1677 def massageEpisode_name(ep_name, series_season_ep):
1678  for edit in override[series_season_ep[0].lower()][1]:
1679  ep_name=ep_name.replace(edit[0],edit[1]) # Edit episode name for each set of strings
1680  return ep_name
1681 # end massageEpisode_name
1682 
1683 # Remove '|' and replace with commas
1684 def change_to_commas(meta_data):
1685  if not meta_data: return meta_data
1686  meta_data = (u'|'.join([d for d in meta_data.split('| ') if d]))
1687  return (u', '.join([d for d in meta_data.split('|') if d]))
1688 # end change_to_commas
1689 
1690 # Change &amp; values to ascii equivalents
1691 def change_amp(text):
1692  if not text: return text
1693  try:
1694  text = text.replace("&quot;", "'").replace("\r\n", " ")
1695  text = text.replace(r"\'", "'")
1696  except Exception as e:
1697  pass
1698  return text
1699 # end change_amp
1700 
1701 # Prepare for includion into a DB
1702 def make_db_ready(text):
1703  if not text: return text
1704  text = text.replace(u'\u2013', "-")
1705  text = text.replace(u'\u2014', "-")
1706  text = text.replace(u'\u2018', "'")
1707  text = text.replace(u'\u2019', "'")
1708  text = text.replace(u'\u2026', "...")
1709  text = text.replace(u'\u201c', '"')
1710  text = text.replace(u'\u201d', '"')
1711  text = text.encode('latin-1', 'backslashreplace')
1712  return text
1713 # end make_db_ready
1714 
1715 # Get Series Episode data by season
1716 def Getseries_episode_data(t, opts, series_season_ep, language = None):
1717  global screenshot_request, http_find, http_replace
1718 
1719  args = len(series_season_ep)
1720  series_name=''
1721  key = series_season_ep[0].lower()
1722  if opts.configure != "" and key in override:
1723  series_name=override[key][0] # Override series name
1724  else:
1725  series_name=series_season_ep[0] # Leave the series name alone
1726 
1727  # Get Cast members
1728  cast_members=''
1729  tmp_cast = ''
1730  try:
1731  series_data = search_for_series(t, series_name, opts.language)
1732  tmp_cast = series_data['_actors']
1733  tmp_cast = sorted(tmp_cast, key=lambda k: k['sortOrder'])
1734  except:
1735  cast_members=''
1736  if len(tmp_cast):
1737  cast_members=''.encode('utf8')
1738  for cast in tmp_cast:
1739  if cast['name']:
1740  cast_members+=(cast['name']+', ').encode('utf8')
1741  if cast_members != '':
1742  try:
1743  cast_members = cast_members[:-2].encode('utf8')
1744  except (UnicodeDecodeError, AttributeError):
1745  cast_members = unicode(cast_members[:-2],'utf8')
1746  cast_members = change_amp(cast_members)
1747  cast_members = change_to_commas(cast_members)
1748  cast_members=cast_members.replace('\n',' ')
1749 
1750  # Get genre(s)
1751  genres=''
1752  try:
1753  genres_string = series_data[u'genre'].encode('utf-8')
1754  except:
1755  genres_string=''
1756  if genres_string is not None and genres_string != '':
1757  genres = change_amp(genres_string)
1758  genres = change_to_commas(genres)
1759 
1760  seasons=sorted(series_data.keys()) # Get the seasons for this series
1761  for season in seasons:
1762  if args > 1: # If a season was specified skip other seasons
1763  if season != int(series_season_ep[1]):
1764  continue
1765  episodes=sorted(series_data[season].keys()) # Get the episodes for this season
1766  for episode in episodes: # If an episode was specified skip other episodes
1767  if args > 2:
1768  if episode != int(series_season_ep[2]):
1769  continue
1770  # get more detailed episode info
1771  t.getDetailedEpisodeInfo(int(series_data['id']), season, episode)
1772  extra_ep_data=[]
1773  available_keys=series_data[season][episode].keys()
1774  if screenshot_request:
1775  if u'filename' in available_keys:
1776  screenshot = series_data[season][episode][u'filename']
1777  if screenshot:
1778  print(screenshot.replace(http_find, http_replace))
1779  return
1780  else:
1781  return
1782  # key ordering is not sorted so that Title is first for existing client
1783  # compatability
1784  key_values=[]
1785  for values in data_keys: # Initialize an array for each possible data element for
1786  key_values.append('') # each episode within a season
1787  for key in sorted(available_keys):
1788  try:
1789  # skip deprecated keys
1790  if key in ['director']:
1791  continue
1792  i = data_keys.index(key) # Include only specific episode data
1793  except ValueError:
1794  if series_data[season][episode][key] is not None:
1795  text = series_data[season][episode][key]
1796  if isinstance(text, dict):
1797  # handle language tuple
1798  text = list(text.values())[0]
1799  elif isinstance(text, list):
1800  # handle guest stars lists
1801  text = ', '.join(text)
1802  text = change_amp(unicode(text))
1803  text = change_to_commas(text)
1804  if text == 'None' and key.title() == 'Director':
1805  text = u"Unknown"
1806  try:
1807  extra_ep_data.append(u"%s:%s" % (key.title(), text))
1808  except UnicodeDecodeError:
1809  extra_ep_data.append(u"%s:%s" % (key.title(), unicode(text, "utf8")))
1810  continue
1811  text = series_data[season][episode][key]
1812 
1813  if text is None and key.title() == 'Director':
1814  text = u"Unknown"
1815  if isinstance(text, list):
1816  text = ', '.join(text)
1817  if text is None or text == 'None':
1818  continue
1819  else:
1820  # handle language tuple
1821  if isinstance(text, dict):
1822  # handle language tuple
1823  text = list(text.values())[0]
1824  text = change_amp(unicode(text))
1825  value = change_to_commas(text)
1826  value = value.replace(u'\n', u' ')
1827  key_values[i]=value
1828  index = 0
1829  if SID == False:
1830  print(u"Title:%s" % series_name) # Ouput the full series name
1831  else:
1832  print(u"Title:%s" % series_data[u'seriesname'])
1833 
1834  for key in data_titles:
1835  if key_values[index] is not None:
1836  if data_titles[index] == u'ReleaseDate:' and len(key_values[index]) > 4:
1837  print(u'%s%s'% (u'Year:', key_values[index][:4]))
1838  if key_values[index] != 'None':
1839  print(u'%s%s' % (data_titles[index], key_values[index]))
1840  index+=1
1841  cast_print=False
1842  for extra_data in extra_ep_data:
1843  if extra_data[:extra_data.index(':')] == u'Gueststars':
1844  extra_cast = extra_data[extra_data.index(':')+1:]
1845  if not extra_cast:
1846  continue
1847  if (len(extra_cast)>128) and not extra_cast.count(','):
1848  continue
1849  if cast_members:
1850  extra_data=(u"Cast:%s" % cast_members)+', '+extra_cast
1851  else:
1852  extra_data=u"Cast:%s" % extra_cast
1853  cast_print=True
1854  print(extra_data)
1855  if cast_print == False:
1856  print(u"Cast:%s" % cast_members)
1857  if genres != '':
1858  print(u"Genres:%s" % genres)
1859  print(u"Runtime:%s" % series_data[u'runtime'])
1860 
1861  # URL to TVDB web site episode web page for this series
1862  for url_data in [u'seriesid', u'seasonid', u'id']:
1863  if not url_data in available_keys:
1864  break
1865  else:
1866  results = series_data
1867  print(u'URL:http://www.thetvdb.com/?tab=episode&seriesid=%s&seasonid=%s&id=%s' %
1868  (results[season][episode][u'seriesid'],
1869  results[season][episode][u'seasonid'],
1870  results[season][episode][u'id']))
1871 # end Getseries_episode_data
1872 
1873 # Get Series Season and Episode numbers
1874 def Getseries_episode_numbers(t, opts, series_season_ep):
1875  def _episode_sort(episode):
1876  seasonnumber = 0
1877  episodenumber = 0
1878  try: seasonnumber = int(episode['seasonnumber'])
1879  except: pass
1880  try: episodenumber = int(episode['episodenumber'])
1881  except: pass
1882  return (-episode.distance, seasonnumber, episodenumber)
1883 
1884  global xmlFlag
1885  series_name=''
1886  ep_name=''
1887  series_sid = None
1888  key = series_season_ep[0].lower()
1889  if opts.configure != "" and key in override:
1890  if override[key][2]:
1891  series_sid=override[key][2] # Override series name
1892  SID = True
1893  else:
1894  series_name=override[key][0] # Override series name
1895  ep_name=series_season_ep[1]
1896  if len(override[series_season_ep[0].lower()][1]) != 0: # Are there search-replace strings?
1897  ep_name=massageEpisode_name(ep_name, series_season_ep)
1898  else:
1899  series_name=series_season_ep[0] # Leave the series name alone
1900  ep_name=series_season_ep[1] # Leave the episode name alone
1901 
1902  if series_sid:
1903  series = search_for_series(t, series_sid, opts.language)
1904  else:
1905  series = search_for_series(t, series_name, opts.language)
1906  season_ep_num = series.fuzzysearch(ep_name, 'episodename')
1907  if len(season_ep_num) != 0:
1908  for episode in sorted(season_ep_num, key=lambda ep: _episode_sort(ep), reverse=True):
1909 # if episode.distance == 0: # exact match
1910  if xmlFlag:
1911  # get more detailed episode info
1912  t.getDetailedEpisodeInfo(series['id'], episode['airedSeason'], episode)
1913  convert_series_to_xml(t, series_season_ep, season_ep_num)
1914  displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']])
1915  return 0
1916  print(season_and_episode_num.replace('\\n', '\n') %
1917  (int(episode['seasonnumber']), int(episode['episodenumber'])))
1918 # elif (episode['episodename'].lower()).startswith(ep_name.lower()):
1919 # if len(episode['episodename']) > (len(ep_name)+1):
1920 # if episode['episodename'][len(ep_name):len(ep_name)+2] != ' (':
1921 # continue # Skip episodes the are not part of a set of (1), (2) ... etc
1922 # if xmlFlag:
1923 # displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']])
1924 # sys.exit(0)
1925 # print(season_and_episode_num.replace('\\n', '\n') % (int(episode['seasonnumber']), int(episode['episodenumber'])))
1926 # end Getseries_episode_numbers
1927 
1928 # Set up a custom interface to get all series matching a partial series name
1929 class returnAllSeriesUI(tvdb_api.BaseUI):
1930  def __init__(self, config, log=None):
1931  self.config = config
1932  self.log = log
1933 
1934  def selectSeries(self, allSeries):
1935  return allSeries
1936 # ends returnAllSeriesUI
1937 
1938 def initialize_override_dictionary(useroptions, language):
1939  """ Change variables through a user supplied configuration file
1940  return False and exit the script if there are issues with the configuration file values
1941  """
1942  if useroptions[0]=='~':
1943  useroptions=os.path.expanduser("~")+useroptions[1:]
1944  if os.path.isfile(useroptions) == False:
1945  sys.stderr.write(
1946  "! The specified user configuration file (%s) is not a file\n" % useroptions
1947  )
1948  sys.exit(1)
1949  massage = {}
1950  overrides = {}
1951  overrides_id = {}
1952  if IS_PY2:
1953  cfg = ConfigParser.SafeConfigParser()
1954  else:
1955  cfg = ConfigParser.ConfigParser()
1956  cfg.read(useroptions)
1957 
1958  for section in cfg.sections():
1959  if section == 'regex':
1960  # Change variables per user config file
1961  for option in cfg.options(section):
1962  name_parse.append(re.compile(cfg.get(section, option)))
1963  continue
1964  if section =='ep_name_massage':
1965  for option in cfg.options(section):
1966  tmp =cfg.get(section, option).split(',')
1967  if len(tmp)%2 and len(cfg.get(section, option)) != 0:
1968  sys.stderr.write("! For (%s) 'ep_name_massage' values must be in pairs\n" % option)
1969  sys.exit(1)
1970  tmp_array=[]
1971  i=0
1972  while i != len(tmp):
1973  tmp_array.append([tmp[i].replace('"',''), tmp[i+1].replace('"','')])
1974  i+=2
1975  massage[option]=tmp_array
1976  continue
1977  if section =='series_name_override':
1978  for option in cfg.options(section):
1979  overrides[option] = cfg.get(section, option)
1980  tvdb = Tvdb(banners=False,
1981  debug = False,
1982  interactive = False,
1983  cache = cache_dir,
1984  custom_ui=returnAllSeriesUI,
1985  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
1986  username=tvdb_account.username,
1987  userkey=tvdb_account.account_identifier)
1988  tvdb.session.verify = False
1989  for key in overrides.keys():
1990  sid = overrides[key]
1991  if len(sid) == 0:
1992  continue
1993  try: # Check that the SID (Series id) is numeric
1994  dummy = int(sid)
1995  except:
1996  sys.stdout.write("! Series (%s) Invalid SID (not numeric) [%s] in config file\n" % (key, sid))
1997  sys.exit(1)
1998  # Make sure that the series name is not empty or all blanks
1999  if len(key.replace(' ','')) == 0:
2000  sys.stdout.write("! Invalid Series name (must have some non-blank characters) [%s] in config file\n" % key)
2001  print(overrides.keys())
2002  sys.exit(1)
2003 
2004  try:
2005  series_name_sid=tvdb.series_by_sid(sid, language)
2006  except:
2007  sys.stdout.write("! Invalid Series (no matches found in thetvdb,com) (%s) sid (%s) in config file\n" % (key, sid))
2008  sys.exit(1)
2009  overrides[key]=unicode(series_name_sid.data[u'seriesName']) #.encode('utf-8')
2010  overrides_id[key] = sid
2011  continue
2012 
2013  for key in overrides.keys():
2014  override[key] = [overrides[key],[],overrides_id[key]]
2015 
2016  for key in massage.keys():
2017  if key in override:
2018  override[key][1]=massage[key]
2019  else:
2020  override[key]=[key, massage[key], None]
2021  return
2022 # END initialize_override_dictionary
2023 
2024 def convert_search_to_xml(t, allSeries):
2025  """
2026  Convert json to xml and set up tvdb_api object as other stuff expects
2027  :param t: tvdb_api object
2028  :param allSeries: json array of series
2029  :return: xml version of allseries
2030  """
2031  # Initialize XML display value to off
2032  t.xml = False
2033  def series_item_func(parent):
2034  if parent == "root":
2035  return "series"
2036  return "alias"
2037  xml = dicttoxml(allSeries, item_func=series_item_func, attr_type=False)
2038  t.searchTree = eTree.XML(xml)
2039  t.seriesInfoTree = None
2040  t.epInfoTree = None
2041  t.actorsInfoTree = None
2042  t.imagesInfoTree = None
2043  t.baseXsltDir = xslt.baseXsltPath
2044 
2045 def convert_series_to_xml(t, series_season_ep, ep_info):
2046  """
2047  Convert json to xml and set up tvdb_api object as other stuff expects
2048  :param t: tvdb_api object
2049  :param ep_info: json array of series
2050  """
2051  # Initialize XML display value to off
2052  t.xml = False
2053  def series_ep_item_func(parent):
2054  if parent == "data":
2055  return "series"
2056  if parent == "_banners_raw":
2057  return "banner"
2058  if parent == "_actors":
2059  return "actor"
2060  return "item"
2061  def series_people_item_func(parent):
2062  if parent == "Actors":
2063  return "Actor"
2064  return "item"
2065  def series_images_item_func(parent):
2066  if parent == "root":
2067  return "images"
2068  return "Banner"
2069  for show_id in t.shows.keys():
2070  break
2071  # sort the cast into sort order
2072  t.shows[show_id].data['_actors'] = sorted(t.shows[show_id].data['_actors'], key=lambda k: k['sortOrder'])
2073  t.searchTree = None
2074  t.seriesInfoTree = None
2075  t.epInfoTree = None
2076  t.actorsInfoTree = None
2077  t.imagesInfoTree = None
2078  sxml = dicttoxml(t.shows[show_id].data, custom_root='series', item_func=series_ep_item_func, attr_type=False)
2079  exml = dicttoxml(t.shows[show_id], custom_root='data', item_func=series_ep_item_func, attr_type=False)
2080  t.seriesInfoTree = eTree.XML(exml)
2081  t.seriesInfoTree.append(eTree.XML(sxml))
2082  t.baseXsltDir = xslt.baseXsltPath
2083 
2084 def initializeXslt(language):
2085  ''' Initalize all data and functions for XSLT stylesheet processing
2086  return nothing
2087  '''
2088  global xslt, tvdbXpath
2089  try:
2090  import MythTV.ttvdb.tvdbXslt as tvdbXslt
2091  except Exception as errmsg:
2092  sys.stderr.write('! Error: Importing tvdbXslt error(%s)\n' % errmsg)
2093  sys.exit(1)
2094 
2095  xslt = tvdbXslt.xpathFunctions()
2096  xslt.language = language
2097  xslt.buildFuncDict()
2098  xslt.baseXsltPath = tvdbXslt.baseXsltDir
2099  tvdbXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythTV_Universal_Metadata_Format')
2100  tvdbXpath.prefix = 'tvdbXpath'
2101  for key in xslt.FuncDict.keys():
2102  tvdbXpath[key] = xslt.FuncDict[key]
2103  return
2104 # end initializeXslt()
2105 
2106 def displaySearchXML(tvdb_api):
2107  '''Using a XSLT style sheet translate TVDB search results into the MythTV Universal Query format
2108  return nothing
2109  '''
2110  global xslt, tvdbXpath
2111 
2112  # Remove duplicates when a non-English language code is specified
2113  if xslt.language != 'en':
2114  compareFilter = etree.XPath('//id[text()=$Id]')
2115  idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
2116  tmpTree = deepcopy(tvdb_api.searchTree)
2117  for seriesId in tmpTree.xpath('//Series/id/text()'):
2118  if len(compareFilter(tvdb_api.searchTree, Id=seriesId)) > 1:
2119  tmpList = idLangFilter(tvdb_api.searchTree, Id=seriesId)
2120  if len(tmpList):
2121  tvdb_api.searchTree.remove(tmpList[0])
2122 
2123  tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbQuery.xsl')))
2124  items = tvdbQueryXslt(tvdb_api.searchTree)
2125  if items.getroot() is not None:
2126  if len(items.xpath('//item')):
2127  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2128  return 0
2129 # end displaySearchXML()
2130 
2131 def displaySeriesXML(tvdb_api, series_season_ep):
2132  '''Using a XSLT style sheet translate TVDB Series data results into the
2133  MythTV Universal Query format
2134  return nothing
2135  '''
2136  global xslt, tvdbXpath
2137  allDataElement = etree.XML(u'<allData></allData>')
2138  requestDetails = etree.XML(u'<requestDetails></requestDetails>')
2139  requestDetails.attrib['lang'] = xslt.language
2140  requestDetails.attrib['series'] = series_season_ep[0]
2141  requestDetails.attrib['season'] = series_season_ep[1]
2142  requestDetails.attrib['episode'] = series_season_ep[2]
2143  allDataElement.append(requestDetails)
2144 
2145  # Combine the various XML inputs into a single XML element and send to the XSLT stylesheet
2146  allDataElement.append(tvdb_api.seriesInfoTree)
2147 
2148  tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbVideo.xsl')))
2149  items = tvdbQueryXslt(allDataElement)
2150  if items.getroot() is not None:
2151  if len(items.xpath('//item')):
2152  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2153  return 0
2154 # end displaySeriesXML()
2155 
2156 def displayCollectionXML(tvdb_api):
2157  '''Using a XSLT style sheet translate TVDB series results into the MythTV Universal Query format
2158  return nothing
2159  '''
2160  global xslt, tvdbXpath
2161 
2162  # Remove duplicates when non-English language code is specified
2163  if xslt.language != 'en':
2164  compareFilter = etree.XPath('//id[text()=$Id]')
2165  idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
2166  tmpTree = deepcopy(tvdb_api.seriesInfoTree)
2167  for seriesId in tmpTree.xpath('//Series/id/text()'):
2168  if len(compareFilter(tvdb_api.seriesInfoTree, Id=seriesId)) > 1:
2169  tmpList = idLangFilter(tvdb_api.seriesInfoTree, Id=seriesId)
2170  if len(tmpList):
2171  tvdb_api.seriesInfoTree.remove(tmpList[0])
2172 
2173  tvdbCollectionXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbCollection.xsl')))
2174  items = tvdbCollectionXslt(tvdb_api.seriesInfoTree)
2175  if items.getroot() is not None:
2176  if len(items.xpath('//item')):
2177  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2178  return 0
2179 # end displayCollectionXML()
2180 
2181 
2182 def doc_test(opts):
2183  import doctest
2184 
2185  if not IS_PY2:
2186  # python 3 doctest capture when teh output is utf8 (bytes)
2187  # convert it back to str
2188  if isinstance(sys.stdout, OutStreamEncoder):
2189  sys.stdout = sys.stdout.out
2190  sys.stderr = sys.stderr.out
2191 
2192  class SpoofDocTestWriter(doctest._SpoofOut):
2193  """Wraps a stream with an decoder"""
2194  def __init__(self, *cwargs, **kwargs):
2195  super(SpoofDocTestWriter, self).__init__(*cwargs, **kwargs)
2196 
2197  def write(self, obj):
2198  """Wraps the output stream, encoding Unicode strings with the specified encoding"""
2199  if isinstance(obj, bytes):
2200  obj = obj.decode('utf-8')
2201  return super(SpoofDocTestWriter, self).write(obj)
2202 
2203  def __getattr__(self, attr):
2204  """Delegate everything but write to the stream"""
2205  return getattr(self.out, attr)
2206 
2207  # replace _SpoofOut with our massager
2208  doctest._SpoofOut = SpoofDocTestWriter
2209  return doctest.testmod(verbose=opts.debug, optionflags=doctest.ELLIPSIS, )
2210 
2211 def main():
2212  global season_and_episode_num, screenshot_request
2213  # reset some globals for doctest mode
2214  screenshot_request = False
2215 
2216  parser = OptionParser(usage=u"%prog usage: ttvdb -hdruviomMPFBDS [parameters]\n <series name or 'series and season number' or 'series and season number and episode number'>\n\nFor details on using ttvdb with Mythvideo see the ttvdb wiki page at:\nhttp://www.mythtv.org/wiki/Ttvdb.py")
2217 
2218  parser.add_option( "--doctest", action="store_true", default=False, dest="doctest",
2219  help=u"Run doctests")
2220  parser.add_option( "-d", "--debug", action="store_true", default=False, dest="debug",
2221  help=u"Show debugging info")
2222  parser.add_option( "-r", "--raw", action="store_true",default=False, dest="raw",
2223  help=u"Dump raw data only")
2224  parser.add_option( "-u", "--usage", action="store_true", default=False, dest="usage",
2225  help=u"Display examples for executing the ttvdb script")
2226  parser.add_option( "-v", "--version", action="store_true", default=False, dest="version",
2227  help=u"Display version and author")
2228  parser.add_option( "-i", "--interactive", action="store_true", default=False, dest="interactive",
2229  help=u"Interaction mode (allows selection of a specific Series)")
2230  parser.add_option( "-c", "--configure", metavar="FILE", default="", dest="configure",
2231  help=u"Use configuration settings")
2232  parser.add_option( "-l", "--language", metavar="LANGUAGE", default=u'en', dest="language",
2233  help=u"Select data that matches the specified language fall back to english if nothing found (e.g. 'es' Español, 'de' Deutsch ... etc)")
2234  parser.add_option( "-a", "--area", metavar="COUNTRY", default=u'', dest="country",
2235  help=u"Select data that matches the specificed country (e.g. 'de' Germany 'gb' UK). This option is currently not used.")
2236  parser.add_option( "-n", "--num_seasons", action="store_true", default=False, dest="num_seasons",
2237  help=u"Return the season numbers for a series")
2238  parser.add_option( "-t", action="store_true", default=False, dest="test",
2239  help=u"Test for the availability of runtime dependencies")
2240  parser.add_option( "-m", "--mythvideo", action="store_true", default=False, dest="mythvideo",
2241  help=u"Conform to mythvideo standards when processing -M, -P, -F and -D")
2242  parser.add_option( "-M", "--list", action="store_true", default=False, dest="list",
2243  help=u"Get matching TV Series list")
2244  parser.add_option( "-P", "--poster", action="store_true", default=False, dest="poster",
2245  help=u"Get Series Poster URL(s)")
2246  parser.add_option( "-F", "--fanart", action="store_true", default=False, dest="fanart",
2247  help=u"Get Series fan art URL(s)")
2248  parser.add_option( "-B", "--backdrop", action="store_true", default=False, dest="banner",
2249  help=u"Get Series banner/backdrop URL(s)")
2250  parser.add_option( "-S", "--screenshot", action="store_true", default=False, dest="screenshot",
2251  help=u"Get Series episode screenshot URL")
2252  parser.add_option( "-D", "--data", action="store_true", default=False, dest="data",
2253  help=u"Get Series episode data")
2254  parser.add_option( "-N", "--numbers", action="store_true", default=False, dest="numbers",
2255  help=u"Get Season and Episode numbers")
2256  parser.add_option( "-C", "--collection", action="store_true", default=False, dest="collection",
2257  help=u'Get a TV Series (collection) "series" level information')
2258 
2259  opts, series_season_ep = parser.parse_args()
2260 
2261  if opts.doctest:
2262  return doc_test(opts)
2263 
2264  # Test mode, if we've made it here, everything is ok
2265  if opts.test:
2266  print("Everything appears to be in order")
2267  return 0
2268 
2269  # Make everything unicode utf8
2270  if IS_PY2:
2271  for index in range(len(series_season_ep)):
2272  series_season_ep[index] = unicode(series_season_ep[index], 'utf8')
2273 
2274  if opts.debug == True:
2275  print("opts", opts)
2276  print("\nargs", series_season_ep)
2277 
2278  # Process version command line requests
2279  if opts.version == True:
2280  version = etree.XML(u'<grabber></grabber>')
2281  etree.SubElement(version, "name").text = __title__
2282  etree.SubElement(version, "author").text = __author__
2283  etree.SubElement(version, "thumbnail").text = 'ttvdb.png'
2284  etree.SubElement(version, "command").text = 'ttvdb.py'
2285  etree.SubElement(version, "type").text = 'television'
2286  etree.SubElement(version, "description").text = 'Search and metadata downloads for thetvdb.com'
2287  etree.SubElement(version, "version").text = __version__
2288  sys.stdout.write(etree.tostring(version, encoding='UTF-8', pretty_print=True))
2289  return 0
2290 
2291  # Process usage command line requests
2292  if opts.usage == True:
2293  sys.stdout.write(usage_txt)
2294  return 0
2295 
2296  if len(series_season_ep) == 0:
2297  parser.error("! No series or series season episode supplied")
2298  return 1
2299 
2300  # Default output format of season and episode numbers
2301  season_and_episode_num='S%02dE%02d' # Format output example "S04E12"
2302 
2303  if opts.numbers == False:
2304  if len(series_season_ep) > 1:
2305  if not _can_int(series_season_ep[1]):
2306  parser.error("! Season is not numeric")
2307  return 1
2308  if len(series_season_ep) > 2:
2309  if not _can_int(series_season_ep[2]):
2310  parser.error("! Episode is not numeric")
2311  return 1
2312  else:
2313  if len(series_season_ep) < 2:
2314  parser.error("! An Episode name must be included")
2315  return 1
2316  if len(series_season_ep) == 3:
2317  season_and_episode_num = series_season_ep[2] # Override default output format
2318 
2319  if opts.screenshot:
2320  if len(series_season_ep) > 1:
2321  if not _can_int(series_season_ep[1]):
2322  parser.error("! Season is not numeric")
2323  return 1
2324  if len(series_season_ep) > 2:
2325  if not _can_int(series_season_ep[2]):
2326  parser.error("! Episode is not numeric")
2327  return 1
2328  if not len(series_season_ep) > 2:
2329  parser.error("! Option (-S), episode screenshot search requires Season and Episode numbers")
2330  return 1
2331  screenshot_request = True
2332 
2333  if opts.debug == True:
2334  print(series_season_ep)
2335 
2336  if opts.debug == True:
2337  print("#"*20)
2338  print("# series_season_ep array(",series_season_ep,")")
2339 
2340  if opts.debug == True:
2341  print("#"*20)
2342  print("# Starting tvtvb")
2343  print("# Processing (%s) Series" % ( series_season_ep[0] ))
2344 
2345  # List of language from http://www.thetvdb.com/api/0629B785CE550C8D/languages.xml
2346  # Hard-coded here as it is realtively static, and saves another HTTP request, as
2347  # recommended on http://thetvdb.com/wiki/index.php/API:languages.xml
2348  valid_languages = ["da", "fi", "nl", "de", "it", "es", "fr","pl", "hu","el","tr", "ru","he","ja","pt","zh","cs","sl", "hr","ko","en","sv","no"]
2349 
2350  # Validate language as specified by user
2351  if not opts.language in valid_languages:
2352  opts.language = 'en' # Set the default to English when an invalid language was specified
2353 
2354  # Set XML to be the default display mode for -N, -M, -D, -C
2355  opts.xml = True
2356  initializeXslt(opts.language)
2357 
2358  # Access thetvdb.com API with banners (Posters, Fanart, banners, screenshots) data retrieval enabled
2359  if opts.list ==True:
2360  t = Tvdb(banners=False,
2361  debug = opts.debug,
2362  cache = cache_dir,
2363  custom_ui=returnAllSeriesUI,
2364  language = opts.language,
2365  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2366  username=tvdb_account.username,
2367  userkey=tvdb_account.account_identifier)
2368  if opts.xml:
2369  t.xml = True
2370  elif opts.interactive == True:
2371  t = Tvdb(banners=True,
2372  debug=opts.debug,
2373  interactive=True,
2374  select_first=False,
2375  cache=cache_dir,
2376  actors = True,
2377  language = opts.language,
2378  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2379  username=tvdb_account.username,
2380  userkey=tvdb_account.account_identifier)
2381  if opts.xml:
2382  t.xml = True
2383  else:
2384  t = Tvdb(banners=True,
2385  debug = opts.debug,
2386  cache = cache_dir,
2387  actors = True,
2388  language = opts.language,
2389  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2390  username=tvdb_account.username,
2391  userkey=tvdb_account.account_identifier)
2392  if opts.xml:
2393  t.xml = True
2394 
2395  # disable certificate check
2396  t.session.verify = False
2397 
2398  # Determine if there is a SID or a series name to search with
2399  global SID
2400  SID = False
2401  if _can_int(series_season_ep[0]): # if it is numeric then assume it is a series ID number
2402  SID = True
2403  else:
2404  SID = False
2405 
2406  # The -C collections options only supports a SID as input
2407  if opts.collection:
2408  if SID:
2409  pass
2410  else:
2411  parser.error("! Option (-C), collection requires an inetref number")
2412  return 1
2413 
2414  if opts.debug == True:
2415  print("# ..got tvdb mirrors")
2416  print("# Start to process series or series_season_ep")
2417  print("#"*20)
2418 
2419  global override
2420  override={} # Initialize series name override dictionary
2421  # If the user wants Series name overrides and a override file exists then create an overide dictionary
2422  if opts.configure != '': # Did the user want to override the default config file name/location
2423  if opts.configure[0]=='~':
2424  opts.configure=os.path.expanduser("~")+opts.configure[1:]
2425  if os.path.exists(opts.configure) == 1: # Do overrides exist?
2426  initialize_override_dictionary(opts.configure, opts.language)
2427  else:
2428  sys.stderr.write("! The specified override file (%s) does not exist\n" % opts.configure)
2429  return 1
2430  else: # Check if there is a default configuration file
2431  default_config = u"%s/%s" % (os.path.expanduser(u"~"), u".mythtv/ttvdb.conf")
2432  if os.path.isfile(default_config):
2433  opts.configure = default_config
2434  initialize_override_dictionary(opts.configure, opts.language)
2435 
2436  if len(override) == 0:
2437  opts.configure = False # Turn off the override option as there is nothing to override
2438 
2439  # Check if a video name was passed and if so parse it for series name, season and episode numbers
2440  if not opts.collection and len(series_season_ep) == 1:
2441  for r in name_parse:
2442  match = r.match(series_season_ep[0])
2443  if match:
2444  seriesname, seasno, epno = match.groups()
2445  #remove ._- characters from name (- removed only if next to end of line)
2446  seriesname = re.sub("[\._]|\-(?=$)", " ", seriesname).strip()
2447  series_season_ep = [seriesname, seasno, epno]
2448  break # Matched - to the next file!
2449 
2450  # Fetch a list of matching series names
2451  if opts.list ==True:
2452  try:
2453  key = series_season_ep[0].lower()
2454  if opts.configure != "" and key in override:
2455  if override[key][2]:
2456  allSeries = t._getSeries(override[key][2], True)
2457  else:
2458  allSeries = t._getSeries(override[key][0])
2459  else:
2460  allSeries=t._getSeries(series_season_ep[0])
2461  except tvdb_shownotfound:
2462  return 0 # No matching series
2463  except Exception as e:
2464  sys.stderr.write("! Error: %s\n" % (e))
2465  raise
2466  return 1 # Most likely a communications error
2467  if opts.xml:
2468  convert_search_to_xml(t, allSeries)
2469  displaySearchXML(t)
2470  return 0
2471  match_list = []
2472  for series_name_sid in allSeries: # list search results
2473  key_value = u"%s:%s" % (series_name_sid['sid'], series_name_sid['name'])
2474  if not key_value in match_list: # Do not add duplicates
2475  match_list.append(key_value)
2476  print(key_value)
2477  return 0 # The Series list option (-M) is the only option honoured when used
2478 
2479  # Fetch TV series collection information
2480  if opts.collection:
2481  try:
2482  t._getShowData(series_season_ep[0], opts.language)
2483  except tvdb_shownotfound:
2484  return 0 # No matching series
2485  except Exception as e:
2486  sys.stderr.write("! Error: %s\n" % (e))
2487  raise
2488  return 1 # Most likely a communications error
2489  convert_series_to_xml(t, series_season_ep, None)
2491  return 0 # The TV Series collection option (-C) is the only option honoured when used
2492 
2493  # Verify that thetvdb.com has the desired series_season_ep.
2494  # Exit this module if series_season_ep is not found
2495  if opts.numbers == False and opts.num_seasons == False:
2496  seriesfound=searchseries(t, opts, series_season_ep)
2497  x=1
2498  else:
2499  x=[]
2500  x.append(series_season_ep[0]) # Only use series name in check
2501  seriesfound=searchseries(t, opts, x)
2502 
2503  # Return the season numbers for a series
2504  if opts.num_seasons == True:
2505  season_numbers=''
2506  for x in sorted(seriesfound.keys()):
2507  season_numbers+='%d,' % x
2508  print(season_numbers[:-1])
2509  return 0 # Option (-n) is the only option honoured when used
2510 
2511  # Dump information accessible for a Series and ONLY first season of episoded data
2512  if opts.debug == True:
2513  print("#"*20)
2514  print("# Starting Raw keys call")
2515  print("Lvl #1:") # Seasons for series
2516  x = t[series_season_ep[0]].keys()
2517  print(t[series_season_ep[0]].keys())
2518  print("#"*20)
2519  print("Lvl #2:") # Episodes for each season
2520  for y in x:
2521  print(t[series_season_ep[0]][y].keys())
2522  print("#"*20)
2523  print("Lvl #3:") # Keys for each episode within the 1st season
2524  z = t[series_season_ep[0]][1].keys()
2525  for aa in z:
2526  print(t[series_season_ep[0]][1][aa].keys())
2527  print("#"*20)
2528  print("Lvl #4:") # Available data for each episode in 1st season
2529  for aa in z:
2530  codes = t[series_season_ep[0]][1][aa].keys()
2531  print("\n\nStart:")
2532  for c in codes:
2533  print("="*50)
2534  print('Key Name=('+c+'):')
2535  print(t[series_season_ep[0]][1][aa][c])
2536  print("="*50)
2537  print("#"*20)
2538  sys.exit (True)
2539 
2540  if opts.numbers == True: # Fetch and output season and episode numbers
2541  global xmlFlag
2542  if opts.xml:
2543  xmlFlag = True
2544  else:
2545  xmlFlag = False
2546  Getseries_episode_numbers(t, opts, series_season_ep)
2547  return 0 # The Numbers option (-N) is the only option honoured when used
2548 
2549  if opts.data or screenshot_request: # Fetch and output episode data
2550  if opts.mythvideo:
2551  if len(series_season_ep) != 3:
2552  print(u"Season and Episode numbers required.")
2553  else:
2554  if opts.xml:
2555  t.getDetailedEpisodeInfo(seriesfound[u'id'], series_season_ep[1], series_season_ep[2])
2556  convert_series_to_xml(t, series_season_ep, seriesfound)
2557  displaySeriesXML(t, series_season_ep)
2558  return 0
2559  Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
2560  else:
2561  if opts.xml and len(series_season_ep) == 3:
2562  t.getDetailedEpisodeInfo(list(t.shows.values())[0].data['id'], series_season_ep[1], series_season_ep[2])
2563  convert_series_to_xml(t, series_season_ep, seriesfound)
2564  displaySeriesXML(t, series_season_ep)
2565  return 0
2566  Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
2567 
2568  # Fetch the requested graphics URL(s)
2569  if opts.debug == True:
2570  print("#"*20)
2571  print("# Checking if Posters, Fanart or Banners are available")
2572  print("#"*20)
2573 
2574  key = series_season_ep[0].lower()
2575  if opts.configure != "" and key in override:
2576  series_info = search_for_series(t, override[key][0], opts.language)
2577  else:
2578  series_info = search_for_series(t, series_season_ep[0], opts.language)
2579  try:
2580  banners_keys = series_info['_banners'].keys()
2581  except:
2582  banners_keys = None
2583 
2584  banner= False
2585  poster= False
2586  fanart= False
2587 
2588  if banners_keys:
2589  for x in banners_keys: # Determine what type of graphics is available
2590  if x == fanart_key:
2591  fanart=True
2592  elif x == poster_key:
2593  poster=True
2594  elif x == season_key or x == banner_key:
2595  banner=True
2596 
2597  # Make sure that some graphics URL(s) (Posters, FanArt or Banners) are available
2598  if ( fanart!=True and poster!=True and banner!=True ):
2599  return 0
2600 
2601  if opts.debug == True:
2602  print("#"*20)
2603  print("# One or more of Posters, Fanart or Banners are available")
2604  print("#"*20)
2605 
2606  # Determine if graphic URL identification output is required
2607  if opts.data: # Along with episode data get all graphics
2608  opts.poster = True
2609  opts.fanart = True
2610  opts.banner = True
2611  single_option = True
2612  fanart, banner, poster = (True, True, True)
2613  else:
2614  y=0
2615  single_option=True
2616  if opts.poster==True:
2617  y+=1
2618  if opts.fanart==True:
2619  y+=1
2620  if opts.banner==True:
2621  y+=1
2622 
2623  if (poster==True and opts.poster==True and opts.raw!=True): # Get posters and send to stdout
2624  season_poster_found = False
2625  if opts.mythvideo:
2626  if len(series_season_ep) < 2:
2627  print(u"Season and Episode numbers required.")
2628  return 0
2629  all_posters = u'Coverart:'
2630  all_empty = len(all_posters)
2631  for p in get_graphics(t, opts, series_season_ep, poster_type, single_option, opts.language):
2632  all_posters = all_posters+p+u','
2633  season_poster_found = True
2634  if season_poster_found == False: # If there were no season posters get the series top poster
2635  series_name=''
2636  key = series_season_ep[0].lower()
2637  if opts.configure != "" and key in override:
2638  series_name=override[key][0] # Override series name
2639  else:
2640  series_name=series_season_ep[0] # Leave the series name alone
2641  for p in get_graphics(t, opts, [series_name], poster_type, single_option, opts.language):
2642  all_posters = all_posters+p+u','
2643  if len(all_posters) > all_empty:
2644  if all_posters[-1] == u',':
2645  print(all_posters[:-1])
2646  else:
2647  print(all_posters)
2648 
2649  if (fanart==True and opts.fanart==True and opts.raw!=True): # Get Fan Art and send to stdout
2650  all_fanart = u'Fanart:'
2651  all_empty = len(all_fanart)
2652  for f in get_graphics(t, opts, series_season_ep, fanart_type, single_option, opts.language):
2653  all_fanart = all_fanart+f+u','
2654  if len(all_fanart) > all_empty:
2655  if all_fanart[-1] == u',':
2656  print(all_fanart[:-1])
2657  else:
2658  print(all_fanart)
2659 
2660  if (banner==True and opts.banner==True and opts.raw!=True): # Also change to get ALL Series graphics
2661  season_banner_found = False
2662  if opts.mythvideo:
2663  if len(series_season_ep) < 2:
2664  print(u"Season and Episode numbers required.")
2665  return 0
2666  all_banners = u'Banner:'
2667  all_empty = len(all_banners)
2668  for b in get_graphics(t, opts, series_season_ep, banner_type, single_option, opts.language):
2669  all_banners = all_banners+b+u','
2670  season_banner_found = True
2671  if not season_banner_found: # If there were no season banner get the series top banner
2672  series_name=''
2673  key = series_season_ep[0].lower()
2674  if opts.configure != "" and key in override:
2675  series_name=override[key][0] # Override series name
2676  else:
2677  series_name=series_season_ep[0] # Leave the series name alone
2678  for b in get_graphics(t, opts, [series_name], banner_type, single_option, opts.language):
2679  all_banners = all_banners+b+u','
2680  if len(all_banners) > all_empty:
2681  if all_banners[-1] == u',':
2682  print(all_banners[:-1])
2683  else:
2684  print(all_banners)
2685 
2686  if opts.debug == True:
2687  print("#"*20)
2688  print("# Processing complete")
2689  print("#"*20)
2690  return 0
2691 #end main
2692 
2693 if __name__ == "__main__":
2694  sys.exit(main())
ttvdb.search_for_series
def search_for_series(tvdb, sid_or_name, language)
Definition: ttvdb.py:1510
ttvdb.main
def main()
Definition: ttvdb.py:2211
ttvdb.OutStreamEncoder
Definition: ttvdb.py:1403
mythburn.write
def write(text, progress=True)
Definition: mythburn.py:308
ttvdb.searchseries
def searchseries(t, opts, series_season_ep)
Definition: ttvdb.py:1519
ttvdb.convert_series_to_xml
def convert_series_to_xml(t, series_season_ep, ep_info)
Definition: ttvdb.py:2045
ttvdb.massageEpisode_name
def massageEpisode_name(ep_name, series_season_ep)
Definition: ttvdb.py:1677
ttvdb.change_amp
def change_amp(text)
Definition: ttvdb.py:1691
ttvdb.OutStreamEncoder.__getattr__
def __getattr__(self, attr)
Definition: ttvdb.py:1421
ttvdb.Episode.distance
distance
Definition: ttvdb.py:1465
ttvdb.change_to_commas
def change_to_commas(meta_data)
Definition: ttvdb.py:1684
ttvdb.OutStreamEncoder.write
def write(self, obj)
Definition: ttvdb.py:1412
ttvdb.Season
Definition: ttvdb.py:1440
ttvdb.displayCollectionXML
def displayCollectionXML(tvdb_api)
Definition: ttvdb.py:2156
ttvdb.OutStreamEncoder.out
out
Definition: ttvdb.py:1406
ttvdb.Season.fuzzysearch
def fuzzysearch(self, term=None, key=None)
Definition: ttvdb.py:1441
ttvdb.Getseries_episode_data
def Getseries_episode_data(t, opts, series_season_ep, language=None)
Definition: ttvdb.py:1716
ttvdb.OutStreamEncoder.encoding
encoding
Definition: ttvdb.py:1408
ttvdb.returnAllSeriesUI
Definition: ttvdb.py:1929
ttvdb.get_graphics
def get_graphics(t, opts, series_season_ep, graphics_type, single_option, language=False)
Definition: ttvdb.py:1572
print
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)
Definition: vbi608extractor.cpp:29
ttvdb.doc_test
def doc_test(opts)
Definition: ttvdb.py:2182
ttvdb.unicode
unicode
Definition: ttvdb.py:1309
ttvdb.returnAllSeriesUI.selectSeries
def selectSeries(self, allSeries)
Definition: ttvdb.py:1934
ttvdb.initializeXslt
def initializeXslt(language)
Definition: ttvdb.py:2084
ttvdb.initialize_override_dictionary
def initialize_override_dictionary(useroptions, language)
Definition: ttvdb.py:1938
ttvdb.Tvdb
Definition: ttvdb.py:1479
ttvdb.Episode._re_strippart
_re_strippart
Definition: ttvdb.py:1452
ttvdb.Tvdb._setItem
def _setItem(self, sid, seas, ep, attrib, value)
Definition: ttvdb.py:1491
ttvdb.tvdb_account
Definition: ttvdb.py:1251
ttvdb.Episode
Definition: ttvdb.py:1451
ttvdb.displaySearchXML
def displaySearchXML(tvdb_api)
Definition: ttvdb.py:2106
ttvdb.Tvdb.series_by_sid
def series_by_sid(self, sid, language)
Definition: ttvdb.py:1480
ttvdb.OutStreamEncoder.__init__
def __init__(self, outstream, encoding=None)
Definition: ttvdb.py:1405
ttvdb.returnAllSeriesUI.__init__
def __init__(self, config, log=None)
Definition: ttvdb.py:1930
ttvdb.returnAllSeriesUI.config
config
Definition: ttvdb.py:1931
return
return
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1250
ttvdb._can_int
def _can_int(x)
Definition: ttvdb.py:1388
ttvdb.Show
Definition: ttvdb.py:1429
ttvdb.displaySeriesXML
def displaySeriesXML(tvdb_api, series_season_ep)
Definition: ttvdb.py:2131
ttvdb.Show.fuzzysearch
def fuzzysearch(self, term=None, key=None)
Definition: ttvdb.py:1430
ttvdb.returnAllSeriesUI.log
log
Definition: ttvdb.py:1932
ttvdb.Episode.fuzzysearch
def fuzzysearch(self, term=None, key=None)
Definition: ttvdb.py:1453
ttvdb.make_db_ready
def make_db_ready(text)
Definition: ttvdb.py:1702
ttvdb.Tvdb._setShowData
def _setShowData(self, sid, key, value)
Definition: ttvdb.py:1502
ttvdb.convert_search_to_xml
def convert_search_to_xml(t, allSeries)
Definition: ttvdb.py:2024
ttvdb.Getseries_episode_numbers
def Getseries_episode_numbers(t, opts, series_season_ep)
Definition: ttvdb.py:1874