Plex plugin to to play various online streams (mostly Latvian).

plex.py 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import httplib
  2. import inspect
  3. import logging
  4. import os
  5. import sys
  6. import urllib2
  7. from Framework import components, core
  8. from StringIO import StringIO
  9. GLOBAL_DEFAULT_TIMEOUT = components.networking.GLOBAL_DEFAULT_TIMEOUT
  10. class BasicRequest(object):
  11. def __init__(self, uri, headers={}, method='GET', body=''):
  12. self.uri = uri
  13. self.headers = headers
  14. self.method = method
  15. self.body = body
  16. class FrameworkCore(core.FrameworkCore):
  17. """
  18. Framework core class extension to modify components in testing environment.
  19. """
  20. def _setup_storage(self):
  21. # Don't configure output into real log file.
  22. logging.basicConfig(level=logging.INFO)
  23. #logging.setLoggerClass(core.FrameworkLogger)
  24. logger = logging.getLogger()
  25. #logger.handlers[0].addFilter(core.LogFilter())
  26. #self.log = logging.getLogger(self.identifier)
  27. self.log = logging.getLogger()
  28. self._start_component(components.Storage)
  29. self._values = {}
  30. #self._values_file_path = os.path.join(self.plugin_support_path, 'Data', self.identifier, 'StoredValues')
  31. self._values_file_path = os.path.join(self.bundle_path, 'Data', 'StoredValues')
  32. def get_server_info(self):
  33. # Don't do actual request to Plex server and return dummy values.
  34. self.attributes['machineIdentifier'] = 'Unit testing'
  35. self.attributes['serverVersion'] = None
  36. def start(self):
  37. # Same implementation as in parent class, just without error handling, to allow us to see what went wrong.
  38. if self.init_code:
  39. self.sandbox.execute(self.init_code)
  40. self.sandbox.call_named_function(core.START_FUNCTION_NAME)
  41. def log_exception(self, fmt, *args):
  42. error = sys.exc_info()
  43. if error:
  44. # Catch a special case in the original `Runtime.handle_request`. If error is allowed to raise here, then
  45. # the local variable `body` will not be set and the code fail on `UnboundLocalError` in `finally` later.
  46. (frame, _, _, function_name, _, _) = inspect.getouterframes(inspect.currentframe())[1]
  47. if function_name == 'handle_request' and frame.f_locals['self'].__class__ == Runtime:
  48. self.runtime.request_error = error
  49. return
  50. # In general case, just raise the error.
  51. raise error[0], error[1], error[2]
  52. def _start_component(self, component):
  53. # Replace runtime and networking components with our implementations.
  54. if component == components.Runtime:
  55. component = Runtime
  56. elif component == components.Networking:
  57. component = Networking
  58. super(FrameworkCore, self)._start_component(component)
  59. class PreferenceManager(object):
  60. def __init__(self, values={}):
  61. #TODO ielasa no xml faila ja ir
  62. self._dict = values
  63. def get(self):
  64. return self._dict
  65. class ResponseHeaders(object):
  66. def __init__(self, headers):
  67. self.dict = {k: v.lower() for k, v in headers.iteritems()}
  68. def get(self, header, default=None):
  69. lowercase = header.lower()
  70. return self.dict[lowercase] if lowercase in self.dict else default
  71. class ResponseBody(StringIO):
  72. def __init__(self, string):
  73. StringIO.__init__(self, string)
  74. self.recv = None
  75. @property
  76. def _sock(self):
  77. return self
  78. class MockNetworkingHandler(object):
  79. def __init__(self, networking):
  80. self._networking = networking
  81. def get_mock_response(self, req):
  82. if self._networking.http_response_body is not None:
  83. response = urllib2.addinfourl(ResponseBody(self._networking.http_response_body),
  84. ResponseHeaders(self._networking.http_response_headers),
  85. req.get_full_url())
  86. response.code = 200
  87. response.msg = "OK"
  88. return response
  89. raise RuntimeError('Unhandled HTTP request: %s' % req.get_full_url())
  90. class HTTPHandler(urllib2.HTTPHandler, MockNetworkingHandler):
  91. def __init__(self, networking):
  92. MockNetworkingHandler.__init__(self, networking)
  93. def http_open(self, req):
  94. return self.get_mock_response(req)
  95. if hasattr(httplib, 'HTTPS'):
  96. class HTTPSHandler(urllib2.HTTPSHandler, MockNetworkingHandler):
  97. def __init__(self, networking):
  98. MockNetworkingHandler.__init__(self, networking)
  99. def https_open(self, req):
  100. return self.get_mock_response(req)
  101. class Networking(components.Networking):
  102. """
  103. Networking class extension to avoid sending actual requests in tests.
  104. """
  105. def _init(self):
  106. super(Networking, self)._init()
  107. self.http_response_body = None
  108. self.http_response_headers = {}
  109. self._core.policy.enable_http_caching = False
  110. def build_opener(self, cookie_jar=None):
  111. return urllib2.build_opener(HTTPHandler(self), HTTPSHandler(self))
  112. class Runtime(components.Runtime):
  113. """
  114. Runtime class extension to keep track of the framework's threads and locks.
  115. """
  116. def _init(self):
  117. self._threads = []
  118. super(Runtime, self)._init()
  119. def create_thread(self, f, log=True, sandbox=None, globalize=True, *args, **kwargs):
  120. # Store the created threads so that their status can be checked after tests have run.
  121. thread = super(Runtime, self).create_thread(f, log, sandbox, globalize, *args, **kwargs)
  122. self._threads.append(thread)
  123. return thread
  124. def check_threads(self):
  125. # Check for alive non-daemon threads and throw error if found.
  126. # Ignored threads are just slow but they don't run in a loop so they'll finish eventually.
  127. ignored = ['_setup_shared_code_sandbox']
  128. for thread in self._threads:
  129. if thread.is_alive() and not thread.daemon and thread.name not in ignored:
  130. raise RuntimeError('Alive non-daemon thread %s found, this will prevent shutdown.' % str(thread))
  131. def handle_request(self, request):
  132. # In case the parent method encounters an error reported using `_report_error` method, we'll raise it here.
  133. self.request_error = None
  134. status, headers, body = super(Runtime, self).handle_request(request)
  135. if self.request_error is not None:
  136. raise self.request_error[0], self.request_error[1], self.request_error[2]
  137. return status, headers, body
  138. def _report_error(self, request, controller, kwargs, e):
  139. return
  140. def get_resource_hashes(self):
  141. return