File: ybatchrename.py

package info (click to toggle)
far2l 2.7.0~beta%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 44,304 kB
  • sloc: cpp: 263,566; ansic: 53,886; python: 7,048; sh: 1,516; perl: 410; javascript: 279; xml: 145; makefile: 31
file content (122 lines) | stat: -rw-r--r-- 4,849 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

"""
Plugin to rename list of files all at once, using full power of 
FAR text editor.
"""

__author__ = 'Yaroslav Yanovsky'

import os
import tempfile

from yfar import FarPlugin


RENAME_INFO = b'''Rename list of files. File names to the right of slashes
are destination names. File names to the left are given for reference only,
you can modify them too - only the line order is important.
Full destination paths are supported too - either absolute or relative.

'''

class Plugin(FarPlugin):
    label = 'Batch Rename Files and Directories'
    openFrom = ['PLUGINSMENU', 'FILEPANEL']

    def OpenPlugin(self, _):
        panel = self.get_panel()

        dir = panel.directory
        names = []
        for sel in panel.selected:
            full_fn = sel.file_name
            if '/' in full_fn:
                if dir:
                    self.error('Panel directory {} and full panel name '
                               '{} conflict'.format(dir, full_fn))
                    return
            elif dir:
                full_fn = os.path.join(dir, full_fn)
            else:
                self.error('Panel name {} is local and panel directory not '
                           'specified')
                return
            names.append([full_fn])
        if not names:
            self.notice('No files selected')
            return

        max_len = max(len(os.path.basename(x[0])) for x in names)
        with tempfile.NamedTemporaryFile(delete=False) as f:
            f.write(RENAME_INFO)
            for fn, in names:
                fn = os.path.basename(fn)
                f.write(('{} / {}\n'.format(fn.ljust(max_len), fn)).encode())

        renamed = skipped = 0
        failed = []
        if self.editor(f.name, 'Rename list of files', 
                       line=RENAME_INFO.count(b'\n') + 1, column=max_len + 4):
            i = 0
            with open(f.name, 'rb') as f:
                used_names = set()
                for line in f:
                    if b'/' in line:
                        if i >= len(names):
                            self.error('Aborted: edited list has more '
                                       'names than required')
                            break
                        dst_fn = line.split(b'/', 1)[1].strip().decode()
                        if dst_fn.startswith('~'):
                            dst_fn = os.path.expanduser(dst_fn)
                        elif not dst_fn.startswith('/'):
                            dst_fn = os.path.abspath(os.path.join(
                                os.path.dirname(names[i][0]), dst_fn))
                        if dst_fn in used_names:
                            self.error('Aborted: multiple destination names '
                                       'point to the same file\n'
                                       '{}'.format(dst_fn))
                            break
                        names[i].append(dst_fn)
                        used_names.add(dst_fn)
                        i += 1
                else:
                    if i == len(names):
                        for src_fn, dst_fn in names:
                            if src_fn == dst_fn:
                                skipped += 1
                                continue
                            if os.path.exists(dst_fn):
                                failed.append(dst_fn + ': already exists')
                                continue
                            dst_dir, fn = os.path.split(dst_fn)
                            if not os.path.exists(dst_dir):
                                # create dst dir if not exist
                                try:
                                    os.makedirs(dst_dir)
                                except Exception as e:
                                    failed.append(fn + ' - mkdir: ' + str(e))
                                    continue
                            try:
                                os.rename(src_fn, dst_fn)
                            except Exception as e:
                                failed.append(fn + ': ' + str(e))
                            else:
                                renamed += 1
                    else:
                        self.error('Aborted: edited list has less names than '
                                   'required')
            panel.refresh()
            if renamed or skipped or failed:
                msg = '{:d} file(s) renamed'.format(renamed)
                if skipped:
                    msg += ', {:d} skipped'.format(skipped)
                if failed:
                    self.error(msg + ', {:d} failed:\n{}'.format(
                        len(failed), '\n'.join(failed)))
                else:
                    self.notice(msg)

        if os.path.exists(f.name):
            os.unlink(f.name)