def truncate(self, source): """Truncates an item to a size less than UPLOAD_MAX_SIZE.""" fd, dest = tempfile.mkstemp(u'.ogg') os.close(fd) log.info(u'echonest: truncating {0} to {1}', util.displayable_path(source), util.displayable_path(dest)) opts = [] for arg in TRUNCATE_COMMAND.split(): arg = arg.encode('utf-8') opts.append(Template(arg).substitute(source=source, dest=dest)) # Run the command. try: util.command_output(opts) except (OSError, subprocess.CalledProcessError) as exc: log.debug(u'echonest: truncate failed: {0}', exc) util.remove(dest) return log.info(u'echonest: truncate encoding {0}', util.displayable_path(source)) return dest
def im_resize(maxwidth, path_in, path_out=None): """Resize using ImageMagick. Use the ``magick`` program or ``convert`` on older versions. Return the output path of resized image. """ path_out = path_out or temp_file_for(path_in) log.debug(u'artresizer: ImageMagick resizing {0} to {1}', util.displayable_path(path_in), util.displayable_path(path_out)) # "-resize WIDTHx>" shrinks images with the width larger # than the given width while maintaining the aspect ratio # with regards to the height. cmd = ArtResizer.shared.im_convert_cmd + \ [util.syspath(path_in, prefix=False), '-resize', '{0}x>'.format(maxwidth), util.syspath(path_out, prefix=False)] try: util.command_output(cmd) except subprocess.CalledProcessError: log.warning(u'artresizer: IM convert failed for {0}', util.displayable_path(path_in)) return path_in return path_out
def convert(self, item): """Converts an item in an unsupported media format to ogg. Config pending. This is stolen from Jakob Schnitzers convert plugin. """ fd, dest = tempfile.mkstemp(u'.ogg') os.close(fd) source = item.path log.info(u'echonest: encoding {0} to {1}'.format( util.displayable_path(source), util.displayable_path(dest), )) # Build up the FFmpeg command line. # FIXME: use avconv? command = u'ffmpeg -i $source -y -acodec libvorbis -vn -aq 2 $dest' opts = [] for arg in command.split(): arg = arg.encode('utf-8') opts.append(Template(arg).substitute(source=source, dest=dest)) # Run the command. try: util.command_output(opts) except (OSError, subprocess.CalledProcessError) as exc: log.debug(u'echonest: encode failed: {0}'.format(exc)) util.remove(dest) return log.info(u'echonest: finished encoding {0}'.format( util.displayable_path(source)) ) return dest
def truncate(self, item): """Truncates an item to a size less than UPLOAD_MAX_SIZE.""" fd, dest = tempfile.mkstemp(u'.ogg') os.close(fd) source = item.path log.info(u'echonest: truncating {0} to {1}'.format( util.displayable_path(source), util.displayable_path(dest), )) command = u'ffmpeg -t 300 -i $source -y -acodec copy $dest' opts = [] for arg in command.split(): arg = arg.encode('utf-8') opts.append(Template(arg).substitute(source=source, dest=dest)) # Run the command. try: util.command_output(opts) except (OSError, subprocess.CalledProcessError) as exc: log.debug(u'echonest: truncate failed: {0}'.format(exc)) util.remove(dest) return log.info(u'echonest: truncate encoding {0}'.format( util.displayable_path(source)) ) return dest
def item_candidates(self, item, artist, album): dir = path.dirname(item.path) cues = glob.glob(path.join(dir, "*.cue")) if not cues: return if len(cues) > 1: self._log.info(u"Found multiple cue files doing nothing: {0}", map(displayable_path, cues)) cue_file = cues[0] self._log.info("Found {} for {}", displayable_path(cue_file), item) try: # careful: will ask for input in case of conflicts command_output(['shnsplit', '-f', cue_file, item.path]) except (subprocess.CalledProcessError, OSError): self._log.exception(u'shnsplit execution failed') return tracks = glob(path.join(dir, "*.wav")) self._log.info("Generated {0} tracks", len(tracks)) for t in tracks: title = "dunno lol" track_id = "wtf" index = int(path.basename(t)[len("split-track"):-len(".wav")]) yield TrackInfo(title, track_id, index=index, artist=artist)
def im_resize(maxwidth, path_in, path_out=None, quality=0): """Resize using ImageMagick. Use the ``magick`` program or ``convert`` on older versions. Return the output path of resized image. """ path_out = path_out or temp_file_for(path_in) log.debug(u'artresizer: ImageMagick resizing {0} to {1}', util.displayable_path(path_in), util.displayable_path(path_out)) # "-resize WIDTHx>" shrinks images with the width larger # than the given width while maintaining the aspect ratio # with regards to the height. cmd = ArtResizer.shared.im_convert_cmd + [ util.syspath(path_in, prefix=False), '-resize', '{0}x>'.format(maxwidth), ] if quality > 0: cmd += ['-quality', '{0}'.format(quality)] cmd.append(util.syspath(path_out, prefix=False)) try: util.command_output(cmd) except subprocess.CalledProcessError: log.warning(u'artresizer: IM convert failed for {0}', util.displayable_path(path_in)) return path_in return path_out
def convert(self, source): """Converts an item in an unsupported media format to ogg. Config pending. This is stolen from Jakob Schnitzers convert plugin. """ fd, dest = tempfile.mkstemp(u'.ogg') os.close(fd) self._log.info(u'encoding {0} to {1}', util.displayable_path(source), util.displayable_path(dest)) opts = [] for arg in CONVERT_COMMAND.split(): arg = arg.encode('utf-8') opts.append(Template(arg).substitute(source=source, dest=dest)) # Run the command. try: util.command_output(opts) except (OSError, subprocess.CalledProcessError) as exc: self._log.debug(u'encode failed: {0}', exc) util.remove(dest) return self._log.info(u'finished encoding {0}', util.displayable_path(source)) return dest
def convert(self, source): """Converts an item in an unsupported media format to ogg. Config pending. This is stolen from Jakob Schnitzers convert plugin. """ fd, dest = tempfile.mkstemp(b'.ogg') os.close(fd) self._log.info(u'encoding {0} to {1}', util.displayable_path(source), util.displayable_path(dest)) opts = [] for arg in CONVERT_COMMAND.split(): arg = arg.encode('utf-8') opts.append(Template(arg).substitute(source=source, dest=dest)) # Run the command. try: util.command_output(opts) except (OSError, subprocess.CalledProcessError) as exc: self._log.debug(u'encode failed: {0}', exc) util.remove(dest) return self._log.info(u'finished encoding {0}', util.displayable_path(source)) return dest
def im_resize(maxwidth, path_in, path_out=None): """Resize using ImageMagick's ``convert`` tool. Return the output path of resized image. """ path_out = path_out or temp_file_for(path_in) log.debug(u'artresizer: ImageMagick resizing {0} to {1}', util.displayable_path(path_in), util.displayable_path(path_out)) # "-resize widthxheight>" shrinks images with dimension(s) larger # than the corresponding width and/or height dimension(s). The > # "only shrink" flag is prefixed by ^ escape char for Windows # compatibility. try: util.command_output([ b'convert', util.syspath(path_in, prefix=False), b'-resize', b'{0}x^>'.format(maxwidth), util.syspath(path_out, prefix=False), ]) except subprocess.CalledProcessError: log.warn(u'artresizer: IM convert failed for {0}', util.displayable_path(path_in)) return path_in return path_out
def im_resize(maxwidth, path_in, path_out=None): """Resize using ImageMagick's ``convert`` tool. Return the output path of resized image. """ path_out = path_out or temp_file_for(path_in) log.debug( u"artresizer: ImageMagick resizing {0} to {1}", util.displayable_path(path_in), util.displayable_path(path_out) ) # "-resize widthxheight>" shrinks images with dimension(s) larger # than the corresponding width and/or height dimension(s). The > # "only shrink" flag is prefixed by ^ escape char for Windows # compatibility. try: util.command_output( [ b"convert", util.syspath(path_in, prefix=False), b"-resize", b"{0}x^>".format(maxwidth), util.syspath(path_out, prefix=False), ] ) except subprocess.CalledProcessError: log.warn(u"artresizer: IM convert failed for {0}", util.displayable_path(path_in)) return path_in return path_out
def encode(self, command, source, dest, pretend=False): """Encode `source` to `dest` using command template `command`. Raises `subprocess.CalledProcessError` if the command exited with a non-zero status code. """ # The paths and arguments must be bytes. assert isinstance(command, bytes) assert isinstance(source, bytes) assert isinstance(dest, bytes) quiet = self.config['quiet'].get(bool) if not quiet and not pretend: self._log.info('Encoding {0}', util.displayable_path(source)) command = command.decode(arg_encoding(), 'surrogateescape') source = decode_commandline_path(source) dest = decode_commandline_path(dest) # Substitute $source and $dest in the argument list. args = shlex.split(command) encode_cmd = [] for i, arg in enumerate(args): args[i] = Template(arg).safe_substitute({ 'source': source, 'dest': dest, }) encode_cmd.append(args[i].encode(util.arg_encoding())) if pretend: self._log.info('{0}', ' '.join(ui.decargs(args))) return try: util.command_output(encode_cmd) except subprocess.CalledProcessError as exc: # Something went wrong (probably Ctrl+C), remove temporary files self._log.info('Encoding {0} failed. Cleaning up...', util.displayable_path(source)) self._log.debug('Command {0} exited with status {1}: {2}', args, exc.returncode, exc.output) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) raise except OSError as exc: raise ui.UserError( "convert: couldn't invoke '{}': {}".format( ' '.join(ui.decargs(args)), exc ) ) if not quiet and not pretend: self._log.info('Finished encoding {0}', util.displayable_path(source))
def test_command_output(self, mock_popen): def popen_fail(*args, **kwargs): m = Mock(returncode=1) m.communicate.return_value = u'foo', u'bar' return m mock_popen.side_effect = popen_fail with self.assertRaises(subprocess.CalledProcessError) as exc_context: util.command_output(['taga', '\xc3\xa9']) self.assertEqual(exc_context.exception.returncode, 1) self.assertEqual(exc_context.exception.cmd, 'taga \xc3\xa9')
def test_command_output(self, mock_popen): def popen_fail(*args, **kwargs): m = Mock(returncode=1) m.communicate.return_value = u'foo', u'bar' return m mock_popen.side_effect = popen_fail with self.assertRaises(subprocess.CalledProcessError) as exc_context: util.command_output([b"taga", b"\xc3\xa9"]) self.assertEqual(exc_context.exception.returncode, 1) self.assertEqual(exc_context.exception.cmd, b"taga \xc3\xa9")
def test_command_output(self, mock_popen): def popen_fail(*args, **kwargs): m = Mock(returncode=1) m.communicate.return_value = None, None return m mock_popen.side_effect = popen_fail with self.assertRaises(subprocess.CalledProcessError) as exc_context: util.command_output([b"taga", b"\xc3\xa9"]) self.assertEquals(exc_context.exception.returncode, 1) self.assertEquals(exc_context.exception.cmd, b"taga \xc3\xa9")
def ipfs_get_from_hash(self, lib, _hash): try: cmd = "ipfs get".split() cmd.append(_hash) util.command_output(cmd) except (OSError, subprocess.CalledProcessError) as err: self._log.error("Failed to get {0} from ipfs.\n{1}", _hash, err.output) return False self._log.info("Getting {0} from ipfs", _hash) imp = ui.commands.TerminalImportSession(lib, loghandler=None, query=None, paths=[_hash]) imp.run() shutil.rmtree(_hash)
def encode(self, command, source, dest, pretend=False): """Encode `source` to `dest` using command template `command`. Raises `subprocess.CalledProcessError` if the command exited with a non-zero status code. """ # The paths and arguments must be bytes. assert isinstance(command, bytes) assert isinstance(source, bytes) assert isinstance(dest, bytes) quiet = self.config['quiet'].get(bool) if not quiet and not pretend: self._log.info(u'Encoding {0}', util.displayable_path(source)) # Substitute $source and $dest in the argument list. args = shlex.split(command) for i, arg in enumerate(args): args[i] = Template(arg).safe_substitute({ 'source': source, 'dest': dest, }) if pretend: self._log.info(u' '.join(ui.decargs(args))) return try: util.command_output(args) except subprocess.CalledProcessError as exc: # Something went wrong (probably Ctrl+C), remove temporary files self._log.info(u'Encoding {0} failed. Cleaning up...', util.displayable_path(source)) self._log.debug(u'Command {0} exited with status {1}: {2}', args, exc.returncode, exc.output) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) raise except OSError as exc: raise ui.UserError( u"convert: couldn't invoke '{0}': {1}".format( u' '.join(ui.decargs(args)), exc ) ) if not quiet and not pretend: self._log.info(u'Finished encoding {0}', util.displayable_path(source))
def encode(self, command, source, dest, pretend=False): """Encode `source` to `dest` using command template `command`. Raises `subprocess.CalledProcessError` if the command exited with a non-zero status code. """ # The paths and arguments must be bytes. assert isinstance(command, bytes) assert isinstance(source, bytes) assert isinstance(dest, bytes) quiet = self.config['quiet'].get(bool) if not quiet and not pretend: self._log.info(u'Encoding {0}', util.displayable_path(source)) # Substitute $source and $dest in the argument list. args = shlex.split(command) for i, arg in enumerate(args): args[i] = Template(arg).safe_substitute({ b'source': source, b'dest': dest, }) if pretend: self._log.info(' '.join(args)) return try: util.command_output(args) except subprocess.CalledProcessError as exc: # Something went wrong (probably Ctrl+C), remove temporary files self._log.info(u'Encoding {0} failed. Cleaning up...', util.displayable_path(source)) self._log.debug(u'Command {0} exited with status {1}', exc.cmd.decode('utf8', 'ignore'), exc.returncode) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) raise except OSError as exc: raise ui.UserError( u"convert: could invoke '{0}': {1}".format( ' '.join(args), exc ) ) if not quiet and not pretend: self._log.info(u'Finished encoding {0}', util.displayable_path(source))
def im_deinterlace(path_in, path_out=None): path_out = path_out or temp_file_for(path_in) cmd = ArtResizer.shared.im_convert_cmd + [ util.syspath(path_in, prefix=False), '-interlace', 'none', util.syspath(path_out, prefix=False), ] try: util.command_output(cmd) return path_out except subprocess.CalledProcessError: return path_in
def ipfs_get_from_hash(self, lib, _hash): try: cmd = "ipfs get".split() cmd.append(_hash) util.command_output(cmd) except (OSError, subprocess.CalledProcessError) as err: self._log.error('Failed to get {0} from ipfs.\n{1}', _hash, err.output) return False self._log.info('Getting {0} from ipfs', _hash) imp = ui.commands.TerminalImportSession(lib, loghandler=None, query=None, paths=[_hash]) imp.run() shutil.rmtree(_hash)
def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) bin = self.config['bin'].as_str() for item in items: if item['initial_key'] and not overwrite: continue try: output = util.command_output([bin, '-f', util.syspath(item.path)]) except (subprocess.CalledProcessError, OSError) as exc: self._log.error(u'execution failed: {0}', exc) continue except UnicodeEncodeError: # Workaround for Python 2 Windows bug. # http://bugs.python.org/issue1759845 self._log.error(u'execution failed for Unicode path: {0!r}', item.path) continue key_raw = output.rsplit(None, 1)[-1] try: key = util.text_string(key_raw) except UnicodeDecodeError: self._log.error(u'output is invalid UTF-8') continue item['initial_key'] = key self._log.info(u'added computed initial key {0} for {1}', key, util.displayable_path(item.path)) if write: item.try_write() item.store()
def _checksum(self, item, prog): """Run external `prog` on file path associated with `item`, cache output as flexattr on a key that is the name of the program, and return the key, checksum tuple. """ args = [p.format(file=item.path) for p in shlex.split(prog)] key = args[0] checksum = getattr(item, key, False) if not checksum: self._log.debug( u'key {0} on item {1} not cached:' u'computing checksum', key, displayable_path(item.path)) try: checksum = command_output(args) setattr(item, key, checksum) item.store() self._log.debug(u'computed checksum for {0} using {1}', item.title, key) except subprocess.CalledProcessError as e: self._log.debug(u'failed to checksum {0}: {1}', displayable_path(item.path), e) else: self._log.debug( u'key {0} on item {1} cached:' u'not computing checksum', key, displayable_path(item.path)) return key, checksum
def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) bin = util.bytestring_path(self.config['bin'].get(unicode)) for item in items: if item['initial_key'] and not overwrite: continue try: output = util.command_output([bin, '-f', item.path]) except (subprocess.CalledProcessError, OSError) as exc: self._log.error(u'execution failed: {0}', exc) continue key_raw = output.rsplit(None, 1)[-1] try: key = key_raw.decode('utf8') except UnicodeDecodeError: self._log.error(u'output is invalid UTF-8') continue item['initial_key'] = key self._log.info(u'added computed initial key {0} for {1}', key, util.displayable_path(item.path)) if write: item.try_write() item.store()
def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) bin = self.config['bin'].as_str() for item in items: if item['initial_key'] and not overwrite: continue try: output = util.command_output( [bin, '-f', util.syspath(item.path)]) except (subprocess.CalledProcessError, OSError) as exc: self._log.error(u'execution failed: {0}', exc) continue except UnicodeEncodeError: # Workaround for Python 2 Windows bug. # http://bugs.python.org/issue1759845 self._log.error(u'execution failed for Unicode path: {0!r}', item.path) continue key_raw = output.rsplit(None, 1)[-1] try: key = util.text_string(key_raw) except UnicodeDecodeError: self._log.error(u'output is invalid UTF-8') continue item['initial_key'] = key self._log.info(u'added computed initial key {0} for {1}', key, util.displayable_path(item.path)) if write: item.try_write() item.store()
def _check_method(method=None): """A tuple indicating whether current method is available and its version. If no method is given, it returns a supported one. """ # Guess available method if not method: for m in [IMAGEMAGICK, PIL]: _, version = ArtResizer._check_method(m) if version: return (m, version) return (WEBPROXY, (0)) if method == IMAGEMAGICK: # Try invoking ImageMagick's "convert". try: out = util.command_output(["identify", "--version"]) if "imagemagick" in out.lower(): pattern = r".+ (\d+)\.(\d+)\.(\d+).*" match = re.search(pattern, out) if match: return (IMAGEMAGICK, (int(match.group(1)), int(match.group(2)), int(match.group(3)))) return (IMAGEMAGICK, (0)) except (subprocess.CalledProcessError, OSError): return (IMAGEMAGICK, None) if method == PIL: # Try importing PIL. try: __import__("PIL", fromlist=["Image"]) return (PIL, (0)) except ImportError: return (PIL, None)
def get_extractor_data(self, item): with tempfile.NamedTemporaryFile() as tmpfile: tmpfile.close() args = [self.extractor, util.syspath(item.path), tmpfile.name] try: util.command_output(args) except subprocess.CalledProcessError as exc: self._log.warning(u'{} "{}" "{}" exited with status {}', self.extractor, util.displayable_path(item.path), tmpfile.name, exc.returncode) return with open(tmpfile.name, 'rb') as tmp_file: return json.load(tmp_file)
def version(cls): """Obtain and cache ImageMagick version. Raises `LocalBackendNotAvailableError` if not available. """ if cls._version is None: for cmd_name, legacy in (('magick', False), ('convert', True)): try: out = util.command_output([cmd_name, "--version"]).stdout except (subprocess.CalledProcessError, OSError) as exc: log.debug('ImageMagick version check failed: {}', exc) cls._version = _NOT_AVAILABLE else: if b'imagemagick' in out.lower(): pattern = br".+ (\d+)\.(\d+)\.(\d+).*" match = re.search(pattern, out) if match: cls._version = (int(match.group(1)), int(match.group(2)), int(match.group(3))) cls._legacy = legacy if cls._version is _NOT_AVAILABLE: raise LocalBackendNotAvailableError() else: return cls._version
def _checksum(self, item, prog): """Run external `prog` on file path associated with `item`, cache output as flexattr on a key that is the name of the program, and return the key, checksum tuple. """ args = [p.format(file=item.path) for p in shlex.split(prog)] key = args[0] checksum = getattr(item, key, False) if not checksum: self._log.debug(u'key {0} on item {1} not cached:' u'computing checksum', key, displayable_path(item.path)) try: checksum = command_output(args) setattr(item, key, checksum) item.store() self._log.debug(u'computed checksum for {0} using {1}', item.title, key) except subprocess.CalledProcessError as e: self._log.debug(u'failed to checksum {0}: {1}', displayable_path(item.path), e) else: self._log.debug(u'key {0} on item {1} cached:' u'not computing checksum', key, displayable_path(item.path)) return key, checksum
def play_music(lib, opts, args): """Execute query, create temporary playlist and execute player command passing that playlist. """ command = config['play']['command'].get() # If a command isn't set then let the OS decide how to open the playlist. if not command: sys_name = platform.system() if sys_name == 'Darwin': command = 'open' elif sys_name == 'Windows': command = 'start' else: # If not Mac or Win then assume Linux(or posix based). command = 'xdg-open' # Preform search by album and add folders rather then tracks to playlist. if opts.album: albums = lib.albums(ui.decargs(args)) paths = [] for album in albums: paths.append(album.item_dir()) item_type = 'album' # Preform item query and add tracks to playlist. else: paths = [item.path for item in lib.items(ui.decargs(args))] item_type = 'track' item_type += 's' if len(paths) > 1 else '' if not paths: ui.print_(ui.colorize('yellow', 'No {0} to play.'.format(item_type))) return # Warn user before playing any huge playlists. if len(paths) > 100: ui.print_( ui.colorize( 'yellow', 'You are about to queue {0} {1}.'.format( len(paths), item_type))) if ui.input_options(('Continue', 'Abort')) == 'a': return # Create temporary m3u file to hold our playlist. m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) for item in paths: m3u.write(item + '\n') m3u.close() # Invoke the command and log the output. output = util.command_output([command, m3u.name]) if output: log.debug(u'Output of {0}: {1}'.format(command, output)) ui.print_(u'Playing {0} {1}.'.format(len(paths), item_type))
def deinterlace(self, path_in, path_out=None): path_out = path_out or temp_file_for(path_in) cmd = self.convert_cmd + [ syspath(path_in, prefix=False), '-interlace', 'none', syspath(path_out, prefix=False), ] try: util.command_output(cmd) return path_out except subprocess.CalledProcessError: # FIXME: Should probably issue a warning? return path_in
def encode(command, source, dest, pretend=False): """Encode `source` to `dest` using command template `command`. Raises `subprocess.CalledProcessError` if the command exited with a non-zero status code. """ quiet = config['convert']['quiet'].get() if not quiet and not pretend: log.info(u'Encoding {0}'.format(util.displayable_path(source))) if os.name == 'nt': command = Template(command).safe_substitute({ 'source': '"' + source + '"', 'dest': '"' + dest + '"', }) else: command = Template(command).safe_substitute({ 'source': pipes.quote(source), 'dest': pipes.quote(dest), }) log.debug(u'convert: executing: {0}' .format(util.displayable_path(command))) if pretend: log.info(command) return try: util.command_output(command, shell=True) except subprocess.CalledProcessError: # Something went wrong (probably Ctrl+C), remove temporary files log.info(u'Encoding {0} failed. Cleaning up...' .format(util.displayable_path(source))) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) raise except OSError as exc: raise ui.UserError( u"convert: could invoke '{0}': {0}".format(command, exc) ) if not quiet and not pretend: log.info(u'Finished encoding {0}'.format( util.displayable_path(source)) )
def encode(command, source, dest, pretend=False): """Encode `source` to `dest` using command template `command`. Raises `subprocess.CalledProcessError` if the command exited with a non-zero status code. """ quiet = config['convert']['quiet'].get() if not quiet and not pretend: log.info(u'Encoding {0}'.format(util.displayable_path(source))) # Substitute $source and $dest in the argument list. args = shlex.split(command) for i, arg in enumerate(args): args[i] = Template(arg).safe_substitute({ 'source': source, 'dest': dest, }) if pretend: log.info(' '.join(args)) return try: util.command_output(args) except subprocess.CalledProcessError as exc: # Something went wrong (probably Ctrl+C), remove temporary files log.info(u'Encoding {0} failed. Cleaning up...' .format(util.displayable_path(source))) log.debug(u'Command {0} exited with status {1}'.format( exc.cmd.decode('utf8', 'ignore'), exc.returncode, )) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) raise except OSError as exc: raise ui.UserError( u"convert: could invoke '{0}': {1}".format( ' '.join(args), exc ) ) if not quiet and not pretend: log.info(u'Finished encoding {0}'.format( util.displayable_path(source)) )
def play_music(lib, opts, args): """Execute query, create temporary playlist and execute player command passing that playlist. """ command = config['play']['command'].get() # If a command isn't set then let the OS decide how to open the playlist. if not command: sys_name = platform.system() if sys_name == 'Darwin': command = 'open' elif sys_name == 'Windows': command = 'start' else: # If not Mac or Win then assume Linux(or posix based). command = 'xdg-open' # Preform search by album and add folders rather then tracks to playlist. if opts.album: albums = lib.albums(ui.decargs(args)) paths = [] for album in albums: paths.append(album.item_dir()) item_type = 'album' # Preform item query and add tracks to playlist. else: paths = [item.path for item in lib.items(ui.decargs(args))] item_type = 'track' item_type += 's' if len(paths) > 1 else '' if not paths: ui.print_(ui.colorize('yellow', 'No {0} to play.'.format(item_type))) return # Warn user before playing any huge playlists. if len(paths) > 100: ui.print_(ui.colorize( 'yellow', 'You are about to queue {0} {1}.'.format(len(paths), item_type))) if ui.input_options(('Continue', 'Abort')) == 'a': return # Create temporary m3u file to hold our playlist. m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) for item in paths: m3u.write(item + '\n') m3u.close() # Invoke the command and log the output. output = util.command_output([command, m3u.name]) if output: log.debug(u'Output of {0}: {1}'.format(command, output)) ui.print_(u'Playing {0} {1}.'.format(len(paths), item_type))
def get_format(self, filepath): cmd = self.identify_cmd + ['-format', '%[magick]', syspath(filepath)] try: return util.command_output(cmd).stdout except subprocess.CalledProcessError: # FIXME: Should probably issue a warning? return None
def resize(self, maxwidth, path_in, path_out=None, quality=0, max_filesize=0): """Resize using ImageMagick. Use the ``magick`` program or ``convert`` on older versions. Return the output path of resized image. """ path_out = path_out or temp_file_for(path_in) log.debug('artresizer: ImageMagick resizing {0} to {1}', displayable_path(path_in), displayable_path(path_out)) # "-resize WIDTHx>" shrinks images with the width larger # than the given width while maintaining the aspect ratio # with regards to the height. # ImageMagick already seems to default to no interlace, but we include # it here for the sake of explicitness. cmd = self.convert_cmd + [ syspath(path_in, prefix=False), '-resize', f'{maxwidth}x>', '-interlace', 'none', ] if quality > 0: cmd += ['-quality', f'{quality}'] # "-define jpeg:extent=SIZEb" sets the target filesize for imagemagick # to SIZE in bytes. if max_filesize > 0: cmd += ['-define', f'jpeg:extent={max_filesize}b'] cmd.append(syspath(path_out, prefix=False)) try: util.command_output(cmd) except subprocess.CalledProcessError: log.warning('artresizer: IM convert failed for {0}', displayable_path(path_in)) return path_in return path_out
def im_get_format(filepath): cmd = ArtResizer.shared.im_identify_cmd + [ '-format', '%[magick]', util.syspath(filepath) ] try: return util.command_output(cmd).stdout except subprocess.CalledProcessError: return None
def call(args): """Execute the command and return its output or raise a ReplayGainError on failure. """ try: return command_output(args) except subprocess.CalledProcessError as e: raise ReplayGainError("{0} exited with status {1}".format( args[0], e.returncode))
def call(args): """Execute the command indicated by `args` (a list of strings) and return the command's output. The stderr stream is ignored. If the command exits abnormally, a ArtResizerError is raised. """ try: return util.command_output(args) except subprocess.CalledProcessError as e: raise ArtResizerError("{0} exited with status {1}".format( args[0], e.returncode))
def im_getsize(path_in): try: out = util.command_output([b"identify", b"-format", b"%w %h", util.syspath(path_in)]) except subprocess.CalledProcessError: log.warn("IM cannot compute size of {0}", util.displayable_path(path_in)) return try: return tuple(map(int, out.split(b" "))) except IndexError: log.warn("Could not understand IM output: {0!r}", out)
def call(args): """Execute the command and return its output or raise a ReplayGainError on failure. """ try: return command_output(args) except subprocess.CalledProcessError as e: raise ReplayGainError( "{0} exited with status {1}".format(args[0], e.returncode) )
def call(args): """Execute the command and return its output. Raise a AnalysisABSubmitError on failure. """ try: return util.command_output(args) except subprocess.CalledProcessError as e: raise ABSubmitError(u'{0} exited with status {1}'.format( args[0], e.returncode))
def encode(source, dest): """Encode ``source`` to ``dest`` using the command from ``get_format()``. Raises an ``ui.UserError`` if the command was not found and a ``subprocess.CalledProcessError`` if the command exited with a non-zero status code. """ quiet = config['convert']['quiet'].get() if not quiet: log.info(u'Started encoding {0}'.format(util.displayable_path(source))) command, _ = get_format() opts = [] for arg in command: opts.append(Template(arg).safe_substitute({ 'source': source, 'dest': dest, })) log.debug(u'convert: executing: {0}'.format( u' '.join(pipes.quote(o.decode('utf8', 'ignore')) for o in opts) )) try: util.command_output(opts) except subprocess.CalledProcessError: # Something went wrong (probably Ctrl+C), remove temporary files log.info(u'Encoding {0} failed. Cleaning up...' .format(util.displayable_path(source))) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) raise except OSError as exc: raise ui.UserError( u'convert: could invoke ffmpeg: {0}'.format(exc) ) if not quiet: log.info(u'Finished encoding {0}'.format( util.displayable_path(source)) )
def call(args): """Execute the command indicated by `args` (a list of strings) and return the command's output. The stderr stream is ignored. If the command exits abnormally, a ArtResizerError is raised. """ try: return util.command_output(args) except subprocess.CalledProcessError as e: raise ArtResizerError( "{0} exited with status {1}".format(args[0], e.returncode) )
def call(args): """Execute the command and return its output. Raise a AnalysisABSubmitError on failure. """ try: return util.command_output(args) except subprocess.CalledProcessError as e: raise ABSubmitError( u'{0} exited with status {1}'.format(args[0], e.returncode) )
def encode(command, source, dest, pretend=False): """Encode `source` to `dest` using command template `command`. Raises `subprocess.CalledProcessError` if the command exited with a non-zero status code. """ quiet = config['convert']['quiet'].get() if not quiet and not pretend: log.info(u'Encoding {0}'.format(util.displayable_path(source))) command = Template(command).safe_substitute({ 'source': pipes.quote(source), 'dest': pipes.quote(dest), }) log.debug(u'convert: executing: {0}' .format(util.displayable_path(command))) if pretend: log.info(command) return try: util.command_output(command, shell=True) except subprocess.CalledProcessError: # Something went wrong (probably Ctrl+C), remove temporary files log.info(u'Encoding {0} failed. Cleaning up...' .format(util.displayable_path(source))) util.remove(dest) util.prune_dirs(os.path.dirname(dest)) raise except OSError as exc: raise ui.UserError( u"convert: could invoke '{0}': {0}".format(command, exc) ) if not quiet and not pretend: log.info(u'Finished encoding {0}'.format( util.displayable_path(source)) )
def ipfs_publish(self, lib): with tempfile.NamedTemporaryFile() as tmp: self.ipfs_added_albums(lib, tmp.name) try: cmd = "ipfs add -q ".split() cmd.append(tmp.name) output = util.command_output(cmd) except (OSError, subprocess.CalledProcessError) as err: msg = "Failed to publish library. Error: {0}".format(err) self._log.error(msg) return False self._log.info("hash of library: {0}", output)
def im_getsize(path_in): try: out = util.command_output(['identify', '-format', '%w %h', util.syspath(path_in)]) except subprocess.CalledProcessError: log.warn(u'IM cannot compute size of {0}', util.displayable_path(path_in)) return try: return tuple(map(int, out.split(b' '))) except IndexError: log.warn(u'Could not understand IM output: {0!r}', out)
def im_resize(maxwidth, path_in, path_out=None): """Resize using ImageMagick's ``convert`` tool. Return the output path of resized image. """ path_out = path_out or temp_file_for(path_in) log.debug(u'artresizer: ImageMagick resizing {0} to {1}', util.displayable_path(path_in), util.displayable_path(path_out)) # "-resize WIDTHx>" shrinks images with the width larger # than the given width while maintaining the aspect ratio # with regards to the height. try: util.command_output([ 'convert', util.syspath(path_in, prefix=False), '-resize', '{0}x>'.format(maxwidth), util.syspath(path_out, prefix=False), ]) except subprocess.CalledProcessError: log.warning(u'artresizer: IM convert failed for {0}', util.displayable_path(path_in)) return path_in return path_out
def im_getsize(path_in): try: out = util.command_output( [b'identify', b'-format', b'%w %h', util.syspath(path_in)]) except subprocess.CalledProcessError: log.warn(u'IM cannot compute size of {0}', util.displayable_path(path_in)) return try: return tuple(map(int, out.split(b' '))) except IndexError: log.warn(u'Could not understand IM output: {0!r}', out)
def call(args): """Execute the command and return its output or raise a ReplayGainError on failure. """ try: return command_output(args) except subprocess.CalledProcessError as e: raise ReplayGainError(u"{0} exited with status {1}".format(args[0], e.returncode)) except UnicodeEncodeError: # Due to a bug in Python 2's subprocess on Windows, Unicode # filenames can fail to encode on that platform. See: # https://github.com/google-code-export/beets/issues/499 raise ReplayGainError(u"argument encoding failed")
def ipfs_import(self, lib, args): _hash = args[0] if len(args) > 1: lib_name = args[1] else: lib_name = _hash lib_root = os.path.dirname(lib.path) remote_libs = os.path.join(lib_root, b"remotes") if not os.path.exists(remote_libs): try: os.makedirs(remote_libs) except OSError as e: msg = f"Could not create {remote_libs}. Error: {e}" self._log.error(msg) return False path = os.path.join(remote_libs, lib_name.encode() + b".db") if not os.path.exists(path): cmd = f"ipfs get {_hash} -o".split() cmd.append(path) try: util.command_output(cmd) except (OSError, subprocess.CalledProcessError): self._log.error(f"Could not import {_hash}") return False # add all albums from remotes into a combined library jpath = os.path.join(remote_libs, b"joined.db") jlib = library.Library(jpath) nlib = library.Library(path) for album in nlib.albums(): if not self.already_added(album, jlib): new_album = [] for item in album.items(): item.id = None new_album.append(item) added_album = jlib.add_album(new_album) added_album.ipfs = album.ipfs added_album.store()
def ipfs_import(self, lib, args): _hash = args[0] if len(args) > 1: lib_name = args[1] else: lib_name = _hash lib_root = os.path.dirname(lib.path) remote_libs = lib_root + "/remotes" if not os.path.exists(remote_libs): try: os.makedirs(remote_libs) except OSError as e: msg = "Could not create {0}. Error: {1}".format(remote_libs, e) self._log.error(msg) return False path = remote_libs + "/" + lib_name + ".db" if not os.path.exists(path): cmd = "ipfs get {0} -o".format(_hash).split() cmd.append(path) try: util.command_output(cmd) except (OSError, subprocess.CalledProcessError): self._log.error("Could not import {0}".format(_hash)) return False # add all albums from remotes into a combined library jpath = remote_libs + "/joined.db" jlib = library.Library(jpath) nlib = library.Library(path) for album in nlib.albums(): if not self.already_added(album, jlib): new_album = [] for item in album.items(): item.id = None new_album.append(item) added_album = jlib.add_album(new_album) added_album.ipfs = album.ipfs added_album.store()
def has_IM(): """Return Image Magick version or None if it is unavailable Try invoking ImageMagick's "convert".""" try: out = util.command_output([b"identify", b"--version"]) if "imagemagick" in out.lower(): pattern = r".+ (\d+)\.(\d+)\.(\d+).*" match = re.search(pattern, out) if match: return (int(match.group(1)), int(match.group(2)), int(match.group(3))) return (0,) except (subprocess.CalledProcessError, OSError): return None
def im_getsize(path_in): cmd = [b"identify", b"-format", b"%w %h", util.syspath(path_in)] try: out = util.command_output(cmd) except subprocess.CalledProcessError as exc: log.warn( "ImageMagick invocation failed when " "getting size with command {}: {}: cannot compute size of {0}", cmd, exc, ) return try: return tuple(map(int, out.split(b" "))) except IndexError: log.warn("Could not understand IM output: {0!r}", out)
def im_getsize(path_in): cmd = [b'identify', b'-format', b'%w %h', util.syspath(path_in, prefix=False)] try: out = util.command_output(cmd) except subprocess.CalledProcessError as exc: log.warn(u'ImageMagick size query failed') log.debug( u'`convert` exited with (status {}) when ' u'getting size with command {}:\n{}', exc.returncode, cmd, exc.output.strip() ) return try: return tuple(map(int, out.split(b' '))) except IndexError: log.warn(u'Could not understand IM output: {0!r}', out)