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 == 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  series_name=override[key][0] # Override series name
1525  else:
1526  series_name=series_season_ep[0] # Leave the series name alone
1527  try:
1528  # Search for the series or series & season or series & season & episode
1529  series_data = search_for_series(t, series_name, opts.language)
1530  if len(series_season_ep)>1:
1531  if len(series_season_ep)>2: # series & season & episode
1532  seriesfound=series_data[ int(series_season_ep[1]) ][ int(series_season_ep[2]) ]
1533  else:
1534  seriesfound=series_data[ int(series_season_ep[1]) ] # series & season
1535  else:
1536  seriesfound=series_data # Series only
1537  except tvdb_shownotfound:
1538  # No such show found.
1539  # Use the show-name from the files name, and None as the ep name
1540  sys.exit(0)
1541  except (tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound):
1542  # The season, episode or name wasn't found, but the show was.
1543  # Use the corrected show-name, but no episode name.
1544  sys.exit(0)
1545  except tvdb_error as errormsg:
1546  # Error communicating with thetvdb.com
1547  if SID == True: # Maybe the digits were a series name (e.g. 90210)
1548  SID = False
1549  return searchseries(t, opts, series_season_ep)
1550  sys.exit(0)
1551  except tvdb_userabort as errormsg:
1552  # User aborted selection (q or ^c)
1553  print("\n", errormsg)
1554  sys.exit(0)
1555  else:
1556  if opts.raw==True:
1557  print("="*20)
1558  print("Raw Series Data:\n")
1559  if len(series_season_ep)>1:
1560  print(t[ series_name ][ int(series_season_ep[1]) ])
1561  else:
1562  print(t[ series_name ])
1563  print("="*20)
1564  return(seriesfound)
1565 # end searchseries
1566 
1567 # Retrieve Poster or Fan Art or Banner graphics URL(s)
1568 def get_graphics(t, opts, series_season_ep, graphics_type, single_option, language=False):
1569  banners='_banners'
1570  series_name=''
1571  graphics=[]
1572  key = series_season_ep[0].lower()
1573  if opts.configure != "" and key in override:
1574  series_name=override[key][0] # Override series name
1575  else:
1576  series_name=series_season_ep[0] # Leave the series name alone
1577 
1578  if SID == True:
1579  t._parseBanners(series_name)
1580  else:
1581  t._parseBanners(t._nameToSid(series_name))
1582  bid_order = {'fanart': [], 'poster': [], 'series': [], 'season': [], 'seasonwide': []}
1583  URLs = {'fanart': [], 'poster': [], 'series': [], 'season': [], 'seasonwide': []}
1584 
1585  # get the urls in presented order
1586  for key in t.shows.keys():
1587  banner = t.shows[key].data['_banners']
1588  for graphic_type_items in bid_order.keys():
1589  if graphic_type_items in banner:
1590  for graphic_item in banner[graphic_type_items]['raw']:
1591  url = banner[graphic_type_items][graphic_item['resolution']][graphic_item['id']]
1592  url['rating'] = graphic_item['ratingsInfo']['average']
1593  URLs[graphic_type_items].append(url)
1594 
1595  if graphics_type == fanart_type: # Series fanart graphics
1596  if not len(URLs[u'fanart']):
1597  return []
1598  for url in URLs[u'fanart']:
1599  graphics.append(url)
1600  elif len(series_season_ep) == 1:
1601  if not len(URLs[u'series']):
1602  return []
1603  if graphics_type == banner_type: # Series Banners
1604  for url in URLs[u'series']:
1605  graphics.append(url)
1606  else: # Series Posters
1607  for url in URLs[u'poster']:
1608  graphics.append(url)
1609  else:
1610  if not len(URLs[u'season']):
1611  return []
1612  if graphics_type == banner_type: # Season Banners
1613  season_banners=[]
1614  # seasonwide has blank resolution
1615  for url in URLs[u'seasonwide']:
1616  if url[u'resolution'] == u'' and url[u'subKey'] == series_season_ep[1]:
1617  season_banners.append(url)
1618  if not len(season_banners):
1619  return []
1620  graphics = season_banners
1621  else: # Season Posters
1622  # season has blank resolution
1623  season_posters=[]
1624  for url in URLs[u'season']:
1625  if url[u'resolution'] == u'' and url[u'subKey'] == series_season_ep[1]:
1626  season_posters.append(url)
1627  if not len(season_posters):
1628  return []
1629  graphics = season_posters
1630 
1631  graphicsURLs=[]
1632  if single_option==False:
1633  graphicsURLs.append(graphics_type+':')
1634 
1635  count = 0
1636  wasanythingadded = 0
1637  anyotherlanguagegraphics=[]
1638  englishlanguagegraphics=[]
1639  graphics = sorted(graphics, key=lambda k: k['rating'], reverse=True)
1640  for URL in graphics:
1641  if graphics_type == 'filename':
1642  if URL[graphics_type] == None:
1643  continue
1644  if language and 'language' in URL: # Is there a language to filter URLs on?
1645  if language == URL['language']:
1646  graphicsURLs.append((URL['_bannerpath']).replace(http_find, http_replace))
1647  else: # Check for fall back graphics in case there are no selected language graphics
1648  if u'en' == URL['language']:
1649  englishlanguagegraphics.append((URL['_bannerpath']).replace(http_find, http_replace))
1650  else:
1651  anyotherlanguagegraphics.append((URL['_bannerpath']).replace(http_find, http_replace))
1652  else:
1653  graphicsURLs.append((URL['_bannerpath']).replace(http_find, http_replace))
1654  if wasanythingadded == len(graphicsURLs):
1655  continue
1656  wasanythingadded = len(graphicsURLs)
1657 
1658  if not len(graphicsURLs):
1659  if len(englishlanguagegraphics): # Fall back to English graphics
1660  graphicsURLs = englishlanguagegraphics
1661  elif len(anyotherlanguagegraphics): # Fall back-back to any available graphics
1662  graphicsURLs = anyotherlanguagegraphics
1663 
1664  if opts.debug == True:
1665  print(u"\nGraphics:\n", graphicsURLs)
1666 
1667  if len(graphicsURLs) == 1 and graphicsURLs[0] == graphics_type+':':
1668  return [] # Due to the language filter there may not be any URLs
1669  return(graphicsURLs)
1670 # end get_graphics
1671 
1672 # Massage episode name to match those in thetvdb.com for this series
1673 def massageEpisode_name(ep_name, series_season_ep):
1674  for edit in override[series_season_ep[0].lower()][1]:
1675  ep_name=ep_name.replace(edit[0],edit[1]) # Edit episode name for each set of strings
1676  return ep_name
1677 # end massageEpisode_name
1678 
1679 # Remove '|' and replace with commas
1680 def change_to_commas(meta_data):
1681  if not meta_data: return meta_data
1682  meta_data = (u'|'.join([d for d in meta_data.split('| ') if d]))
1683  return (u', '.join([d for d in meta_data.split('|') if d]))
1684 # end change_to_commas
1685 
1686 # Change &amp; values to ascii equivalents
1687 def change_amp(text):
1688  if not text: return text
1689  try:
1690  text = text.replace("&quot;", "'").replace("\r\n", " ")
1691  text = text.replace(r"\'", "'")
1692  except Exception as e:
1693  pass
1694  return text
1695 # end change_amp
1696 
1697 # Prepare for includion into a DB
1698 def make_db_ready(text):
1699  if not text: return text
1700  text = text.replace(u'\u2013', "-")
1701  text = text.replace(u'\u2014', "-")
1702  text = text.replace(u'\u2018', "'")
1703  text = text.replace(u'\u2019', "'")
1704  text = text.replace(u'\u2026', "...")
1705  text = text.replace(u'\u201c', '"')
1706  text = text.replace(u'\u201d', '"')
1707  text = text.encode('latin-1', 'backslashreplace')
1708  return text
1709 # end make_db_ready
1710 
1711 # Get Series Episode data by season
1712 def Getseries_episode_data(t, opts, series_season_ep, language = None):
1713  global screenshot_request, http_find, http_replace
1714 
1715  args = len(series_season_ep)
1716  series_name=''
1717  key = series_season_ep[0].lower()
1718  if opts.configure != "" and key in override:
1719  series_name=override[key][0] # Override series name
1720  else:
1721  series_name=series_season_ep[0] # Leave the series name alone
1722 
1723  # Get Cast members
1724  cast_members=''
1725  tmp_cast = ''
1726  try:
1727  series_data = search_for_series(t, series_name, opts.language)
1728  tmp_cast = series_data['_actors']
1729  tmp_cast = sorted(tmp_cast, key=lambda k: k['sortOrder'])
1730  except:
1731  cast_members=''
1732  if len(tmp_cast):
1733  cast_members=''.encode('utf8')
1734  for cast in tmp_cast:
1735  if cast['name']:
1736  cast_members+=(cast['name']+', ').encode('utf8')
1737  if cast_members != '':
1738  try:
1739  cast_members = cast_members[:-2].encode('utf8')
1740  except (UnicodeDecodeError, AttributeError):
1741  cast_members = unicode(cast_members[:-2],'utf8')
1742  cast_members = change_amp(cast_members)
1743  cast_members = change_to_commas(cast_members)
1744  cast_members=cast_members.replace('\n',' ')
1745 
1746  # Get genre(s)
1747  genres=''
1748  try:
1749  genres_string = series_data[u'genre'].encode('utf-8')
1750  except:
1751  genres_string=''
1752  if genres_string != None and genres_string != '':
1753  genres = change_amp(genres_string)
1754  genres = change_to_commas(genres)
1755 
1756  seasons=sorted(series_data.keys()) # Get the seasons for this series
1757  for season in seasons:
1758  if args > 1: # If a season was specified skip other seasons
1759  if season != int(series_season_ep[1]):
1760  continue
1761  episodes=sorted(series_data[season].keys()) # Get the episodes for this season
1762  for episode in episodes: # If an episode was specified skip other episodes
1763  if args > 2:
1764  if episode != int(series_season_ep[2]):
1765  continue
1766  # get more detailed episode info
1767  t.getDetailedEpisodeInfo(int(series_data['id']), season, episode)
1768  extra_ep_data=[]
1769  available_keys=series_data[season][episode].keys()
1770  if screenshot_request:
1771  if u'filename' in available_keys:
1772  screenshot = series_data[season][episode][u'filename']
1773  if screenshot:
1774  print(screenshot.replace(http_find, http_replace))
1775  return
1776  else:
1777  return
1778  # key ordering is not sorted so that Title is first for existing client
1779  # compatability
1780  key_values=[]
1781  for values in data_keys: # Initialize an array for each possible data element for
1782  key_values.append('') # each episode within a season
1783  for key in sorted(available_keys):
1784  try:
1785  # skip deprecated keys
1786  if key in ['director']:
1787  continue
1788  i = data_keys.index(key) # Include only specific episode data
1789  except ValueError:
1790  if series_data[season][episode][key] != None:
1791  text = series_data[season][episode][key]
1792  if isinstance(text, dict):
1793  # handle language tuple
1794  text = list(text.values())[0]
1795  elif isinstance(text, list):
1796  # handle guest stars lists
1797  text = ', '.join(text)
1798  text = change_amp(unicode(text))
1799  text = change_to_commas(text)
1800  if text == 'None' and key.title() == 'Director':
1801  text = u"Unknown"
1802  try:
1803  extra_ep_data.append(u"%s:%s" % (key.title(), text))
1804  except UnicodeDecodeError:
1805  extra_ep_data.append(u"%s:%s" % (key.title(), unicode(text, "utf8")))
1806  continue
1807  text = series_data[season][episode][key]
1808 
1809  if text == None and key.title() == 'Director':
1810  text = u"Unknown"
1811  if isinstance(text, list):
1812  text = ', '.join(text)
1813  if text == None or text == 'None':
1814  continue
1815  else:
1816  # handle language tuple
1817  if isinstance(text, dict):
1818  # handle language tuple
1819  text = list(text.values())[0]
1820  text = change_amp(unicode(text))
1821  value = change_to_commas(text)
1822  value = value.replace(u'\n', u' ')
1823  key_values[i]=value
1824  index = 0
1825  if SID == False:
1826  print(u"Title:%s" % series_name) # Ouput the full series name
1827  else:
1828  print(u"Title:%s" % series_data[u'seriesname'])
1829 
1830  for key in data_titles:
1831  if key_values[index] != None:
1832  if data_titles[index] == u'ReleaseDate:' and len(key_values[index]) > 4:
1833  print(u'%s%s'% (u'Year:', key_values[index][:4]))
1834  if key_values[index] != 'None':
1835  print(u'%s%s' % (data_titles[index], key_values[index]))
1836  index+=1
1837  cast_print=False
1838  for extra_data in extra_ep_data:
1839  if extra_data[:extra_data.index(':')] == u'Gueststars':
1840  extra_cast = extra_data[extra_data.index(':')+1:]
1841  if not extra_cast:
1842  continue
1843  if (len(extra_cast)>128) and not extra_cast.count(','):
1844  continue
1845  if cast_members:
1846  extra_data=(u"Cast:%s" % cast_members)+', '+extra_cast
1847  else:
1848  extra_data=u"Cast:%s" % extra_cast
1849  cast_print=True
1850  print(extra_data)
1851  if cast_print == False:
1852  print(u"Cast:%s" % cast_members)
1853  if genres != '':
1854  print(u"Genres:%s" % genres)
1855  print(u"Runtime:%s" % series_data[u'runtime'])
1856 
1857  # URL to TVDB web site episode web page for this series
1858  for url_data in [u'seriesid', u'seasonid', u'id']:
1859  if not url_data in available_keys:
1860  break
1861  else:
1862  results = series_data
1863  print(u'URL:http://www.thetvdb.com/?tab=episode&seriesid=%s&seasonid=%s&id=%s' %
1864  (results[season][episode][u'seriesid'],
1865  results[season][episode][u'seasonid'],
1866  results[season][episode][u'id']))
1867 # end Getseries_episode_data
1868 
1869 # Get Series Season and Episode numbers
1870 def Getseries_episode_numbers(t, opts, series_season_ep):
1871  def _episode_sort(episode):
1872  seasonnumber = 0
1873  episodenumber = 0
1874  try: seasonnumber = int(episode['seasonnumber'])
1875  except: pass
1876  try: episodenumber = int(episode['episodenumber'])
1877  except: pass
1878  return (-episode.distance, seasonnumber, episodenumber)
1879 
1880  global xmlFlag
1881  series_name=''
1882  ep_name=''
1883  key = series_season_ep[0].lower()
1884  if opts.configure != "" and key in override:
1885  series_name=override[key][0] # Override series name
1886  ep_name=series_season_ep[1]
1887  if len(override[series_season_ep[0].lower()][1]) != 0: # Are there search-replace strings?
1888  ep_name=massageEpisode_name(ep_name, series_season_ep)
1889  else:
1890  series_name=series_season_ep[0] # Leave the series name alone
1891  ep_name=series_season_ep[1] # Leave the episode name alone
1892 
1893  series = search_for_series(t, series_name, opts.language)
1894  season_ep_num = series.fuzzysearch(ep_name, 'episodename')
1895  if len(season_ep_num) != 0:
1896  for episode in sorted(season_ep_num, key=lambda ep: _episode_sort(ep), reverse=True):
1897 # if episode.distance == 0: # exact match
1898  if xmlFlag:
1899  # get more detailed episode info
1900  t.getDetailedEpisodeInfo(series['id'], episode['airedSeason'], episode)
1901  convert_series_to_xml(t, series_season_ep, season_ep_num)
1902  displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']])
1903  return 0
1904  print(season_and_episode_num.replace('\\n', '\n') %
1905  (int(episode['seasonnumber']), int(episode['episodenumber'])))
1906 # elif (episode['episodename'].lower()).startswith(ep_name.lower()):
1907 # if len(episode['episodename']) > (len(ep_name)+1):
1908 # if episode['episodename'][len(ep_name):len(ep_name)+2] != ' (':
1909 # continue # Skip episodes the are not part of a set of (1), (2) ... etc
1910 # if xmlFlag:
1911 # displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']])
1912 # sys.exit(0)
1913 # print(season_and_episode_num.replace('\\n', '\n') % (int(episode['seasonnumber']), int(episode['episodenumber'])))
1914 # end Getseries_episode_numbers
1915 
1916 # Set up a custom interface to get all series matching a partial series name
1917 class returnAllSeriesUI(tvdb_api.BaseUI):
1918  def __init__(self, config, log=None):
1919  self.config = config
1920  self.log = log
1921 
1922  def selectSeries(self, allSeries):
1923  return allSeries
1924 # ends returnAllSeriesUI
1925 
1926 def initialize_override_dictionary(useroptions, language):
1927  """ Change variables through a user supplied configuration file
1928  return False and exit the script if there are issues with the configuration file values
1929  """
1930  if useroptions[0]=='~':
1931  useroptions=os.path.expanduser("~")+useroptions[1:]
1932  if os.path.isfile(useroptions) == False:
1933  sys.stderr.write(
1934  "! The specified user configuration file (%s) is not a file\n" % useroptions
1935  )
1936  sys.exit(1)
1937  massage = {}
1938  overrides = {}
1939  cfg = ConfigParser.SafeConfigParser()
1940  cfg.read(useroptions)
1941 
1942  for section in cfg.sections():
1943  if section == 'regex':
1944  # Change variables per user config file
1945  for option in cfg.options(section):
1946  name_parse.append(re.compile(cfg.get(section, option)))
1947  continue
1948  if section =='ep_name_massage':
1949  for option in cfg.options(section):
1950  tmp =cfg.get(section, option).split(',')
1951  if len(tmp)%2 and len(cfg.get(section, option)) != 0:
1952  sys.stderr.write("! For (%s) 'ep_name_massage' values must be in pairs\n" % option)
1953  sys.exit(1)
1954  tmp_array=[]
1955  i=0
1956  while i != len(tmp):
1957  tmp_array.append([tmp[i].replace('"',''), tmp[i+1].replace('"','')])
1958  i+=2
1959  massage[option]=tmp_array
1960  continue
1961  if section =='series_name_override':
1962  for option in cfg.options(section):
1963  overrides[option] = cfg.get(section, option)
1964  tvdb = Tvdb(banners=False,
1965  debug = False,
1966  interactive = False,
1967  cache = cache_dir,
1968  custom_ui=returnAllSeriesUI,
1969  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
1970  username=tvdb_account.username,
1971  userkey=tvdb_account.account_identifier)
1972  tvdb.session.verify = False
1973  for key in overrides.keys():
1974  sid = overrides[key]
1975  if len(sid) == 0:
1976  continue
1977  try: # Check that the SID (Series id) is numeric
1978  dummy = int(sid)
1979  except:
1980  sys.stdout.write("! Series (%s) Invalid SID (not numeric) [%s] in config file\n" % (key, sid))
1981  sys.exit(1)
1982  # Make sure that the series name is not empty or all blanks
1983  if len(key.replace(' ','')) == 0:
1984  sys.stdout.write("! Invalid Series name (must have some non-blank characters) [%s] in config file\n" % key)
1985  print(overrides.keys())
1986  sys.exit(1)
1987 
1988  try:
1989  series_name_sid=tvdb.series_by_sid(sid, language)
1990  except:
1991  sys.stdout.write("! Invalid Series (no matches found in thetvdb,com) (%s) sid (%s) in config file\n" % (key, sid))
1992  sys.exit(1)
1993  overrides[key]=unicode(series_name_sid.data[u'seriesName']) #.encode('utf-8')
1994  continue
1995 
1996  for key in overrides.keys():
1997  override[key] = [overrides[key],[]]
1998 
1999  for key in massage.keys():
2000  if key in override:
2001  override[key][1]=massage[key]
2002  else:
2003  override[key]=[key, massage[key]]
2004  return
2005 # END initialize_override_dictionary
2006 
2007 def convert_search_to_xml(t, allSeries):
2008  """
2009  Convert json to xml and set up tvdb_api object as other stuff expects
2010  :param t: tvdb_api object
2011  :param allSeries: json array of series
2012  :return: xml version of allseries
2013  """
2014  # Initialize XML display value to off
2015  t.xml = False
2016  def series_item_func(parent):
2017  if parent == "root":
2018  return "series"
2019  return "alias"
2020  xml = dicttoxml(allSeries, item_func=series_item_func, attr_type=False)
2021  t.searchTree = eTree.XML(xml)
2022  t.seriesInfoTree = None
2023  t.epInfoTree = None
2024  t.actorsInfoTree = None
2025  t.imagesInfoTree = None
2026  t.baseXsltDir = xslt.baseXsltPath
2027 
2028 def convert_series_to_xml(t, series_season_ep, ep_info):
2029  """
2030  Convert json to xml and set up tvdb_api object as other stuff expects
2031  :param t: tvdb_api object
2032  :param ep_info: json array of series
2033  """
2034  # Initialize XML display value to off
2035  t.xml = False
2036  def series_ep_item_func(parent):
2037  if parent == "data":
2038  return "series"
2039  if parent == "_banners_raw":
2040  return "banner"
2041  if parent == "_actors":
2042  return "actor"
2043  return "item"
2044  def series_people_item_func(parent):
2045  if parent == "Actors":
2046  return "Actor"
2047  return "item"
2048  def series_images_item_func(parent):
2049  if parent == "root":
2050  return "images"
2051  return "Banner"
2052  for show_id in t.shows.keys():
2053  break
2054  # sort the cast into sort order
2055  t.shows[show_id].data['_actors'] = sorted(t.shows[show_id].data['_actors'], key=lambda k: k['sortOrder'])
2056  t.searchTree = None
2057  t.seriesInfoTree = None
2058  t.epInfoTree = None
2059  t.actorsInfoTree = None
2060  t.imagesInfoTree = None
2061  sxml = dicttoxml(t.shows[show_id].data, custom_root='series', item_func=series_ep_item_func, attr_type=False)
2062  exml = dicttoxml(t.shows[show_id], custom_root='data', item_func=series_ep_item_func, attr_type=False)
2063  t.seriesInfoTree = eTree.XML(exml)
2064  t.seriesInfoTree.append(eTree.XML(sxml))
2065  t.baseXsltDir = xslt.baseXsltPath
2066 
2067 def initializeXslt(language):
2068  ''' Initalize all data and functions for XSLT stylesheet processing
2069  return nothing
2070  '''
2071  global xslt, tvdbXpath
2072  try:
2073  import MythTV.ttvdb.tvdbXslt as tvdbXslt
2074  except Exception as errmsg:
2075  sys.stderr.write('! Error: Importing tvdbXslt error(%s)\n' % errmsg)
2076  sys.exit(1)
2077 
2078  xslt = tvdbXslt.xpathFunctions()
2079  xslt.language = language
2080  xslt.buildFuncDict()
2081  xslt.baseXsltPath = tvdbXslt.baseXsltDir
2082  tvdbXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythTV_Universal_Metadata_Format')
2083  tvdbXpath.prefix = 'tvdbXpath'
2084  for key in xslt.FuncDict.keys():
2085  tvdbXpath[key] = xslt.FuncDict[key]
2086  return
2087 # end initializeXslt()
2088 
2089 def displaySearchXML(tvdb_api):
2090  '''Using a XSLT style sheet translate TVDB search results into the MythTV Universal Query format
2091  return nothing
2092  '''
2093  global xslt, tvdbXpath
2094 
2095  # Remove duplicates when a non-English language code is specified
2096  if xslt.language != 'en':
2097  compareFilter = etree.XPath('//id[text()=$Id]')
2098  idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
2099  tmpTree = deepcopy(tvdb_api.searchTree)
2100  for seriesId in tmpTree.xpath('//Series/id/text()'):
2101  if len(compareFilter(tvdb_api.searchTree, Id=seriesId)) > 1:
2102  tmpList = idLangFilter(tvdb_api.searchTree, Id=seriesId)
2103  if len(tmpList):
2104  tvdb_api.searchTree.remove(tmpList[0])
2105 
2106  tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbQuery.xsl')))
2107  items = tvdbQueryXslt(tvdb_api.searchTree)
2108  if items.getroot() != None:
2109  if len(items.xpath('//item')):
2110  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2111  return 0
2112 # end displaySearchXML()
2113 
2114 def displaySeriesXML(tvdb_api, series_season_ep):
2115  '''Using a XSLT style sheet translate TVDB Series data results into the
2116  MythTV Universal Query format
2117  return nothing
2118  '''
2119  global xslt, tvdbXpath
2120  allDataElement = etree.XML(u'<allData></allData>')
2121  requestDetails = etree.XML(u'<requestDetails></requestDetails>')
2122  requestDetails.attrib['lang'] = xslt.language
2123  requestDetails.attrib['series'] = series_season_ep[0]
2124  requestDetails.attrib['season'] = series_season_ep[1]
2125  requestDetails.attrib['episode'] = series_season_ep[2]
2126  allDataElement.append(requestDetails)
2127 
2128  # Combine the various XML inputs into a single XML element and send to the XSLT stylesheet
2129  allDataElement.append(tvdb_api.seriesInfoTree)
2130 
2131  tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbVideo.xsl')))
2132  items = tvdbQueryXslt(allDataElement)
2133  if items.getroot() != None:
2134  if len(items.xpath('//item')):
2135  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2136  return 0
2137 # end displaySeriesXML()
2138 
2139 def displayCollectionXML(tvdb_api):
2140  '''Using a XSLT style sheet translate TVDB series results into the MythTV Universal Query format
2141  return nothing
2142  '''
2143  global xslt, tvdbXpath
2144 
2145  # Remove duplicates when non-English language code is specified
2146  if xslt.language != 'en':
2147  compareFilter = etree.XPath('//id[text()=$Id]')
2148  idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
2149  tmpTree = deepcopy(tvdb_api.seriesInfoTree)
2150  for seriesId in tmpTree.xpath('//Series/id/text()'):
2151  if len(compareFilter(tvdb_api.seriesInfoTree, Id=seriesId)) > 1:
2152  tmpList = idLangFilter(tvdb_api.seriesInfoTree, Id=seriesId)
2153  if len(tmpList):
2154  tvdb_api.seriesInfoTree.remove(tmpList[0])
2155 
2156  tvdbCollectionXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbCollection.xsl')))
2157  items = tvdbCollectionXslt(tvdb_api.seriesInfoTree)
2158  if items.getroot() != None:
2159  if len(items.xpath('//item')):
2160  sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
2161  return 0
2162 # end displayCollectionXML()
2163 
2164 
2165 def doc_test(opts):
2166  import doctest
2167 
2168  if not IS_PY2:
2169  # python 3 doctest capture when teh output is utf8 (bytes)
2170  # convert it back to str
2171  if isinstance(sys.stdout, OutStreamEncoder):
2172  sys.stdout = sys.stdout.out
2173  sys.stderr = sys.stderr.out
2174 
2175  class SpoofDocTestWriter(doctest._SpoofOut):
2176  """Wraps a stream with an decoder"""
2177  def __init__(self, *cwargs, **kwargs):
2178  super(SpoofDocTestWriter, self).__init__(*cwargs, **kwargs)
2179 
2180  def write(self, obj):
2181  """Wraps the output stream, encoding Unicode strings with the specified encoding"""
2182  if isinstance(obj, bytes):
2183  obj = obj.decode('utf-8')
2184  return super(SpoofDocTestWriter, self).write(obj)
2185 
2186  def __getattr__(self, attr):
2187  """Delegate everything but write to the stream"""
2188  return getattr(self.out, attr)
2189 
2190  # replace _SpoofOut with our massager
2191  doctest._SpoofOut = SpoofDocTestWriter
2192  return doctest.testmod(verbose=opts.debug, optionflags=doctest.ELLIPSIS, )
2193 
2194 def main():
2195  global season_and_episode_num, screenshot_request
2196  # reset some globals for doctest mode
2197  screenshot_request = False
2198 
2199  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")
2200 
2201  parser.add_option( "--doctest", action="store_true", default=False, dest="doctest",
2202  help=u"Run doctests")
2203  parser.add_option( "-d", "--debug", action="store_true", default=False, dest="debug",
2204  help=u"Show debugging info")
2205  parser.add_option( "-r", "--raw", action="store_true",default=False, dest="raw",
2206  help=u"Dump raw data only")
2207  parser.add_option( "-u", "--usage", action="store_true", default=False, dest="usage",
2208  help=u"Display examples for executing the ttvdb script")
2209  parser.add_option( "-v", "--version", action="store_true", default=False, dest="version",
2210  help=u"Display version and author")
2211  parser.add_option( "-i", "--interactive", action="store_true", default=False, dest="interactive",
2212  help=u"Interaction mode (allows selection of a specific Series)")
2213  parser.add_option( "-c", "--configure", metavar="FILE", default="", dest="configure",
2214  help=u"Use configuration settings")
2215  parser.add_option( "-l", "--language", metavar="LANGUAGE", default=u'en', dest="language",
2216  help=u"Select data that matches the specified language fall back to english if nothing found (e.g. 'es' Español, 'de' Deutsch ... etc)")
2217  parser.add_option( "-a", "--area", metavar="COUNTRY", default=u'', dest="country",
2218  help=u"Select data that matches the specificed country (e.g. 'de' Germany 'gb' UK). This option is currently not used.")
2219  parser.add_option( "-n", "--num_seasons", action="store_true", default=False, dest="num_seasons",
2220  help=u"Return the season numbers for a series")
2221  parser.add_option( "-t", action="store_true", default=False, dest="test",
2222  help=u"Test for the availability of runtime dependencies")
2223  parser.add_option( "-m", "--mythvideo", action="store_true", default=False, dest="mythvideo",
2224  help=u"Conform to mythvideo standards when processing -M, -P, -F and -D")
2225  parser.add_option( "-M", "--list", action="store_true", default=False, dest="list",
2226  help=u"Get matching TV Series list")
2227  parser.add_option( "-P", "--poster", action="store_true", default=False, dest="poster",
2228  help=u"Get Series Poster URL(s)")
2229  parser.add_option( "-F", "--fanart", action="store_true", default=False, dest="fanart",
2230  help=u"Get Series fan art URL(s)")
2231  parser.add_option( "-B", "--backdrop", action="store_true", default=False, dest="banner",
2232  help=u"Get Series banner/backdrop URL(s)")
2233  parser.add_option( "-S", "--screenshot", action="store_true", default=False, dest="screenshot",
2234  help=u"Get Series episode screenshot URL")
2235  parser.add_option( "-D", "--data", action="store_true", default=False, dest="data",
2236  help=u"Get Series episode data")
2237  parser.add_option( "-N", "--numbers", action="store_true", default=False, dest="numbers",
2238  help=u"Get Season and Episode numbers")
2239  parser.add_option( "-C", "--collection", action="store_true", default=False, dest="collection",
2240  help=u'Get a TV Series (collection) "series" level information')
2241 
2242  opts, series_season_ep = parser.parse_args()
2243 
2244  if opts.doctest:
2245  return doc_test(opts)
2246 
2247  # Test mode, if we've made it here, everything is ok
2248  if opts.test:
2249  print("Everything appears to be in order")
2250  return 0
2251 
2252  # Make everything unicode utf8
2253  if IS_PY2:
2254  for index in range(len(series_season_ep)):
2255  series_season_ep[index] = unicode(series_season_ep[index], 'utf8')
2256 
2257  if opts.debug == True:
2258  print("opts", opts)
2259  print("\nargs", series_season_ep)
2260 
2261  # Process version command line requests
2262  if opts.version == True:
2263  version = etree.XML(u'<grabber></grabber>')
2264  etree.SubElement(version, "name").text = __title__
2265  etree.SubElement(version, "author").text = __author__
2266  etree.SubElement(version, "thumbnail").text = 'ttvdb.png'
2267  etree.SubElement(version, "command").text = 'ttvdb.py'
2268  etree.SubElement(version, "type").text = 'television'
2269  etree.SubElement(version, "description").text = 'Search and metadata downloads for thetvdb.com'
2270  etree.SubElement(version, "version").text = __version__
2271  sys.stdout.write(etree.tostring(version, encoding='UTF-8', pretty_print=True))
2272  return 0
2273 
2274  # Process usage command line requests
2275  if opts.usage == True:
2276  sys.stdout.write(usage_txt)
2277  return 0
2278 
2279  if len(series_season_ep) == 0:
2280  parser.error("! No series or series season episode supplied")
2281  return 1
2282 
2283  # Default output format of season and episode numbers
2284  season_and_episode_num='S%02dE%02d' # Format output example "S04E12"
2285 
2286  if opts.numbers == False:
2287  if len(series_season_ep) > 1:
2288  if not _can_int(series_season_ep[1]):
2289  parser.error("! Season is not numeric")
2290  return 1
2291  if len(series_season_ep) > 2:
2292  if not _can_int(series_season_ep[2]):
2293  parser.error("! Episode is not numeric")
2294  return 1
2295  else:
2296  if len(series_season_ep) < 2:
2297  parser.error("! An Episode name must be included")
2298  return 1
2299  if len(series_season_ep) == 3:
2300  season_and_episode_num = series_season_ep[2] # Override default output format
2301 
2302  if opts.screenshot:
2303  if len(series_season_ep) > 1:
2304  if not _can_int(series_season_ep[1]):
2305  parser.error("! Season is not numeric")
2306  return 1
2307  if len(series_season_ep) > 2:
2308  if not _can_int(series_season_ep[2]):
2309  parser.error("! Episode is not numeric")
2310  return 1
2311  if not len(series_season_ep) > 2:
2312  parser.error("! Option (-S), episode screenshot search requires Season and Episode numbers")
2313  return 1
2314  screenshot_request = True
2315 
2316  if opts.debug == True:
2317  print(series_season_ep)
2318 
2319  if opts.debug == True:
2320  print("#"*20)
2321  print("# series_season_ep array(",series_season_ep,")")
2322 
2323  if opts.debug == True:
2324  print("#"*20)
2325  print("# Starting tvtvb")
2326  print("# Processing (%s) Series" % ( series_season_ep[0] ))
2327 
2328  # List of language from http://www.thetvdb.com/api/0629B785CE550C8D/languages.xml
2329  # Hard-coded here as it is realtively static, and saves another HTTP request, as
2330  # recommended on http://thetvdb.com/wiki/index.php/API:languages.xml
2331  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"]
2332 
2333  # Validate language as specified by user
2334  if not opts.language in valid_languages:
2335  opts.language = 'en' # Set the default to English when an invalid language was specified
2336 
2337  # Set XML to be the default display mode for -N, -M, -D, -C
2338  opts.xml = True
2339  initializeXslt(opts.language)
2340 
2341  # Access thetvdb.com API with banners (Posters, Fanart, banners, screenshots) data retrieval enabled
2342  if opts.list ==True:
2343  t = Tvdb(banners=False,
2344  debug = opts.debug,
2345  cache = cache_dir,
2346  custom_ui=returnAllSeriesUI,
2347  language = opts.language,
2348  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2349  username=tvdb_account.username,
2350  userkey=tvdb_account.account_identifier)
2351  if opts.xml:
2352  t.xml = True
2353  elif opts.interactive == True:
2354  t = Tvdb(banners=True,
2355  debug=opts.debug,
2356  interactive=True,
2357  select_first=False,
2358  cache=cache_dir,
2359  actors = True,
2360  language = opts.language,
2361  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2362  username=tvdb_account.username,
2363  userkey=tvdb_account.account_identifier)
2364  if opts.xml:
2365  t.xml = True
2366  else:
2367  t = Tvdb(banners=True,
2368  debug = opts.debug,
2369  cache = cache_dir,
2370  actors = True,
2371  language = opts.language,
2372  apikey=tvdb_account.apikey, # thetvdb.com API key requested by MythTV
2373  username=tvdb_account.username,
2374  userkey=tvdb_account.account_identifier)
2375  if opts.xml:
2376  t.xml = True
2377 
2378  # disable certificate check
2379  t.session.verify = False
2380 
2381  # Determine if there is a SID or a series name to search with
2382  global SID
2383  SID = False
2384  if _can_int(series_season_ep[0]): # if it is numeric then assume it is a series ID number
2385  SID = True
2386  else:
2387  SID = False
2388 
2389  # The -C collections options only supports a SID as input
2390  if opts.collection:
2391  if SID:
2392  pass
2393  else:
2394  parser.error("! Option (-C), collection requires an inetref number")
2395  return 1
2396 
2397  if opts.debug == True:
2398  print("# ..got tvdb mirrors")
2399  print("# Start to process series or series_season_ep")
2400  print("#"*20)
2401 
2402  global override
2403  override={} # Initialize series name override dictionary
2404  # If the user wants Series name overrides and a override file exists then create an overide dictionary
2405  if opts.configure != '': # Did the user want to override the default config file name/location
2406  if opts.configure[0]=='~':
2407  opts.configure=os.path.expanduser("~")+opts.configure[1:]
2408  if os.path.exists(opts.configure) == 1: # Do overrides exist?
2409  initialize_override_dictionary(opts.configure, opts.language)
2410  else:
2411  sys.stderr.write("! The specified override file (%s) does not exist\n" % opts.configure)
2412  return 1
2413  else: # Check if there is a default configuration file
2414  default_config = u"%s/%s" % (os.path.expanduser(u"~"), u".mythtv/ttvdb.conf")
2415  if os.path.isfile(default_config):
2416  opts.configure = default_config
2417  initialize_override_dictionary(opts.configure, opts.language)
2418 
2419  if len(override) == 0:
2420  opts.configure = False # Turn off the override option as there is nothing to override
2421 
2422  # Check if a video name was passed and if so parse it for series name, season and episode numbers
2423  if not opts.collection and len(series_season_ep) == 1:
2424  for r in name_parse:
2425  match = r.match(series_season_ep[0])
2426  if match:
2427  seriesname, seasno, epno = match.groups()
2428  #remove ._- characters from name (- removed only if next to end of line)
2429  seriesname = re.sub("[\._]|\-(?=$)", " ", seriesname).strip()
2430  series_season_ep = [seriesname, seasno, epno]
2431  break # Matched - to the next file!
2432 
2433  # Fetch a list of matching series names
2434  if opts.list ==True:
2435  try:
2436  key = series_season_ep[0].lower()
2437  if opts.configure != "" and key in override:
2438  allSeries = t._getSeries(override[key][0])
2439  else:
2440  allSeries=t._getSeries(series_season_ep[0])
2441  except tvdb_shownotfound:
2442  return 0 # No matching series
2443  except Exception as e:
2444  sys.stderr.write("! Error: %s\n" % (e))
2445  raise
2446  return 1 # Most likely a communications error
2447  if opts.xml:
2448  convert_search_to_xml(t, allSeries)
2449  displaySearchXML(t)
2450  return 0
2451  match_list = []
2452  for series_name_sid in allSeries: # list search results
2453  key_value = u"%s:%s" % (series_name_sid['sid'], series_name_sid['name'])
2454  if not key_value in match_list: # Do not add duplicates
2455  match_list.append(key_value)
2456  print(key_value)
2457  return 0 # The Series list option (-M) is the only option honoured when used
2458 
2459  # Fetch TV series collection information
2460  if opts.collection:
2461  try:
2462  t._getShowData(series_season_ep[0], opts.language)
2463  except tvdb_shownotfound:
2464  return 0 # No matching series
2465  except Exception as e:
2466  sys.stderr.write("! Error: %s\n" % (e))
2467  raise
2468  return 1 # Most likely a communications error
2469  convert_series_to_xml(t, series_season_ep, None)
2471  return 0 # The TV Series collection option (-C) is the only option honoured when used
2472 
2473  # Verify that thetvdb.com has the desired series_season_ep.
2474  # Exit this module if series_season_ep is not found
2475  if opts.numbers == False and opts.num_seasons == False:
2476  seriesfound=searchseries(t, opts, series_season_ep)
2477  x=1
2478  else:
2479  x=[]
2480  x.append(series_season_ep[0]) # Only use series name in check
2481  seriesfound=searchseries(t, opts, x)
2482 
2483  # Return the season numbers for a series
2484  if opts.num_seasons == True:
2485  season_numbers=''
2486  for x in sorted(seriesfound.keys()):
2487  season_numbers+='%d,' % x
2488  print(season_numbers[:-1])
2489  return 0 # Option (-n) is the only option honoured when used
2490 
2491  # Dump information accessible for a Series and ONLY first season of episoded data
2492  if opts.debug == True:
2493  print("#"*20)
2494  print("# Starting Raw keys call")
2495  print("Lvl #1:") # Seasons for series
2496  x = t[series_season_ep[0]].keys()
2497  print(t[series_season_ep[0]].keys())
2498  print("#"*20)
2499  print("Lvl #2:") # Episodes for each season
2500  for y in x:
2501  print(t[series_season_ep[0]][y].keys())
2502  print("#"*20)
2503  print("Lvl #3:") # Keys for each episode within the 1st season
2504  z = t[series_season_ep[0]][1].keys()
2505  for aa in z:
2506  print(t[series_season_ep[0]][1][aa].keys())
2507  print("#"*20)
2508  print("Lvl #4:") # Available data for each episode in 1st season
2509  for aa in z:
2510  codes = t[series_season_ep[0]][1][aa].keys()
2511  print("\n\nStart:")
2512  for c in codes:
2513  print("="*50)
2514  print('Key Name=('+c+'):')
2515  print(t[series_season_ep[0]][1][aa][c])
2516  print("="*50)
2517  print("#"*20)
2518  sys.exit (True)
2519 
2520  if opts.numbers == True: # Fetch and output season and episode numbers
2521  global xmlFlag
2522  if opts.xml:
2523  xmlFlag = True
2524  else:
2525  xmlFlag = False
2526  Getseries_episode_numbers(t, opts, series_season_ep)
2527  return 0 # The Numbers option (-N) is the only option honoured when used
2528 
2529  if opts.data or screenshot_request: # Fetch and output episode data
2530  if opts.mythvideo:
2531  if len(series_season_ep) != 3:
2532  print(u"Season and Episode numbers required.")
2533  else:
2534  if opts.xml:
2535  t.getDetailedEpisodeInfo(seriesfound[u'id'], series_season_ep[1], series_season_ep[2])
2536  convert_series_to_xml(t, series_season_ep, seriesfound)
2537  displaySeriesXML(t, series_season_ep)
2538  return 0
2539  Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
2540  else:
2541  if opts.xml and len(series_season_ep) == 3:
2542  t.getDetailedEpisodeInfo(list(t.shows.values())[0].data['id'], series_season_ep[1], series_season_ep[2])
2543  convert_series_to_xml(t, series_season_ep, seriesfound)
2544  displaySeriesXML(t, series_season_ep)
2545  return 0
2546  Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
2547 
2548  # Fetch the requested graphics URL(s)
2549  if opts.debug == True:
2550  print("#"*20)
2551  print("# Checking if Posters, Fanart or Banners are available")
2552  print("#"*20)
2553 
2554  key = series_season_ep[0].lower()
2555  if opts.configure != "" and key in override:
2556  series_info = search_for_series(t, override[key][0], opts.language)
2557  else:
2558  series_info = search_for_series(t, series_season_ep[0], opts.language)
2559  try:
2560  banners_keys = series_info['_banners'].keys()
2561  except:
2562  banners_keys = None
2563 
2564  banner= False
2565  poster= False
2566  fanart= False
2567 
2568  if banners_keys:
2569  for x in banners_keys: # Determine what type of graphics is available
2570  if x == fanart_key:
2571  fanart=True
2572  elif x == poster_key:
2573  poster=True
2574  elif x == season_key or x == banner_key:
2575  banner=True
2576 
2577  # Make sure that some graphics URL(s) (Posters, FanArt or Banners) are available
2578  if ( fanart!=True and poster!=True and banner!=True ):
2579  return 0
2580 
2581  if opts.debug == True:
2582  print("#"*20)
2583  print("# One or more of Posters, Fanart or Banners are available")
2584  print("#"*20)
2585 
2586  # Determine if graphic URL identification output is required
2587  if opts.data: # Along with episode data get all graphics
2588  opts.poster = True
2589  opts.fanart = True
2590  opts.banner = True
2591  single_option = True
2592  fanart, banner, poster = (True, True, True)
2593  else:
2594  y=0
2595  single_option=True
2596  if opts.poster==True:
2597  y+=1
2598  if opts.fanart==True:
2599  y+=1
2600  if opts.banner==True:
2601  y+=1
2602 
2603  if (poster==True and opts.poster==True and opts.raw!=True): # Get posters and send to stdout
2604  season_poster_found = False
2605  if opts.mythvideo:
2606  if len(series_season_ep) < 2:
2607  print(u"Season and Episode numbers required.")
2608  return 0
2609  all_posters = u'Coverart:'
2610  all_empty = len(all_posters)
2611  for p in get_graphics(t, opts, series_season_ep, poster_type, single_option, opts.language):
2612  all_posters = all_posters+p+u','
2613  season_poster_found = True
2614  if season_poster_found == False: # If there were no season posters get the series top poster
2615  series_name=''
2616  key = series_season_ep[0].lower()
2617  if opts.configure != "" and key in override:
2618  series_name=override[key][0] # Override series name
2619  else:
2620  series_name=series_season_ep[0] # Leave the series name alone
2621  for p in get_graphics(t, opts, [series_name], poster_type, single_option, opts.language):
2622  all_posters = all_posters+p+u','
2623  if len(all_posters) > all_empty:
2624  if all_posters[-1] == u',':
2625  print(all_posters[:-1])
2626  else:
2627  print(all_posters)
2628 
2629  if (fanart==True and opts.fanart==True and opts.raw!=True): # Get Fan Art and send to stdout
2630  all_fanart = u'Fanart:'
2631  all_empty = len(all_fanart)
2632  for f in get_graphics(t, opts, series_season_ep, fanart_type, single_option, opts.language):
2633  all_fanart = all_fanart+f+u','
2634  if len(all_fanart) > all_empty:
2635  if all_fanart[-1] == u',':
2636  print(all_fanart[:-1])
2637  else:
2638  print(all_fanart)
2639 
2640  if (banner==True and opts.banner==True and opts.raw!=True): # Also change to get ALL Series graphics
2641  season_banner_found = False
2642  if opts.mythvideo:
2643  if len(series_season_ep) < 2:
2644  print(u"Season and Episode numbers required.")
2645  return 0
2646  all_banners = u'Banner:'
2647  all_empty = len(all_banners)
2648  for b in get_graphics(t, opts, series_season_ep, banner_type, single_option, opts.language):
2649  all_banners = all_banners+b+u','
2650  season_banner_found = True
2651  if not season_banner_found: # If there were no season banner get the series top banner
2652  series_name=''
2653  key = series_season_ep[0].lower()
2654  if opts.configure != "" and key in override:
2655  series_name=override[key][0] # Override series name
2656  else:
2657  series_name=series_season_ep[0] # Leave the series name alone
2658  for b in get_graphics(t, opts, [series_name], banner_type, single_option, opts.language):
2659  all_banners = all_banners+b+u','
2660  if len(all_banners) > all_empty:
2661  if all_banners[-1] == u',':
2662  print(all_banners[:-1])
2663  else:
2664  print(all_banners)
2665 
2666  if opts.debug == True:
2667  print("#"*20)
2668  print("# Processing complete")
2669  print("#"*20)
2670  return 0
2671 #end main
2672 
2673 if __name__ == "__main__":
2674  sys.exit(main())
def write(text, progress=True)
Definition: mythburn.py:279
def fuzzysearch(self, term=None, key=None)
Definition: ttvdb.py:1441
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)
def series_by_sid(self, sid, language)
Definition: ttvdb.py:1480
def selectSeries(self, allSeries)
Definition: ttvdb.py:1922
def displaySeriesXML(tvdb_api, series_season_ep)
Definition: ttvdb.py:2114
def massageEpisode_name(ep_name, series_season_ep)
Definition: ttvdb.py:1673
def searchseries(t, opts, series_season_ep)
Definition: ttvdb.py:1519
def _setItem(self, sid, seas, ep, attrib, value)
Definition: ttvdb.py:1491
def make_db_ready(text)
Definition: ttvdb.py:1698
def convert_series_to_xml(t, series_season_ep, ep_info)
Definition: ttvdb.py:2028
def change_to_commas(meta_data)
Definition: ttvdb.py:1680
def displaySearchXML(tvdb_api)
Definition: ttvdb.py:2089
def write(self, obj)
Definition: ttvdb.py:1412
def _can_int(x)
Definition: ttvdb.py:1388
def fuzzysearch(self, term=None, key=None)
Definition: ttvdb.py:1453
def __init__(self, config, log=None)
Definition: ttvdb.py:1918
def __getattr__(self, attr)
Definition: ttvdb.py:1421
def get_graphics(t, opts, series_season_ep, graphics_type, single_option, language=False)
Definition: ttvdb.py:1568
unicode
Definition: ttvdb.py:1309
def Getseries_episode_numbers(t, opts, series_season_ep)
Definition: ttvdb.py:1870
def search_for_series(tvdb, sid_or_name, language)
Definition: ttvdb.py:1510
def Getseries_episode_data(t, opts, series_season_ep, language=None)
Definition: ttvdb.py:1712
def _setShowData(self, sid, key, value)
Definition: ttvdb.py:1502
def doc_test(opts)
Definition: ttvdb.py:2165
def convert_search_to_xml(t, allSeries)
Definition: ttvdb.py:2007
def __init__(self, outstream, encoding=None)
Definition: ttvdb.py:1405
def change_amp(text)
Definition: ttvdb.py:1687
def initialize_override_dictionary(useroptions, language)
Definition: ttvdb.py:1926
def fuzzysearch(self, term=None, key=None)
Definition: ttvdb.py:1430
def main()
Definition: ttvdb.py:2194
def initializeXslt(language)
Definition: ttvdb.py:2067
def displayCollectionXML(tvdb_api)
Definition: ttvdb.py:2139