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.

185 lines
4.5 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. d = {}
  26. for i, title in enumerate(item):
  27. t = '%s %d (%s)' % (pref, i + 1, title.time)
  28. d[t] = i
  29. return d
  30. class IterTransfer(pb.Viewable):
  31. def __init__(self, iterable, request):
  32. self.iter = iter(iterable)
  33. self.request = request
  34. request.registerProducer(self, 0)
  35. def resumeProducing(self):
  36. if not self.request:
  37. return
  38. # get data and write to request.
  39. try:
  40. data = self.iter.next()
  41. if data:
  42. # this .write will spin the reactor, calling
  43. # .doWrite and then .resumeProducing again, so
  44. # be prepared for a re-entrant call
  45. self.request.write(data)
  46. except StopIteration:
  47. if self.request:
  48. self.request.unregisterProducer()
  49. self.request.finish()
  50. self.request = None
  51. def pauseProducing(self):
  52. pass
  53. def stopProducing(self):
  54. # close zipfile
  55. self.request = None
  56. # Remotely relay producer interface.
  57. def view_resumeProducing(self, issuer):
  58. self.resumeProducing()
  59. def view_pauseProducing(self, issuer):
  60. self.pauseProducing()
  61. def view_stopProducing(self, issuer):
  62. self.stopProducing()
  63. synchronized = ['resumeProducing', 'stopProducing']
  64. threadable.synchronize(IterTransfer)
  65. class IterGenResource(resource.Resource):
  66. isLeaf = True
  67. def __init__(self, itergen):
  68. resource.Resource.__init__(self)
  69. self.itergen = itergen
  70. def render(self, request):
  71. request.setHeader('content-type', 'video/mpeg')
  72. if request.method == 'HEAD':
  73. return ''
  74. # return data
  75. IterTransfer(self.itergen(), request)
  76. # and make sure the connection doesn't get closed
  77. return server.NOT_DONE_YET
  78. class DVDChapter(VideoItem):
  79. def __init__(self, *args, **kwargs):
  80. self.dvdtitle = kwargs['dvdtitle']
  81. self.chapter = kwargs['chapter']
  82. del kwargs['dvdtitle'], kwargs['chapter']
  83. audio = self.dvdtitle.selectaudio(default_audio_lang)
  84. kwargs['content'] = IterGenResource(lambda i = self.chapter,
  85. p = audio.pos: audiofilter(i, 0x80 + p))
  86. VideoItem.__init__(self, *args, **kwargs)
  87. self.url = '%s/%s' % (self.cd.urlbase, self.id)
  88. self.res = Resource(self.url, 'http-get:*:video/mpeg:*')
  89. #self.res.size = self.chapter.size
  90. def doUpdate(self):
  91. pass
  92. class DVDTitle(StorageFolder):
  93. def __init__(self, *args, **kwargs):
  94. self.dvdtitle = kwargs['dvdtitle']
  95. self.dvddisc = kwargs['dvddisc']
  96. del kwargs['dvdtitle'], kwargs['dvddisc']
  97. audio = self.dvdtitle.selectaudio(default_audio_lang)
  98. kwargs['content'] = IterGenResource(lambda dt = self.dvdtitle,
  99. p = audio.pos: audiofilter(itertools.chain(*dt), 0x80 + p))
  100. StorageFolder.__init__(self, *args, **kwargs)
  101. self.url = '%s/%s' % (self.cd.urlbase, self.id)
  102. self.res = Resource(self.url, 'http-get:*:video/mpeg:*')
  103. # mapping from path to objectID
  104. self.pathObjmap = {}
  105. def checkUpdate(self):
  106. self.doUpdate()
  107. #return self.dvddisc.checkUpdate()
  108. def genChildren(self):
  109. return gennameindexes('Chapter', self.dvdtitle)
  110. def createObject(self, i, arg):
  111. return DVDChapter, i, (), { 'dvdtitle': self.dvdtitle,
  112. 'chapter': self.dvdtitle[arg] }
  113. class DVDDisc(FSObject, StorageFolder):
  114. def __init__(self, *args, **kwargs):
  115. path = kwargs['path']
  116. del kwargs['path']
  117. StorageFolder.__init__(self, *args, **kwargs)
  118. FSObject.__init__(self, path)
  119. def genChildren(self):
  120. self.dvd = DVD(self.FSpath)
  121. return gennameindexes('Title', self.dvd)
  122. def createObject(self, i, arg):
  123. return DVDTitle, i, (), { 'dvdtitle': self.dvd[arg],
  124. 'dvddisc': self }
  125. def detectdvd(path, fobj):
  126. if os.path.isdir(path):
  127. # Make sure we there is only a VIDEO_TS in there, even
  128. # if there is a VIDEO_TS w/ other files, we will open
  129. # the VIDEO_TS as a DVD (if it is one)
  130. ld = os.listdir(path)
  131. if ld == ['VIDEO_TS' ]:
  132. pass
  133. elif not filter(lambda x: x[:4] != 'VTS_' and
  134. x[:9] != 'VIDEO_TS.', ld):
  135. pass
  136. else:
  137. return None, None
  138. d = DVD(path)
  139. return DVDDisc, { 'path': path }
  140. registerklassfun(detectdvd)