From a61aa7406122682c725a850353a9f937e74b9517 Mon Sep 17 00:00:00 2001 From: Vexatos Date: Tue, 20 Aug 2019 16:07:01 +0200 Subject: [PATCH] convert: add option to hardlink instead of copying. Overrides the --link option. As proposed in #2324. --- beetsplug/convert.py | 42 ++++++++++++++++++++++++++++++++++------ docs/changelog.rst | 4 ++++ docs/plugins/convert.rst | 13 ++++++++++++- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/beetsplug/convert.py b/beetsplug/convert.py index 47ae9fbba0..cee1e080a0 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -115,6 +115,7 @@ def __init__(self): u'dest': None, u'pretend': False, u'link': False, + u'hardlink': False, u'threads': util.cpu_count(), u'format': u'mp3', u'id3v23': u'inherit', @@ -171,6 +172,9 @@ def commands(self): cmd.parser.add_option('-l', '--link', action='store_true', dest='link', help=u'symlink files that do not need transcoding. \ Don\'t use with \'embed\'.') + cmd.parser.add_option('-H', '--hardlink', action='store_true', dest='hardlink', + help=u'hardlink files that do not need transcoding. \ + Overrides --link. Don\'t use with \'embed\'.') cmd.parser.add_album_option() cmd.func = self.convert_func return [cmd] @@ -255,7 +259,7 @@ def encode(self, command, source, dest, pretend=False): util.displayable_path(source)) def convert_item(self, dest_dir, keep_new, path_formats, fmt, - pretend=False, link=False): + pretend=False, link=False, hardlink=False): """A pipeline thread that converts `Item` objects from a library. """ @@ -308,7 +312,17 @@ def convert_item(self, dest_dir, keep_new, path_formats, fmt, except subprocess.CalledProcessError: continue else: - if link: + if hardlink: + if pretend: + self._log.info(u'ln {0} {1}', + util.displayable_path(original), + util.displayable_path(converted)) + else: + # No transcoding necessary. + self._log.info(u'Hardlinking {0}', + util.displayable_path(item.path)) + util.hardlink(original, converted) + elif link: if pretend: self._log.info(u'ln -s {0} {1}', util.displayable_path(original), @@ -362,7 +376,7 @@ def convert_item(self, dest_dir, keep_new, path_formats, fmt, dest=converted, keepnew=False) def copy_album_art(self, album, dest_dir, path_formats, pretend=False, - link=False): + link=False, hardlink=False): """Copies or converts the associated cover art of the album. Album must have at least one track. """ @@ -415,7 +429,17 @@ def copy_album_art(self, album, dest_dir, path_formats, pretend=False, if not pretend: ArtResizer.shared.resize(maxwidth, album.artpath, dest) else: - if link: + if hardlink: + if pretend: + self._log.info(u'ln {0} {1}', + util.displayable_path(album.artpath), + util.displayable_path(dest)) + else: + self._log.info(u'Hardlinking cover art from {0} to {1}', + util.displayable_path(album.artpath), + util.displayable_path(dest)) + util.hardlink(album.artpath, dest) + elif link: if pretend: self._log.info(u'ln -s {0} {1}', util.displayable_path(album.artpath), @@ -458,6 +482,11 @@ def convert_func(self, lib, opts, args): else: link = self.config['link'].get(bool) + if opts.hardlink is not None: + hardlink = opts.hardlink + else: + hardlink = self.config['hardlink'].get(bool) + if opts.album: albums = lib.albums(ui.decargs(args)) items = [i for a in albums for i in a.items()] @@ -478,14 +507,15 @@ def convert_func(self, lib, opts, args): if opts.album and self.config['copy_album_art']: for album in albums: - self.copy_album_art(album, dest, path_formats, pretend, link) + self.copy_album_art(album, dest, path_formats, pretend, link, hardlink) convert = [self.convert_item(dest, opts.keep_new, path_formats, fmt, pretend, - link) + link, + hardlink) for _ in range(threads)] pipe = util.pipeline.Pipeline([iter(items), convert]) pipe.run_parallel() diff --git a/docs/changelog.rst b/docs/changelog.rst index 310e5fb0d4..c35114390d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -14,6 +14,10 @@ New features: * :doc:`/plugins/convert`: Added new ``-l`` (``--link``) flag and ``link`` option which symlinks files that do not need to be converted instead of copying them. :bug:`2324` +* :doc:`/plugins/convert`: Added new ``-H`` (``--hardlink``) flag and ``hardlink`` + option which hardlinks files that do not need to be converted + instead of copying them. + :bug:`2324` * :doc:`/plugins/bpd`: BPD now supports most of the features of version 0.16 of the MPD protocol. This is enough to get it talking to more complicated clients like ncmpcpp, but there are still some incompatibilities, largely due diff --git a/docs/plugins/convert.rst b/docs/plugins/convert.rst index 775434d66c..15b4fd2f27 100644 --- a/docs/plugins/convert.rst +++ b/docs/plugins/convert.rst @@ -50,7 +50,9 @@ them. By default, files that do not need to be transcoded will be copied to their destination. Passing the ``-l`` (``--link``) flag creates symbolic links -instead. Refer to the ``link`` option below for potential issues with this. +instead, passing ``-H`` (``--hardlink``) creates hard links. +Refer to the ``link`` and ``hardlink`` options below +for potential issues with this. Configuration @@ -104,6 +106,15 @@ file. The available options are: enabled. For this reason, it is highly recommended not use to ``link`` and ``embed`` at the same time. Default: ``false``. +- **hardlink**: By default, files that do not need to be transcoded will be + copied to their destination. This option creates hard links instead. Note that + options such as ``embed`` that modify the output files after the transcoding + step will cause the original files to be modified as well if ``hardlink`` is + enabled. For this reason, it is highly recommended not use to ``hardlink`` and + ``embed`` at the same time. + This option overrides ``link``. Only works when converting to a directory + on the same filesystem as the library. + Default: ``false``. You can also configure the format to use for transcoding (see the next section):