Move write_xattr to utils.py
There are some other places that use xattr functions. It's better to move it to a common place so that others can use it.
This commit is contained in:
		
							parent
							
								
									475f8a4580
								
							
						
					
					
						commit
						efa97bdcf1
					
				
					 2 changed files with 106 additions and 107 deletions
				
			
		| 
						 | 
				
			
			@ -1,37 +1,15 @@
 | 
			
		|||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
import sys
 | 
			
		||||
import errno
 | 
			
		||||
 | 
			
		||||
from .common import PostProcessor
 | 
			
		||||
from ..compat import compat_os_name
 | 
			
		||||
from ..utils import (
 | 
			
		||||
    check_executable,
 | 
			
		||||
    hyphenate_date,
 | 
			
		||||
    version_tuple,
 | 
			
		||||
    PostProcessingError,
 | 
			
		||||
    encodeArgument,
 | 
			
		||||
    encodeFilename,
 | 
			
		||||
    write_xattr,
 | 
			
		||||
    XAttrMetadataError,
 | 
			
		||||
    XAttrUnavailableError,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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):
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
| 
						 | 
				
			
			@ -48,88 +26,6 @@ class XAttrMetadataPP(PostProcessor):
 | 
			
		|||
    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')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -159,6 +55,10 @@ class XAttrMetadataPP(PostProcessor):
 | 
			
		|||
 | 
			
		||||
            return [], info
 | 
			
		||||
 | 
			
		||||
        except XAttrUnavailableError as e:
 | 
			
		||||
            self._downloader.report_error(str(e))
 | 
			
		||||
            return [], info
 | 
			
		||||
 | 
			
		||||
        except XAttrMetadataError as e:
 | 
			
		||||
            if e.reason == 'NO_SPACE':
 | 
			
		||||
                self._downloader.report_warning(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ from .compat import (
 | 
			
		|||
    compat_html_entities_html5,
 | 
			
		||||
    compat_http_client,
 | 
			
		||||
    compat_kwargs,
 | 
			
		||||
    compat_os_name,
 | 
			
		||||
    compat_parse_qs,
 | 
			
		||||
    compat_shlex_quote,
 | 
			
		||||
    compat_socket_create_connection,
 | 
			
		||||
| 
						 | 
				
			
			@ -775,6 +776,25 @@ class ContentTooShortError(Exception):
 | 
			
		|||
        self.expected = expected
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XAttrMetadataError(Exception):
 | 
			
		||||
    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 XAttrUnavailableError(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _create_http_connection(ydl_handler, http_class, is_https, *args, **kwargs):
 | 
			
		||||
    # Working around python 2 bug (see http://bugs.python.org/issue17849) by limiting
 | 
			
		||||
    # expected HTTP responses to meet HTTP/1.0 or later (see also
 | 
			
		||||
| 
						 | 
				
			
			@ -3131,3 +3151,82 @@ def decode_png(png_data):
 | 
			
		|||
            current_row.append(color)
 | 
			
		||||
 | 
			
		||||
    return width, height, pixels
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_xattr(path, key, value):
 | 
			
		||||
    # This mess below finds the best xattr tool for the job
 | 
			
		||||
    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):
 | 
			
		||||
            # TODO: fallback to CLI tools
 | 
			
		||||
            raise XAttrUnavailableError(
 | 
			
		||||
                '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__))
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
            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:
 | 
			
		||||
 | 
			
		||||
                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'):
 | 
			
		||||
                    raise XAttrUnavailableError(
 | 
			
		||||
                        "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:
 | 
			
		||||
                    raise XAttrUnavailableError(
 | 
			
		||||
                        "Couldn't find a tool to set the xattrs. "
 | 
			
		||||
                        "Install either the python 'xattr' module, "
 | 
			
		||||
                        "or the 'xattr' binary.")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue