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.

226 lines
5.4 KiB

  1. #!/usr/bin/env python
  2. # Copyright 2006 John-Mark Gurney <jmg@funkthat.com>
  3. '''DVD Handling'''
  4. __version__ = '$Change$'
  5. # $Id$
  6. default_audio_lang = 'en'
  7. import itertools
  8. import os
  9. import sets
  10. import sys
  11. sys.path.append('mpegts')
  12. try:
  13. import mpegts
  14. audiofilter = lambda x, y: mpegts.DVDAudioFilter(x, y)
  15. except ImportError:
  16. print >>sys.stderr, 'module mpegts could not be loaded, not filtering audio'
  17. audiofilter = lambda x, y: x
  18. from pydvdread import *
  19. from DIDLLite import StorageFolder, Movie, VideoItem, Resource
  20. from FSStorage import FSObject, registerklassfun
  21. from twisted.python import log, threadable
  22. from twisted.spread import pb
  23. from twisted.web import resource, server
  24. def gennameindexes(pref, item):
  25. ret = []
  26. d = {}
  27. for i, title in enumerate(item):
  28. t = '%s %d (%s)' % (pref, i + 1, title.time)
  29. ret.append(t)
  30. d[t] = i
  31. return ret, d
  32. class IterTransfer(pb.Viewable):
  33. def __init__(self, iterable, request):
  34. self.iter = iter(iterable)
  35. self.request = request
  36. request.registerProducer(self, 0)
  37. def resumeProducing(self):
  38. if not self.request:
  39. return
  40. # get data and write to request.
  41. try:
  42. data = self.iter.next()
  43. if data:
  44. # this .write will spin the reactor, calling
  45. # .doWrite and then .resumeProducing again, so
  46. # be prepared for a re-entrant call
  47. self.request.write(data)
  48. except StopIteration:
  49. if self.request:
  50. self.request.unregisterProducer()
  51. self.request.finish()
  52. self.request = None
  53. def pauseProducing(self):
  54. pass
  55. def stopProducing(self):
  56. # close zipfile
  57. self.request = None
  58. # Remotely relay producer interface.
  59. def view_resumeProducing(self, issuer):
  60. self.resumeProducing()
  61. def view_pauseProducing(self, issuer):
  62. self.pauseProducing()
  63. def view_stopProducing(self, issuer):
  64. self.stopProducing()
  65. synchronized = ['resumeProducing', 'stopProducing']
  66. threadable.synchronize(IterTransfer)
  67. class IterGenResource(resource.Resource):
  68. isLeaf = True
  69. def __init__(self, itergen):
  70. resource.Resource.__init__(self)
  71. self.itergen = itergen
  72. def render(self, request):
  73. request.setHeader('content-type', 'video/mpeg')
  74. if request.method == 'HEAD':
  75. return ''
  76. # return data
  77. IterTransfer(self.itergen(), request)
  78. # and make sure the connection doesn't get closed
  79. return server.NOT_DONE_YET
  80. class DVDChapter(VideoItem):
  81. def __init__(self, *args, **kwargs):
  82. self.dvdtitle = kwargs['dvdtitle']
  83. self.chapter = kwargs['chapter']
  84. del kwargs['dvdtitle'], kwargs['chapter']
  85. audio = self.dvdtitle.selectaudio(default_audio_lang)
  86. kwargs['content'] = IterGenResource(lambda i = self.chapter,
  87. p = audio.pos: audiofilter(i, 0x80 + p))
  88. VideoItem.__init__(self, *args, **kwargs)
  89. self.url = '%s/%s' % (self.cd.urlbase, self.id)
  90. self.res = Resource(self.url, 'http-get:*:video/mpeg:*')
  91. #self.res.size = self.chapter.size
  92. def doUpdate(self):
  93. pass
  94. class DVDTitle(StorageFolder):
  95. def __init__(self, *args, **kwargs):
  96. self.dvdtitle = kwargs['dvdtitle']
  97. self.dvddisc = kwargs['dvddisc']
  98. del kwargs['dvdtitle'], kwargs['dvddisc']
  99. audio = self.dvdtitle.selectaudio(default_audio_lang)
  100. kwargs['content'] = IterGenResource(lambda dt = self.dvdtitle,
  101. p = audio.pos: audiofilter(itertools.chain(*dt), 0x80 + p))
  102. StorageFolder.__init__(self, *args, **kwargs)
  103. self.url = '%s/%s' % (self.cd.urlbase, self.id)
  104. self.res = Resource(self.url, 'http-get:*:video/mpeg:*')
  105. # mapping from path to objectID
  106. self.pathObjmap = {}
  107. def checkUpdate(self):
  108. self.doUpdate()
  109. #return self.dvddisc.checkUpdate()
  110. return self
  111. def doUpdate(self):
  112. doupdate = False
  113. origchildren, toindex = gennameindexes('Chapter', self.dvdtitle)
  114. children = sets.Set(origchildren)
  115. for i in self.pathObjmap.keys():
  116. if i not in children:
  117. doupdate = True
  118. # delete
  119. self.cd.delItem(self.pathObjmap[i])
  120. del self.pathObjmap[i]
  121. for i in origchildren:
  122. if i in self.pathObjmap:
  123. continue
  124. # new object
  125. self.pathObjmap[i] = self.cd.addItem(self.id,
  126. DVDChapter, i, dvdtitle = self.dvdtitle,
  127. chapter = self.dvdtitle[toindex[i]])
  128. doupdate = True
  129. if doupdate:
  130. StorageFolder.doUpdate(self)
  131. class DVDDisc(FSObject, StorageFolder):
  132. def __init__(self, *args, **kwargs):
  133. path = kwargs['path']
  134. del kwargs['path']
  135. StorageFolder.__init__(self, *args, **kwargs)
  136. FSObject.__init__(self, path)
  137. # mapping from path to objectID
  138. self.pathObjmap = {}
  139. def doUpdate(self):
  140. # open the DVD as necessary.
  141. self.dvd = DVD(self.FSpath)
  142. doupdate = False
  143. origchildren, toindex = gennameindexes('Title', self.dvd)
  144. children = sets.Set(origchildren)
  145. for i in self.pathObjmap.keys():
  146. if i not in children:
  147. doupdate = True
  148. # delete
  149. self.cd.delItem(self.pathObjmap[i])
  150. del self.pathObjmap[i]
  151. for i in origchildren:
  152. if i in self.pathObjmap:
  153. continue
  154. # new object
  155. self.pathObjmap[i] = self.cd.addItem(self.id, DVDTitle,
  156. i, dvdtitle = self.dvd[toindex[i]], dvddisc = self)
  157. doupdate = True
  158. if doupdate:
  159. StorageFolder.doUpdate(self)
  160. def detectdvd(path, fobj):
  161. if os.path.isdir(path):
  162. # Make sure we there is only a VIDEO_TS in there, even
  163. # if there is a VIDEO_TS w/ other files, we will open
  164. # the VIDEO_TS as a DVD (if it is one)
  165. ld = os.listdir(path)
  166. if ld == ['VIDEO_TS' ]:
  167. pass
  168. elif not filter(lambda x: x[:4] != 'VTS_' and
  169. x[:9] != 'VIDEO_TS.', ld):
  170. pass
  171. else:
  172. return None, None
  173. d = DVD(path)
  174. return DVDDisc, { 'path': path }
  175. registerklassfun(detectdvd)