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.

216 lines
5.0 KiB

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