A Python UPnP Media Server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
2.5 KiB

  1. #!/usr/bin/env python
  2. # Copyright 2009 John-Mark Gurney <jmg@funkthat.com>
  3. '''MPEG-TS clip'''
  4. __version__ = '$Change$'
  5. # $Id$
  6. import bisect
  7. import email
  8. import os.path
  9. from DIDLLite import VideoItem, Resource
  10. from FSStorage import registerklassfun
  11. from twisted.web import static
  12. class ClipProxyFile:
  13. def __init__(self, f, p):
  14. self.fp = open(f)
  15. self.p = p
  16. self.pos = 0
  17. lp = p[-1]
  18. self.size = lp[0] + lp[1]
  19. def read(self, s=None):
  20. if s is None:
  21. s = self.size - self.pos
  22. p = bisect.bisect_right(self.p, (self.pos,))
  23. if p > 0:
  24. p -= 1
  25. # We might be able to do this with proper construction of
  26. # self.p, but this is easier.
  27. r = []
  28. fp = self.fp
  29. records = iter(self.p[p:])
  30. while s:
  31. rec = next(records)
  32. diff = self.pos - rec[0]
  33. rlen = min(s, rec[1] - diff)
  34. fp.seek(rec[2] + diff)
  35. r.append(fp.read(rlen))
  36. s -= rlen
  37. self.pos += rlen
  38. return ''.join(r)
  39. def close(self):
  40. self.fp.close()
  41. def seek(self, p, d=0):
  42. assert d == 0
  43. self.pos = p
  44. if self.pos > self.size:
  45. self.pos = self.size
  46. def tell(self):
  47. return self.pos
  48. class ClipProxy(static.File):
  49. isLeaf = True
  50. synchronized = [ 'parsefile', 'getsize', 'open' ]
  51. def __init__(self, f, *args):
  52. self.__mtime = None
  53. static.File.__init__(self, f, *args)
  54. self.parsefile(self.path)
  55. def parsefile(self, f):
  56. if self.getModificationTime() == self.__mtime:
  57. return
  58. self.__mtime = self.getModificationTime()
  59. i = email.message_from_file(open(f))
  60. self.origfile = i['file']
  61. self.date = eval(i['datetuple'], { '__builtins__': {} })
  62. # date is UTC
  63. p = [ list(map(int, x.split())) for x in i.get_payload().split('\n') if x ]
  64. pos = 0
  65. self.pos = par = []
  66. for j in p:
  67. l = j[1] - j[0] + 188
  68. par.append((pos, l, j[0]))
  69. pos += l
  70. self.__size = pos
  71. def getsize(self):
  72. return self.__size
  73. def open(self):
  74. return ClipProxyFile(self.origfile, self.pos)
  75. def restat(self):
  76. static.File.restat(self)
  77. self.parsefile(self.path)
  78. class ClipFile(VideoItem):
  79. def __init__(self, *args, **kwargs):
  80. file = kwargs.pop('file')
  81. mimetype = 'video/mpeg'
  82. kwargs['content'] = ClipProxy(file, mimetype)
  83. VideoItem.__init__(self, *args, **kwargs)
  84. self.url = '%s/%s' % (self.cd.urlbase, self.id)
  85. self.res = Resource(self.url, 'http-get:*:%s:*' % mimetype)
  86. def detectclipfile(origpath, fobj):
  87. path = os.path.basename(origpath)
  88. ext = os.path.splitext(path)[1]
  89. if ext == '.clip':
  90. return ClipFile, { 'file': origpath }
  91. return None, None
  92. registerklassfun(detectclipfile)