xattrpp.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import os
  2. import subprocess
  3. import sys
  4. from .common import PostProcessor
  5. from ..utils import (
  6. hyphenate_date,
  7. )
  8. class XAttrMetadataPP(PostProcessor):
  9. #
  10. # More info about extended attributes for media:
  11. # http://freedesktop.org/wiki/CommonExtendedAttributes/
  12. # http://www.freedesktop.org/wiki/PhreedomDraft/
  13. # http://dublincore.org/documents/usageguide/elements.shtml
  14. #
  15. # TODO:
  16. # * capture youtube keywords and put them in 'user.dublincore.subject' (comma-separated)
  17. # * figure out which xattrs can be used for 'duration', 'thumbnail', 'resolution'
  18. #
  19. def run(self, info):
  20. """ Set extended attributes on downloaded file (if xattr support is found). """
  21. # This mess below finds the best xattr tool for the job and creates a
  22. # "write_xattr" function.
  23. try:
  24. # try the pyxattr module...
  25. import xattr
  26. def write_xattr(path, key, value):
  27. return xattr.setxattr(path, key, value)
  28. except ImportError:
  29. if os.name == 'posix':
  30. def which(bin):
  31. for dir in os.environ["PATH"].split(":"):
  32. path = os.path.join(dir, bin)
  33. if os.path.exists(path):
  34. return path
  35. user_has_setfattr = which("setfattr")
  36. user_has_xattr = which("xattr")
  37. if user_has_setfattr or user_has_xattr:
  38. def write_xattr(path, key, value):
  39. import errno
  40. potential_errors = {
  41. # setfattr: /tmp/blah: Operation not supported
  42. "Operation not supported": errno.EOPNOTSUPP,
  43. # setfattr: ~/blah: No such file or directory
  44. # xattr: No such file: ~/blah
  45. "No such file": errno.ENOENT,
  46. }
  47. if user_has_setfattr:
  48. cmd = ['setfattr', '-n', key, '-v', value, path]
  49. elif user_has_xattr:
  50. cmd = ['xattr', '-w', key, value, path]
  51. try:
  52. subprocess.check_output(cmd, stderr=subprocess.STDOUT)
  53. except subprocess.CalledProcessError as e:
  54. errorstr = e.output.strip().decode()
  55. for potential_errorstr, potential_errno in potential_errors.items():
  56. if errorstr.find(potential_errorstr) > -1:
  57. e = OSError(potential_errno, potential_errorstr)
  58. e.__cause__ = None
  59. raise e
  60. raise # Reraise unhandled error
  61. else:
  62. # On Unix, and can't find pyxattr, setfattr, or xattr.
  63. if sys.platform.startswith('linux'):
  64. self._downloader.report_error("Couldn't find a tool to set the xattrs. Install either the python 'pyxattr' or 'xattr' modules, or the GNU 'attr' package (which contains the 'setfattr' tool).")
  65. elif sys.platform == 'darwin':
  66. self._downloader.report_error("Couldn't find a tool to set the xattrs. Install either the python 'xattr' module, or the 'xattr' binary.")
  67. else:
  68. # Write xattrs to NTFS Alternate Data Streams: http://en.wikipedia.org/wiki/NTFS#Alternate_data_streams_.28ADS.29
  69. def write_xattr(path, key, value):
  70. assert(key.find(":") < 0)
  71. assert(path.find(":") < 0)
  72. assert(os.path.exists(path))
  73. ads_fn = path + ":" + key
  74. with open(ads_fn, "w") as f:
  75. f.write(value)
  76. # Write the metadata to the file's xattrs
  77. self._downloader.to_screen('[metadata] Writing metadata to file\'s xattrs...')
  78. filename = info['filepath']
  79. try:
  80. xattr_mapping = {
  81. 'user.xdg.referrer.url': 'webpage_url',
  82. # 'user.xdg.comment': 'description',
  83. 'user.dublincore.title': 'title',
  84. 'user.dublincore.date': 'upload_date',
  85. 'user.dublincore.description': 'description',
  86. 'user.dublincore.contributor': 'uploader',
  87. 'user.dublincore.format': 'format',
  88. }
  89. for xattrname, infoname in xattr_mapping.items():
  90. value = info.get(infoname)
  91. if value:
  92. if infoname == "upload_date":
  93. value = hyphenate_date(value)
  94. write_xattr(filename, xattrname, value)
  95. return True, info
  96. except OSError:
  97. self._downloader.report_error("This filesystem doesn't support extended attributes. (You may have to enable them in your /etc/fstab)")
  98. return False, info