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.

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