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.

224 lines
5.3 KiB

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