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

proxy.py 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #!/usr/bin/env python
  2. # -*- Mode: Python -*-
  3. # vi:si:et:sw=4:sts=4:ts=4
  4. #
  5. # Copyright (C) 2009-2010 Fluendo, S.L. (www.fluendo.com).
  6. # Copyright (C) 2009-2010 Marc-Andre Lureau <marcandre.lureau@gmail.com>
  7. # Copyright (C) 2010 Zaheer Abbas Merali <zaheerabbas at merali dot org>
  8. # Copyright (C) 2010 Andoni Morales Alastruey <ylatuya@gmail.com>
  9. # Copyright (C) 2014 Juan Font Alonso <juanfontalonso@gmail.com>
  10. # This file may be distributed and/or modified under the terms of
  11. # the GNU General Public License version 2 as published by
  12. # the Free Software Foundation.
  13. # This file is distributed without any warranty; without even the implied
  14. # warranty of merchantability or fitness for a particular purpose.
  15. # See "LICENSE" in the source distribution for more information.
  16. import sys
  17. import os
  18. import argparse
  19. from twisted.web import server, resource
  20. from twisted.web.server import NOT_DONE_YET
  21. from twisted.internet import reactor
  22. from fetcher import HLSFetcher
  23. from m3u8 import M3U8
  24. from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
  25. from SocketServer import ThreadingMixIn
  26. import threading
  27. from functools import partial
  28. import urlparse
  29. if sys.version_info < (2, 4):
  30. raise ImportError("Cannot run with Python version < 2.4")
  31. class HLSControler:
  32. def __init__(self, fetcher=None):
  33. self.fetcher = fetcher
  34. self.player = None
  35. self._player_sequence = None
  36. self._n_segments_keep = None
  37. def set_player(self, player):
  38. self.player = player
  39. if player:
  40. self.player.connect_about_to_finish(self.on_player_about_to_finish)
  41. self._n_segments_keep = self.fetcher.n_segments_keep
  42. self.fetcher.n_segments_keep = -1
  43. def _start(self, first_file):
  44. (path, l, f) = first_file
  45. self._player_sequence = f['sequence']
  46. if self.player:
  47. self.player.set_uri(path)
  48. self.player.play()
  49. def start(self):
  50. d = self.fetcher.start()
  51. d.addCallback(self._start)
  52. def _set_next_uri(self):
  53. # keep only the past three segments
  54. if self._n_segments_keep != -1:
  55. self.fetcher.delete_cache(lambda x:
  56. x <= self._player_sequence - self._n_segments_keep)
  57. self._player_sequence += 1
  58. d = self.fetcher.get_file(self._player_sequence)
  59. d.addCallback(self.player.set_uri)
  60. def on_player_about_to_finish(self):
  61. if not self.player._request._disconnected:
  62. reactor.callFromThread(self._set_next_uri)
  63. else:
  64. self.fetcher.stop()
  65. self._start = None
  66. del self.fetcher
  67. del self.player
  68. class HTTPPlayer:
  69. def __init__(self, request):
  70. print "Starting player"
  71. self._playing = False
  72. self._need_data = False
  73. self._cb = None
  74. self._request = request
  75. def need_data(self):
  76. print "need"
  77. return self._need_data
  78. def play(self):
  79. self._playing = True
  80. def stop(self):
  81. print "stop"
  82. self._playing = False
  83. def set_uri(self, filepath):
  84. size = os.path.getsize(filepath)
  85. print str(size)
  86. count = 0
  87. self._on_about_to_finish()
  88. with open(filepath, 'rb') as f:
  89. for chunk in iter(partial(f.read, 1024), ''):
  90. if not self._request._disconnected:
  91. self._request.write(str(chunk))
  92. count += 1024
  93. else:
  94. self.stop()
  95. break
  96. os.remove(filepath)
  97. def on_message(self, bus, message):
  98. print "msg"
  99. def on_sync_message(self, bus, message):
  100. print "sync"
  101. def on_decoded_pad(self, decodebin, pad, more_pad):
  102. print "decoded"
  103. def on_enough_data(self):
  104. print("Player is full up!");
  105. self._need_data = False;
  106. def on_need_data(self, src, length):
  107. self._need_data = True;
  108. self._on_about_to_finish()
  109. def _on_about_to_finish(self, p=None):
  110. if self._cb:
  111. self._cb()
  112. def connect_about_to_finish(self, cb):
  113. self._cb = cb
  114. def quit_server(): #workaround
  115. os.system("kill -9 "+str(os.getpid()))
  116. class HLSProxy(resource.Resource):
  117. isLeaf = True
  118. def render_GET(self, request):
  119. if 'url' in request.args:
  120. url = request.args['url'][0]
  121. if 'bitrate' in request.args:
  122. br = int(request.args['bitrate'][0])
  123. else:
  124. br = 200000
  125. c = HLSControler(HLSFetcher(url, bitrate=br))
  126. p = HTTPPlayer(request)
  127. c.set_player(p)
  128. c.start()
  129. return NOT_DONE_YET
  130. class ThreadedHTTPServer(HTTPServer, ThreadingMixIn):
  131. """Handle requests in a separate thread."""
  132. if __name__ == '__main__':
  133. parser = argparse.ArgumentParser()
  134. parser.add_argument("-p", "--port", type=int, dest="port", required=False,
  135. metavar="PORT", default=8081, help="Port")
  136. args = parser.parse_args()
  137. #server = ThreadedHTTPServer(('', args.port), Handler)
  138. print 'Starting server on port '+str(args.port)+', use <Ctrl-C> to stop'
  139. site = server.Site(HLSProxy())
  140. reactor.listenTCP(args.port, site)
  141. reactor.run()