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

bootstrap0.py 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #!/usr/bin/python
  2. #
  3. # Plex Extension Framework
  4. # Copyright (C) 2008-2012 Plex, Inc. (James Clarke, Elan Feingold). All Rights Reserved.
  5. #
  6. ## CONFIGURE THE PYTHON ENVIRONMENT ##
  7. import sys
  8. reload(sys)
  9. sys.setdefaultencoding('utf-8')
  10. import subsystem
  11. import os
  12. import config
  13. if sys.platform == "win32":
  14. # This is needed to ensure binary data transfer over stdio between PMS and plugins
  15. import msvcrt
  16. msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
  17. msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
  18. # This is needed to prevent explosions when there's a system Python installed whose libraries conflict with our own
  19. if 'PLEXLOCALAPPDATA' in os.environ:
  20. key = 'PLEXLOCALAPPDATA'
  21. else:
  22. key = 'LOCALAPPDATA'
  23. ourlocalappdata = os.path.join(os.environ[key], 'Plex Media Server')
  24. for x in [x for x in sys.path if sys.prefix.lower() not in x.lower() and ourlocalappdata.lower() not in x.lower()]:
  25. sys.path.remove(x)
  26. else:
  27. import traceback
  28. import sys
  29. def dumpstacks(signal, frame):
  30. code = []
  31. for threadId, stack in sys._current_frames().items():
  32. code.append("\n# Thread: %d" % (threadId))
  33. for filename, lineno, name, line in traceback.extract_stack(stack):
  34. code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
  35. if line:
  36. code.append(" %s" % (line.strip()))
  37. print "\n".join(code)
  38. import signal
  39. signal.signal(signal.SIGUSR1, dumpstacks)
  40. PYTHON_DIR = os.path.dirname(os.path.abspath(sys.argv[0]))
  41. FRAMEWORK_DIR = os.path.abspath(os.path.join(PYTHON_DIR, '..'))
  42. # Redirect stdout to stderr
  43. sys.stdout = sys.stderr
  44. if sys.platform == "win32":
  45. os_name = "Windows"
  46. cpu_name = "i386"
  47. #TODO - support Windows x64 (Win64)
  48. else:
  49. uname = os.uname()
  50. os_name = uname[0]
  51. cpu_name = uname[4]
  52. mapped_cpu = config.cpu_map.get(cpu_name, cpu_name)
  53. # Special case for Linux/x64 (really should be special case for OS X...)
  54. if os_name == 'Linux' and cpu_name == 'x86_64':
  55. mapped_cpu = 'x86_64'
  56. PLATFORM_DIR = os.path.abspath(os.path.join(FRAMEWORK_DIR, '..', '..', "Platforms", config.os_map[os_name], mapped_cpu))
  57. SHARED_DIR = os.path.abspath(os.path.join(FRAMEWORK_DIR, '..', '..', "Platforms", "Shared"))
  58. #TODO: Check for errors
  59. # If the environment variable PLEXBUNDLEDEXTS is set, this indicates a newer
  60. # server which comes bundled with its own binaries so we'll skip this step
  61. # completely.
  62. #
  63. if 'PLEXBUNDLEDEXTS' not in os.environ:
  64. lib_path = os.path.join(PLATFORM_DIR, "Libraries")
  65. # The binary lib path goes at the end on non-Mac platforms, because binary
  66. # extensions should be picked up from the PMS Exts directory first.
  67. #
  68. if sys.platform != "darwin":
  69. sys.path.append(lib_path)
  70. else:
  71. sys.path.insert(0, lib_path)
  72. # Insert the shared (Python-only) libraries.
  73. sys.path.insert(0, os.path.join(SHARED_DIR, "Libraries"))
  74. ## LOAD AND CONFIGURE THE FRAMEWORK ##
  75. import Framework
  76. import Framework.constants as const
  77. from optparse import OptionParser
  78. parser = OptionParser()
  79. parser.add_option("-i", "--interface", dest="interface", default=config.default_interface)
  80. parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=True)
  81. parser.add_option("-p", "--socket-interface-port", dest="socket_interface_port", default=config.socket_interface_port)
  82. parser.add_option("-s", "--server-version", dest="server_version")
  83. parser.add_option("-d", "--daemon", dest="daemon_command")
  84. parser.add_option("-P", "--pid-file", dest="pid_file")
  85. parser.add_option("-l", "--log-file", dest="log_file")
  86. parser.add_option("-c", "--config-file", dest="config_file")
  87. (options, args) = parser.parse_args()
  88. bundle_path = args[0]
  89. del parser
  90. del OptionParser
  91. # Select the interface class to use
  92. if options.interface == const.interface.pipe:
  93. interface_class = Framework.interfaces.PipeInterface
  94. elif options.interface == const.interface.socket:
  95. interface_class = Framework.interfaces.SocketInterface
  96. if int(options.socket_interface_port) != config.socket_interface_port:
  97. config.socket_interface_port = int(options.socket_interface_port)
  98. else:
  99. #TODO: Error info - no matching interface found
  100. sys.stderr.write('No matching interface found.\n')
  101. sys.exit(1)
  102. if options.server_version != None:
  103. config.server_version = options.server_version
  104. # Configure the log_dir, if one was given
  105. if options.log_file:
  106. config.log_file = os.path.abspath(options.log_file)
  107. # Configure the pid file, if one was given
  108. if options.pid_file:
  109. config.pid_file = os.path.abspath(options.pid_file)
  110. # Load the config file if one was provided
  111. if options.config_file:
  112. import simplejson
  113. f = open(options.config_file, 'r')
  114. json_config = simplejson.load(f)
  115. f.close()
  116. for key in json_config:
  117. setattr(config, key, json_config[key])
  118. def run(daemonized=False):
  119. # Copy the damonized attribute into config
  120. setattr(config, 'daemonized', daemonized)
  121. # Create a core object for the plug-in bundle
  122. core = Framework.core.FrameworkCore(bundle_path, FRAMEWORK_DIR, config)
  123. # Try to load the plug-in code
  124. if not core.load_code():
  125. sys.stderr.write('Error loading bundle code.\n')
  126. sys.exit(2)
  127. # Create an instance of the selected interface
  128. interface = interface_class(core)
  129. # Try to start the core
  130. if not core.start():
  131. sys.stderr.write('Error starting framework core for %s.\n' % bundle_path)
  132. sys.exit(3)
  133. # Start listening on the interface
  134. interface.listen(daemonized)
  135. # If running normally, start the core in the current process
  136. if options.daemon_command == None:
  137. run()
  138. # If issued a daemon command, check what we're supposed to do
  139. else:
  140. import plistlib, daemon, tempfile
  141. # Read the plist to get the identifier
  142. plist_path = os.path.join(bundle_path, 'Contents', 'Info.plist')
  143. ident = plistlib.readPlist(plist_path)['CFBundleIdentifier']
  144. class PluginDaemon(daemon.Daemon):
  145. def run(self):
  146. run(True)
  147. # Make sure we have a pid file for this daemon
  148. pid_config = dict(identifier = ident, port = config.socket_interface_port)
  149. pid_dir = os.path.join(config.root_path, config.pid_files_dir)
  150. # Create the pid files dir if it doesn't exist
  151. if not os.path.exists(pid_dir):
  152. try:
  153. os.makedirs(pid_dir)
  154. except:
  155. pass
  156. if not config.pid_file:
  157. pid_file = os.path.join(pid_dir, '%(identifier)s.%(port)d.pid' % pid_config)
  158. else:
  159. pid_file = config.pid_file % pid_config
  160. # Create the daemon object
  161. d = PluginDaemon(pid_file)
  162. # Decide which action to perform
  163. if options.daemon_command == 'start':
  164. d.start()
  165. elif options.daemon_command == 'restart':
  166. d.restart()
  167. elif options.daemon_command == 'stop':
  168. # Wait up to 60 seconds for the interface to stop
  169. port = 0
  170. try:
  171. port = pid_config['port']
  172. urllib2.urlopen('http://127.0.0.1:%d/:/shutdownInterface' % port, timeout=60)
  173. except:
  174. print "Error shutting down interface on port %d" % port
  175. # Kill the daemon process
  176. d.stop()
  177. else:
  178. print "Unknown command '%s'" % options.daemon_command