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 
2072  # dict for 'data['_banners']['poster']['raw'] must exist for fetching coverarts,
2073  # check with ttvdb.py -l de -a CH -D 89901 36 4
2074  try:
2075  if 'poster' not in t.shows[show_id].data['_banners'].keys():
2076  t.shows[show_id].data['_banners']['poster'] = {}
2077  t.shows[show_id].data['_banners']['poster']['raw'] = {}
2078  except KeyError:
2079  # no banner fanart exists
2080  pass
2081 
2082  # sort the cast into sort order
2083  t.shows[show_id].data['_actors'] = sorted(t.shows[show_id].data['_actors'], key=lambda k: k['sortOrder'])
2084  t.searchTree = None
2085  t.seriesInfoTree = None
2086  t.epInfoTree = None
2087  t.actorsInfoTree = None
2088  t.imagesInfoTree = None
2089  sxml = dicttoxml(t.shows[show_id].data, custom_root='series', item_func=series_ep_item_func, attr_type=False)
2090  exml = dicttoxml(t.shows[show_id], custom_root='data', item_func=series_ep_item_func, attr_type=False)
2091  t.seriesInfoTree = eTree.XML(exml)
2092  t.seriesInfoTree.append(eTree.XML(sxml))
2093  t.baseXsltDir = xslt.baseXsltPath
2094 
2095 def initializeXslt(language):
2096  ''' Initalize all data and functions for XSLT stylesheet processing
2097  return nothing
2098  '''
2099  global xslt, tvdbXpath
2100  try:
2101  import MythTV.ttvdb.tvdbXslt as tvdbXslt
2102  except Exception as errmsg:
2103  sys.stderr.write('! Error: Importing tvdbXslt error(%s)\n' % errmsg)
2104  sys.exit(1)
2105 
2106  xslt = tvdbXslt.xpathFunctions()
2107  xslt.language = language
2108  xslt.buildFuncDict()
2109  xslt.baseXsltPath = tvdbXslt.baseXsltDir
2110  tvdbXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythTV_Universal_Metadata_Format')
2111  tvdbXpath.prefix = 'tvdbXpath'
2112  for key in xslt.FuncDict.keys():
2113  tvdbXpath[key] = xslt.FuncDict[key]
2114  return
2115 # end initializeXslt()
2116 
2117 def displaySearchXML(tvdb_api):
2118  '''Using a XSLT style sheet translate TVDB search results into the MythTV Universal Query format
2119  return nothing
2120  '''
2121  global xslt, tvdbXpath
2122 
2123  # Remove duplicates when a non-English language code is specified
2124  if xslt.language != 'en':
2125  compareFilter = etree.XPath('//id[text()=$Id]')
2126  idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
2127  tmpTree = deepcopy(tvdb_api.searchTree)
2128  for seriesId in tmpTree.xpath('//Series/id/text()'):
2129  if len(compareFilter(tvdb_api.searchTree, Id=seriesId)) > 1:
2130  tmpList = idLangFilter(tvdb_api.searchTree, Id=seriesId)
2131  if len(tmpList):
2132  tvdb_api.searchTree.remove(tmpList[0])
2133 
2134  tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbQuery.xsl')))
2135  items = tvdbQueryXslt(tvdb_api.searchTree)
2136  if items.getroot() is not None:
2137  if len(items.xpath('//item')):
2138  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2139  return 0
2140 # end displaySearchXML()
2141 
2142 def displaySeriesXML(tvdb_api, series_season_ep):
2143  '''Using a XSLT style sheet translate TVDB Series data results into the
2144  MythTV Universal Query format
2145  return nothing
2146  '''
2147  global xslt, tvdbXpath
2148  allDataElement = etree.XML(u'<allData></allData>')
2149  requestDetails = etree.XML(u'<requestDetails></requestDetails>')
2150  requestDetails.attrib['lang'] = xslt.language
2151  requestDetails.attrib['series'] = series_season_ep[0]
2152  requestDetails.attrib['season'] = series_season_ep[1]
2153  requestDetails.attrib['episode'] = series_season_ep[2]
2154  allDataElement.append(requestDetails)
2155 
2156  # Combine the various XML inputs into a single XML element and send to the XSLT stylesheet
2157  allDataElement.append(tvdb_api.seriesInfoTree)
2158 
2159  tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbVideo.xsl')))
2160  items = tvdbQueryXslt(allDataElement)
2161 
2162  # temporary fix for missing coverart: use global coverart from series
2163  if len(items.xpath("//image[@type='coverart']")) == 0:
2164  for el in allDataElement.iter("series"):
2165  glob_poster = el.find("poster")
2166  if glob_poster is not None and glob_poster.text != 'http://thetvdb.com/banners/':
2167  glob_url = glob_poster.text
2168  glob_thumb = glob_url.replace("posters", "_cache/posters")
2169  glob_coverart = etree.Element("image", type = "coverart", url = glob_url, thumb = glob_thumb)
2170  image_items = items.find("item").find("images")
2171  if image_items is None:
2172  etree.SubElement(items.find("item"), "images")
2173  items.find("item").find("images").append(glob_coverart)
2174  break
2175 
2176  if items.getroot() is not None:
2177  if len(items.xpath('//item')):
2178  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2179  return 0
2180 # end displaySeriesXML()
2181 
2182 def displayCollectionXML(tvdb_api):
2183  '''Using a XSLT style sheet translate TVDB series results into the MythTV Universal Query format
2184  return nothing
2185  '''
2186  global xslt, tvdbXpath
2187 
2188  # Remove duplicates when non-English language code is specified
2189  if xslt.language != 'en':
2190  compareFilter = etree.XPath('//id[text()=$Id]')
2191  idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
2192  tmpTree = deepcopy(tvdb_api.seriesInfoTree)
2193  for seriesId in tmpTree.xpath('//Series/id/text()'):
2194  if len(compareFilter(tvdb_api.seriesInfoTree, Id=seriesId)) > 1:
2195  tmpList = idLangFilter(tvdb_api.seriesInfoTree, Id=seriesId)
2196  if len(tmpList):
2197  tvdb_api.seriesInfoTree.remove(tmpList[0])
2198 
2199  tvdbCollectionXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbCollection.xsl')))
2200  items = tvdbCollectionXslt(tvdb_api.seriesInfoTree)
2201  if items.getroot() is not None:
2202  if len(items.xpath('//item')):
2203  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2204  return 0
2205 # end displayCollectionXML()
2206 
2207 
2208 def doc_test(opts):
2209  import doctest
2210 
2211  if not IS_PY2:
2212  # python 3 doctest capture when teh output is utf8 (bytes)
2213  # convert it back to str
2214  if isinstance(sys.stdout, OutStreamEncoder):
2215  sys.stdout = sys.stdout.out
2216  sys.stderr = sys.stderr.out
2217 
2218  class SpoofDocTestWriter(doctest._SpoofOut):
2219  """Wraps a stream with an decoder"""
2220  def __init__(self, *cwargs, **kwargs):
2221  super(SpoofDocTestWriter, self).__init__(*cwargs, **kwargs)
2222 
2223  def write(self, obj):
2224  """Wraps the output stream, encoding Unicode strings with the specified encoding"""
2225  if isinstance(obj, bytes):
2226  obj = obj.decode('utf-8')
2227  return super(SpoofDocTestWriter, self).write(obj)
2228 
2229  def __getattr__(self, attr):
2230  """Delegate everything but write to the stream"""
2231  return getattr(self.out, attr)
2232 
2233  # replace _SpoofOut with our massager
2234  doctest._SpoofOut = SpoofDocTestWriter
2235  return doctest.testmod(verbose=opts.debug, optionflags=doctest.ELLIPSIS, )
2236 
2237 def main():
2238  global season_and_episode_num, screenshot_request
2239  # reset some globals for doctest mode
2240  screenshot_request = False
2241 
2242  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")
2243 
2244  parser.add_option( "--doctest", action="store_true", default=False, dest="doctest",
2245  help=u"Run doctests")
2246  parser.add_option( "-d", "--debug", action="store_true", default=False, dest="debug",
2247  help=u"Show debugging info")
2248  parser.add_option( "-r", "--raw", action="store_true",default=False, dest="raw",
2249  help=u"Dump raw data only")
2250  parser.add_option( "-u", "--usage", action="store_true", default=False, dest="usage",
2251  help=u"Display examples for executing the ttvdb script")
2252  parser.add_option( "-v", "--version", action="store_true", default=False, dest="version",
2253  help=u"Display version and author")
2254  parser.add_option( "-i", "--interactive", action="store_true", default=False, dest="interactive",
2255  help=u"Interaction mode (allows selection of a specific Series)")
2256  parser.add_option( "-c", "--configure", metavar="FILE", default="", dest="configure",
2257  help=u"Use configuration settings")
2258  parser.add_option( "-l", "--language", metavar="LANGUAGE", default=u'en', dest="language",
2259  help=u"Select data that matches the specified language fall back to english if nothing found (e.g. 'es' Español, 'de' Deutsch ... etc)")
2260  parser.add_option( "-a", "--area", metavar="COUNTRY", default=u'', dest="country",
2261  help=u"Select data that matches the specificed country (e.g. 'de' Germany 'gb' UK). This option is currently not used.")
2262  parser.add_option( "-n", "--num_seasons", action="store_true", default=False, dest="num_seasons",
2263  help=u"Return the season numbers for a series")
2264  parser.add_option( "-t", action="store_true", default=False, dest="test",
2265  help=u"Test for the availability of runtime dependencies")
2266  parser.add_option( "-m", "--mythvideo", action="store_true", default=False, dest="mythvideo",
2267  help=u"Conform to mythvideo standards when processing -M, -P, -F and -D")
2268  parser.add_option( "-M", "--list", action="store_true", default=False, dest="list",
2269  help=u"Get matching TV Series list")
2270  parser.add_option( "-P", "--poster", action="store_true", default=False, dest="poster",
2271  help=u"Get Series Poster URL(s)")
2272  parser.add_option( "-F", "--fanart", action="store_true", default=False, dest="fanart",
2273  help=u"Get Series fan art URL(s)")
2274  parser.add_option( "-B", "--backdrop", action="store_true", default=False, dest="banner",
2275  help=u"Get Series banner/backdrop URL(s)")
2276  parser.add_option( "-S", "--screenshot", action="store_true", default=False, dest="screenshot",
2277  help=u"Get Series episode screenshot URL")
2278  parser.add_option( "-D", "--data", action="store_true", default=False, dest="data",
2279  help=u"Get Series episode data")
2280  parser.add_option( "-N", "--numbers", action="store_true", default=False, dest="numbers",
2281  help=u"Get Season and Episode numbers")
2282  parser.add_option( "-C", "--collection", action="store_true", default=False, dest="collection",
2283  help=u'Get a TV Series (collection) "series" level information')
2284 
2285  opts, series_season_ep = parser.parse_args()
2286 
2287  if opts.doctest:
2288  return doc_test(opts)
2289 
2290  # Test mode, if we've made it here, everything is ok
2291  if opts.test:
2292  print("Everything appears to be in order")
2293  return 0
2294 
2295  # Make everything unicode utf8
2296  if IS_PY2:
2297  for index in range(len(series_season_ep)):
2298  series_season_ep[index] = unicode(series_season_ep[index], 'utf8')
2299 
2300  if opts.debug == True:
2301  print("opts", opts)
2302  print("\nargs", series_season_ep)
2303 
2304  # Process version command line requests
2305  if opts.version == True:
2306  version = etree.XML(u'<grabber></grabber>')
2307  etree.SubElement(version, "name").text = __title__
2308  etree.SubElement(version, "author").text = __author__
2309  etree.SubElement(version, "thumbnail").text = 'ttvdb.png'
2310  etree.SubElement(version, "command").text = 'ttvdb.py'
2311  etree.SubElement(version, "type").text = 'television'
2312  etree.SubElement(version, "description").text = 'Search and metadata downloads for thetvdb.com'
2313  etree.SubElement(version, "version").text = __version__
2314  sys.stdout.write(etree.tostring(version, encoding='UTF-8', pretty_print=True))
2315  return 0
2316 
2317  # Process usage command line requests
2318  if opts.usage == True:
2319  sys.stdout.write(usage_txt)
2320  return 0
2321 
2322  if len(series_season_ep) == 0:
2323  parser.error("! No series or series season episode supplied")
2324  return 1
2325 
2326  # Default output format of season and episode numbers
2327  season_and_episode_num='S%02dE%02d' # Format output example "S04E12"
2328 
2329  if opts.numbers == False:
2330  if len(series_season_ep) > 1:
2331  if not _can_int(series_season_ep[1]):
2332  parser.error("! Season is not numeric")
2333  return 1
2334  if len(series_season_ep) > 2:
2335  if not _can_int(series_season_ep[2]):
2336  parser.error("! Episode is not numeric")
2337  return 1
2338  else:
2339  if len(series_season_ep) < 2:
2340  parser.error("! An Episode name must be included")
2341  return 1
2342  if len(series_season_ep) == 3:
2343  season_and_episode_num = series_season_ep[2] # Override default output format
2344 
2345  if opts.screenshot:
2346  if len(series_season_ep) > 1:
2347  if not _can_int(series_season_ep[1]):
2348  parser.error("! Season is not numeric")
2349  return 1
2350  if len(series_season_ep) > 2:
2351  if not _can_int(series_season_ep[2]):
2352  parser.error("! Episode is not numeric")
2353  return 1
2354  if not len(series_season_ep) > 2:
2355  parser.error("! Option (-S), episode screenshot search requires Season and Episode numbers")
2356  return 1
2357  screenshot_request = True
2358 
2359  if opts.debug == True:
2360  print(series_season_ep)
2361 
2362  if opts.debug == True:
2363  print("#"*20)
2364  print("# series_season_ep array(",series_season_ep,")")
2365 
2366  if opts.debug == True:
2367  print("#"*20)
2368  print("# Starting tvtvb")
2369  print("# Processing (%s) Series" % ( series_season_ep[0] ))
2370 
2371  # List of language from http://www.thetvdb.com/api/0629B785CE550C8D/languages.xml
2372  # Hard-coded here as it is realtively static, and saves another HTTP request, as
2373  # recommended on http://thetvdb.com/wiki/index.php/API:languages.xml
2374  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"]
2375 
2376  # Validate language as specified by user
2377  if not opts.language in valid_languages:
2378  opts.language = 'en' # Set the default to English when an invalid language was specified
2379 
2380  # Set XML to be the default display mode for -N, -M, -D, -C
2381  opts.xml = True
2382  initializeXslt(opts.language)
2383 
2384  # Access thetvdb.com API with banners (Posters, Fanart, banners, screenshots) data retrieval enabled
2385  if opts.list ==True:
2386  t = Tvdb(banners=False,
2387  debug = opts.debug,
2388  cache = cache_dir,
2389  custom_ui=returnAllSeriesUI,
2390  language = opts.language,
2391  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2392  username=tvdb_account.username,
2393  userkey=tvdb_account.account_identifier)
2394  if opts.xml:
2395  t.xml = True
2396  elif opts.interactive == True:
2397  t = Tvdb(banners=True,
2398  debug=opts.debug,
2399  interactive=True,
2400  select_first=False,
2401  cache=cache_dir,
2402  actors = True,
2403  language = opts.language,
2404  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2405  username=tvdb_account.username,
2406  userkey=tvdb_account.account_identifier)
2407  if opts.xml:
2408  t.xml = True
2409  else:
2410  t = Tvdb(banners=True,
2411  debug = opts.debug,
2412  cache = cache_dir,
2413  actors = True,
2414  language = opts.language,
2415  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2416  username=tvdb_account.username,
2417  userkey=tvdb_account.account_identifier)
2418  if opts.xml:
2419  t.xml = True
2420 
2421  # disable certificate check
2422  t.session.verify = False
2423 
2424  # Determine if there is a SID or a series name to search with
2425  global SID
2426  SID = False
2427  if _can_int(series_season_ep[0]): # if it is numeric then assume it is a series ID number
2428  SID = True
2429  else:
2430  SID = False
2431 
2432  # The -C collections options only supports a SID as input
2433  if opts.collection:
2434  if SID:
2435  pass
2436  else:
2437  parser.error("! Option (-C), collection requires an inetref number")
2438  return 1
2439 
2440  if opts.debug == True:
2441  print("# ..got tvdb mirrors")
2442  print("# Start to process series or series_season_ep")
2443  print("#"*20)
2444 
2445  global override
2446  override={} # Initialize series name override dictionary
2447  # If the user wants Series name overrides and a override file exists then create an overide dictionary
2448  if opts.configure != '': # Did the user want to override the default config file name/location
2449  if opts.configure[0]=='~':
2450  opts.configure=os.path.expanduser("~")+opts.configure[1:]
2451  if os.path.exists(opts.configure) == 1: # Do overrides exist?
2452  initialize_override_dictionary(opts.configure, opts.language)
2453  else:
2454  sys.stderr.write("! The specified override file (%s) does not exist\n" % opts.configure)
2455  return 1
2456  else: # Check if there is a default configuration file
2457  default_config = u"%s/%s" % (os.path.expanduser(u"~"), u".mythtv/ttvdb.conf")
2458  if os.path.isfile(default_config):
2459  opts.configure = default_config
2460  initialize_override_dictionary(opts.configure, opts.language)
2461 
2462  if len(override) == 0:
2463  opts.configure = False # Turn off the override option as there is nothing to override
2464 
2465  # Check if a video name was passed and if so parse it for series name, season and episode numbers
2466  if not opts.collection and len(series_season_ep) == 1:
2467  for r in name_parse:
2468  match = r.match(series_season_ep[0])
2469  if match:
2470  seriesname, seasno, epno = match.groups()
2471  #remove ._- characters from name (- removed only if next to end of line)
2472  seriesname = re.sub("[\._]|\-(?=$)", " ", seriesname).strip()
2473  series_season_ep = [seriesname, seasno, epno]
2474  break # Matched - to the next file!
2475 
2476  # Fetch a list of matching series names
2477  if opts.list ==True:
2478  try:
2479  key = series_season_ep[0].lower()
2480  if opts.configure != "" and key in override:
2481  if override[key][2]:
2482  allSeries = t._getSeries(override[key][2], True)
2483  else:
2484  allSeries = t._getSeries(override[key][0])
2485  else:
2486  allSeries=t._getSeries(series_season_ep[0])
2487  except tvdb_shownotfound:
2488  return 0 # No matching series
2489  except Exception as e:
2490  sys.stderr.write("! Error: %s\n" % (e))
2491  raise
2492  return 1 # Most likely a communications error
2493  if opts.xml:
2494  convert_search_to_xml(t, allSeries)
2495  displaySearchXML(t)
2496  return 0
2497  match_list = []
2498  for series_name_sid in allSeries: # list search results
2499  key_value = u"%s:%s" % (series_name_sid['sid'], series_name_sid['name'])
2500  if not key_value in match_list: # Do not add duplicates
2501  match_list.append(key_value)
2502  print(key_value)
2503  return 0 # The Series list option (-M) is the only option honoured when used
2504 
2505  # Fetch TV series collection information
2506  if opts.collection:
2507  try:
2508  t._getShowData(series_season_ep[0], opts.language)
2509  except tvdb_shownotfound:
2510  return 0 # No matching series
2511  except Exception as e:
2512  sys.stderr.write("! Error: %s\n" % (e))
2513  raise
2514  return 1 # Most likely a communications error
2515  convert_series_to_xml(t, series_season_ep, None)
2517  return 0 # The TV Series collection option (-C) is the only option honoured when used
2518 
2519  # Verify that thetvdb.com has the desired series_season_ep.
2520  # Exit this module if series_season_ep is not found
2521  if opts.numbers == False and opts.num_seasons == False:
2522  seriesfound=searchseries(t, opts, series_season_ep)
2523  x=1
2524  else:
2525  x=[]
2526  x.append(series_season_ep[0]) # Only use series name in check
2527  seriesfound=searchseries(t, opts, x)
2528 
2529  # Return the season numbers for a series
2530  if opts.num_seasons == True:
2531  season_numbers=''
2532  for x in sorted(seriesfound.keys()):
2533  season_numbers+='%d,' % x
2534  print(season_numbers[:-1])
2535  return 0 # Option (-n) is the only option honoured when used
2536 
2537  # Dump information accessible for a Series and ONLY first season of episoded data
2538  if opts.debug == True:
2539  print("#"*20)
2540  print("# Starting Raw keys call")
2541  print("Lvl #1:") # Seasons for series
2542  x = t[series_season_ep[0]].keys()
2543  print(t[series_season_ep[0]].keys())
2544  print("#"*20)
2545  print("Lvl #2:") # Episodes for each season
2546  for y in x:
2547  print(t[series_season_ep[0]][y].keys())
2548  print("#"*20)
2549  print("Lvl #3:") # Keys for each episode within the 1st season
2550  z = t[series_season_ep[0]][1].keys()
2551  for aa in z:
2552  print(t[series_season_ep[0]][1][aa].keys())
2553  print("#"*20)
2554  print("Lvl #4:") # Available data for each episode in 1st season
2555  for aa in z:
2556  codes = t[series_season_ep[0]][1][aa].keys()
2557  print("\n\nStart:")
2558  for c in codes:
2559  print("="*50)
2560  print('Key Name=('+c+'):')
2561  print(t[series_season_ep[0]][1][aa][c])
2562  print("="*50)
2563  print("#"*20)
2564  sys.exit (True)
2565 
2566  if opts.numbers == True: # Fetch and output season and episode numbers
2567  global xmlFlag
2568  if opts.xml:
2569  xmlFlag = True
2570  else:
2571  xmlFlag = False
2572  Getseries_episode_numbers(t, opts, series_season_ep)
2573  return 0 # The Numbers option (-N) is the only option honoured when used
2574 
2575  if opts.data or screenshot_request: # Fetch and output episode data
2576  if opts.mythvideo:
2577  if len(series_season_ep) != 3:
2578  print(u"Season and Episode numbers required.")
2579  else:
2580  if opts.xml:
2581  t.getDetailedEpisodeInfo(seriesfound[u'id'], series_season_ep[1], series_season_ep[2])
2582  convert_series_to_xml(t, series_season_ep, seriesfound)
2583  displaySeriesXML(t, series_season_ep)
2584  return 0
2585  Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
2586  else:
2587  if opts.xml and len(series_season_ep) == 3:
2588  t.getDetailedEpisodeInfo(list(t.shows.values())[0].data['id'], series_season_ep[1], series_season_ep[2])
2589  convert_series_to_xml(t, series_season_ep, seriesfound)
2590  displaySeriesXML(t, series_season_ep)
2591  return 0
2592  Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
2593 
2594  # Fetch the requested graphics URL(s)
2595  if opts.debug == True:
2596  print("#"*20)
2597  print("# Checking if Posters, Fanart or Banners are available")
2598  print("#"*20)
2599 
2600  key = series_season_ep[0].lower()
2601  if opts.configure != "" and key in override:
2602  series_info = search_for_series(t, override[key][0], opts.language)
2603  else:
2604  series_info = search_for_series(t, series_season_ep[0], opts.language)
2605  try:
2606  banners_keys = series_info['_banners'].keys()
2607  except:
2608  banners_keys = None
2609 
2610  banner= False
2611  poster= False
2612  fanart= False
2613 
2614  if banners_keys:
2615  for x in banners_keys: # Determine what type of graphics is available
2616  if x == fanart_key:
2617  fanart=True
2618  elif x == poster_key:
2619  poster=True
2620  elif x == season_key or x == banner_key:
2621  banner=True
2622 
2623  # Make sure that some graphics URL(s) (Posters, FanArt or Banners) are available
2624  if ( fanart!=True and poster!=True and banner!=True ):
2625  return 0
2626 
2627  if opts.debug == True:
2628  print("#"*20)
2629  print("# One or more of Posters, Fanart or Banners are available")
2630  print("#"*20)
2631 
2632  # Determine if graphic URL identification output is required
2633  if opts.data: # Along with episode data get all graphics
2634  opts.poster = True
2635  opts.fanart = True
2636  opts.banner = True
2637  single_option = True
2638  fanart, banner, poster = (True, True, True)
2639  else:
2640  y=0
2641  single_option=True
2642  if opts.poster==True:
2643  y+=1
2644  if opts.fanart==True:
2645  y+=1
2646  if opts.banner==True:
2647  y+=1
2648 
2649  if (poster==True and opts.poster==True and opts.raw!=True): # Get posters and send to stdout
2650  season_poster_found = False
2651  if opts.mythvideo:
2652  if len(series_season_ep) < 2:
2653  print(u"Season and Episode numbers required.")
2654  return 0
2655  all_posters = u'Coverart:'
2656  all_empty = len(all_posters)
2657  for p in get_graphics(t, opts, series_season_ep, poster_type, single_option, opts.language):
2658  all_posters = all_posters+p+u','
2659  season_poster_found = True
2660  if season_poster_found == False: # If there were no season posters get the series top poster
2661  series_name=''
2662  key = series_season_ep[0].lower()
2663  if opts.configure != "" and key in override:
2664  series_name=override[key][0] # Override series name
2665  else:
2666  series_name=series_season_ep[0] # Leave the series name alone
2667  for p in get_graphics(t, opts, [series_name], poster_type, single_option, opts.language):
2668  all_posters = all_posters+p+u','
2669  if len(all_posters) > all_empty:
2670  if all_posters[-1] == u',':
2671  print(all_posters[:-1])
2672  else:
2673  print(all_posters)
2674 
2675  if (fanart==True and opts.fanart==True and opts.raw!=True): # Get Fan Art and send to stdout
2676  all_fanart = u'Fanart:'
2677  all_empty = len(all_fanart)
2678  for f in get_graphics(t, opts, series_season_ep, fanart_type, single_option, opts.language):
2679  all_fanart = all_fanart+f+u','
2680  if len(all_fanart) > all_empty:
2681  if all_fanart[-1] == u',':
2682  print(all_fanart[:-1])
2683  else:
2684  print(all_fanart)
2685 
2686  if (banner==True and opts.banner==True and opts.raw!=True): # Also change to get ALL Series graphics
2687  season_banner_found = False
2688  if opts.mythvideo:
2689  if len(series_season_ep) < 2:
2690  print(u"Season and Episode numbers required.")
2691  return 0
2692  all_banners = u'Banner:'
2693  all_empty = len(all_banners)
2694  for b in get_graphics(t, opts, series_season_ep, banner_type, single_option, opts.language):
2695  all_banners = all_banners+b+u','
2696  season_banner_found = True
2697  if not season_banner_found: # If there were no season banner get the series top banner
2698  series_name=''
2699  key = series_season_ep[0].lower()
2700  if opts.configure != "" and key in override:
2701  series_name=override[key][0] # Override series name
2702  else:
2703  series_name=series_season_ep[0] # Leave the series name alone
2704  for b in get_graphics(t, opts, [series_name], banner_type, single_option, opts.language):
2705  all_banners = all_banners+b+u','
2706  if len(all_banners) > all_empty:
2707  if all_banners[-1] == u',':
2708  print(all_banners[:-1])
2709  else:
2710  print(all_banners)
2711 
2712  if opts.debug == True:
2713  print("#"*20)
2714  print("# Processing complete")
2715  print("#"*20)
2716  return 0
2717 #end main
2718 
2719 if __name__ == "__main__":
2720  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:2237
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:2182
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:2208
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:2095
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:2117
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
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:2142
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
find
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
Definition: dvbstreamhandler.cpp:356
ttvdb.Getseries_episode_numbers
def Getseries_episode_numbers(t, opts, series_season_ep)
Definition: ttvdb.py:1874