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.

214 lines
4.9 KiB

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