00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 __title__ ="rev3_api - Simple-to-use Python interface to the Revision3 RSS feeds (http://revision3.com/)"
00015 __author__="R.D. Vaughan"
00016 __purpose__='''
00017 This python script is intended to perform a variety of utility functions to search and access text
00018 meta data, video and image URLs from rev3. These routines process RSS feeds provided by Revision3
00019 (http://revision3.com/). The specific Revision3 RSS feeds that are processed are controled through
00020 a user XML preference file usually found at "~/.mythtv/MythNetvision/userGrabberPrefs/rev3.xml"
00021 '''
00022
00023 __version__="v0.1.4"
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 import os, struct, sys, re, time, datetime, urllib, re
00034 import logging
00035 from socket import gethostname, gethostbyname
00036 from threading import Thread
00037 from copy import deepcopy
00038
00039 from rev3_exceptions import (Rev3UrlError, Rev3HttpError, Rev3RssError, Rev3VideoNotFound, Rev3ConfigFileError, Rev3UrlDownloadError)
00040
00041 class OutStreamEncoder(object):
00042 """Wraps a stream with an encoder"""
00043 def __init__(self, outstream, encoding=None):
00044 self.out = outstream
00045 if not encoding:
00046 self.encoding = sys.getfilesystemencoding()
00047 else:
00048 self.encoding = encoding
00049
00050 def write(self, obj):
00051 """Wraps the output stream, encoding Unicode strings with the specified encoding"""
00052 if isinstance(obj, unicode):
00053 try:
00054 self.out.write(obj.encode(self.encoding))
00055 except IOError:
00056 pass
00057 else:
00058 try:
00059 self.out.write(obj)
00060 except IOError:
00061 pass
00062
00063 def __getattr__(self, attr):
00064 """Delegate everything but write to the stream"""
00065 return getattr(self.out, attr)
00066 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
00067 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
00068
00069
00070 try:
00071 from StringIO import StringIO
00072 from lxml import etree
00073 except Exception, e:
00074 sys.stderr.write(u'\n! Error - Importing the "lxml" and "StringIO" python libraries failed on error(%s)\n' % e)
00075 sys.exit(1)
00076
00077
00078
00079
00080
00081 version = ''
00082 for digit in etree.LIBXML_VERSION:
00083 version+=str(digit)+'.'
00084 version = version[:-1]
00085 if version < '2.7.2':
00086 sys.stderr.write(u'''
00087 ! Error - The installed version of the "lxml" python library "libxml" version is too old.
00088 At least "libxml" version 2.7.2 must be installed. Your version is (%s).
00089 ''' % version)
00090 sys.exit(1)
00091
00092
00093 class Videos(object):
00094 """Main interface to http://www.rev3.com/
00095 This is done to support a common naming framework for all python Netvision plugins no matter their site
00096 target.
00097
00098 Supports search methods
00099 The apikey is a not required to access http://www.rev3.com/
00100 """
00101 def __init__(self,
00102 apikey,
00103 mythtv = True,
00104 interactive = False,
00105 select_first = False,
00106 debug = False,
00107 custom_ui = None,
00108 language = None,
00109 search_all_languages = False,
00110 ):
00111 """apikey (str/unicode):
00112 Specify the target site API key. Applications need their own key in some cases
00113
00114 mythtv (True/False):
00115 When True, the returned meta data is being returned has the key and values massaged to match MythTV
00116 When False, the returned meta data is being returned matches what target site returned
00117
00118 interactive (True/False): (This option is not supported by all target site apis)
00119 When True, uses built-in console UI is used to select the correct show.
00120 When False, the first search result is used.
00121
00122 select_first (True/False): (This option is not supported currently implemented in any grabbers)
00123 Automatically selects the first series search result (rather
00124 than showing the user a list of more than one series).
00125 Is overridden by interactive = False, or specifying a custom_ui
00126
00127 debug (True/False):
00128 shows verbose debugging information
00129
00130 custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
00131 A callable subclass of interactive class (overrides interactive option)
00132
00133 language (2 character language abbreviation): (This option is not supported by all target site apis)
00134 The language of the returned data. Is also the language search
00135 uses. Default is "en" (English). For full list, run..
00136
00137 search_all_languages (True/False): (This option is not supported by all target site apis)
00138 By default, a Netvision grabber will only search in the language specified using
00139 the language option. When this is True, it will search for the
00140 show in any language
00141
00142 """
00143 self.config = {}
00144
00145 if apikey is not None:
00146 self.config['apikey'] = apikey
00147 else:
00148 pass
00149
00150 self.config['debug_enabled'] = debug
00151 self.common = common
00152 self.common.debug = debug
00153
00154 self.log_name = u'Rev3_Grabber'
00155 self.common.logger = self.common.initLogger(path=sys.stderr, log_name=self.log_name)
00156 self.logger = self.common.logger
00157
00158 self.config['custom_ui'] = custom_ui
00159
00160 self.config['interactive'] = interactive
00161
00162 self.config['select_first'] = select_first
00163
00164 self.config['search_all_languages'] = search_all_languages
00165
00166 self.error_messages = {'Rev3UrlError': u"! Error: The URL (%s) cause the exception error (%s)\n", 'Rev3HttpError': u"! Error: An HTTP communications error with Rev3 was raised (%s)\n", 'Rev3RssError': u"! Error: Invalid RSS meta data\nwas received from Rev3 error (%s). Skipping item.\n", 'Rev3VideoNotFound': u"! Error: Video search with Rev3 did not return any results (%s)\n", 'Rev3ConfigFileError': u"! Error: rev3_config.xml file missing\nit should be located in and named as (%s).\n", 'Rev3UrlDownloadError': u"! Error: Downloading a RSS feed or Web page (%s).\n", }
00167
00168
00169 self.channel = {'channel_title': u'Revision3', 'channel_link': u'http://revision3.com/', 'channel_description': u"Revision3 is the leading television network for the internet generation.", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0}
00170
00171
00172 self.s_e_Patterns = [
00173
00174 re.compile(u'''^.+?Season\\ (?P<seasno>[0-9]+).*.+?Episode\\ (?P<epno>[0-9]+).*$''', re.UNICODE),
00175
00176 re.compile(u'''^.+?Episode\\ (?P<seasno>[0-9]+).*$''', re.UNICODE),
00177
00178 re.compile(u'''Episode\\ (?P<seasno>[0-9]+).*$''', re.UNICODE),
00179
00180 re.compile(u'''^.+?--(?P<seasno>[0-9]+)--.*$''', re.UNICODE),
00181 ]
00182
00183 self.FullScreen = u'http://revision3.com/show/popupPlayer?video_id=%s&quality=high&offset=0'
00184 self.FullScreenParser = self.common.parsers['html'].copy()
00185 self.FullScreenVidIDxPath = etree.XPath('//object', namespaces=self.common.namespaces )
00186
00187 self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/rev3.png'
00188
00189
00190
00191
00192
00193
00194
00195
00196 def getRev3Config(self):
00197 ''' Read the MNV Revision3 grabber "rev3_config.xml" configuration file
00198 return nothing
00199 '''
00200
00201 url = u'file://%s/nv_python_libs/configs/XML/rev3_config.xml' % (baseProcessingDir, )
00202 if not os.path.isfile(url[7:]):
00203 raise Rev3ConfigFileError(self.error_messages['Rev3ConfigFileError'] % (url[7:], ))
00204
00205 if self.config['debug_enabled']:
00206 print url
00207 print
00208 try:
00209 self.rev3_config = etree.parse(url)
00210 except Exception, e:
00211 raise Rev3UrlError(self.error_messages['Rev3UrlError'] % (url, errormsg))
00212 return
00213
00214
00215
00216 def getUserPreferences(self):
00217 '''Read the rev3_config.xml and user preference rev3.xml file.
00218 If the rev3.xml file does not exist then create it.
00219 If the rev3.xml file is too old then update it.
00220 return nothing
00221 '''
00222
00223 self.getRev3Config()
00224
00225
00226 userPreferenceFile = self.rev3_config.find('userPreferenceFile').text
00227 if userPreferenceFile[0] == '~':
00228 self.rev3_config.find('userPreferenceFile').text = u"%s%s" % (os.path.expanduser(u"~"), userPreferenceFile[1:])
00229 if os.path.isfile(self.rev3_config.find('userPreferenceFile').text):
00230
00231 url = u'file://%s' % (self.rev3_config.find('userPreferenceFile').text, )
00232 if self.config['debug_enabled']:
00233 print url
00234 print
00235 try:
00236 self.userPrefs = etree.parse(url)
00237 except Exception, e:
00238 raise Rev3UrlError(self.error_messages['Rev3UrlError'] % (url, errormsg))
00239
00240 nextUpdateSecs = int(self.userPrefs.find('updateDuration').text)*86400
00241 nextUpdate = time.localtime(os.path.getmtime(self.rev3_config.find('userPreferenceFile').text)+nextUpdateSecs)
00242 now = time.localtime()
00243 if nextUpdate > now:
00244 return
00245 create = False
00246 else:
00247 create = True
00248
00249
00250 self.updateRev3(create)
00251 return
00252
00253
00254 def updateRev3(self, create=False):
00255 ''' Create or update the rev3.xml user preferences file
00256 return nothing
00257 '''
00258 userRev3 = etree.XML(u'''
00259 <userRev3>
00260 <!--
00261 The shows are split into three top level directories which represent how Rev3 categories
00262 their videos. Each top level directory has one or more shows. Each show has one or more
00263 MP4 formats that can be downloaded. The formats represent various video quality levels.
00264 Initially only three shows are enabled. You must change a show's "mp4Format" element's
00265 "enabled" attribute to 'true'. When you change the attribute to 'true' that show's RSS feed
00266 will be added to the Rev3 tree view. You could activate more than one MP4 format but that
00267 would result in duplicates. With the exception of "Tekzilla" which is a show that has
00268 both a weekly and daily video RSS feed within the same show element.
00269 When the Rev3 Tree view is created it will have links to the video's web page but also a
00270 link to the MP4 file that you can download through the MythNetvision interface.
00271 If a show moves from one top level directory to another your show sections will be
00272 preserved as long as that format is available in the new top level directory.
00273 Updates to the "rev3.xml" file is made every X number of days as determined by the value of
00274 the "updateDuration" element in this file. The default is every 3 days.
00275 -->
00276 <!-- Number of days between updates to the config file -->
00277 <updateDuration>3</updateDuration>
00278
00279 <!--
00280 Personal RSS feed.
00281 "globalmax" (optional) Is a way to limit the number of items processed per RSS feed for all
00282 treeview URLs. A value of zero (0) means there are no limitions.
00283 "max" (optional) Is a way to limit the number of items processed for an individual RSS feed.
00284 This value will override any "globalmax" setting. A value of zero (0) means
00285 there are no limitions and would be the same if the attribute was no included at all.
00286 "enabled" If you want to remove a RSS feed then change the "enabled" attribute to "false".
00287
00288 See details: http://revision3.com/blog/2010/03/11/pick-your-favorite-shows-create-a-personalized-feed/
00289 Once you sign up and create your personal RSS feed replace the url in the example below with the
00290 Revision3 "Your RSS Feed Address" URL and change the "enabled" element attribute to "true".
00291 -->
00292 <treeviewURLS globalmax="0">
00293 <url enabled="false">http://revision3.com/feed/user/EXAMPLE</url>
00294 </treeviewURLS>
00295 </userRev3>
00296 ''')
00297
00298
00299 linksTree = self.common.getUrlData(self.rev3_config.find('treeviewUrls'))
00300
00301 if self.config['debug_enabled']:
00302 print "create(%s)" % create
00303 print "linksTree:"
00304 sys.stdout.write(etree.tostring(linksTree, encoding='UTF-8', pretty_print=True))
00305 print
00306
00307
00308 showData = etree.XML(u'<xml></xml>')
00309 complexFilter = u"//div[@class='subscribe_rss']//div//p[.='MP4']/..//a"
00310 for result in linksTree.xpath('//results'):
00311 tmpDirectory = etree.XML(u'<directory></directory>')
00312 dirName = result.find('name').text
00313 tmpDirectory.attrib['name'] = dirName
00314
00315 if self.config['debug_enabled']:
00316 print "Results: #Items(%s) for (%s)" % (len(result.xpath('.//a')), dirName)
00317 print
00318
00319 for anchor in result.xpath('.//a'):
00320 showURL = None
00321 if dirName == 'Shows':
00322 showURL = anchor.attrib.get('href')
00323 showFilter = complexFilter
00324 tmpName = anchor.text
00325 elif dirName == 'Revision3 Beta':
00326 tmpName = etree.tostring(anchor, method="text", encoding=unicode)
00327 showURL = u'http://revision3beta.com%sfeed/' % anchor.attrib.get('href')
00328 showFilter = None
00329 elif dirName == 'Archived Shows':
00330 showURL = u'http://revision3.com%s' % anchor.attrib.get('href')
00331 showFilter = complexFilter
00332 tmpName = anchor.text
00333 if tmpName == u'Revision3 Beta':
00334 continue
00335 if showURL != None:
00336 url = etree.SubElement(tmpDirectory, "url")
00337 etree.SubElement(url, "name").text = tmpName
00338 etree.SubElement(url, "href").text = showURL
00339 etree.SubElement(url, "filter").text = showFilter
00340 etree.SubElement(url, "parserType").text = u'html'
00341 if tmpDirectory.find('url') != None:
00342 showData.append(tmpDirectory)
00343
00344 if self.config['debug_enabled']:
00345 print "showData:"
00346 sys.stdout.write(etree.tostring(showData, encoding='UTF-8', pretty_print=True))
00347 print
00348
00349
00350 for directory in showData.findall('directory'):
00351 if create:
00352 firstEnabled = True
00353 else:
00354 firstEnabled = False
00355 tmpDirectory = etree.XML(u'<directory></directory>')
00356 tmpDirectory.attrib['name'] = directory.attrib['name']
00357 if directory.attrib['name'] == u'Revision3 Beta':
00358 for show in directory.findall('url'):
00359 tmpShow = etree.XML(u'<show></show>')
00360 tmpShow.attrib['name'] = show.find('name').text
00361 mp4Format = etree.SubElement(tmpShow, u"mp4Format")
00362 if firstEnabled:
00363 mp4Format.attrib['enabled'] = u'true'
00364 firstEnabled = False
00365 else:
00366 mp4Format.attrib['enabled'] = u'false'
00367 mp4Format.attrib['name'] = u'Web Only'
00368 mp4Format.attrib['rss'] = show.find('href').text
00369 tmpDirectory.append(tmpShow)
00370 else:
00371 showResults = self.common.getUrlData(directory)
00372 for show in showResults.xpath('//results'):
00373 tmpShow = etree.XML(u'<show></show>')
00374 tmpShow.attrib['name'] = show.find('name').text
00375
00376 if self.config['debug_enabled']:
00377 print "Results: #Items(%s) for (%s)" % (len(show.xpath('.//a')), tmpShow.attrib['name'])
00378 print
00379
00380 for format in show.xpath('.//a'):
00381 link = u'http://revision3.com%s' % format.attrib['href']
00382
00383
00384 if link.find('/tekzilla/') != -1 and link.find('?subshow=false') == -1:
00385 continue
00386 mp4Format = etree.SubElement(tmpShow, "mp4Format")
00387 if firstEnabled:
00388 mp4Format.attrib['enabled'] = u'true'
00389 firstEnabled = False
00390 else:
00391 mp4Format.attrib['enabled'] = u'false'
00392 mp4Format.attrib['name'] = format.text
00393 mp4Format.attrib['rss'] = link
00394 if tmpShow.find('mp4Format') != None:
00395 tmpDirectory.append(tmpShow)
00396
00397
00398 if tmpDirectory.find('show') != None:
00399 userRev3.append(tmpDirectory)
00400
00401 if self.config['debug_enabled']:
00402 print "Before any merging userRev3:"
00403 sys.stdout.write(etree.tostring(userRev3, encoding='UTF-8', pretty_print=True))
00404 print
00405
00406
00407 if not create:
00408 userRev3.find('updateDuration').text = self.userPrefs.find('updateDuration').text
00409 for mp4Format in self.userPrefs.xpath("//mp4Format[@enabled='true']"):
00410 showName = mp4Format.getparent().attrib['name']
00411 mp4name = mp4Format.attrib['name']
00412 elements = userRev3.xpath("//show[@name=$showName]/mp4Format[@name=$mp4name]", showName=showName, mp4name=mp4name)
00413 if len(elements):
00414 elements[0].attrib['enabled'] = u'true'
00415
00416 if self.config['debug_enabled']:
00417 print "After any merging userRev3:"
00418 sys.stdout.write(etree.tostring(userRev3, encoding='UTF-8', pretty_print=True))
00419 print
00420
00421
00422 prefDir = self.rev3_config.find('userPreferenceFile').text.replace(u'/rev3.xml', u'')
00423 if not os.path.isdir(prefDir):
00424 os.makedirs(prefDir)
00425 fd = open(self.rev3_config.find('userPreferenceFile').text, 'w')
00426 fd.write(u'<userRev3>\n'+u''.join(etree.tostring(element, encoding='UTF-8', pretty_print=True) for element in userRev3)+u'</userRev3>')
00427 fd.close()
00428
00429
00430 try:
00431 self.userPrefs = etree.parse(self.rev3_config.find('userPreferenceFile').text)
00432 except Exception, e:
00433 raise Rev3UrlError(self.error_messages['Rev3UrlError'] % (url, errormsg))
00434 return
00435
00436
00437 def getSeasonEpisode(self, title, link=None):
00438 ''' Check is there is any season or episode number information in an item's title
00439 return array of season and/or episode numbers
00440 return array with None values
00441 '''
00442 s_e = [None, None]
00443 for index in range(len(self.s_e_Patterns)):
00444 match = self.s_e_Patterns[index].match(title)
00445 if not match:
00446 if link:
00447 match = self.s_e_Patterns[index].match(link)
00448 if not match:
00449 continue
00450 else:
00451 continue
00452 if index < 2:
00453 s_e[0], s_e[1] = match.groups()
00454 break
00455 else:
00456 s_e[1] = u'%s' % int(match.groups()[0])
00457 break
00458 return s_e
00459
00460
00461 def getVideoID(self, link):
00462 ''' Read the Web page to find the video ID number used for fullscreen autoplay
00463 return the video ID number
00464 return None if the number cannot be found
00465 '''
00466 videoID = None
00467 try:
00468 eTree = etree.parse(link, self.FullScreenParser)
00469 except Exception, errormsg:
00470 sys.stderr.write(u"! Error: The URL (%s) cause the exception error (%s)\n" % (link, errormsg))
00471 return videoID
00472
00473 if self.config['debug_enabled']:
00474 print "Raw unfiltered URL imput:"
00475 sys.stdout.write(etree.tostring(eTree, encoding='UTF-8', pretty_print=True))
00476 print
00477
00478 if not eTree:
00479 return videoID
00480
00481
00482 try:
00483 tmpVideoID = self.FullScreenVidIDxPath(eTree)
00484 except AssertionError, e:
00485 sys.stderr.write("No filter results for VideoID from url(%s)\n" % link)
00486 sys.stderr.write("! Error:(%s)\n" % e)
00487 return videoID
00488
00489 if len(tmpVideoID):
00490 if tmpVideoID[0].get('id'):
00491 videoID = tmpVideoID[0].attrib['id'].strip().replace(u'player-', u'')
00492
00493 return videoID
00494
00495
00496
00497
00498
00499
00500
00501
00502 def searchTitle(self, title, pagenumber, pagelen):
00503 '''Key word video search of the Rev3 web site
00504 return an array of matching item elements
00505 return
00506 '''
00507 try:
00508 searchVar = u'?q=%s' % (urllib.quote(title.encode("utf-8")).replace(u' ', u'+'))
00509 except UnicodeDecodeError:
00510 searchVar = u'?q=%s' % (urllib.quote(title).replace(u' ', u'+'))
00511 url = self.rev3_config.find('searchURLS').xpath(".//href")[0].text+searchVar
00512
00513 if self.config['debug_enabled']:
00514 print url
00515 print
00516
00517 self.rev3_config.find('searchURLS').xpath(".//href")[0].text = url
00518
00519
00520 try:
00521 resultTree = self.common.getUrlData(self.rev3_config.find('searchURLS'), pageFilter=self.rev3_config.find('searchURLS').xpath(".//pageFilter")[0].text)
00522 except Exception, errormsg:
00523 raise Rev3UrlDownloadError(self.error_messages['Rev3UrlDownloadError'] % (errormsg))
00524
00525 if resultTree is None:
00526 raise Rev3VideoNotFound(u"No Rev3 Video matches found for search value (%s)" % title)
00527
00528 searchResults = resultTree.xpath('//result//li[@class="video"]')
00529 if not len(searchResults):
00530 raise Rev3VideoNotFound(u"No Rev3 Video matches found for search value (%s)" % title)
00531
00532
00533 self.channel['channel_numresults'] = len(searchResults)
00534
00535
00536
00537 pubDate = datetime.datetime.now().strftime(self.common.pubDateFormat)
00538
00539
00540 thumbnailFilter = etree.XPath('.//a[@class="thumbnail"]/img/@src')
00541 titleFilter = etree.XPath('.//a[@class="title"]')
00542 descFilter = etree.XPath('.//div[@class="description"]')
00543 itemThumbNail = etree.XPath('.//media:thumbnail', namespaces=self.common.namespaces)
00544 itemDwnLink = etree.XPath('.//media:content', namespaces=self.common.namespaces)
00545 itemDict = {}
00546 for result in searchResults:
00547 if len(titleFilter(result)):
00548 rev3Item = etree.XML(self.common.mnvItem)
00549
00550 thumbNail = self.common.ampReplace(thumbnailFilter(result)[0])
00551 title = self.common.massageText(titleFilter(result)[0].text.strip())
00552 tmpDesc = etree.tostring(descFilter(result)[0], method="text", encoding=unicode).strip()
00553 index = tmpDesc.find(u'–')
00554 if index != -1:
00555 tmpDesc = tmpDesc[index+1:].strip()
00556 description = self.common.massageText(tmpDesc)
00557 link = self.common.ampReplace(titleFilter(result)[0].attrib['href'])
00558 author = u'Revision3'
00559
00560 rev3Item.find('title').text = title
00561 rev3Item.find('author').text = author
00562 rev3Item.find('pubDate').text = pubDate
00563 rev3Item.find('description').text = description
00564 rev3Item.find('link').text = link
00565 itemThumbNail(rev3Item)[0].attrib['url'] = thumbNail
00566 itemDwnLink(rev3Item)[0].attrib['url'] = link
00567 s_e = self.getSeasonEpisode(title, None)
00568 if s_e[0]:
00569 etree.SubElement(rev3Item, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}season").text = s_e[0]
00570 if s_e[1]:
00571 etree.SubElement(rev3Item, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}episode").text = s_e[1]
00572 itemDict[title.lower()] = rev3Item
00573
00574 if not len(itemDict.keys()):
00575 raise Rev3VideoNotFound(u"No Rev3 Video matches found for search value (%s)" % title)
00576
00577 return [itemDict, resultTree.xpath('//pageInfo')[0].text]
00578
00579
00580
00581 def searchForVideos(self, title, pagenumber):
00582 """Common name for a video search. Used to interface with MythTV plugin NetVision
00583 """
00584
00585 self.getRev3Config()
00586
00587 if self.config['debug_enabled']:
00588 print "self.rev3_config:"
00589 sys.stdout.write(etree.tostring(self.rev3_config, encoding='UTF-8', pretty_print=True))
00590 print
00591
00592
00593
00594
00595
00596
00597 try:
00598 data = self.searchTitle(title, pagenumber, self.page_limit)
00599 except Rev3VideoNotFound, msg:
00600 sys.stderr.write(u"%s\n" % msg)
00601 sys.exit(0)
00602 except Rev3UrlError, msg:
00603 sys.stderr.write(u'%s\n' % msg)
00604 sys.exit(1)
00605 except Rev3HttpError, msg:
00606 sys.stderr.write(self.error_messages['Rev3HttpError'] % msg)
00607 sys.exit(1)
00608 except Rev3RssError, msg:
00609 sys.stderr.write(self.error_messages['Rev3RssError'] % msg)
00610 sys.exit(1)
00611 except Exception, e:
00612 sys.stderr.write(u"! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e))
00613 sys.exit(1)
00614
00615
00616 rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
00617
00618
00619 itemCount = len(data[0].keys())
00620 if data[1] == 'true':
00621 self.channel['channel_returned'] = itemCount
00622 self.channel['channel_startindex'] = itemCount
00623 self.channel['channel_numresults'] = itemCount+(self.page_limit*(int(pagenumber)-1)+1)
00624 else:
00625 self.channel['channel_returned'] = itemCount+(self.page_limit*(int(pagenumber)-1))
00626 self.channel['channel_startindex'] = self.channel['channel_returned']
00627 self.channel['channel_numresults'] = self.channel['channel_returned']
00628
00629
00630 channelTree = self.common.mnvChannelElement(self.channel)
00631 rssTree.append(channelTree)
00632
00633 lastKey = None
00634 for key in sorted(data[0].keys()):
00635 if lastKey != key:
00636 channelTree.append(data[0][key])
00637 lastKey = key
00638
00639
00640 sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')
00641 sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True))
00642 sys.exit(0)
00643
00644
00645 def displayTreeView(self):
00646 '''Gather the Revision3 feeds then get a max page of videos meta data in each of them
00647 Display the results and exit
00648 '''
00649 personalFeed = u"Personal Feed"
00650
00651
00652 try:
00653 self.getUserPreferences()
00654 except Exception, e:
00655 sys.stderr.write(u'%s' % e)
00656 sys.exit(1)
00657
00658 if self.config['debug_enabled']:
00659 print "self.userPrefs:"
00660 sys.stdout.write(etree.tostring(self.userPrefs, encoding='UTF-8', pretty_print=True))
00661 print
00662
00663
00664 rssFeeds = self.userPrefs.xpath("//mp4Format[@enabled='true']")
00665 personalFeeds = self.userPrefs.xpath("//treeviewURLS//url[@enabled='true']")
00666 if not len(rssFeeds) and not len(personalFeeds):
00667 sys.stderr.write(u'There are no mp4Format or personal RSS feed elements "enabled" in your "rev3.xml" user preferences\nfile (%s)\n' % self.rev3_config.find('userPreferenceFile').text)
00668 sys.exit(1)
00669
00670
00671 showData = etree.XML(u'<xml></xml>')
00672 for feed in personalFeeds:
00673 rssFeeds.append(feed)
00674 count = 0
00675 for rssFeed in rssFeeds:
00676 if rssFeed.getparent().tag == 'treeviewURLS':
00677 uniqueName = u'%s:%s' % (personalFeed, count)
00678 count+=1
00679 else:
00680 uniqueName = u'%s:%s:%s' % (rssFeed.getparent().getparent().attrib['name'], rssFeed.getparent().attrib['name'], rssFeed.attrib['name'])
00681 url = etree.XML(u'<url></url>')
00682 etree.SubElement(url, "name").text = uniqueName
00683 if uniqueName.startswith(personalFeed):
00684 etree.SubElement(url, "href").text = rssFeed.text
00685 else:
00686 etree.SubElement(url, "href").text = rssFeed.attrib['rss']
00687 etree.SubElement(url, "filter").text = u"//channel"
00688 etree.SubElement(url, "parserType").text = u'xml'
00689 showData.append(url)
00690
00691 if self.config['debug_enabled']:
00692 print "showData:"
00693 sys.stdout.write(etree.tostring(showData, encoding='UTF-8', pretty_print=True))
00694 print
00695
00696
00697 try:
00698 resultTree = self.common.getUrlData(showData)
00699 except Exception, errormsg:
00700 raise Rev3UrlDownloadError(self.error_messages['Rev3UrlDownloadError'] % (errormsg))
00701
00702 if resultTree is None:
00703 sys.exit(0)
00704
00705 if self.config['debug_enabled']:
00706 print "resultTree:"
00707 sys.stdout.write(etree.tostring(resultTree, encoding='UTF-8', pretty_print=True))
00708 print
00709
00710
00711 rssTree = etree.XML(self.common.mnvRSS+u'</rss>')
00712
00713
00714 channelTree = self.common.mnvChannelElement(self.channel)
00715 rssTree.append(channelTree)
00716
00717
00718 itemFilter = etree.XPath('.//item')
00719 channelFilter = etree.XPath('./result/channel')
00720 imageFilter = etree.XPath('.//image/url')
00721 itemDwnLink = './/media:content'
00722 itemThumbNail = './/media:thumbnail'
00723 itemDuration = './/media:content'
00724 itemLanguage = './/media:content'
00725 categoryDir = None
00726 showDir = None
00727 categoryElement = etree.XML(u'<directory></directory>')
00728 itemAuthor = u'Revision3'
00729 for result in resultTree.findall('results'):
00730 names = result.find('name').text.split(':')
00731 for index in range(len(names)):
00732 names[index] = self.common.massageText(names[index])
00733 channel = channelFilter(result)[0]
00734 if channel.find('image') != None:
00735 channelThumbnail = self.common.ampReplace(imageFilter(channel)[0].text)
00736 else:
00737 channelThumbnail = self.common.ampReplace(channel.find('link').text.replace(u'/watch/', u'/images/')+u'100.jpg')
00738 channelLanguage = u'en'
00739 if channel.find('language') != None:
00740 channelLanguage = channel.find('language').text[:2]
00741
00742 if names[0] != categoryDir:
00743 if categoryDir != None:
00744 channelTree.append(categoryElement)
00745 categoryElement = etree.XML(u'<directory></directory>')
00746 if names[0] == personalFeed:
00747 categoryElement.attrib['name'] = channel.find('title').text
00748 else:
00749 categoryElement.attrib['name'] = names[0]
00750 categoryElement.attrib['thumbnail'] = self.channel_icon
00751 categoryDir = names[0]
00752 if names[1] != showDir:
00753 if names[0] == personalFeed:
00754 showElement = categoryElement
00755 else:
00756 showElement = etree.SubElement(categoryElement, u"directory")
00757 if names[2] == 'Web Only':
00758 showElement.attrib['name'] = u'%s' % (names[1])
00759 else:
00760 showElement.attrib['name'] = u'%s: %s' % (names[1], names[2])
00761 showElement.attrib['thumbnail'] = channelThumbnail
00762 showDir = names[1]
00763
00764 if self.config['debug_enabled']:
00765 print "Results: #Items(%s) for (%s)" % (len(itemFilter(result)), names)
00766 print
00767
00768
00769 for itemData in itemFilter(result):
00770 rev3Item = etree.XML(self.common.mnvItem)
00771
00772 rev3Item.find('title').text = self.common.massageText(itemData.find('title').text.strip())
00773 rev3Item.find('author').text = itemAuthor
00774 rev3Item.find('pubDate').text = self.common.massageText(itemData.find('pubDate').text)
00775 rev3Item.find('description').text = self.common.massageText(itemData.find('description').text.strip())
00776 link = self.common.ampReplace(itemData.find('link').text)
00777 downLoadLink = self.common.ampReplace(itemData.xpath(itemDwnLink, namespaces=self.common.namespaces)[0].attrib['url'])
00778
00779
00780
00781 if names[0] == 'Shows' or names[0] == personalFeed:
00782 fullScreenVideoID = self.getVideoID(itemData.find('link').text)
00783 if fullScreenVideoID:
00784 if link == downLoadLink:
00785 downLoadLink = self.common.ampReplace(self.FullScreen % fullScreenVideoID)
00786 link = self.common.ampReplace(self.FullScreen % fullScreenVideoID)
00787
00788 rev3Item.find('link').text = self.common.ampReplace(link)
00789 rev3Item.xpath(itemDwnLink, namespaces=self.common.namespaces)[0].attrib['url'] = downLoadLink
00790 try:
00791 rev3Item.xpath(itemThumbNail, namespaces=self.common.namespaces)[0].attrib['url'] = self.common.ampReplace(itemData.xpath(itemThumbNail, namespaces=self.common.namespaces)[0].attrib['url'].replace(u'--mini', u'--medium'))
00792 except IndexError:
00793 pass
00794 try:
00795 rev3Item.xpath(itemDuration, namespaces=self.common.namespaces)[0].attrib['duration'] = itemData.xpath(itemDuration, namespaces=self.common.namespaces)[0].attrib['duration']
00796 except KeyError:
00797 pass
00798 rev3Item.xpath(itemLanguage, namespaces=self.common.namespaces)[0].attrib['lang'] = channelLanguage
00799 if rev3Item.xpath(itemThumbNail, namespaces=self.common.namespaces)[0].get('url'):
00800 s_e = self.getSeasonEpisode(rev3Item.find('title').text, rev3Item.xpath(itemThumbNail, namespaces=self.common.namespaces)[0].attrib['url'])
00801 else:
00802 s_e = self.getSeasonEpisode(rev3Item.find('title').text, rev3Item.xpath(itemDwnLink, namespaces=self.common.namespaces)[0].attrib['url'])
00803 if s_e[0]:
00804 etree.SubElement(rev3Item, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}season").text = s_e[0]
00805 if s_e[1]:
00806 etree.SubElement(rev3Item, "{http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format}episode").text = s_e[1]
00807 if s_e[0] and s_e[1]:
00808 rev3Item.find('title').text = u'S%02dE%02d: %s' % (int(s_e[0]), int (s_e[1]), rev3Item.find('title').text)
00809 elif s_e[0]:
00810 rev3Item.find('title').text = u'S%02d: %s' % (int(s_e[0]), rev3Item.find('title').text)
00811 elif s_e[1]:
00812 rev3Item.find('title').text = u'Ep%03d: %s' % (int(s_e[1]), rev3Item.find('title').text)
00813 showElement.append(rev3Item)
00814
00815
00816 if categoryElement.xpath('.//item') != None:
00817 channelTree.append(categoryElement)
00818
00819
00820 if len(rssTree.xpath('//item')):
00821
00822 sys.stdout.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')
00823 sys.stdout.write(etree.tostring(rssTree, encoding='UTF-8', pretty_print=True))
00824
00825 sys.exit(0)
00826
00827