MythTV  master
bliptv_api.py
Go to the documentation of this file.
1 # -*- coding: UTF-8 -*-
2 
3 # ----------------------
4 # Name: bliptv_api - Simple-to-use Python interface to the bliptv API (http://blip.tv/)
5 # Python Script
6 # Author: R.D. Vaughan
7 # Purpose: This python script is intended to perform a variety of utility functions to search and access text
8 # meta data, video and image URLs from blip.tv.
9 # These routines are based on the v2.0 api. Specifications
10 # for this api are published at http://blip.tv/about/api/
11 #
12 # License:Creative Commons GNU GPL v2
13 # (http://creativecommons.org/licenses/GPL/2.0/)
14 #-------------------------------------
15 __title__ ="bliptv_api - Simple-to-use Python interface to the bliptv API (http://blip.tv/about/api/)"
16 __author__="R.D. Vaughan"
17 __purpose__='''
18 This python script is intended to perform a variety of utility functions to search and access text
19 meta data, video and image URLs from blip.tv. These routines are based on the v2.0 api. Specifications
20 for this api are published at http://blip.tv/about/api/
21 '''
22 
23 __version__="v0.2.5"
24 # 0.1.0 Initial development
25 # 0.1.1 Changed to use bliptv's rss data rather than JSON as JSON ad a number of error
26 # 0.1.2 Changes Search to parse XML and added Tree view
27 # 0.1.3 Added directory image logic
28 # 0.1.4 Documentation updates
29 # 0.2.0 Public release
30 # 0.2.1 New python bindings conversion
31 # Better exception error reporting
32 # Better handling of invalid unicode data from source
33 # 0.2.2 Completed exception message improvements
34 # Removed the unused import of the feedparser library
35 # 0.2.3 Fixed an exception message output code error in two places
36 # 0.2.4 Removed the need for python MythTV bindings and added "%SHAREDIR%" to icon directory path
37 # 0.2.5 Changed link URL to full screen when a "blip:embedUrl" exists for an item
38 # Removed a subdirectory level as the "Featured" RSS feed has been discontinued
39 
40 import os, struct, sys, re, time
41 import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse
42 import logging
43 from MythTV import MythXML
44 
45 try:
46  import xml.etree.cElementTree as ElementTree
47 except ImportError:
48  import xml.etree.ElementTree as ElementTree
49 
50 from .bliptv_exceptions import (BliptvUrlError, BliptvHttpError, BliptvRssError, BliptvVideoNotFound, BliptvXmlError)
51 import io
52 
53 class OutStreamEncoder(object):
54  """Wraps a stream with an encoder"""
55  def __init__(self, outstream, encoding=None):
56  self.out = outstream
57  if not encoding:
58  self.encoding = sys.getfilesystemencoding()
59  else:
60  self.encoding = encoding
61 
62  def write(self, obj):
63  """Wraps the output stream, encoding Unicode strings with the specified encoding"""
64  if isinstance(obj, str):
65  obj = obj.encode(self.encoding)
66  self.out.buffer.write(obj)
67 
68  def __getattr__(self, attr):
69  """Delegate everything but write to the stream"""
70  return getattr(self.out, attr)
71 
72 if isinstance(sys.stdout, io.TextIOWrapper):
73  sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
74  sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
75 
76 
77 class XmlHandler:
78  """Deals with retrieval of XML files from API
79  """
80  def __init__(self, url):
81  self.url = url
82 
83  def _grabUrl(self, url):
84  try:
85  urlhandle = urllib.request.urlopen(url)
86  except IOError as errormsg:
87  raise BliptvHttpError(errormsg)
88  return urlhandle.read()
89 
90  def getEt(self):
91  xml = self._grabUrl(self.url)
92  try:
93  et = ElementTree.fromstring(xml)
94  except SyntaxError as errormsg:
95  raise BliptvXmlError(errormsg)
96  return et
97 
98 
99 class Videos(object):
100  """Main interface to http://blip.tv/
101  This is done to support a common naming framework for all python Netvision plugins no matter their site
102  target.
103 
104  Supports search and tree view methods
105  The apikey is a not required to access http://blip.tv/
106  """
107  def __init__(self,
108  apikey,
109  mythtv = True,
110  interactive = False,
111  select_first = False,
112  debug = False,
113  custom_ui = None,
114  language = None,
115  search_all_languages = False,
116  ):
117  """apikey (str/unicode):
118  Specify the target site API key. Applications need their own key in some cases
119 
120  mythtv (True/False):
121  When True, the returned meta data is being returned has the key and values massaged to match MythTV
122  When False, the returned meta data is being returned matches what target site returned
123 
124  interactive (True/False): (This option is not supported by all target site apis)
125  When True, uses built-in console UI is used to select the correct show.
126  When False, the first search result is used.
127 
128  select_first (True/False): (This option is not supported currently implemented in any grabbers)
129  Automatically selects the first series search result (rather
130  than showing the user a list of more than one series).
131  Is overridden by interactive = False, or specifying a custom_ui
132 
133  debug (True/False):
134  shows verbose debugging information
135 
136  custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
137  A callable subclass of interactive class (overrides interactive option)
138 
139  language (2 character language abbreviation): (This option is not supported by all target site apis)
140  The language of the returned data. Is also the language search
141  uses. Default is "en" (English). For full list, run..
142 
143  search_all_languages (True/False): (This option is not supported by all target site apis)
144  By default, a Netvision grabber will only search in the language specified using
145  the language option. When this is True, it will search for the
146  show in any language
147 
148  """
149  self.config = {}
150  self.mythxml = MythXML()
151 
152  if apikey is not None:
153  self.config['apikey'] = apikey
154  else:
155  pass # blip.tv does not require an apikey
156 
157  self.config['debug_enabled'] = debug # show debugging messages
158 
159  self.log_name = "Bliptv"
160  self.log = self._initLogger() # Setups the logger (self.log.debug() etc)
161 
162  self.config['custom_ui'] = custom_ui
163 
164  self.config['interactive'] = interactive # prompt for correct series?
165 
166  self.config['select_first'] = select_first
167 
168  self.config['search_all_languages'] = search_all_languages
169 
170  # Defaulting to ENGISH but the blip.tv apis do not support specifying a language
171  self.config['language'] = "en"
172 
173  self.error_messages = {'BliptvUrlError': "! Error: The URL (%s) cause the exception error (%s)\n", 'BliptvHttpError': "! Error: An HTTP communicating error with blip.tv was raised (%s)\n", 'BliptvRssError': "! Error: Invalid RSS meta data\nwas received from blip.tv error (%s). Skipping item.\n", 'BliptvVideoNotFound': "! Error: Video search with blip.tv did not return any results (%s)\n", }
174 
175  # This is an example that must be customized for each target site
176  self.key_translation = [{'channel_title': 'channel_title', 'channel_link': 'channel_link', 'channel_description': 'channel_description', 'channel_numresults': 'channel_numresults', 'channel_returned': 'channel_returned', 'channel_startindex': 'channel_startindex'}, {'title': 'item_title', 'blip_safeusername': 'item_author', 'updated': 'item_pubdate', 'blip_puredescription': 'item_description', 'link': 'item_link', 'blip_picture': 'item_thumbnail', 'video': 'item_url', 'blip_runtime': 'item_duration', 'blip_rating': 'item_rating', 'width': 'item_width', 'height': 'item_height', 'language': 'item_lang'}]
177 
178  # The following url_ configs are based of the
179  # http://blip.tv/about/api/
180  self.config['base_url'] = "http://www.blip.tv%s"
181  self.config['thumb_url'] = "http://a.images.blip.tv%s"
182 
183  self.config['urls'] = {}
184 
185  # v2 api calls - An example that must be customized for each target site
186  self.config['urls']['video.search'] = "http://www.blip.tv/?search=%s;&page=%s;&pagelen=%s;&language_code=%s;&skin=rss"
187  self.config['urls']['categories'] = "http://www.blip.tv/?section=categories&cmd=view&skin=api"
188 
189  self.config['image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions
190 
191  # Functions that parse video data from RSS data
192  self.config['item_parser'] = {}
193  self.config['item_parser']['main'] = self.getVideosForURL
194 
195  # Tree view url and the function that parses that urls meta data
196  self.config['urls']['tree.view'] = {
197  'P_R_R_F': {
198  '__all__': ['http://www.blip.tv/%s/?skin=rss', 'main'],
199  },
200  'categories': {
201  '__all__': ['http://www.blip.tv/rss/', 'main'],
202  },
203  }
204 
205  # Tree view categories are disabled until their results can be made more meaningful
206  #self.tree_order = ['P_R_R_F', 'categories', ]
207  self.tree_order = ['P_R_R_F']
208 
209  self.tree_org = {
210 # 'P_R_R_F': [['Popular/Recent/Features/Random ...', ['popular', 'recent', 'random', 'featured',]],
211  'P_R_R_F': [['', ['popular', 'recent', 'random', 'featured',]],
212  ],
213  # categories are dynamically filled in from a list retrieved from the blip.tv site
214  'categories': [
215  ['Categories', ''],
216  ],
217  }
218 
219  self.tree_customize = {
220  'P_R_R_F': {
221  '__default__': { },
222  #'cat name': {},
223  },
224  'categories': {
225  '__default__': {'categories_id': '', 'sort': '', },
226  #'cat name': {},
227  },
228  }
229 
230  self.feed_names = {
231  'P_R_R_F': {'popular': 'Most Comments', 'recent': 'Most Recent', 'random': 'Random selection',
232  },
233  'categories': {'featured': 'Featured Videos', 'popular': 'Most Comments', 'recent': 'Most Recent', 'random': 'Random selection',
234  },
235  }
236 
237  self.feed_icons = {
238  'P_R_R_F': {'popular': 'directories/topics/most_comments', 'recent': 'directories/topics/most_recent', 'random': 'directories/topics/random',
239  },
240  'categories': {'featured': 'directories/topics/featured', 'popular': 'directories/topics/most_comments', 'recent': 'directories/topics/most_recent', 'random': 'directories/topics/random',
241  },
242  }
243 
244  # Initialize the tree view flag so that the item parsing code can be used for multiple purposes
245  self.categories = False
246  self.treeview = False
247  self.channel_icon = '%SHAREDIR%/mythnetvision/icons/bliptv.png'
248  # end __init__()
249 
250 
251 
256 
258  '''Get longitude and latitiude to find videos relative to your location. Up to three different
259  servers will be tried before giving up.
260  return a dictionary e.g.
261  {'Latitude': '43.6667', 'Country': 'Canada', 'Longitude': '-79.4167', 'City': 'Toronto'}
262  return an empty dictionary if there were any errors
263  Code found at: http://blog.suinova.com/2009/04/from-ip-to-geolocation-country-city.html
264  '''
265  def getExternalIP():
266  '''Find the external IP address of this computer.
267  '''
268  url = urllib.request.URLopener()
269  try:
270  resp = url.open('http://www.whatismyip.com/automation/n09230945.asp')
271  return resp.read()
272  except:
273  return None
274  # end getExternalIP()
275 
276  ip = getExternalIP()
277 
278  if ip is None:
279  return {}
280 
281  try:
282  gs = urllib.request.urlopen('http://blogama.org/ip_query.php?ip=%s&output=xml' % ip)
283  txt = gs.read()
284  except:
285  try:
286  gs = urllib.request.urlopen('http://www.seomoz.org/ip2location/look.php?ip=%s' % ip)
287  txt = gs.read()
288  except:
289  try:
290  gs = urllib.request.urlopen('http://api.hostip.info/?ip=%s' % ip)
291  txt = gs.read()
292  except:
293  logging.error('GeoIP servers not available')
294  return {}
295  try:
296  if txt.find('<Response>') > 0:
297  countrys = re.findall(r'<CountryName>([\w ]+)<',txt)[0]
298  citys = re.findall(r'<City>([\w ]+)<',txt)[0]
299  lats,lons = re.findall(r'<Latitude>([\d\-\.]+)</Latitude>\s*<Longitude>([\d\-\.]+)<',txt)[0]
300  elif txt.find('GLatLng') > 0:
301  citys,countrys = re.findall(r'<br />\s*([^<]+)<br />\s*([^<]+)<',txt)[0]
302  lats,lons = re.findall(r'LatLng\(([-\d\.]+),([-\d\.]+)',txt)[0]
303  elif txt.find('<gml:coordinates>') > 0:
304  citys = re.findall(r'<Hostip>\s*<gml:name>(\w+)</gml:name>',txt)[0]
305  countrys = re.findall(r'<countryName>([\w ,\.]+)</countryName>',txt)[0]
306  lats,lons = re.findall(r'gml:coordinates>([-\d\.]+),([-\d\.]+)<',txt)[0]
307  else:
308  logging.error('error parsing IP result %s'%txt)
309  return {}
310  return {'Country':countrys,'City':citys,'Latitude':lats,'Longitude':lons}
311  except:
312  logging.error('Error parsing IP result %s'%txt)
313  return {}
314  # end detectUserLocationByIP()
315 
316  def massageDescription(self, text):
317  '''Removes HTML markup from a text string.
318  @param text The HTML source.
319  @return The plain text. If the HTML source contains non-ASCII
320  entities or character references, this is a Unicode string.
321  '''
322  def fixup(m):
323  text = m.group(0)
324  if text[:1] == "<":
325  return "" # ignore tags
326  if text[:2] == "&#":
327  try:
328  if text[:3] == "&#x":
329  return chr(int(text[3:-1], 16))
330  else:
331  return chr(int(text[2:-1]))
332  except ValueError:
333  pass
334  elif text[:1] == "&":
335  import html.entities
336  entity = html.entities.entitydefs.get(text[1:-1])
337  if entity:
338  if entity[:2] == "&#":
339  try:
340  return chr(int(entity[2:-1]))
341  except ValueError:
342  pass
343  else:
344  return str(entity, "iso-8859-1")
345  return text # leave as is
346  return self.ampReplace(re.sub(r"(?s)<[^>]*>|&#?\w+;", fixup, self.textUtf8(text))).replace('\n',' ')
347  # end massageDescription()
348 
349 
350  def _initLogger(self):
351  """Setups a logger using the logging module, returns a log object
352  """
353  logger = logging.getLogger(self.log_name)
354  formatter = logging.Formatter('%(asctime)s) %(levelname)s %(message)s')
355 
356  hdlr = logging.StreamHandler(sys.stdout)
357 
358  hdlr.setFormatter(formatter)
359  logger.addHandler(hdlr)
360 
361  if self.config['debug_enabled']:
362  logger.setLevel(logging.DEBUG)
363  else:
364  logger.setLevel(logging.WARNING)
365  return logger
366  #end initLogger
367 
368 
369  def textUtf8(self, text):
370  if text is None:
371  return text
372  try:
373  return str(text, 'utf8')
374  except UnicodeDecodeError:
375  return ''
376  except (UnicodeEncodeError, TypeError):
377  return text
378  # end textUtf8()
379 
380 
381  def ampReplace(self, text):
382  '''Replace all "&" characters with "&amp;"
383  '''
384  text = self.textUtf8(text)
385  return text.replace('&amp;','~~~~~').replace('&','&amp;').replace('~~~~~', '&amp;')
386  # end ampReplace()
387 
388  def setTreeViewIcon(self, dir_icon=None):
389  '''Check if there is a specific generic tree view icon. If not default to the channel icon.
390  return self.tree_dir_icon
391  '''
393  if not dir_icon:
394  if self.tree_key not in self.feed_icons:
395  return self.tree_dir_icon
396  if self.feed not in self.feed_icons[self.tree_key]:
397  return self.tree_dir_icon
398  dir_icon = self.feed_icons[self.tree_key][self.feed]
399  if not dir_icon:
400  return self.tree_dir_icon
401  self.tree_dir_icon = '%%SHAREDIR%%/mythnetvision/icons/%s.png' % (dir_icon, )
402  return self.tree_dir_icon
403  # end setTreeViewIcon()
404 
405 
410 
411  def processVideoUrl(self, url):
412  playerUrl = self.mythxml.getInternetContentUrl("nv_python_libs/configs/HTML/bliptv.html", \
413  url.replace('http://blip.tv/play/', ''))
414  return self.ampReplace(playerUrl)
415 
416  def searchTitle(self, title, pagenumber, pagelen):
417  '''Key word video search of the blip.tv web site
418  return an array of matching item dictionaries
419  return
420  '''
421  url = self.config['urls']['video.search'] % (urllib.parse.quote_plus(title.encode("utf-8")), pagenumber, pagelen, self.config['language'])
422 
423  if self.config['debug_enabled']:
424  print("Search URL:")
425  print(url)
426  print()
427 
428  try:
429  etree = XmlHandler(url).getEt()
430  except Exception as errormsg:
431  raise BliptvUrlError(self.error_messages['BliptvUrlError'] % (url, errormsg))
432 
433  if etree is None:
434  raise BliptvVideoNotFound("1-No blip.tv Video matches found for search value (%s)" % title)
435 
436  # Massage each field and eliminate any item without a URL
437  elements_final = []
438  dictionary_first = False
439  directory_image = ''
440  self.next_page = False
441  language = self.config['language']
442  for elements in etree.find('channel'):
443  if elements.tag == 'language':
444  if elements.text:
445  language = elements.text[:2]
446  continue
447  if not elements.tag == 'item':
448  continue
449  item = {}
450  item['language'] = language
451  embedURL = ''
452  for elem in elements:
453  if elem.tag == 'title':
454  if elem.text:
455  item['title'] = self.massageDescription(elem.text.strip())
456  continue
457  if elem.tag.endswith('safeusername'):
458  if elem.text:
459  item['blip_safeusername'] = self.massageDescription(elem.text.strip())
460  continue
461  if elem.tag.endswith('pubDate'):
462  if elem.text:
463  item['updated'] = self.massageDescription(elem.text.strip())
464  continue
465  if elem.tag.endswith('puredescription'):
466  if elem.text:
467  item['blip_puredescription'] = self.massageDescription(elem.text.strip())
468  continue
469  if elem.tag.endswith('link'):
470  if elem.text:
471  item['link'] = self.ampReplace(elem.text.strip())
472  continue
473  if elem.tag.endswith('embedUrl'):
474  if elem.text:
475  embedURL = self.ampReplace(elem.text.strip())
476  continue
477  if elem.tag.endswith('thumbnail'):
478  if elem.get('url'):
479  item['blip_picture'] = self.ampReplace(elem.get('url').strip())
480  continue
481  if elem.tag.endswith('group'):
482  file_size = 0
483  for e in elem:
484  if e.tag.endswith('content'):
485  if e.get('fileSize'):
486  try:
487  if int(e.get('fileSize')) > file_size:
488  item['video'] = self.ampReplace(e.get('url').strip())
489  file_size = int(e.get('fileSize'))
490  except:
491  pass
492  continue
493  continue
494  if elem.tag.endswith('runtime'):
495  if elem.text:
496  item['blip_runtime'] = self.massageDescription(elem.text.strip())
497  continue
498  if elem.tag.endswith('rating'):
499  if elem.text:
500  item['blip_rating'] = self.massageDescription(elem.text.strip())
501  continue
502  if 'video' not in item and 'link' not in item and not embedURL:
503  continue
504  if embedURL:
505  item['link'] = self.processVideoUrl(embedURL)
506  if 'link' in item and 'video' not in item:
507  continue
508  if 'video' in item and 'link' not in item:
509  item['link'] = item['video']
510  elements_final.append(item)
511 
512  if not len(elements_final):
513  raise BliptvVideoNotFound("2-No blip.tv Video matches found for search value (%s)" % title)
514 
515  return elements_final
516  # end searchTitle()
517 
518 
519  def searchForVideos(self, title, pagenumber):
520  """Common name for a video search. Used to interface with MythTV plugin NetVision
521  """
522  try:
523  data = self.searchTitle(title, pagenumber, self.page_limit)
524  except BliptvVideoNotFound as msg:
525  sys.stderr.write("%s\n" % msg)
526  return None
527  except BliptvUrlError as msg:
528  sys.stderr.write('%s' % msg)
529  sys.exit(1)
530  except BliptvHttpError as msg:
531  sys.stderr.write(self.error_messages['BliptvHttpError'] % msg)
532  sys.exit(1)
533  except BliptvRssError as msg:
534  sys.stderr.write(self.error_messages['BliptvRssError'] % msg)
535  sys.exit(1)
536  except Exception as e:
537  sys.stderr.write("! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e))
538  sys.exit(1)
539 
540  if data is None:
541  return None
542  if not len(data):
543  return None
544 
545  items = []
546  for match in data:
547  item_data = {}
548  for key in list(self.key_translation[1].keys()):
549  if key in list(match.keys()):
550  item_data[self.key_translation[1][key]] = match[key]
551  else:
552  item_data[self.key_translation[1][key]] = ''
553  items.append(item_data)
554 
555  # Channel details and search results
556  channel = {'channel_title': 'blip.tv', 'channel_link': 'http://blip.tv', 'channel_description': "We're the next generation television network", 'channel_numresults': 0, 'channel_returned': 1, 'channel_startindex': 0}
557 
558  if len(items) == self.page_limit:
559  channel['channel_numresults'] = self.page_limit * int(pagenumber) + 1
560  else:
561  channel['channel_numresults'] = self.page_limit * int(pagenumber)
562  channel['channel_startindex'] = self.page_limit * int(pagenumber)
563  channel['channel_returned'] = len(items)
564 
565  if len(items):
566  return [[channel, items]]
567  return None
568  # end searchForVideos()
569 
570 
571  def getCategories(self):
572  '''Get the list of valid category ids and their name and update the proper dictionaries
573  return nothing
574  '''
575  url = self.config['urls']['categories']
576  if self.config['debug_enabled']:
577  print("Category list URL:")
578  print(url)
579  print()
580 
581  try:
582  etree = XmlHandler(url).getEt()
583  except Exception as errormsg:
584  sys.stderr.write(self.error_messages['BliptvUrlError'] % (url, errormsg))
585  self.tree_order.remove('categories')
586  return
587 
588  if etree is None:
589  sys.stderr.write('1-No Categories found at (%s)\n' % url)
590  self.tree_order.remove('categories')
591  return
592 
593  if not etree.find('payload'):
594  sys.stderr.write('2-No Categories found at (%s)\n' % url)
595  self.tree_order.remove('categories')
596  return
597 
598  category = False
599  for element in etree.find('payload'):
600  if element.tag == 'category':
601  tmp_name = ''
602  tmp_id = ''
603  for e in element:
604  if e.tag == 'id':
605  if e.text == '-1':
606  break
607  if e.text:
608  tmp_id = self.massageDescription(e.text.strip())
609  if e.tag == 'name':
610  if e.text:
611  tmp_name = self.massageDescription(e.text.strip())
612  if tmp_id and tmp_name:
613  category = True
614  self.tree_org['categories'].append([tmp_name, ['popular', 'recent', 'random', 'featured',]])
615  self.feed_names['categories'][tmp_name] = tmp_id
616 
617  if not category:
618  sys.stderr.write('3-No Categories found at (%s)\n' % url)
619  self.tree_order.remove('categories')
620  return
621 
622  self.tree_org['categories'].append(['', '']) # Adds a end of the Categories directory indicator
623 
624  return
625  # end getCategories()
626 
627  def displayTreeView(self):
628  '''Gather the categories/feeds/...etc then retrieve a max page of videos meta data in each of them
629  return array of directories and their video meta data
630  '''
631  # Channel details and search results
632  self.channel = {'channel_title': 'blip.tv', 'channel_link': 'http://blip.tv', 'channel_description': "We're the next generation television network", 'channel_numresults': 0, 'channel_returned': 1, 'channel_startindex': 0}
633 
634  if self.config['debug_enabled']:
635  print(self.config['urls'])
636  print()
637 
638  # Get category ids
639  self.getCategories()
640 
641  # Process the various video feeds/categories/... etc
642  self.treeview = True
643  dictionaries = []
644  for key in self.tree_order:
645  if key == 'categories':
646  self.categories = True
647  else:
648  self.categories = False
649  self.tree_key = key
650  dictionaries = self.getVideos(self.tree_org[key], dictionaries)
651 
652  return [[self.channel, dictionaries]]
653  # end displayTreeView()
654 
655  def makeURL(self, URL):
656  '''Form a URL to search for videos
657  return a URL
658  '''
659  additions = dict(self.tree_customize[self.tree_key]['__default__']) # Set defaults
660 
661  # Add customizations
662  if self.feed in list(self.tree_customize[self.tree_key].keys()):
663  for element in list(self.tree_customize[self.tree_key][self.feed].keys()):
664  additions[element] = self.tree_customize[self.tree_key][self.feed][element]
665 
666  # Make the search extension string that is added to the URL
667  addition = ''
668  for ky in list(additions.keys()):
669  if ky.startswith('add_'):
670  addition+='/%s' % additions[ky]
671  else:
672  addition+='?%s=%s' % (ky, additions[ky])
673  index = URL.find('%')
674  if index == -1:
675  return (URL+addition)
676  else:
677  return (URL+addition) % self.feed
678  # end makeURL()
679 
680 
681  def getVideos(self, dir_dict, dictionaries):
682  '''Parse a list made of categories and retrieve video meta data
683  return a dictionary of directory names and categories video meta data
684  '''
685  for sets in dir_dict:
686  if not isinstance(sets[1], list):
687  if sets[0] != '': # Add the nested dictionaries display name
688  dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
689  else:
690  dictionaries.append(['', '']) # Add the nested dictionary indicator
691  continue
692  temp_dictionary = []
693  for self.feed in sets[1]:
694  if self.categories:
695  self.tree_customize[self.tree_key]['__default__']['categories_id'] = self.feed_names['categories'][sets[0]]
696  self.tree_customize[self.tree_key]['__default__']['sort'] = self.feed
697  if '__all__' in self.config['urls']['tree.view'][self.tree_key]:
698  URL = self.config['urls']['tree.view'][self.tree_key]['__all__']
699  else:
700  URL = self.config['urls']['tree.view'][self.tree_key][self.feed]
701  temp_dictionary = self.config['item_parser'][URL[1]](self.makeURL(URL[0]), temp_dictionary)
702  if len(temp_dictionary):
703  if len(sets[0]): # Add the nested dictionaries display name
704  dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
705  for element in temp_dictionary:
706  dictionaries.append(element)
707  if len(sets[0]):
708  dictionaries.append(['', '']) # Add the nested dictionary indicator
709  return dictionaries
710  # end getVideos()
711 
712  def getVideosForURL(self, url, dictionaries):
713  '''Get the video meta data for url search
714  return the video dictionary of directories and their video mata data
715  '''
716  initial_length = len(dictionaries)
717 
718  if self.config['debug_enabled']:
719  print("Video URL:")
720  print(url)
721  print()
722 
723  try:
724  etree = XmlHandler(url).getEt()
725  except Exception as errormsg:
726  sys.stderr.write(self.error_messages['BliptvUrlError'] % (url, errormsg))
727  return dictionaries
728 
729  if etree is None:
730  sys.stderr.write('1-No Videos for (%s)\n' % self.feed)
731  return dictionaries
732 
733  dictionary_first = False
734  self.next_page = False
735  language = self.config['language']
736  for elements in etree.find('channel'):
737  if elements.tag.endswith('language'):
738  if elements.text:
739  language = elements.text[:2]
740  continue
741 
742  if not elements.tag.endswith('item'):
743  continue
744 
745  item = {}
746  item['language'] = language
747  embedURL = ''
748  for elem in elements:
749  if elem.tag == 'title':
750  if elem.text:
751  item['title'] = self.massageDescription(elem.text.strip())
752  continue
753  if elem.tag.endswith('safeusername'):
754  if elem.text:
755  item['blip_safeusername'] = self.massageDescription(elem.text.strip())
756  continue
757  if elem.tag.endswith('pubDate'):
758  if elem.text:
759  item['updated'] = self.massageDescription(elem.text.strip())
760  continue
761  if elem.tag.endswith('puredescription'):
762  if elem.text:
763  item['blip_puredescription'] = self.massageDescription(elem.text.strip())
764  continue
765  if elem.tag == 'link':
766  if elem.text:
767  item['link'] = self.ampReplace(elem.text.strip())
768  continue
769  if elem.tag.endswith('embedUrl'):
770  if elem.text:
771  embedURL = self.ampReplace(elem.text.strip())
772  continue
773  if elem.tag.endswith('thumbnail'):
774  if elem.get('url'):
775  item['blip_picture'] = self.ampReplace(elem.get('url').strip())
776  continue
777  if elem.tag.endswith('group'):
778  file_size = 0
779  for e in elem:
780  if e.tag.endswith('content'):
781  for key in list(e.keys()):
782  if key.endswith('vcodec'):
783  break
784  else:
785  continue
786  if e.get('fileSize'):
787  try:
788  if int(e.get('fileSize')) > file_size:
789  item['video'] = self.ampReplace(e.get('url').strip())
790  file_size = int(e.get('fileSize'))
791  except:
792  pass
793  if e.get('height'):
794  item['height'] = e.get('height').strip()
795  if e.get('width'):
796  item['width'] = e.get('width').strip()
797  continue
798  continue
799  if elem.tag.endswith('runtime'):
800  if elem.text:
801  item['blip_runtime'] = self.massageDescription(elem.text.strip())
802  continue
803  if elem.tag.endswith('rating'):
804  if elem.text:
805  item['blip_rating'] = self.massageDescription(elem.text.strip())
806  continue
807  if 'video' not in item and 'link' not in item:
808  continue
809  if embedURL:
810  item['link'] = embedURL
811  if 'link' in item and 'video' not in item:
812  continue
813  if 'video' in item and 'link' not in item:
814  item['link'] = item['video']
815 
816  if self.treeview:
817  if not dictionary_first: # Add the dictionaries display name
818  dictionaries.append([self.massageDescription(self.feed_names[self.tree_key][self.feed]), self.setTreeViewIcon()])
819  dictionary_first = True
820 
821  final_item = {}
822  for key in list(self.key_translation[1].keys()):
823  if key not in item:
824  final_item[self.key_translation[1][key]] = ''
825  else:
826  final_item[self.key_translation[1][key]] = item[key]
827  dictionaries.append(final_item)
828 
829  if self.treeview:
830  if initial_length < len(dictionaries): # Need to check if there was any items for this Category
831  dictionaries.append(['', '']) # Add the nested dictionary indicator
832  return dictionaries
833  # end getVideosForURL()
834 # end Videos() class
nv_python_libs.bliptv.bliptv_api.Videos.textUtf8
def textUtf8(self, text)
Definition: bliptv_api.py:369
nv_python_libs.bliptv.bliptv_api.Videos.categories
categories
Definition: bliptv_api.py:236
nv_python_libs.bliptv.bliptv_api.Videos.__init__
def __init__(self, apikey, mythtv=True, interactive=False, select_first=False, debug=False, custom_ui=None, language=None, search_all_languages=False)
Definition: bliptv_api.py:107
nv_python_libs.bliptv.bliptv_api.Videos.next_page
next_page
Definition: bliptv_api.py:440
nv_python_libs.bliptv.bliptv_api.Videos.log
log
Definition: bliptv_api.py:151
nv_python_libs.bliptv.bliptv_api.Videos._initLogger
def _initLogger(self)
Definition: bliptv_api.py:350
nv_python_libs.bliptv.bliptv_api.Videos.channel_icon
channel_icon
Definition: bliptv_api.py:238
nv_python_libs.bliptv.bliptv_api.Videos.config
config
Definition: bliptv_api.py:140
nv_python_libs.bliptv.bliptv_api.Videos.mythxml
mythxml
Definition: bliptv_api.py:141
nv_python_libs.bliptv.bliptv_exceptions.BliptvXmlError
Definition: bliptv_exceptions.py:44
nv_python_libs.bliptv.bliptv_api.Videos
Definition: bliptv_api.py:99
nv_python_libs.bliptv.bliptv_api.Videos.setTreeViewIcon
def setTreeViewIcon(self, dir_icon=None)
Definition: bliptv_api.py:388
nv_python_libs.bliptv.bliptv_api.OutStreamEncoder.write
def write(self, obj)
Definition: bliptv_api.py:62
nv_python_libs.bliptv.bliptv_api.Videos.getCategories
def getCategories(self)
Definition: bliptv_api.py:571
hardwareprofile.software.dict
dictionary dict
Definition: software.py:55
nv_python_libs.bliptv.bliptv_api.Videos.massageDescription
def massageDescription(self, text)
Definition: bliptv_api.py:316
nv_python_libs.bliptv.bliptv_api.Videos.tree_org
tree_org
Definition: bliptv_api.py:200
nv_python_libs.bliptv.bliptv_api.Videos.searchTitle
def searchTitle(self, title, pagenumber, pagelen)
Definition: bliptv_api.py:416
nv_python_libs.bliptv.bliptv_api.Videos.getVideosForURL
def getVideosForURL(self, url, dictionaries)
Definition: bliptv_api.py:712
nv_python_libs.bliptv.bliptv_api.Videos.searchForVideos
def searchForVideos(self, title, pagenumber)
Definition: bliptv_api.py:519
nv_python_libs.bliptv.bliptv_api.Videos.log_name
log_name
Definition: bliptv_api.py:150
nv_python_libs.bliptv.bliptv_api.XmlHandler.__init__
def __init__(self, url)
Definition: bliptv_api.py:80
nv_python_libs.bliptv.bliptv_api.Videos.ampReplace
def ampReplace(self, text)
Definition: bliptv_api.py:381
nv_python_libs.bliptv.bliptv_api.Videos.treeview
treeview
Definition: bliptv_api.py:237
nv_python_libs.bliptv.bliptv_api.Videos.channel
channel
Definition: bliptv_api.py:632
nv_python_libs.bliptv.bliptv_api.Videos.feed_names
feed_names
Definition: bliptv_api.py:221
nv_python_libs.bliptv.bliptv_api.Videos.feed_icons
feed_icons
Definition: bliptv_api.py:228
nv_python_libs.bliptv.bliptv_api.Videos.tree_dir_icon
tree_dir_icon
Definition: bliptv_api.py:392
nv_python_libs.bliptv.bliptv_api.Videos.detectUserLocationByIP
def detectUserLocationByIP(self)
Start - Utility functions.
Definition: bliptv_api.py:257
nv_python_libs.bliptv.bliptv_api.Videos.tree_key
tree_key
Definition: bliptv_api.py:649
nv_python_libs.bliptv.bliptv_api.Videos.error_messages
error_messages
Definition: bliptv_api.py:164
print
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)
Definition: vbi608extractor.cpp:29
nv_python_libs.bliptv.bliptv_api.OutStreamEncoder.out
out
Definition: bliptv_api.py:56
nv_python_libs.bliptv.bliptv_api.OutStreamEncoder.encoding
encoding
Definition: bliptv_api.py:58
nv_python_libs.bliptv.bliptv_exceptions.BliptvUrlError
Definition: bliptv_exceptions.py:24
nv_python_libs.bliptv.bliptv_api.XmlHandler.getEt
def getEt(self)
Definition: bliptv_api.py:90
nv_python_libs.bliptv.bliptv_api.XmlHandler
Definition: bliptv_api.py:77
nv_python_libs.bliptv.bliptv_api.XmlHandler._grabUrl
def _grabUrl(self, url)
Definition: bliptv_api.py:83
nv_python_libs.bliptv.bliptv_api.Videos.getVideos
def getVideos(self, dir_dict, dictionaries)
Definition: bliptv_api.py:681
nv_python_libs.bliptv.bliptv_api.Videos.makeURL
def makeURL(self, URL)
Definition: bliptv_api.py:655
nv_python_libs.bliptv.bliptv_api.Videos.processVideoUrl
def processVideoUrl(self, url)
End of Utility functions.
Definition: bliptv_api.py:411
nv_python_libs.bliptv.bliptv_api.Videos.key_translation
key_translation
Definition: bliptv_api.py:167
nv_python_libs.bliptv.bliptv_api.XmlHandler.url
url
Definition: bliptv_api.py:81
nv_python_libs.bliptv.bliptv_api.OutStreamEncoder.__getattr__
def __getattr__(self, attr)
Definition: bliptv_api.py:68
nv_python_libs.bliptv.bliptv_api.Videos.displayTreeView
def displayTreeView(self)
Definition: bliptv_api.py:627
nv_python_libs.bliptv.bliptv_exceptions.BliptvHttpError
Definition: bliptv_exceptions.py:29
nv_python_libs.bliptv.bliptv_exceptions.BliptvVideoNotFound
Definition: bliptv_exceptions.py:39
nv_python_libs.bliptv.bliptv_api.Videos.tree_order
tree_order
Definition: bliptv_api.py:198
nv_python_libs.bliptv.bliptv_api.OutStreamEncoder
Definition: bliptv_api.py:53
nv_python_libs.bliptv.bliptv_api.OutStreamEncoder.__init__
def __init__(self, outstream, encoding=None)
Definition: bliptv_api.py:55
nv_python_libs.bliptv.bliptv_api.Videos.tree_customize
tree_customize
Definition: bliptv_api.py:210