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.

156 lines
3.8 KiB

  1. #!/usr/bin/env python
  2. import errno
  3. import os
  4. import sets
  5. import stat
  6. from DIDLLite import StorageFolder, Item, VideoItem, AudioItem, TextItem, ImageItem, Resource
  7. from twisted.web import static
  8. from twisted.python import log
  9. mimedict = static.loadMimeTypes()
  10. def statcmp(a, b, cmpattrs = [ 'st_ino', 'st_dev', 'st_size', 'st_mtime', ]):
  11. if a is None or b is None:
  12. return False
  13. for i in cmpattrs:
  14. if getattr(a, i) != getattr(b, i):
  15. return False
  16. return True
  17. class FSObject(object):
  18. def __init__(self, path):
  19. self.FSpath = path
  20. self.pstat = None
  21. def checkUpdate(self):
  22. # need to handle no such file or directory
  23. # push it up? but still need to handle disappearing
  24. try:
  25. nstat = os.stat(self.FSpath)
  26. if statcmp(self.pstat, nstat):
  27. return
  28. self.pstat = nstat
  29. self.doUpdate()
  30. except OSError, x:
  31. log.msg('OSError: %s' % x)
  32. if x.errno in (errno.ENOENT, errno.ENOTDIR, errno.EPERM, ):
  33. # We can't access it anymore, delete it
  34. self.cd.delItem(self.id)
  35. else:
  36. raise x
  37. except:
  38. import traceback
  39. print traceback.print_exc()
  40. def doUpdate(self):
  41. raise NotImplementedError
  42. class FSItem(FSObject, Item):
  43. def __init__(self, *args, **kwargs):
  44. FSObject.__init__(self, kwargs['path'])
  45. del kwargs['path']
  46. urlbase = kwargs['urlbase']
  47. del kwargs['urlbase']
  48. mimetype = kwargs['mimetype']
  49. del kwargs['mimetype']
  50. Item.__init__(self, *args, **kwargs)
  51. self.url = '%s%s' % (urlbase, self.FSpath)
  52. self.mimetype = mimetype
  53. def doUpdate(self):
  54. self.res = Resource(self.url, 'http-get:*:%s:*' % self.mimetype)
  55. self.res.size = os.path.getsize(fpath)
  56. class FSVideoItem(FSItem, VideoItem):
  57. pass
  58. class FSAudioItem(FSItem, AudioItem):
  59. pass
  60. class FSTextItem(FSItem, TextItem):
  61. pass
  62. class FSImageItem(FSItem, ImageItem):
  63. pass
  64. mimetoklass = {
  65. 'application/ogg': FSAudioItem,
  66. 'video': FSVideoItem,
  67. 'audio': FSAudioItem,
  68. 'text': FSTextItem,
  69. 'image': FSImageItem,
  70. }
  71. def dofileadd(cd, parent, urlbase, path, name):
  72. fn, ext = os.path.splitext(name)
  73. ext = ext.lower()
  74. try:
  75. mt = mimedict[ext]
  76. except KeyError:
  77. log.msg('no mime-type for: %s' % name)
  78. return
  79. ty = mt.split('/')[0]
  80. if mimetoklass.has_key(mt):
  81. klass = mimetoklass[mt]
  82. elif mimetoklass.has_key(ty):
  83. klass = mimetoklass[ty]
  84. else:
  85. raise KeyError, 'no item for mt: %s' % mt
  86. return cd.addItem(parent, klass, name, urlbase = urlbase,
  87. path = os.path.join(path, name), mimetype = mt)
  88. class FSDirectory(StorageFolder, FSObject):
  89. def __init__(self, *args, **kwargs):
  90. path = kwargs['path']
  91. del kwargs['path']
  92. urlbase = kwargs['urlbase']
  93. del kwargs['urlbase']
  94. StorageFolder.__init__(self, *args, **kwargs)
  95. FSObject.__init__(self, path)
  96. # mapping from path to objectID
  97. self.pathObjmap = {}
  98. self.urlbase = urlbase
  99. def doUpdate(self):
  100. # We need to rescan this dir, and see if our children has
  101. # changed any.
  102. log.msg('doUpdate: %s, path: %s' % (self.title, self.FSpath))
  103. children = sets.Set(os.listdir(self.FSpath))
  104. for i in self.pathObjmap:
  105. if i not in children:
  106. # delete
  107. self.cd.delItem(self.pathObjmap[i])
  108. log.msg('doUpdate: %s, children: %s' % (self.title, children))
  109. for i in children:
  110. fname = os.path.join(self.FSpath, i)
  111. if i in self.pathObjmap:
  112. continue
  113. # new object
  114. if os.path.isdir(fname):
  115. # new dir
  116. nf = self.cd.addContainer(self.id, i, klass = FSDirectory, path = fname, urlbase = self.urlbase)
  117. elif os.path.isfile(fname):
  118. # new file
  119. nf = dofileadd(self.cd, self.id, self.urlbase, self.FSpath, i)
  120. else:
  121. nf = None
  122. log.msg('skipping: %s' % fname)
  123. if nf is not None:
  124. self.pathObjmap[i] = nf
  125. log.msg('i: %s, nf: %s' % (i, nf))
  126. self.cd[nf].checkUpdate()
  127. # sort our children
  128. log.msg('doUpdate: %s, sorting: %s' % (self.title, list.__str__(self)))
  129. self.sort(lambda x, y: cmp(x.title, y.title))
  130. log.msg('sorted')