00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 __title__ ="TheTVDB.com";
00039 __author__="R.D.Vaughan"
00040 __version__="1.1.5"
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140 usage_txt='''
00141 Usage: ttvdb.py usage: ttvdb -hdruviomMPFBDSC [parameters]
00142 <series name or 'series and season number' or 'series and season number and episode number'>
00143
00144 For details on using ttvdb with Mythvideo see the ttvdb wiki page at:
00145 http://www.mythtv.org/wiki/Ttvdb.py
00146
00147 Options:
00148 -h, --help show this help message and exit
00149 -d, --debug Show debugging info
00150 -r, --raw Dump raw data only
00151 -u, --usage Display examples for executing the ttvdb script
00152 -v, --version Display version and author
00153 -t Test mode, to check for installed dependencies
00154 -i, --interactive Interaction mode (allows selection of a specific
00155 Series)
00156 -c FILE, --configure=FILE
00157 Use configuration settings
00158 -l LANGUAGE, --language=LANGUAGE
00159 Select data that matches the specified language fall
00160 back to english if nothing found (e.g. 'es' Español,
00161 'de' Deutsch ... etc)
00162 -n, --num_seasons Return the season numbers for a series
00163 -m, --mythvideo Conform to mythvideo standards when processing -M, -P,
00164 -F and -D
00165 -M, --list Get matching TV Series list
00166 -P, --poster Get Series Poster URL(s)
00167 -F, --fanart Get Series fan art URL(s)
00168 -B, --backdrop Get Series banner/backdrop URL(s)
00169 -S, --screenshot Get Series episode screenshot URL
00170 -D, --data Get Series episode data
00171 -N, --numbers Get Season and Episode numbers
00172 -C, --collection Get A TV Series (collection) series specific information
00173
00174 Command examples:
00175 (Return the banner graphics for a series)
00176 > ttvdb -B "Sanctuary"
00177 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
00178
00179 (Return the banner graphics for a Series specific to a season)
00180 > ttvdb -B "SG-1" 1
00181 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
00182
00183 (Return the screen shot graphic for a Series Episode)
00184 > ttvdb -S "SG-1" 1 10
00185 http://www.thetvdb.com/banners/episodes/72449/85759.jpg
00186
00187 (Return the banner graphics for a SID (series ID) specific to a season)
00188 (SID "72449" is specific for the series "SG-1")
00189 > ttvdb -B 72449 1
00190 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
00191
00192 (Return the banner graphics for a file name)
00193 > ttvdb -B "Stargate SG-1 - S08E03 - Lockdown"
00194 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
00195
00196 (Return the posters, banners and fan art for a series)
00197 > ttvdb -PFB "Sanctuary"
00198 Coverart:http://www.thetvdb.com/banners/posters/80159-2.jpg,http://www.thetvdb.com/banners/posters/80159-1.jpg
00199 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
00200 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
00201
00202 (Return thetvdb.com's top rated poster, banner and fan art for a TV Series)
00203 (NOTE: If there is no graphic for a type or any graphics at all then those types are not returned)
00204 > ttvdb -tPFB "Stargate SG-1"
00205 Coverart:http://www.thetvdb.com/banners/posters/72449-1.jpg
00206 Fanart:http://www.thetvdb.com/banners/fanart/original/72449-1.jpg
00207 Banner:http://www.thetvdb.com/banners/graphical/185-g3.jpg
00208 > ttvdb -tB "Night Gallery"
00209 http://www.thetvdb.com/banners/blank/70382.jpg
00210
00211 (Return graphics only matching the local language for a TV series)
00212 (In this case banner 73739-g9.jpg is not included because it does not match the language 'en')
00213 > ttvdb -Bl en "Lost"
00214 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
00215
00216 (Return a season and episode numbers using the override file to identify the series as the US version)
00217 > ttvdb -N --configure="/home/user/.tvdb/tvdb.conf" "Eleventh Hour" "H2O"
00218 <?xml version='1.0' encoding='UTF-8'?>
00219 <metadata>
00220 <item>
00221 <title>Eleventh Hour (US)</title>
00222 <subtitle>H2O</subtitle>
00223 <language>en</language>
00224 <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>
00225 <season>1</season>
00226 <episode>10</episode>
00227 ...
00228 <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"/>
00229 <image type="banner" url="http://www.thetvdb.com/banners/graphical/83066-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/83066-g.jpg"/>
00230 </images>
00231 </item>
00232 </metadata>
00233
00234 (Return the season numbers for a series)
00235 > ttvdb --configure="/home/user/.tvdb/tvdb.conf" -n "SG-1"
00236 0,1,2,3,4,5,6,7,8,9,10
00237
00238 (Return the meta data for a specific series/season/episode)
00239 > ttvdb.py -D 80159 2 2
00240 <?xml version='1.0' encoding='UTF-8'?>
00241 <metadata>
00242 <item>
00243 <title>Sanctuary</title>
00244 <subtitle>End of Nights (2)</subtitle>
00245 <language>en</language>
00246 <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>
00247 <season>2</season>
00248 <episode>2</episode>
00249 <certifications>
00250 <certification locale="us" name="TV-PG"/>
00251 </certifications>
00252 <categories>
00253 <category type="genre" name="Action and Adventure"/>
00254 <category type="genre" name="Science-Fiction"/>
00255 </categories>
00256 <studios>
00257 <studio name="SciFi"/>
00258 </studios>
00259 ...
00260 <image type="banner" url="http://www.thetvdb.com/banners/graphical/80159-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/80159-g.jpg"/>
00261 </images>
00262 </item>
00263 </metadata>
00264
00265 (Return a list of "thetv.com series id and series name" that contain specific search word(s) )
00266 (!! Be careful with this option as poorly defined search words can result in large lists being returned !!)
00267 > ttvdb.py -M "night a"
00268 <?xml version='1.0' encoding='UTF-8'?>
00269 <metadata>
00270 <item>
00271 <language>en</language>
00272 <title>Love on a Saturday Night</title>
00273 <inetref>74382</inetref>
00274 <releasedate>2004-02-01</releasedate>
00275 </item>
00276 <item>
00277 <language>en</language>
00278 <title>A Night on Mount Edna</title>
00279 <inetref>108281</inetref>
00280 </item>
00281 <item>
00282 <language>en</language>
00283 <title>A Night at the Office</title>
00284 <inetref>118511</inetref>
00285 <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>
00286 <releasedate>2009-01-01</releasedate>
00287 <images>
00288 <image type="banner" url="http://www.thetvdb.com/banners/graphical/118511-g.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/118511-g.jpg"/>
00289 </images>
00290 </item>
00291 <item>
00292 <language>en</language>
00293 <title>Star For A Night</title>
00294 <inetref>71476</inetref>
00295 <releasedate>1999-01-01</releasedate>
00296 </item>
00297 </metadata>
00298
00299 (Return TV series collection data of "thetv.com series id" for a specified language)
00300 > ttvdb.py -l de -C 80159
00301 <?xml version='1.0' encoding='UTF-8'?>
00302 <metadata>
00303 <item>
00304 <language>de</language>
00305 <title>Sanctuary</title>
00306 <network>Syfy</network>
00307 <airday>Friday</airday>
00308 <airtime>22:00</airtime>
00309 <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>
00310 <certifications>
00311 <certification locale="us" name="TV-PG"/>
00312 </certifications>
00313 <categories>
00314 <category type="genre" name="Action and Adventure"/>
00315 <category type="genre" name="Science-Fiction"/>
00316 </categories>
00317 <studios>
00318 <studio name="Syfy"/>
00319 </studios>
00320 <runtime>60</runtime>
00321 <inetref>80159</inetref>
00322 <imdb>0965394</imdb>
00323 <tmsref>EP01085421</tmsref>
00324 <userrating>8.0</userrating>
00325 <ratingcount>128</ratingcount>
00326 <year>2007</year>
00327 <releasedate>2007-05-14</releasedate>
00328 <lastupdated>Fri, 17 Feb 2012 16:57:02 GMT</lastupdated>
00329 <status>Continuing</status>
00330 <images>
00331 <image type="coverart" url="http://www.thetvdb.com/banners/posters/80159-4.jpg" thumb="http://www.thetvdb.com/banners/_cache/posters/80159-4.jpg"/>
00332 <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"/>
00333 <image type="banner" url="http://www.thetvdb.com/banners/graphical/80159-g6.jpg" thumb="http://www.thetvdb.com/banners/_cache/graphical/80159-g6.jpg"/>
00334 </images>
00335 </item>
00336 </metadata>
00337 '''
00338
00339
00340 '''
00341 'episodenumber'
00342 'rating'
00343 'overview'
00344 'dvd_episodenumber'
00345 'dvd_discid'
00346 'combined_episodenumber'
00347 'epimgflag'
00348 'id'
00349 'seasonid'
00350 'seasonnumber'
00351 'writer'
00352 'lastupdated'
00353 'filename'
00354 'absolute_number'
00355 'combined_season'
00356 'imdb_id'
00357 'director'
00358 'dvd_chapter'
00359 'dvd_season'
00360 'gueststars'
00361 'seriesid'
00362 'language'
00363 'productioncode'
00364 'firstaired'
00365 'episodename'
00366 '''
00367
00368
00369
00370 import sys, os, re, locale, ConfigParser
00371 from optparse import OptionParser
00372 from copy import deepcopy
00373
00374
00375 try:
00376
00377 import MythTV.ttvdb.tvdb_ui as tvdb_ui
00378
00379 import MythTV.ttvdb.tvdb_api as tvdb_api
00380 from MythTV.ttvdb.tvdb_exceptions import (tvdb_error, tvdb_shownotfound, tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_episodenotfound, tvdb_attributenotfound, tvdb_userabort)
00381
00382
00383 if tvdb_api.__version__ < '1.0':
00384 print "\nYour current installed tvdb_api.py version is (%s)\n" % tvdb_api.__version__
00385 raise
00386 except Exception, e:
00387 print '''
00388 The modules tvdb_api.py (v1.0.0 or greater), tvdb_ui.py, tvdb_exceptions.py and cache.py.
00389 They should have been installed along with the MythTV python bindings.
00390 Error:(%s)
00391 ''' % e
00392 sys.exit(1)
00393
00394 try:
00395 from MythTV.utility import levenshtein
00396 except Exception, e:
00397 print """Could not import levenshtein string distance method from MythTV Python Bindings
00398 Error:(%s)
00399 """ % e
00400 sys.exit(1)
00401
00402 try:
00403 from StringIO import StringIO
00404 from lxml import etree as etree
00405 except Exception, e:
00406 sys.stderr.write(u'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
00407 sys.exit(1)
00408
00409
00410
00411
00412
00413 version = ''
00414 for digit in etree.LIBXML_VERSION:
00415 version+=str(digit)+'.'
00416 version = version[:-1]
00417 if version < '2.7.2':
00418 sys.stderr.write(u'''
00419 ! Error - The installed version of the "lxml" python library "libxml" version is too old.
00420 At least "libxml" version 2.7.2 must be installed. Your version is (%s).
00421 ''' % version)
00422 sys.exit(1)
00423
00424
00425
00426 http_find="http://www.thetvdb.com"
00427 http_replace="http://www.thetvdb.com"
00428
00429 logfile="/tmp/ttvdb.log"
00430
00431 name_parse=[
00432
00433 re.compile('''^(.+?)[ \._\-]\[[Ss]([0-9]+?)\]_\[[Ee]([0-9]+?)\]?[^\\/]*$'''),
00434
00435 re.compile('''^(.+?)[ \._\-]\[?([0-9]+)x([0-9]+)[^\\/]*$'''),
00436
00437 re.compile('''^(.+?)[ \._\-][Ss]([0-9]+)[\.\- ]?[Ee]([0-9]+)[^\\/]*$'''),
00438
00439 re.compile('''^(.+)[ \._\-]([0-9]{1})([0-9]{2})[\._ -][^\\/]*$'''),
00440
00441 re.compile('''^(.+)[ \._\-]([0-9]{2})([0-9]{2,3})[\._ -][^\\/]*$'''),
00442 ]
00443
00444
00445 massage={'writer':'|','director':'|', 'overview':'&', 'gueststars':'|' }
00446
00447 data_keys =['seasonnumber','episodenumber','episodename','firstaired','director','overview','rating','writer','filename','language' ]
00448 data_titles=['Season:','Episode:','Subtitle:','ReleaseDate:','Director:','Plot:','UserRating:','Writers:','Screenshot:','Language:' ]
00449
00450 fanart_key='fanart'
00451 banner_key='series'
00452 poster_key='poster'
00453 season_key='season'
00454
00455 poster_series_key='680x1000'
00456 poster_season_key='season'
00457 fanart_hires_key='1920x1080'
00458 fanart_lowres_key='1280x720'
00459 banner_series_key='graphical'
00460 banner_season_key='seasonwide'
00461
00462 poster_type='Poster'
00463 fanart_type='Fanart'
00464 banner_type='Banner'
00465 screenshot_request = False
00466
00467
00468 cache_dir="/tmp/tvdb_api_%s/" % os.geteuid()
00469
00470 def _can_int(x):
00471 """Takes a string, checks if it is numeric.
00472 >>> _can_int("2")
00473 True
00474 >>> _can_int("A test")
00475 False
00476 """
00477 try:
00478 int(x)
00479 except ValueError:
00480 return False
00481 else:
00482 return True
00483
00484
00485 def debuglog(message):
00486 message+='\n'
00487 target_socket = open(logfile, "a")
00488 target_socket.write(message)
00489 target_socket.close()
00490 return
00491
00492
00493 class OutStreamEncoder(object):
00494 """Wraps a stream with an encoder"""
00495 def __init__(self, outstream, encoding=None):
00496 self.out = outstream
00497 if not encoding:
00498 self.encoding = sys.getfilesystemencoding()
00499 else:
00500 self.encoding = encoding
00501
00502 def write(self, obj):
00503 """Wraps the output stream, encoding Unicode strings with the specified encoding"""
00504 if isinstance(obj, unicode):
00505 self.out.write(obj.encode(self.encoding))
00506 else:
00507 self.out.write(obj)
00508
00509 def __getattr__(self, attr):
00510 """Delegate everything but write to the stream"""
00511 return getattr(self.out, attr)
00512 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
00513 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
00514
00515
00516 class Show( tvdb_api.Show ):
00517 def fuzzysearch(self, term = None, key = None):
00518 results = []
00519 for cur_season in self.values():
00520 searchresult = cur_season.fuzzysearch(term = term, key = key)
00521 if len(searchresult) != 0:
00522 results.extend(searchresult)
00523 return results
00524
00525
00526
00527 class Season( tvdb_api.Season ):
00528 def fuzzysearch(self, term = None, key = None):
00529 results = []
00530 for episode in self.values():
00531 searchresult = episode.fuzzysearch(term = term, key = key)
00532 if searchresult is not None:
00533 results.append(searchresult)
00534 return results
00535
00536
00537
00538 class Episode( tvdb_api.Episode ):
00539 _re_strippart = re.compile('(.*) \([0-9]+\)')
00540 def fuzzysearch(self, term = None, key = None):
00541 if term == None:
00542 raise TypeError("must supply string to search for (contents)")
00543
00544 term = unicode(term).lower()
00545 for cur_key, cur_value in self.items():
00546 cur_key, cur_value = [unicode(a).lower() for a in [cur_key, cur_value]]
00547 if key is not None and cur_key != key:
00548 continue
00549 distance = levenshtein(cur_value, term)
00550 if distance <= 3:
00551
00552 self.distance = distance
00553 return self
00554 if distance <= 5:
00555
00556 match = self._re_strippart.match(cur_value)
00557 if match:
00558 tmp = match.group(1)
00559 if levenshtein(tmp, term) <= 3:
00560 self.distance = distance
00561 return self
00562 return None
00563
00564
00565
00566 class Tvdb( tvdb_api.Tvdb ):
00567 def series_by_sid(self, sid):
00568 "Lookup a series via it's sid"
00569 seriesid = 'sid:' + sid
00570 if not self.corrections.has_key(seriesid):
00571 self._getShowData(sid)
00572 self.corrections[seriesid] = sid
00573 return self.shows[sid]
00574
00575
00576
00577 def _setItem(self, sid, seas, ep, attrib, value):
00578 if sid not in self.shows:
00579 self.shows[sid] = Show()
00580 if seas not in self.shows[sid]:
00581 self.shows[sid][seas] = Season()
00582 if ep not in self.shows[sid][seas]:
00583 self.shows[sid][seas][ep] = Episode()
00584 self.shows[sid][seas][ep][attrib] = value
00585
00586
00587
00588 def _setShowData(self, sid, key, value):
00589 if sid not in self.shows:
00590 self.shows[sid] = Show()
00591 self.shows[sid].data[key] = value
00592
00593
00594
00595
00596 def search_for_series(tvdb, sid_or_name):
00597 "Get series data by sid or series name of the Tv show"
00598 if SID == True:
00599 return tvdb.series_by_sid(sid_or_name)
00600 else:
00601 return tvdb[sid_or_name]
00602
00603
00604
00605 def searchseries(t, opts, series_season_ep):
00606 global SID
00607 series_name=''
00608 if opts.configure != "" and override.has_key(series_season_ep[0].lower()):
00609 series_name=override[series_season_ep[0].lower()][0]
00610 else:
00611 series_name=series_season_ep[0]
00612 try:
00613
00614 if len(series_season_ep)>1:
00615 if len(series_season_ep)>2:
00616 seriesfound=search_for_series(t, series_name)[ int(series_season_ep[1]) ][ int(series_season_ep[2]) ]
00617 else:
00618 seriesfound=search_for_series(t, series_name)[ int(series_season_ep[1]) ]
00619 else:
00620 seriesfound=search_for_series(t, series_name)
00621 except tvdb_shownotfound:
00622
00623
00624 sys.exit(0)
00625 except (tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound):
00626
00627
00628 sys.exit(0)
00629 except tvdb_error, errormsg:
00630
00631 if SID == True:
00632 SID = False
00633 return searchseries(t, opts, series_season_ep)
00634 sys.exit(0)
00635 except tvdb_userabort, errormsg:
00636
00637 print "\n", errormsg
00638 sys.exit(0)
00639 else:
00640 if opts.raw==True:
00641 print "="*20
00642 print "Raw Series Data:\n"
00643 if len(series_season_ep)>1:
00644 print t[ series_name ][ int(series_season_ep[1]) ]
00645 else:
00646 print t[ series_name ]
00647 print "="*20
00648 return(seriesfound)
00649
00650
00651
00652 def get_graphics(t, opts, series_season_ep, graphics_type, single_option, language=False):
00653 banners='_banners'
00654 series_name=''
00655 graphics=[]
00656 if opts.configure != "" and override.has_key(series_season_ep[0].lower()):
00657 series_name=override[series_season_ep[0].lower()][0]
00658 else:
00659 series_name=series_season_ep[0]
00660
00661 if SID == True:
00662 URLs = t.ttvdb_parseBanners(series_name)
00663 else:
00664 URLs = t.ttvdb_parseBanners(t._nameToSid(series_name))
00665
00666 if graphics_type == fanart_type:
00667 if not len(URLs[u'fanart']):
00668 return []
00669 for url in URLs[u'fanart']:
00670 graphics.append(url)
00671 elif len(series_season_ep) == 1:
00672 if not len(URLs[u'series']):
00673 return []
00674 if graphics_type == banner_type:
00675 for url in URLs[u'series']:
00676 graphics.append(url)
00677 else:
00678 for url in URLs[u'poster']:
00679 graphics.append(url)
00680 else:
00681 if not len(URLs[u'season']):
00682 return []
00683 if graphics_type == banner_type:
00684 season_banners=[]
00685 for url in URLs[u'season']:
00686 if url[u'bannertype2'] == u'seasonwide' and url[u'season'] == series_season_ep[1]:
00687 season_banners.append(url)
00688 if not len(season_banners):
00689 return []
00690 graphics = season_banners
00691 else:
00692 season_posters=[]
00693 for url in URLs[u'season']:
00694 if url[u'bannertype2'] == u'season' and url[u'season'] == series_season_ep[1]:
00695 season_posters.append(url)
00696 if not len(season_posters):
00697 return []
00698 graphics = season_posters
00699
00700 graphicsURLs=[]
00701 if single_option==False:
00702 graphicsURLs.append(graphics_type+':')
00703
00704 count = 0
00705 wasanythingadded = 0
00706 anyotherlanguagegraphics=[]
00707 englishlanguagegraphics=[]
00708 for URL in graphics:
00709 if graphics_type == 'filename':
00710 if URL[graphics_type] == None:
00711 continue
00712 if language:
00713 if language == URL['language']:
00714 graphicsURLs.append((URL['_bannerpath']).replace(http_find, http_replace))
00715 else:
00716 if u'en' == URL['language']:
00717 englishlanguagegraphics.append((URL['_bannerpath']).replace(http_find, http_replace))
00718 else:
00719 anyotherlanguagegraphics.append((URL['_bannerpath']).replace(http_find, http_replace))
00720 else:
00721 graphicsURLs.append((URL['_bannerpath']).replace(http_find, http_replace))
00722 if wasanythingadded == len(graphicsURLs):
00723 continue
00724 wasanythingadded = len(graphicsURLs)
00725
00726 if not len(graphicsURLs):
00727 if len(englishlanguagegraphics):
00728 graphicsURLs = englishlanguagegraphics
00729 elif len(anyotherlanguagegraphics):
00730 graphicsURLs = anyotherlanguagegraphics
00731
00732 if opts.debug == True:
00733 print u"\nGraphics:\n", graphicsURLs
00734
00735 if len(graphicsURLs) == 1 and graphicsURLs[0] == graphics_type+':':
00736 return []
00737 return(graphicsURLs)
00738
00739
00740
00741 def massageEpisode_name(ep_name, series_season_ep):
00742 for edit in override[series_season_ep[0].lower()][1]:
00743 ep_name=ep_name.replace(edit[0],edit[1])
00744 return ep_name
00745
00746
00747
00748 def change_to_commas(meta_data):
00749 if not meta_data: return meta_data
00750 meta_data = (u'|'.join([d for d in meta_data.split('| ') if d]))
00751 return (u', '.join([d for d in meta_data.split('|') if d]))
00752
00753
00754
00755 def change_amp(text):
00756 if not text: return text
00757 text = text.replace(""", "'").replace("\r\n", " ")
00758 text = text.replace(r"\'", "'")
00759 return text
00760
00761
00762
00763 def make_db_ready(text):
00764 if not text: return text
00765 text = text.replace(u'\u2013', "-")
00766 text = text.replace(u'\u2014', "-")
00767 text = text.replace(u'\u2018', "'")
00768 text = text.replace(u'\u2019', "'")
00769 text = text.replace(u'\u2026', "...")
00770 text = text.replace(u'\u201c', '"')
00771 text = text.replace(u'\u201d', '"')
00772 text = text.encode('latin-1', 'backslashreplace')
00773 return text
00774
00775
00776
00777 def Getseries_episode_data(t, opts, series_season_ep, language = None):
00778 global screenshot_request, http_find, http_replace
00779
00780 args = len(series_season_ep)
00781 series_name=''
00782 if opts.configure != "" and override.has_key(series_season_ep[0].lower()):
00783 series_name=override[series_season_ep[0].lower()][0]
00784 else:
00785 series_name=series_season_ep[0]
00786
00787
00788 cast_members=''
00789 try:
00790 tmp_cast = search_for_series(t, series_name)['_actors']
00791 except:
00792 cast_members=''
00793 if len(tmp_cast):
00794 cast_members=''
00795 for cast in tmp_cast:
00796 if cast['name']:
00797 cast_members+=(cast['name']+u', ').encode('utf8')
00798 if cast_members != '':
00799 try:
00800 cast_members = cast_members[:-2].encode('utf8')
00801 except UnicodeDecodeError:
00802 cast_members = unicode(cast_members[:-2],'utf8')
00803 cast_members = change_amp(cast_members)
00804 cast_members = change_to_commas(cast_members)
00805 cast_members=cast_members.replace('\n',' ')
00806
00807
00808 genres=''
00809 try:
00810 genres_string = search_for_series(t, series_name)[u'genre'].encode('utf-8')
00811 except:
00812 genres_string=''
00813 if genres_string != None and genres_string != '':
00814 genres = change_amp(genres_string)
00815 genres = change_to_commas(genres)
00816
00817 seasons=search_for_series(t, series_name).keys()
00818 for season in seasons:
00819 if args > 1:
00820 if season != int(series_season_ep[1]):
00821 continue
00822 episodes=search_for_series(t, series_name)[season].keys()
00823 for episode in episodes:
00824 if args > 2:
00825 if episode != int(series_season_ep[2]):
00826 continue
00827 extra_ep_data=[]
00828 available_keys=search_for_series(t, series_name)[season][episode].keys()
00829 if screenshot_request:
00830 if u'filename' in available_keys:
00831 screenshot = search_for_series(t, series_name)[season][episode][u'filename']
00832 if screenshot:
00833 print screenshot.replace(http_find, http_replace)
00834 return
00835 else:
00836 return
00837 key_values=[]
00838 for values in data_keys:
00839 key_values.append('')
00840 for key in available_keys:
00841 try:
00842 i = data_keys.index(key)
00843 except ValueError:
00844 if search_for_series(t, series_name)[season][episode][key] != None:
00845 text = search_for_series(t, series_name)[season][episode][key]
00846 text = change_amp(text)
00847 text = change_to_commas(text)
00848 if text == 'None' and key.title() == 'Director':
00849 text = u"Unknown"
00850 try:
00851 extra_ep_data.append(u"%s:%s" % (key.title(), text))
00852 except UnicodeDecodeError:
00853 extra_ep_data.append(u"%s:%s" % (key.title(), unicode(text, "utf8")))
00854 continue
00855 text = search_for_series(t, series_name)[season][episode][key]
00856
00857 if text == None and key.title() == 'Director':
00858 text = u"Unknown"
00859 if text == None or text == 'None':
00860 continue
00861 else:
00862 text = change_amp(text)
00863 value = change_to_commas(text)
00864 value = value.replace(u'\n', u' ')
00865 key_values[i]=value
00866 index = 0
00867 if SID == False:
00868 print u"Title:%s" % series_name
00869 else:
00870 print u"Title:%s" % search_for_series(t, series_name)[u'seriesname']
00871
00872 for key in data_titles:
00873 if key_values[index] != None:
00874 if data_titles[index] == u'ReleaseDate:' and len(key_values[index]) > 4:
00875 print u'%s%s'% (u'Year:', key_values[index][:4])
00876 if key_values[index] != 'None':
00877 print u'%s%s' % (data_titles[index], key_values[index])
00878 index+=1
00879 cast_print=False
00880 for extra_data in extra_ep_data:
00881 if extra_data[:extra_data.index(':')] == u'Gueststars':
00882 extra_cast = extra_data[extra_data.index(':')+1:]
00883 if (len(extra_cast)>128) and not extra_cast.count(','):
00884 continue
00885 if cast_members:
00886 extra_data=(u"Cast:%s" % cast_members)+', '+extra_cast
00887 else:
00888 extra_data=u"Cast:%s" % extra_cast
00889 cast_print=True
00890 print extra_data
00891 if cast_print == False:
00892 print u"Cast:%s" % cast_members
00893 if genres != '':
00894 print u"Genres:%s" % genres
00895 print u"Runtime:%s" % search_for_series(t, series_name)[u'runtime']
00896
00897
00898 for url_data in [u'seriesid', u'seasonid', u'id']:
00899 if not url_data in available_keys:
00900 break
00901 else:
00902 print u'URL:http://www.thetvdb.com/?tab=episode&seriesid=%s&seasonid=%s&id=%s' % (search_for_series(t, series_name)[season][episode][u'seriesid'], search_for_series(t, series_name)[season][episode][u'seasonid'],search_for_series(t, series_name)[season][episode][u'id'])
00903
00904
00905
00906 def Getseries_episode_numbers(t, opts, series_season_ep):
00907 def _episode_sort(episode):
00908 seasonnumber = 0
00909 episodenumber = 0
00910 try: seasonnumber = int(episode['seasonnumber'])
00911 except: pass
00912 try: episodenumber = int(episode['episodenumber'])
00913 except: pass
00914 return (-episode.distance, seasonnumber, episodenumber)
00915
00916 global xmlFlag
00917 series_name=''
00918 ep_name=''
00919 if opts.configure != "" and override.has_key(series_season_ep[0].lower()):
00920 series_name=override[series_season_ep[0].lower()][0]
00921 ep_name=series_season_ep[1]
00922 if len(override[series_season_ep[0].lower()][1]) != 0:
00923 ep_name=massageEpisode_name(ep_name, series_season_ep)
00924 else:
00925 series_name=series_season_ep[0]
00926 ep_name=series_season_ep[1]
00927
00928 season_ep_num=search_for_series(t, series_name).fuzzysearch(ep_name, 'episodename')
00929 if len(season_ep_num) != 0:
00930 for episode in sorted(season_ep_num, key=lambda ep: _episode_sort(ep), reverse=True):
00931
00932 if xmlFlag:
00933 displaySeriesXML(t, [series_name, episode['seasonnumber'], episode['episodenumber']])
00934 sys.exit(0)
00935 print season_and_episode_num.replace('\\n', '\n') % (int(episode['seasonnumber']), int(episode['episodenumber']))
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947 class returnAllSeriesUI(tvdb_ui.BaseUI):
00948 def __init__(self, config, log):
00949 self.config = config
00950 self.log = log
00951
00952 def selectSeries(self, allSeries):
00953 return allSeries
00954
00955
00956 def initialize_override_dictionary(useroptions):
00957 """ Change variables through a user supplied configuration file
00958 return False and exit the script if there are issues with the configuration file values
00959 """
00960 if useroptions[0]=='~':
00961 useroptions=os.path.expanduser("~")+useroptions[1:]
00962 if os.path.isfile(useroptions) == False:
00963 sys.stderr.write(
00964 "! The specified user configuration file (%s) is not a file\n" % useroptions
00965 )
00966 sys.exit(1)
00967 massage = {}
00968 overrides = {}
00969 cfg = ConfigParser.SafeConfigParser()
00970 cfg.read(useroptions)
00971
00972 for section in cfg.sections():
00973 if section == 'regex':
00974
00975 for option in cfg.options(section):
00976 name_parse.append(re.compile(cfg.get(section, option)))
00977 continue
00978 if section =='ep_name_massage':
00979 for option in cfg.options(section):
00980 tmp =cfg.get(section, option).split(',')
00981 if len(tmp)%2 and len(cfg.get(section, option)) != 0:
00982 sys.stderr.write("! For (%s) 'ep_name_massage' values must be in pairs\n" % option)
00983 sys.exit(1)
00984 tmp_array=[]
00985 i=0
00986 while i != len(tmp):
00987 tmp_array.append([tmp[i].replace('"',''), tmp[i+1].replace('"','')])
00988 i+=2
00989 massage[option]=tmp_array
00990 continue
00991 if section =='series_name_override':
00992 for option in cfg.options(section):
00993 overrides[option] = cfg.get(section, option)
00994 tvdb = Tvdb(banners=False, debug = False, interactive = False, cache = cache_dir, custom_ui=returnAllSeriesUI, apikey="0BB856A59C51D607")
00995 for key in overrides.keys():
00996 sid = overrides[key]
00997 if len(sid) == 0:
00998 continue
00999 try:
01000 dummy = int(sid)
01001 except:
01002 sys.stdout.write("! Series (%s) Invalid SID (not numeric) [%s] in config file\n" % (key, sid))
01003 sys.exit(1)
01004
01005 if len(key.replace(' ','')) == 0:
01006 sys.stdout.write("! Invalid Series name (must have some non-blank characters) [%s] in config file\n" % key)
01007 print parts
01008 sys.exit(1)
01009
01010 try:
01011 series_name_sid=tvdb.series_by_sid(sid)
01012 except:
01013 sys.stdout.write("! Invalid Series (no matches found in thetvdb,com) (%s) sid (%s) in config file\n" % (key, sid))
01014 sys.exit(1)
01015 overrides[key]=series_name_sid[u'seriesname'].encode('utf-8')
01016 continue
01017
01018 for key in overrides.keys():
01019 override[key] = [overrides[key],[]]
01020
01021 for key in massage.keys():
01022 if override.has_key(key):
01023 override[key][1]=massage[key]
01024 else:
01025 override[key]=[key, massage[key]]
01026 return
01027
01028
01029 def initializeXslt(language):
01030 ''' Initalize all data and functions for XSLT stylesheet processing
01031 return nothing
01032 '''
01033 global xslt, tvdbXpath
01034 try:
01035 import MythTV.ttvdb.tvdbXslt as tvdbXslt
01036 except Exception, errmsg:
01037 sys.stderr.write('! Error: Importing tvdbXslt error(%s)\n' % errmsg)
01038 sys.exit(1)
01039
01040 xslt = tvdbXslt.xpathFunctions()
01041 xslt.language = language
01042 xslt.buildFuncDict()
01043 tvdbXpath = etree.FunctionNamespace('http://www.mythtv.org/wiki/MythTV_Universal_Metadata_Format')
01044 tvdbXpath.prefix = 'tvdbXpath'
01045 for key in xslt.FuncDict.keys():
01046 tvdbXpath[key] = xslt.FuncDict[key]
01047 return
01048
01049
01050 def displaySearchXML(tvdb_api):
01051 '''Using a XSLT style sheet translate TVDB search results into the MythTV Universal Query format
01052 return nothing
01053 '''
01054 global xslt, tvdbXpath
01055
01056
01057 if xslt.language != 'en':
01058 compareFilter = etree.XPath('//id[text()=$Id]')
01059 idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
01060 tmpTree = deepcopy(tvdb_api.searchTree)
01061 for seriesId in tmpTree.xpath('//Series/id/text()'):
01062 if len(compareFilter(tvdb_api.searchTree, Id=seriesId)) > 1:
01063 tmpList = idLangFilter(tvdb_api.searchTree, Id=seriesId)
01064 if len(tmpList):
01065 tvdb_api.searchTree.remove(tmpList[0])
01066
01067 tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbQuery.xsl')))
01068 items = tvdbQueryXslt(tvdb_api.searchTree)
01069 if items.getroot() != None:
01070 if len(items.xpath('//item')):
01071 sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
01072 sys.exit(0)
01073
01074
01075 def displaySeriesXML(tvdb_api, series_season_ep):
01076 '''Using a XSLT style sheet translate TVDB Series data results into the
01077 MythTV Universal Query format
01078 return nothing
01079 '''
01080 global xslt, tvdbXpath
01081 allDataElement = etree.XML(u'<allData></allData>')
01082 requestDetails = etree.XML(u'<requestDetails></requestDetails>')
01083 requestDetails.attrib['lang'] = xslt.language
01084 requestDetails.attrib['series'] = series_season_ep[0]
01085 requestDetails.attrib['season'] = series_season_ep[1]
01086 requestDetails.attrib['episode'] = series_season_ep[2]
01087 allDataElement.append(requestDetails)
01088
01089
01090 if tvdb_api.epInfoTree != None:
01091 allDataElement.append(tvdb_api.epInfoTree)
01092 else:
01093 sys.exit(0)
01094 if tvdb_api.actorsInfoTree != None:
01095 allDataElement.append(tvdb_api.actorsInfoTree)
01096 else:
01097 allDataElement.append(etree.XML(u'<Actors></Actors>'))
01098 if tvdb_api.imagesInfoTree != None:
01099 allDataElement.append(tvdb_api.imagesInfoTree)
01100 else:
01101 allDataElement.append(etree.XML(u'<Banners></Banners>'))
01102
01103 tvdbQueryXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbVideo.xsl')))
01104 items = tvdbQueryXslt(allDataElement)
01105 if items.getroot() != None:
01106 if len(items.xpath('//item')):
01107 sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
01108 sys.exit(0)
01109
01110
01111 def displayCollectionXML(tvdb_api):
01112 '''Using a XSLT style sheet translate TVDB series results into the MythTV Universal Query format
01113 return nothing
01114 '''
01115 global xslt, tvdbXpath
01116
01117
01118 if xslt.language != 'en':
01119 compareFilter = etree.XPath('//id[text()=$Id]')
01120 idLangFilter = etree.XPath('//id[text()=$Id]/../language[text()="en"]/..')
01121 tmpTree = deepcopy(tvdb_api.seriesInfoTree)
01122 for seriesId in tmpTree.xpath('//Series/id/text()'):
01123 if len(compareFilter(tvdb_api.seriesInfoTree, Id=seriesId)) > 1:
01124 tmpList = idLangFilter(tvdb_api.seriesInfoTree, Id=seriesId)
01125 if len(tmpList):
01126 tvdb_api.seriesInfoTree.remove(tmpList[0])
01127
01128 tvdbCollectionXslt = etree.XSLT(etree.parse(u'%s%s' % (tvdb_api.baseXsltDir, u'tvdbCollection.xsl')))
01129 items = tvdbCollectionXslt(tvdb_api.seriesInfoTree)
01130 if items.getroot() != None:
01131 if len(items.xpath('//item')):
01132 sys.stdout.write(etree.tostring(items, encoding='UTF-8', method="xml", xml_declaration=True, pretty_print=True, ))
01133 sys.exit(0)
01134
01135
01136 def main():
01137 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")
01138
01139 parser.add_option( "-d", "--debug", action="store_true", default=False, dest="debug",
01140 help=u"Show debugging info")
01141 parser.add_option( "-r", "--raw", action="store_true",default=False, dest="raw",
01142 help=u"Dump raw data only")
01143 parser.add_option( "-u", "--usage", action="store_true", default=False, dest="usage",
01144 help=u"Display examples for executing the ttvdb script")
01145 parser.add_option( "-v", "--version", action="store_true", default=False, dest="version",
01146 help=u"Display version and author")
01147 parser.add_option( "-i", "--interactive", action="store_true", default=False, dest="interactive",
01148 help=u"Interaction mode (allows selection of a specific Series)")
01149 parser.add_option( "-c", "--configure", metavar="FILE", default="", dest="configure",
01150 help=u"Use configuration settings")
01151 parser.add_option( "-l", "--language", metavar="LANGUAGE", default=u'en', dest="language",
01152 help=u"Select data that matches the specified language fall back to english if nothing found (e.g. 'es' Español, 'de' Deutsch ... etc)")
01153 parser.add_option( "-n", "--num_seasons", action="store_true", default=False, dest="num_seasons",
01154 help=u"Return the season numbers for a series")
01155 parser.add_option( "-t", action="store_true", default=False, dest="test",
01156 help=u"Test for the availability of runtime dependencies")
01157 parser.add_option( "-m", "--mythvideo", action="store_true", default=False, dest="mythvideo",
01158 help=u"Conform to mythvideo standards when processing -M, -P, -F and -D")
01159 parser.add_option( "-M", "--list", action="store_true", default=False, dest="list",
01160 help=u"Get matching TV Series list")
01161 parser.add_option( "-P", "--poster", action="store_true", default=False, dest="poster",
01162 help=u"Get Series Poster URL(s)")
01163 parser.add_option( "-F", "--fanart", action="store_true", default=False, dest="fanart",
01164 help=u"Get Series fan art URL(s)")
01165 parser.add_option( "-B", "--backdrop", action="store_true", default=False, dest="banner",
01166 help=u"Get Series banner/backdrop URL(s)")
01167 parser.add_option( "-S", "--screenshot", action="store_true", default=False, dest="screenshot",
01168 help=u"Get Series episode screenshot URL")
01169 parser.add_option( "-D", "--data", action="store_true", default=False, dest="data",
01170 help=u"Get Series episode data")
01171 parser.add_option( "-N", "--numbers", action="store_true", default=False, dest="numbers",
01172 help=u"Get Season and Episode numbers")
01173 parser.add_option( "-C", "--collection", action="store_true", default=False, dest="collection",
01174 help=u'Get a TV Series (collection) "series" level information')
01175
01176 opts, series_season_ep = parser.parse_args()
01177
01178
01179
01180 if opts.test:
01181 print "Everything appears to be in order"
01182 sys.exit(0)
01183
01184
01185 for index in range(len(series_season_ep)):
01186 series_season_ep[index] = unicode(series_season_ep[index], 'utf8')
01187
01188 if opts.debug == True:
01189 print "opts", opts
01190 print "\nargs", series_season_ep
01191
01192
01193 if opts.version == True:
01194 version = etree.XML(u'<grabber></grabber>')
01195 etree.SubElement(version, "name").text = __title__
01196 etree.SubElement(version, "author").text = __author__
01197 etree.SubElement(version, "thumbnail").text = 'ttvdb.png'
01198 etree.SubElement(version, "command").text = 'ttvdb.py'
01199 etree.SubElement(version, "type").text = 'television'
01200 etree.SubElement(version, "description").text = 'Search and metadata downloads for thetvdb.com'
01201 etree.SubElement(version, "version").text = __version__
01202 sys.stdout.write(etree.tostring(version, encoding='UTF-8', pretty_print=True))
01203 sys.exit(0)
01204
01205
01206 if opts.usage == True:
01207 sys.stdout.write(usage_txt)
01208 sys.exit(0)
01209
01210 if len(series_season_ep) == 0:
01211 parser.error("! No series or series season episode supplied")
01212 sys.exit(1)
01213
01214
01215 global season_and_episode_num, screenshot_request
01216 season_and_episode_num='S%02dE%02d'
01217
01218 if opts.numbers == False:
01219 if len(series_season_ep) > 1:
01220 if not _can_int(series_season_ep[1]):
01221 parser.error("! Season is not numeric")
01222 sys.exit(1)
01223 if len(series_season_ep) > 2:
01224 if not _can_int(series_season_ep[2]):
01225 parser.error("! Episode is not numeric")
01226 sys.exit(1)
01227 else:
01228 if len(series_season_ep) < 2:
01229 parser.error("! An Episode name must be included")
01230 sys.exit(1)
01231 if len(series_season_ep) == 3:
01232 season_and_episode_num = series_season_ep[2]
01233
01234 if opts.screenshot:
01235 if len(series_season_ep) > 1:
01236 if not _can_int(series_season_ep[1]):
01237 parser.error("! Season is not numeric")
01238 sys.exit(1)
01239 if len(series_season_ep) > 2:
01240 if not _can_int(series_season_ep[2]):
01241 parser.error("! Episode is not numeric")
01242 sys.exit(1)
01243 if not len(series_season_ep) > 2:
01244 parser.error("! Option (-S), episode screenshot search requires Season and Episode numbers")
01245 sys.exit(1)
01246 screenshot_request = True
01247
01248 if opts.debug == True:
01249 print series_season_ep
01250
01251 if opts.debug == True:
01252 print "#"*20
01253 print "# series_season_ep array(",series_season_ep,")"
01254
01255 if opts.debug == True:
01256 print "#"*20
01257 print "# Starting tvtvb"
01258 print "# Processing (%s) Series" % ( series_season_ep[0] )
01259
01260
01261
01262
01263 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"]
01264
01265
01266 if not opts.language in valid_languages:
01267 opts.language = 'en'
01268
01269
01270 opts.xml = True
01271 initializeXslt(opts.language)
01272
01273
01274 if opts.list ==True:
01275 t = Tvdb(banners=False, debug = opts.debug, cache = cache_dir, custom_ui=returnAllSeriesUI, language = opts.language, apikey="0BB856A59C51D607")
01276 if opts.xml:
01277 t.xml = True
01278 elif opts.interactive == True:
01279 t = Tvdb(banners=True, debug=opts.debug, interactive=True, select_first=False, cache=cache_dir, actors = True, language = opts.language, apikey="0BB856A59C51D607")
01280 if opts.xml:
01281 t.xml = True
01282 else:
01283 t = Tvdb(banners=True, debug = opts.debug, cache = cache_dir, actors = True, language = opts.language, apikey="0BB856A59C51D607")
01284 if opts.xml:
01285 t.xml = True
01286
01287
01288 global SID
01289 SID = False
01290 if _can_int(series_season_ep[0]):
01291 SID = True
01292 else:
01293 SID = False
01294
01295
01296 if opts.collection:
01297 if SID:
01298 pass
01299 else:
01300 parser.error("! Option (-C), collection requires an inetref number")
01301 sys.exit(1)
01302
01303 if opts.debug == True:
01304 print "# ..got tvdb mirrors"
01305 print "# Start to process series or series_season_ep"
01306 print "#"*20
01307
01308 global override
01309 override={}
01310
01311 if opts.configure != '':
01312 if opts.configure[0]=='~':
01313 opts.configure=os.path.expanduser("~")+opts.configure[1:]
01314 if os.path.exists(opts.configure) == 1:
01315 initialize_override_dictionary(opts.configure)
01316 else:
01317 debuglog("! The specified override file (%s) does not exist" % opts.configure)
01318 sys.exit(1)
01319 else:
01320 default_config = u"%s/%s" % (os.path.expanduser(u"~"), u".mythtv/ttvdb.conf")
01321 if os.path.isfile(default_config):
01322 opts.configure = default_config
01323 initialize_override_dictionary(opts.configure)
01324
01325 if len(override) == 0:
01326 opts.configure = False
01327
01328
01329 if not opts.collection and len(series_season_ep) == 1:
01330 for r in name_parse:
01331 match = r.match(series_season_ep[0])
01332 if match:
01333 seriesname, seasno, epno = match.groups()
01334
01335 seriesname = re.sub("[\._]|\-(?=$)", " ", seriesname).strip()
01336 series_season_ep = [seriesname, seasno, epno]
01337 break
01338
01339
01340 if opts.list ==True:
01341 try:
01342 allSeries=t._getSeries(series_season_ep[0])
01343 except tvdb_shownotfound:
01344 sys.exit(0)
01345 except Exception, e:
01346 sys.stderr.write("! Error: %s\n" % (e))
01347 sys.exit(1)
01348 if opts.xml:
01349 displaySearchXML(t)
01350 sys.exit(0)
01351 match_list = []
01352 for series_name_sid in allSeries:
01353 key_value = u"%s:%s" % (series_name_sid['sid'], series_name_sid['name'])
01354 if not key_value in match_list:
01355 match_list.append(key_value)
01356 print key_value
01357 sys.exit(0)
01358
01359
01360 if opts.collection:
01361 try:
01362 t._getShowData(series_season_ep[0])
01363 except tvdb_shownotfound:
01364 sys.exit(0)
01365 except Exception, e:
01366 sys.stderr.write("! Error: %s\n" % (e))
01367 sys.exit(1)
01368 displayCollectionXML(t)
01369 sys.exit(0)
01370
01371
01372
01373 if opts.numbers == False and opts.num_seasons == False:
01374 seriesfound=searchseries(t, opts, series_season_ep)
01375 x=1
01376 else:
01377 x=[]
01378 x.append(series_season_ep[0])
01379 seriesfound=searchseries(t, opts, x)
01380
01381
01382 if opts.num_seasons == True:
01383 season_numbers=''
01384 for x in seriesfound.keys():
01385 season_numbers+='%d,' % x
01386 print season_numbers[:-1]
01387 sys.exit(0)
01388
01389
01390 if opts.debug == True:
01391 print "#"*20
01392 print "# Starting Raw keys call"
01393 print "Lvl #1:"
01394 x = t[series_season_ep[0]].keys()
01395 print t[series_season_ep[0]].keys()
01396 print "#"*20
01397 print "Lvl #2:"
01398 for y in x:
01399 print t[series_season_ep[0]][y].keys()
01400 print "#"*20
01401 print "Lvl #3:"
01402 z = t[series_season_ep[0]][1].keys()
01403 for aa in z:
01404 print t[series_season_ep[0]][1][aa].keys()
01405 print "#"*20
01406 print "Lvl #4:"
01407 for aa in z:
01408 codes = t[series_season_ep[0]][1][aa].keys()
01409 print "\n\nStart:"
01410 for c in codes:
01411 print "="*50
01412 print 'Key Name=('+c+'):'
01413 print t[series_season_ep[0]][1][aa][c]
01414 print "="*50
01415 print "#"*20
01416 sys.exit (True)
01417
01418 if opts.numbers == True:
01419 global xmlFlag
01420 if opts.xml:
01421 xmlFlag = True
01422 else:
01423 xmlFlag = False
01424 Getseries_episode_numbers(t, opts, series_season_ep)
01425 sys.exit(0)
01426
01427 if opts.data or screenshot_request:
01428 if opts.mythvideo:
01429 if len(series_season_ep) != 3:
01430 print u"Season and Episode numbers required."
01431 else:
01432 if opts.xml:
01433 displaySeriesXML(t, series_season_ep)
01434 sys.exit(0)
01435 Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
01436 else:
01437 if opts.xml and len(series_season_ep) == 3:
01438 displaySeriesXML(t, series_season_ep)
01439 sys.exit(0)
01440 Getseries_episode_data(t, opts, series_season_ep, language=opts.language)
01441
01442
01443 if opts.debug == True:
01444 print "#"*20
01445 print "# Checking if Posters, Fanart or Banners are available"
01446 print "#"*20
01447
01448 if opts.configure != "" and override.has_key(series_season_ep[0].lower()):
01449 banners_keys = search_for_series(t, override[series_season_ep[0].lower()][0])['_banners'].keys()
01450 else:
01451 banners_keys = search_for_series(t, series_season_ep[0])['_banners'].keys()
01452
01453 banner= False
01454 poster= False
01455 fanart= False
01456
01457 for x in banners_keys:
01458 if x == fanart_key:
01459 fanart=True
01460 elif x== poster_key:
01461 poster=True
01462 elif x==season_key or x==banner_key:
01463 banner=True
01464
01465
01466 if ( fanart!=True and poster!=True and banner!=True ):
01467 sys.exit(0)
01468
01469 if opts.debug == True:
01470 print "#"*20
01471 print "# One or more of Posters, Fanart or Banners are available"
01472 print "#"*20
01473
01474
01475 if opts.data:
01476 opts.poster = True
01477 opts.fanart = True
01478 opts.banner = True
01479 single_option = True
01480 fanart, banner, poster = (True, True, True)
01481 else:
01482 y=0
01483 single_option=True
01484 if opts.poster==True:
01485 y+=1
01486 if opts.fanart==True:
01487 y+=1
01488 if opts.banner==True:
01489 y+=1
01490
01491 if (poster==True and opts.poster==True and opts.raw!=True):
01492 season_poster_found = False
01493 if opts.mythvideo:
01494 if len(series_season_ep) < 2:
01495 print u"Season and Episode numbers required."
01496 sys.exit(0)
01497 all_posters = u'Coverart:'
01498 all_empty = len(all_posters)
01499 for p in get_graphics(t, opts, series_season_ep, poster_type, single_option, opts.language):
01500 all_posters = all_posters+p+u','
01501 season_poster_found = True
01502 if season_poster_found == False:
01503 series_name=''
01504 if opts.configure != "" and override.has_key(series_season_ep[0].lower()):
01505 series_name=override[series_season_ep[0].lower()][0]
01506 else:
01507 series_name=series_season_ep[0]
01508 for p in get_graphics(t, opts, [series_name], poster_type, single_option, opts.language):
01509 all_posters = all_posters+p+u','
01510 if len(all_posters) > all_empty:
01511 if all_posters[-1] == u',':
01512 print all_posters[:-1]
01513 else:
01514 print all_posters
01515
01516 if (fanart==True and opts.fanart==True and opts.raw!=True):
01517 all_fanart = u'Fanart:'
01518 all_empty = len(all_fanart)
01519 for f in get_graphics(t, opts, series_season_ep, fanart_type, single_option, opts.language):
01520 all_fanart = all_fanart+f+u','
01521 if len(all_fanart) > all_empty:
01522 if all_fanart[-1] == u',':
01523 print all_fanart[:-1]
01524 else:
01525 print all_fanart
01526
01527 if (banner==True and opts.banner==True and opts.raw!=True):
01528 season_banner_found = False
01529 if opts.mythvideo:
01530 if len(series_season_ep) < 2:
01531 print u"Season and Episode numbers required."
01532 sys.exit(0)
01533 all_banners = u'Banner:'
01534 all_empty = len(all_banners)
01535 for b in get_graphics(t, opts, series_season_ep, banner_type, single_option, opts.language):
01536 all_banners = all_banners+b+u','
01537 season_banner_found = True
01538 if not season_banner_found:
01539 series_name=''
01540 if opts.configure != "" and override.has_key(series_season_ep[0].lower()):
01541 series_name=override[series_season_ep[0].lower()][0]
01542 else:
01543 series_name=series_season_ep[0]
01544 for b in get_graphics(t, opts, [series_name], banner_type, single_option, opts.language):
01545 all_banners = all_banners+b+u','
01546 if len(all_banners) > all_empty:
01547 if all_banners[-1] == u',':
01548 print all_banners[:-1]
01549 else:
01550 print all_banners
01551
01552 if opts.debug == True:
01553 print "#"*20
01554 print "# Processing complete"
01555 print "#"*20
01556 sys.exit(0)
01557
01558
01559 if __name__ == "__main__":
01560 main()