From 57ae06e139ebf501b450e238395ce0ff346eaada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20K=C3=A5berg?= Date: Fri, 5 Apr 2013 13:48:09 +0200 Subject: [PATCH] initial link support --- couchpotato/core/plugins/renamer/__init__.py | 14 ++++ couchpotato/core/plugins/renamer/main.py | 8 ++- libs/linktastic/README.txt | 19 +++++ libs/linktastic/__init__.py | 0 libs/linktastic/linktastic.py | 76 ++++++++++++++++++++ libs/linktastic/setup.py | 13 ++++ 6 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 libs/linktastic/README.txt create mode 100644 libs/linktastic/__init__.py create mode 100644 libs/linktastic/linktastic.py create mode 100644 libs/linktastic/setup.py diff --git a/couchpotato/core/plugins/renamer/__init__.py b/couchpotato/core/plugins/renamer/__init__.py index 22277ba5..0e46396c 100644 --- a/couchpotato/core/plugins/renamer/__init__.py +++ b/couchpotato/core/plugins/renamer/__init__.py @@ -112,6 +112,20 @@ config = [{ 'label': 'Separator', 'description': 'Replace all the spaces with a character. Example: ".", "-" (without quotes). Leave empty to use spaces.', }, + { + 'name': 'hardlink', + 'type': 'bool', + 'description': 'Use hard links instead of moving files, ideal for Torrent users. NOTE: From location and to location needs to be on the same drive/partition.', + 'default': False, + 'advanced': True, + }, + { + 'name': 'symlink', + 'type': 'bool', + 'description': 'Use sym links instead of moving files.', + 'default': False, + 'advanced': True, + }, { 'advanced': True, 'name': 'ntfs_permission', diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index a6260d7b..cd3447d2 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -10,6 +10,7 @@ from couchpotato.core.plugins.base import Plugin from couchpotato.core.settings.model import Library, File, Profile, Release, \ ReleaseInfo from couchpotato.environment import Env +import linktastic.linktastic as linktastic import errno import os import re @@ -523,7 +524,12 @@ class Renamer(Plugin): def moveFile(self, old, dest): dest = ss(dest) try: - shutil.move(old, dest) + if self.conf('hardlink') and not self.conf('symlink'): + linktastic.link(old, dest) + elif self.conf('symlink') and not self.conf('hardlink'): + linktastic.symlink(old, dest) + else: + shutil.move(old, dest) try: os.chmod(dest, Env.getPermission('file')) diff --git a/libs/linktastic/README.txt b/libs/linktastic/README.txt new file mode 100644 index 00000000..7fad24db --- /dev/null +++ b/libs/linktastic/README.txt @@ -0,0 +1,19 @@ +Linktastic + +Linktastic is an extension of the os.link and os.symlink functionality provided +by the python language since version 2. Python only supports file linking on +*NIX-based systems, even though it is relatively simple to engineer a solution +to utilize NTFS's built-in linking functionality. Linktastic attempts to unify +linking on the windows platform with linking on *NIX-based systems. + +Usage + +Linktastic is a single python module and can be imported as such. Examples: + +# Hard linking src to dest +import linktastic +linktastic.link(src, dest) + +# Symlinking src to dest +import linktastic +linktastic.symlink(src, dest) diff --git a/libs/linktastic/__init__.py b/libs/linktastic/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/linktastic/linktastic.py b/libs/linktastic/linktastic.py new file mode 100644 index 00000000..76687666 --- /dev/null +++ b/libs/linktastic/linktastic.py @@ -0,0 +1,76 @@ +# Linktastic Module +# - A python2/3 compatible module that can create hardlinks/symlinks on windows-based systems +# +# Linktastic is distributed under the MIT License. The follow are the terms and conditions of using Linktastic. +# +# The MIT License (MIT) +# Copyright (c) 2012 Solipsis Development +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +# associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import subprocess +from subprocess import CalledProcessError +import os + + +# Prevent spaces from messing with us! +def _escape_param(param): + return '"%s"' % param + + +# Private function to create link on nt-based systems +def _link_windows(src, dest): + try: + subprocess.check_output( + 'cmd /C mklink /H %s %s' % (_escape_param(dest), _escape_param(src)), + stderr=subprocess.STDOUT) + except CalledProcessError as err: + + raise IOError(err.output.decode('utf-8')) + + # TODO, find out what kind of messages Windows sends us from mklink + # print(stdout) + # assume if they ret-coded 0 we're good + + +def _symlink_windows(src, dest): + try: + subprocess.check_output( + 'cmd /C mklink %s %s' % (_escape_param(dest), _escape_param(src)), + stderr=subprocess.STDOUT) + except CalledProcessError as err: + raise IOError(err.output.decode('utf-8')) + + # TODO, find out what kind of messages Windows sends us from mklink + # print(stdout) + # assume if they ret-coded 0 we're good + + +# Create a hard link to src named as dest +# This version of link, unlike os.link, supports nt systems as well +def link(src, dest): + if os.name == 'nt': + _link_windows(src, dest) + else: + os.link(src, dest) + + +# Create a symlink to src named as dest, but don't fail if you're on nt +def symlink(src, dest): + if os.name == 'nt': + _symlink_windows(src, dest) + else: + os.symlink(src, dest) diff --git a/libs/linktastic/setup.py b/libs/linktastic/setup.py new file mode 100644 index 00000000..e15cc2b7 --- /dev/null +++ b/libs/linktastic/setup.py @@ -0,0 +1,13 @@ +from distutils.core import setup + +setup( + name='Linktastic', + version='0.1.0', + author='Jon "Berkona" Monroe', + author_email='solipsis.dev@gmail.com', + py_modules=['linktastic'], + url="http://github.com/berkona/linktastic", + license='MIT License - See http://opensource.org/licenses/MIT for details', + description='Truly platform-independent file linking', + long_description=open('README.txt').read(), +)