| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 | from __future__ import unicode_literalsimport osimport subprocessimport sysimport errnofrom .common import PostProcessorfrom ..compat import compat_os_namefrom ..utils import (    check_executable,    hyphenate_date,    version_tuple,    PostProcessingError,    encodeArgument,    encodeFilename,)class XAttrMetadataError(PostProcessingError):    def __init__(self, code=None, msg='Unknown error'):        super(XAttrMetadataError, self).__init__(msg)        self.code = code        # Parsing code and msg        if (self.code in (errno.ENOSPC, errno.EDQUOT) or                'No space left' in self.msg or 'Disk quota excedded' in self.msg):            self.reason = 'NO_SPACE'        elif self.code == errno.E2BIG or 'Argument list too long' in self.msg:            self.reason = 'VALUE_TOO_LONG'        else:            self.reason = 'NOT_SUPPORTED'class XAttrMetadataPP(PostProcessor):    #    # More info about extended attributes for media:    #   http://freedesktop.org/wiki/CommonExtendedAttributes/    #   http://www.freedesktop.org/wiki/PhreedomDraft/    #   http://dublincore.org/documents/usageguide/elements.shtml    #    # TODO:    #  * capture youtube keywords and put them in 'user.dublincore.subject' (comma-separated)    #  * figure out which xattrs can be used for 'duration', 'thumbnail', 'resolution'    #    def run(self, info):        """ Set extended attributes on downloaded file (if xattr support is found). """        # This mess below finds the best xattr tool for the job and creates a        # "write_xattr" function.        try:            # try the pyxattr module...            import xattr            # Unicode arguments are not supported in python-pyxattr until            # version 0.5.0            # See https://github.com/rg3/youtube-dl/issues/5498            pyxattr_required_version = '0.5.0'            if version_tuple(xattr.__version__) < version_tuple(pyxattr_required_version):                self._downloader.report_warning(                    'python-pyxattr is detected but is too old. '                    'youtube-dl requires %s or above while your version is %s. '                    'Falling back to other xattr implementations' % (                        pyxattr_required_version, xattr.__version__))                raise ImportError            def write_xattr(path, key, value):                try:                    xattr.set(path, key, value)                except EnvironmentError as e:                    raise XAttrMetadataError(e.errno, e.strerror)        except ImportError:            if compat_os_name == 'nt':                # Write xattrs to NTFS Alternate Data Streams:                # http://en.wikipedia.org/wiki/NTFS#Alternate_data_streams_.28ADS.29                def write_xattr(path, key, value):                    assert ':' not in key                    assert os.path.exists(path)                    ads_fn = path + ':' + key                    try:                        with open(ads_fn, 'wb') as f:                            f.write(value)                    except EnvironmentError as e:                        raise XAttrMetadataError(e.errno, e.strerror)            else:                user_has_setfattr = check_executable('setfattr', ['--version'])                user_has_xattr = check_executable('xattr', ['-h'])                if user_has_setfattr or user_has_xattr:                    def write_xattr(path, key, value):                        value = value.decode('utf-8')                        if user_has_setfattr:                            executable = 'setfattr'                            opts = ['-n', key, '-v', value]                        elif user_has_xattr:                            executable = 'xattr'                            opts = ['-w', key, value]                        cmd = ([encodeFilename(executable, True)] +                               [encodeArgument(o) for o in opts] +                               [encodeFilename(path, True)])                        try:                            p = subprocess.Popen(                                cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)                        except EnvironmentError as e:                            raise XAttrMetadataError(e.errno, e.strerror)                        stdout, stderr = p.communicate()                        stderr = stderr.decode('utf-8', 'replace')                        if p.returncode != 0:                            raise XAttrMetadataError(p.returncode, stderr)                else:                    # On Unix, and can't find pyxattr, setfattr, or xattr.                    if sys.platform.startswith('linux'):                        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).")                    else:                        self._downloader.report_error(                            "Couldn't find a tool to set the xattrs. "                            "Install either the python 'xattr' module, "                            "or the 'xattr' binary.")        # Write the metadata to the file's xattrs        self._downloader.to_screen('[metadata] Writing metadata to file\'s xattrs')        filename = info['filepath']        try:            xattr_mapping = {                'user.xdg.referrer.url': 'webpage_url',                # 'user.xdg.comment':            'description',                'user.dublincore.title': 'title',                'user.dublincore.date': 'upload_date',                'user.dublincore.description': 'description',                'user.dublincore.contributor': 'uploader',                'user.dublincore.format': 'format',            }            for xattrname, infoname in xattr_mapping.items():                value = info.get(infoname)                if value:                    if infoname == 'upload_date':                        value = hyphenate_date(value)                    byte_value = value.encode('utf-8')                    write_xattr(filename, xattrname, byte_value)            return [], info        except XAttrMetadataError as e:            if e.reason == 'NO_SPACE':                self._downloader.report_warning(                    'There\'s no disk space left or disk quota exceeded. ' +                    'Extended attributes are not written.')            elif e.reason == 'VALUE_TOO_LONG':                self._downloader.report_warning(                    'Unable to write extended attributes due to too long values.')            else:                msg = 'This filesystem doesn\'t support extended attributes. '                if compat_os_name == 'nt':                    msg += 'You need to use NTFS.'                else:                    msg += '(You may have to enable them in your /etc/fstab)'                self._downloader.report_error(msg)            return [], info
 |