flexget.plugins.output.move
Covered: 24 lines
Missed: 92 lines
Skipped 29 lines
Percent: 20 %
  1
import os
  2
import shutil
  3
import logging
  4
import time
  5
from flexget import validator
  6
from flexget.plugin import register_plugin
  7
from flexget.utils.template import RenderError
  9
log = logging.getLogger('move')
 12
def get_directory_size(directory):
 13
    """
 14
    :param directory: Path
 15
    :return: Size in bytes (recursively)
 16
    """
 17
    dir_size = 0
 18
    for (path, dirs, files) in os.walk(directory):
 19
        for file in files:
 20
            filename = os.path.join(path, file)
 21
            dir_size += os.path.getsize(filename)
 22
    return dir_size
 25
class MovePlugin(object):
 27
    def validator(self):
 28
        root = validator.factory()
 29
        root.accept('boolean')
 30
        config = root.accept('dict')
 31
        config.accept('path', key='to', allow_replacement=True)
 32
        config.accept('text', key='filename')
 33
        config.accept('boolean', key='unpack_safety')
 35
        config.accept('number', key='clean_source')
 36
        return root
 38
    def on_feed_output(self, feed, config):
 39
        if config is True:
 40
            config = {}
 41
        elif config is False:
 42
            return
 43
        for entry in feed.accepted:
 44
            if not 'location' in entry:
 45
                log.warning('Cannot move `%s` because entry does not have location field.' % entry['title'])
 46
                continue
 49
            src = entry['location']
 50
            if not os.path.exists(src):
 51
                log.warning('Cannot move `%s` because location `%s` does not exists (anymore)' % (entry['title'], src))
 52
                continue
 53
            if not os.path.isfile(src):
 54
                log.warning('Cannot move `%s` because location `%s` is not a file' % (entry['title'], src))
 55
                continue
 58
            filepath, filename = os.path.split(src)
 60
            dst_path = entry.get('path', config.get('to', filepath))
 62
            if entry.get('filename') and entry['filename'] != filename:
 65
                dst_filename = entry['filename']
 66
            elif 'filename' in config:
 68
                dst_filename = config['filename']
 69
            else:
 71
                dst_filename = filename
 73
            try:
 74
                dst_path = entry.render(dst_path)
 75
            except RenderError:
 76
                log.error('Path value replacement `%s` failed for `%s`' % (dst_path, entry['title']))
 77
                continue
 78
            try:
 79
                dst_filename = entry.render(dst_filename)
 80
            except RenderError:
 81
                log.error('Filename value replacement `%s` failed for `%s`' % (dst_filename, entry['title']))
 82
                continue
 84
            dst = os.path.join(dst_path, dst_filename)
 85
            if dst == entry['location']:
 86
                log.info('Not moving %s because source and destination are the same.' % dst)
 87
                continue
 89
            if not os.path.exists(dst_path):
 90
                if feed.manager.options.test:
 91
                    log.info('Would create `%s`' % dst_path)
 92
                else:
 93
                    log.info('Creating destination directory `%s`' % dst_path)
 94
                    os.makedirs(dst_path)
 95
            if not os.path.isdir(dst_path) and not feed.manager.options.test:
 96
                log.warning('Cannot move `%s` because destination `%s` is not a directory' % (entry['title'], dst_path))
 97
                continue
 99
            if src == dst:
100
                log.verbose('Source and destination are same, skipping `%s`' % entry['title'])
101
                continue
104
            if config.get('unpack_safety', entry.get('unpack_safety', True)):
105
                count = 0
106
                while True:
107
                    if count > 60 * 30:
108
                        feed.fail(entry, 'Move has been waiting unpacking for 30 minutes')
109
                        continue
110
                    size = os.path.getsize(src)
111
                    time.sleep(1)
112
                    new_size = os.path.getsize(src)
113
                    if size != new_size:
114
                        if not count % 10:
115
                            log.verbose('File `%s` is possibly being unpacked, waiting ...' % filename)
116
                    else:
117
                        break
118
                    count += 1
121
            if feed.manager.options.test:
122
                log.info('Would move `%s` to `%s`' % (src, dst))
123
            else:
124
                log.info('Moving `%s` to `%s`' % (src, dst))
125
                shutil.move(src, dst)
126
            if 'clean_source' in config:
127
                if not os.path.isdir(src):
128
                    base_path = os.path.split(src)[0]
129
                    size = get_directory_size(base_path) / 1024 / 1024
130
                    log.debug('base_path: %s size: %s' % (base_path, size))
131
                    if size <= config['clean_source']:
132
                        if feed.manager.options.test:
133
                            log.info('Would delete %s and everything under it' % base_path)
134
                        else:
135
                            log.info('Deleting `%s`' % base_path)
136
                            shutil.rmtree(base_path, ignore_errors=True)
137
                    else:
138
                        log.info(
139
                            'Path `%s` left because it exceeds safety value set in clean_source option' % base_path)
140
                else:
141
                    log.verbose('Cannot clean_source `%s` because source is a directory' % src)
144
register_plugin(MovePlugin, 'move', api_ver=2)