Browse Source

Python is the new bash

master
parent
commit
9f800f2972
6 changed files with 297 additions and 197 deletions
  1. 185
    135
      scripts/compressPictureMovies
  2. 39
    0
      scripts/musiqueBof
  3. 29
    58
      scripts/replayGain
  4. 17
    0
      scripts/tagCreatorPhotos
  5. 1
    0
      vimpcrc
  6. 26
    4
      vimrc

+ 185
- 135
scripts/compressPictureMovies View File

@@ -4,20 +4,98 @@ import os
4 4
 import shutil
5 5
 import subprocess
6 6
 import sys
7
+import logging
8
+import coloredlogs
9
+import progressbar
10
+import time
11
+import hashlib
12
+import tempfile
13
+import json
14
+import statistics
15
+import datetime
16
+
17
+coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
18
+log = logging.getLogger()
7 19
 
8 20
 # Constants
9 21
 PICTURES_FOLDER = os.path.join(os.path.expanduser("~"), "Images")
10
-ORIGNAL_FOLDER = os.path.join(PICTURES_FOLDER, ".Originaux")
11
-MOVIE_EXTENSIONS = ["mov", "avi", "mp4"]
12
-OUTPUT_EXTENSION = "mp4"
13
-OUTPUT_FFMPEG_PARAMETERS = ["-codec:v", "libx265", "-crf", "28", "-preset:v", "slower", "-codec:a", "libfdk_aac", "-movflags", "+faststart", "-vbr", "5"]
14
-OUTPUT_METADATA_FIELD = ["episode_id"]
22
+ORIGINAL_FOLDER = os.path.join(os.path.expanduser("~"), ".ImagesOriginaux")
23
+MOVIE_EXTENSIONS = ["mov", "avi", "mp4", "3gp", "webm", "mkv"]
24
+OUTPUT_EXTENSION = "webm"
25
+OUTPUT_FFMPEG_PARAMETERS = ["-c:v", "libvpx-vp9", "-crf", "30", "-b:v", "0"]
26
+# OUTPUT_FFMPEG_PARAMETERS = ["-c:v", "libaom-av1", "-crf", "30", "-strict", "experimental", "-c:a", "libopus"]
27
+DURATION_MAX_DEV = 1
28
+
29
+
30
+def videoMetadata(filename):
31
+    assert os.path.isfile(filename)
32
+    cmd = ["ffmpeg", "-i", filename, "-f", "ffmetadata", "-"]
33
+    p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
34
+    p.check_returncode()
35
+    metadataRaw = p.stdout
36
+    data = dict()
37
+    for metadataLine in metadataRaw.split(b'\n'):
38
+        # Skip empty lines
39
+        if not len(metadataLine):
40
+            continue
41
+        # Skip comments
42
+        if metadataLine.startswith(b';'):
43
+            continue
44
+        # Parse key-value
45
+        metadataLineSplit = metadataLine.split(b'=')
46
+        if len(metadataLineSplit) != 2:
47
+            log.warning("Unparsed metadata line: `{}`".format(metadataLine))
48
+            continue
49
+        key, val = metadataLineSplit
50
+        key = key.decode().lower()
51
+        val = val.decode()
52
+        data[key] = val
53
+    return data
54
+
55
+def videoInfos(filename):
56
+    assert os.path.isfile(filename)
57
+    cmd = ["ffprobe", filename, "-print_format", "json", "-show_streams"]
58
+    p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
59
+    p.check_returncode()
60
+    infosRaw = p.stdout
61
+    infos = json.loads(infosRaw)
62
+    return infos
63
+
64
+from pprint import pprint
65
+def streamDuration(stream):
66
+    if "duration" in stream:
67
+        return float(stream["duration"])
68
+    elif "sample_rate" in stream and "nb_frames" in stream:
69
+        return int(stream["nb_frames"]) / int(stream["sample_rate"])
70
+    elif "tags" in stream and "DURATION" in stream["tags"]:
71
+        durRaw = stream["tags"]["DURATION"]
72
+        durSplit = durRaw.split(":")
73
+        assert len(durSplit) == 3
74
+        durSplitFloat = [float(a) for a in durSplit]
75
+        hours, minutes, seconds = durSplitFloat
76
+        return (hours * 60 + minutes) * 60 + seconds
77
+    else:
78
+        raise KeyError("Can't find duration information in stream")
79
+
80
+def videoDuration(filename):
81
+    # TODO Doesn't work with VP8 / webm
82
+    infos = videoInfos(filename)
83
+    durations = [streamDuration(stream) for stream in infos["streams"]]
84
+    dev = statistics.stdev(durations)
85
+    assert dev <= DURATION_MAX_DEV, "Too much deviation ({} s)".format(dev)
86
+    return sum(durations)/len(durations)
87
+
15 88
 
89
+todos = set()
90
+totalSize = 0
91
+totalDuration = 0
16 92
 
17 93
 # Walk folders
94
+log.info("Listing files in {}".format(PICTURES_FOLDER))
95
+allVideos = list()
18 96
 for root, dirs, files in os.walk(PICTURES_FOLDER):
19 97
     # If folder is in ORIGINAL_FOLDER, skip it
20
-    if root.startswith(ORIGNAL_FOLDER):
98
+    if root.startswith(ORIGINAL_FOLDER):
21 99
         continue
22 100
     # Iterate over files
23 101
     for inputName in files:
@@ -27,137 +105,109 @@ for root, dirs, files in os.walk(PICTURES_FOLDER):
27 105
         if inputExt not in MOVIE_EXTENSIONS:
28 106
             continue
29 107
 
30
-        # Generates all needed filepaths
31
-        ## Found file
32
-        inputFull = os.path.join(root, inputName)
33
-        inputRel = os.path.relpath(inputFull, PICTURES_FOLDER)
34
-        ## Original file
35
-        originalFull = os.path.join(ORIGNAL_FOLDER, inputRel)
36
-        originalRel = inputRel
37
-        ## Compressed file
38
-        outputFull = os.path.join(root, inputNameBase + "." + OUTPUT_EXTENSION)
39
-
40
-        # If the extension is the same of the output one
41
-        if inputExt == OUTPUT_EXTENSION:
42
-            # Read the metadata of the video
43
-            metadataRaw = subprocess.run(["ffmpeg", "-i", inputFull, "-f", "ffmetadata", "-"], stdout=subprocess.PIPE).stdout
44
-            # If it has the field with the original file 
45
-            originalRel = None
46
-            wantedPattern = OUTPUT_METADATA_FIELD.encode() + b"="
47
-            for metadataLine in metadataRaw.split('\n'):
48
-                if metadataLine.startswith(wantedPattern):
49
-                    originalRel = metadataLine[len(wantedPattern)+1:]
50
-                    break
51
-            if originalRel:
52
-                # If the original file does not exists, warn about it
53
-                originalFull = os.path.join(ORIGNAL_FOLDER, originalRel)
54
-                if not os.path.isfile(originalFull):
55
-                    print("WARN {inputRel} states to have {originalRel} as original but this file doesn't exist".format(inputRel=inputRel, originalRel=originalRel))
56
-                # If the original is not aligned with the compressed, warn about it (TODO move it automatically)
57
-                if inputRel != originalRel:
58
-                    print("WARN {inputRel} is not aligned with original {originalRel}".format(inputRel=inputRel, originalRel=originalRel))
59
-                # Skip file
60
-                continue
61
-            # Initiate a conversion in a temporary file
62
-            # If the temporary file does not have the same caracteristics as the original
63
-                # Warn about it
64
-                # Delete it
65
-                # Skip file
66
-            # Move the original to the corresponding original folder
67
-            # Move the converted file in place of the original
68
-
69
-# TODO Iterate over the orignal folder to find non-matching compressed videos not found in the above pass
70
-
71
-sys.exit(0)
72
-
73
-# Constants
74
-SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique")
75
-OUTPUT_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueCompressed")
76
-CONVERSIONS = {"flac": "m4a"}
77
-FORBIDDEN_EXTENSIONS = ["jpg", "pdf", "ffs_db"]
78
-FORGIVEN_FILENAMES = ["cover.jpg"]
79
-IGNORED_EMPTY_FOLDER = [".stfolder"]
80
-
81
-
82
-
83
-# Listing files
84
-sourceFiles = dict()
85
-for root, dirs, files in os.walk(SOURCE_FOLDER):
86
-    for f in files:
87
-        fullPath = os.path.join(root, f)
88
-        path = os.path.relpath(fullPath, SOURCE_FOLDER)
89
-        sourceFiles[path] = os.path.getctime(fullPath)
90
-
91
-outputFiles = dict()
92
-for root, dirs, files in os.walk(OUTPUT_FOLDER):
93
-    for f in files:
94
-        fullPath = os.path.join(root, f)
95
-        path = os.path.relpath(fullPath, OUTPUT_FOLDER)
96
-        outputFiles[path] = os.path.getctime(fullPath)
97
-
98
-# Sorting files
99
-remainingConversions = dict()
100
-extraFiles = list(outputFiles.keys())
101
-
102
-def convertPath(path):
103
-    filename, extension = os.path.splitext(path)
104
-    extension = extension[1:].lower()
105
-    # If the extension isn't allowed
106
-    if extension in FORBIDDEN_EXTENSIONS:
107
-        basename = os.path.basename(path)
108
-        # And the filename is not an exception
109
-        if basename not in FORGIVEN_FILENAMES:
110
-            # This file shouldn't be copied nor converted
111
-            return False
112
-    # If this needs a conversion
113
-    elif extension in CONVERSIONS:
114
-        extension = CONVERSIONS[extension]
115
-        return filename + "." + extension
116
-    # In all other case, this is a simple copy
117
-    return path
118
-
119
-for sourceFile in sourceFiles:
120
-    outputFile = convertPath(sourceFile)
121
-    # If the file should not be converted, do nothing
122
-    if outputFile == False:
123
-        continue
124
-    # If the file already has something as an output
125
-    elif outputFile in outputFiles:
126
-        extraFiles.remove(outputFile)
127
-        # If the output file is newer than the source file, do not initiate a conversion
128
-        if outputFiles[outputFile] >= sourceFiles[sourceFile]:
108
+        allVideos.append((root, inputName))
109
+
110
+log.info("Analyzing videos")
111
+for root, inputName in progressbar.progressbar(allVideos):
112
+    inputNameBase, inputExt = os.path.splitext(inputName)
113
+    inputExt = inputExt[1:].lower()
114
+
115
+    # Generates all needed filepaths
116
+    ## Found file
117
+    inputFull = os.path.join(root, inputName)
118
+    inputRel = os.path.relpath(inputFull, PICTURES_FOLDER)
119
+    ## Original file
120
+    originalFull = os.path.join(ORIGINAL_FOLDER, inputRel)
121
+    originalRel = inputRel
122
+    assert not os.path.isfile(originalFull), originalFile + " exists"
123
+
124
+    ## Compressed file
125
+    outputFull = os.path.join(root, inputNameBase + "." + OUTPUT_EXTENSION)
126
+
127
+    # If the extension is the same of the output one
128
+    if inputExt == OUTPUT_EXTENSION:
129
+        # Read the metadata of the video
130
+        meta = videoMetadata(inputFull)
131
+
132
+        # If it has the field with the original file
133
+        if 'original' in meta:
134
+            # Skip file
129 135
             continue
130
-    # If the file needs to be converted, do it
131
-    remainingConversions[sourceFile] = outputFile
132
-
133
-# Converting files
134
-for sourceFile in remainingConversions:
135
-    outputFile = remainingConversions[sourceFile]
136
-
137
-    # Creating folder if it doesn't exists
138
-    fullOutputFile = os.path.join(OUTPUT_FOLDER, outputFile)
139
-    fullOutputDir = os.path.dirname(fullOutputFile)
140
-    os.makedirs(fullOutputDir, exist_ok=True)
141
-
142
-    # Converting
143
-    fullSourceFile = os.path.join(SOURCE_FOLDER, sourceFile)
144
-    print(fullSourceFile, "→", fullOutputFile)
145
-    if sourceFile == outputFile:
146
-        # shutil.copy(fullSourceFile, fullOutputFile)
147
-        os.link(fullSourceFile, fullOutputFile)
148 136
     else:
149
-        subprocess.run(["ffmpeg", "-y", "-i", fullSourceFile, "-codec:a", "libfdk_aac", "-cutoff", "18000", "-movflags", "+faststart", "-vbr", "5", fullOutputFile])
137
+        assert not os.path.isfile(outputFull), outputFull + " exists"
150 138
 
151
-# Removing extra files
152
-for extraFile in extraFiles:
153
-    fullExtraFile = os.path.join(OUTPUT_FOLDER, extraFile)
154
-    os.remove(fullExtraFile)
155
-
156
-# Removing empty dirs
157
-for root, dirs, files in os.walk(OUTPUT_FOLDER):
158
-    if not dirs and not files:
159
-        dirBasename = os.path.basename(root)
160
-        if dirBasename not in IGNORED_EMPTY_FOLDER:
161
-            os.rmdir(root)
162 139
 
140
+    size = os.stat(inputFull).st_size
141
+    try:
142
+        duration = videoDuration(inputFull)
143
+    except Exception as e:
144
+        log.warning("Can't determine duration of {}, skipping".format(inputFull))
145
+        log.debug(e, exc_info=True)
146
+        continue
163 147
 
148
+    todo = (inputFull, originalFull, outputFull, size, duration)
149
+
150
+    totalDuration += duration
151
+    totalSize += size
152
+    todos.add(todo)
153
+
154
+log.info("Converting {} videos ({})".format(len(todos), datetime.timedelta(seconds=totalDuration)))
155
+
156
+# From https://stackoverflow.com/a/3431838
157
+def sha256(fname):
158
+    hash_sha256 = hashlib.sha256()
159
+    with open(fname, "rb") as f:
160
+        for chunk in iter(lambda: f.read(131072), b""):
161
+            hash_sha256.update(chunk)
162
+    return hash_sha256.hexdigest()
163
+
164
+# Progress bar things
165
+totalDataSize = progressbar.widgets.DataSize()
166
+totalDataSize.variable = 'max_value'
167
+barWidgets = [progressbar.widgets.DataSize(), ' of ', totalDataSize, ' ', progressbar.widgets.Bar(), ' ', progressbar.widgets.FileTransferSpeed(), ' ', progressbar.widgets.AdaptiveETA()]
168
+bar = progressbar.DataTransferBar(max_value=totalSize, widgets=barWidgets)
169
+bar.start()
170
+processedSize = 0
171
+
172
+
173
+for inputFull, originalFull, outputFull, size, duration in todos:
174
+    tmpfile = tempfile.mkstemp(prefix="compressPictureMovies", suffix="."+OUTPUT_EXTENSION)[1]
175
+    try:
176
+        # Calculate the sum of the original file
177
+        checksum = sha256(inputFull)
178
+
179
+        # Initiate a conversion in a temporary file
180
+        originalRel = os.path.relpath(originalFull, ORIGINAL_FOLDER)
181
+        originalContent = "{} {}".format(originalRel, checksum)
182
+        metadataCmd = ["-metadata", 'original="{}"'.format(originalContent)]
183
+        cmd = ["ffmpeg", "-hide_banner", "-y", "-i", inputFull] + OUTPUT_FFMPEG_PARAMETERS + metadataCmd + [tmpfile]
184
+        p = subprocess.run(cmd)
185
+        p.check_returncode()
186
+
187
+        # Verify the durartion of the new file
188
+        newDuration = videoDuration(tmpfile)
189
+        dev = statistics.stdev((duration, newDuration))
190
+        assert dev < DURATION_MAX_DEV, "Too much deviation in duration"
191
+
192
+        # Move the original to the corresponding original folder
193
+        originalDir = os.path.dirname(originalFull)
194
+        os.makedirs(originalDir, exist_ok=True)
195
+        shutil.move(inputFull, originalFull)
196
+
197
+        # Move the converted file in place of the original
198
+        shutil.move(tmpfile, outputFull)
199
+    except Exception as e:
200
+        log.error("Couldn't process file {}".format(inputFull))
201
+        log.error(e, exc_info=True)
202
+        try:
203
+            os.unlink(tmpfile)
204
+        except Exception:
205
+            pass
206
+    # Progress bar things
207
+    processedSize += size
208
+    bar.update(processedSize)
209
+bar.finish()
210
+
211
+
212
+# TODO Iterate over the already compressed videos to assert the originals are
213
+# in their correct place, else move them

+ 39
- 0
scripts/musiqueBof View File

@@ -0,0 +1,39 @@
1
+#!/usr/bin/env python3
2
+
3
+import sys
4
+import os
5
+import shutil
6
+import logging
7
+import coloredlogs
8
+
9
+coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
10
+log = logging.getLogger()
11
+
12
+MUSICS_FOLDER = os.path.join(os.path.expanduser("~"), "Musique")
13
+BOF_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueBof")
14
+
15
+for f in sys.argv[1:]:
16
+    src = os.path.realpath(f)
17
+    if not os.path.isfile(src):
18
+        log.error("{} does not exists".format(src))
19
+        continue
20
+
21
+    srcBase = None
22
+    if src.startswith(MUSICS_FOLDER):
23
+        srcBase = MUSICS_FOLDER
24
+        dstBase = BOF_FOLDER
25
+    elif src.startswith(BOF_FOLDER):
26
+        srcBase = BOF_FOLDER
27
+        dstBase = MUSIC_FOLDER
28
+    else:
29
+        log.error("{} not in any music folder".format(src))
30
+        continue
31
+
32
+    common = os.path.relpath(src, srcBase)
33
+    dst = os.path.join(dstBase, common)
34
+    dstFolder = os.path.dirname(dst)
35
+
36
+    log.info("{} → {}".format(src, dst))
37
+    os.makedirs(dstFolder, exist_ok=True)
38
+    shutil.move(src, dst)
39
+

+ 29
- 58
scripts/replayGain View File

@@ -4,94 +4,65 @@
4 4
 # which is usually -89.0 dB
5 5
 
6 6
 import os
7
-import subprocess
8 7
 import coloredlogs
9 8
 import logging
10
-import progressbar
9
+import r128gain
10
+import sys
11 11
 
12 12
 coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
13 13
 log = logging.getLogger()
14 14
 
15
+# TODO Remove debug
15 16
 
16 17
 # Constants
17
-FORCE = False # TODO UX cli argument
18
-SOURCE_FOLDER = os.path.join(os.path.expanduser("~"), "Musique")
19
-GAIN_COMMANDS = {("flac",): ["metaflac", "--add-replay-gain"],
20
-                 ("mp3",): ["mp3gain", "-a", "-k"] + (["-s", "r"] if FORCE else []),
21
-                 ("m4a", "mp4"): ["aacgain", "-a", "-k"] + (["-s", "r"] if FORCE else []),
22
-                 ("ogg",): ["vorbisgain", "--album"] + ([] if FORCE else ["--fast"]),
23
-                 }
18
+FORCE = '-f' in sys.argv
19
+if FORCE:
20
+    sys.argv.remove('-f')
21
+SOURCE_FOLDER = os.path.realpath(sys.argv[1]) if len(sys.argv) >= 2 else os.path.join(os.path.expanduser("~"), "Musique")
24 22
 
25
-# Since metaflac ALWAYS recalculate the tags, we need to specificaly verify
26
-# that the tags are present on every track of an album to skip it
27
-def isFlacTagged(f):
28
-    # TODO PERF Run metaflac --show-tag for the whole album and compare the
29
-    # output with the number of files tagged
30
-    for tag in ["REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_ALBUM_GAIN"]:
31
-        cmd = ["metaflac", "--show-tag", tag, f]
32
-        proc = subprocess.run(cmd, stdout=subprocess.PIPE)
33
-        res = len(proc.stdout.strip()) > 0
34
-        if not res:
35
-            return False
36
-    return True
37
-
38
-# TODO UX Do the same thing with other formats so the output is not
39
-# inconsistent
23
+def isMusic(f):
24
+    ext = os.path.splitext(f)[1][1:].lower()
25
+    return ext in r128gain.AUDIO_EXTENSIONS
40 26
 
41 27
 # Get album paths
28
+log.info("Listing albums and tracks")
42 29
 albums = set()
30
+singleFiles = set()
43 31
 for root, dirs, files in os.walk(SOURCE_FOLDER):
44 32
 
45 33
     relRoot = os.path.relpath(root, SOURCE_FOLDER)
46 34
 
47
-    # See if it's an album (only 2 components in the path)
48 35
     head, tail = os.path.split(relRoot)
36
+    # 1 component in the path: save files path as single
49 37
     if not len(head):
50
-        continue
38
+        for f in files:
39
+            if isMusic(f):
40
+                fullPath = os.path.join(root, f)
41
+                singleFiles.add(fullPath)
51 42
     head, tail = os.path.split(head)
52 43
     if len(head):
53 44
         continue
45
+    # 2 components in the path: save album path
54 46
     albums.add(root)
55 47
 
56
-cmds = list()
48
+log.info("Processing single files")
49
+# r128gain.process(list(singleFiles), album_gain=False, skip_tagged=not FORCE, report=True)
57 50
 for album in albums:
58 51
     albumName = os.path.relpath(album, SOURCE_FOLDER)
59 52
     log.info("Processing album {}".format(albumName))
60 53
 
61
-    musicFiles = dict()
54
+    musicFiles = set()
62 55
     for root, dirs, files in os.walk(album):
63 56
         for f in files:
64
-            ext = os.path.splitext(f)[1][1:].lower()
65
-
66
-            for exts in GAIN_COMMANDS.keys():
67
-                if ext in exts:
68
-                    if exts not in musicFiles.keys():
69
-                        musicFiles[exts] = set()
70
-                    fullPath = os.path.join(root, f)
71
-                    musicFiles[exts].add(fullPath)
72
-
73
-    if len(musicFiles) >= 2:
74
-        log.warn("Different extensions for album {}. AlbumGain won't be on par.".format(albumName))
57
+            if isMusic(f):
58
+                fullPath = os.path.join(root, f)
59
+                musicFiles.add(fullPath)
75 60
 
76
-    for exts, files in musicFiles.items():
77
-
78
-        if exts == ("flac",) and not FORCE:
79
-            allTagged = True
80
-            for f in files:
81
-                if not isFlacTagged(f):
82
-                    allTaged = False
83
-                    break
84
-            if allTagged:
85
-                log.debug("Already tagged (for flac only)!")
86
-                break
87
-
88
-        cmd = GAIN_COMMANDS[exts] + list(files)
89
-        log.debug("Registering command: `{}`".format(" ".join(cmd)))
90
-        cmds.append(cmd)
91
-
92
-logging.info("Executing commands")
93
-for cmd in progressbar.progressbar(cmds):
94
-    subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
61
+    # print(musicFiles)
62
+    if not len(musicFiles):
63
+        continue
64
+    r128gain.process(list(musicFiles), album_gain=True, skip_tagged=not FORCE, report=True)
65
+    print("==============================")
95 66
 
96 67
 
97 68
 

+ 17
- 0
scripts/tagCreatorPhotos View File

@@ -0,0 +1,17 @@
1
+#!/usr/bin/env python3
2
+
3
+import sys
4
+import os
5
+import piexif
6
+
7
+assert len(sys.argv) >= 3, "Usage {} CREATOR FILENAMES...".format(sys.argv[0])
8
+creator = sys.argv[1]
9
+filenames = sys.argv[2:]
10
+
11
+for filename in filenames:
12
+    assert os.path.isfile(filename)
13
+    exifDict = piexif.load(filename)
14
+    exifDict['0th'][piexif.ImageIFD.Copyright] = creator.encode()
15
+    exifBytes = piexif.dump(exifDict)
16
+    piexif.insert(exifBytes, filename)
17
+

+ 1
- 0
vimpcrc View File

@@ -5,3 +5,4 @@ map ° D:browse<C-M>A:shuffle<C-M>:play<C-M>:playlist<C-M>
5 5
 set songformat {%a - %b: %t}|{%f}$E$R $H[$H%l$H]$H
6 6
 set libraryformat %n \| {%t}|{%f}$E$R $H[$H%l$H]$H
7 7
 set ignorecase
8
+set sort library

+ 26
- 4
vimrc View File

@@ -41,15 +41,30 @@ Plug 'tomtom/tcomment_vim'
41 41
 " Plug 'tomlion/vim-solidity'
42 42
 " Plug 'godlygeek/tabular'
43 43
 " Plug 'jrozner/vim-antlr'
44
-Plug 'maralla/completor.vim'
44
+"
45
+" Plug 'maralla/completor.vim'
46
+if has('nvim')
47
+  Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' }
48
+else
49
+  Plug 'Shougo/deoplete.nvim'
50
+  Plug 'roxma/nvim-yarp'
51
+  Plug 'roxma/vim-hug-neovim-rpc'
52
+endif
53
+Plug 'zchee/deoplete-jedi'
54
+
45 55
 Plug 'python-mode/python-mode', { 'branch': 'develop' }
46 56
 Plug 'junegunn/fzf', {'do': './install --bin'}
47 57
 Plug 'junegunn/fzf.vim'
48 58
 Plug 'ervandew/supertab'
49 59
 Plug 'dpelle/vim-LanguageTool'
60
+Plug 'terryma/vim-smooth-scroll'
50 61
 
51 62
 call plug#end()
52 63
 
64
+""" COMPLETOR """
65
+
66
+let g:deoplete#enable_at_startup = 1
67
+
53 68
 """ UNDOTREE """
54 69
 
55 70
 nmap <F7> :UndotreeToggle<CR>
@@ -69,7 +84,7 @@ let g:airline#extensions#tabline#enabled = 1
69 84
 let g:airline_section_a = airline#section#create(['mode'])
70 85
 let g:airline_section_b = airline#section#create(['branch', 'hunks'])
71 86
 let g:airline_section_z = airline#section#create(['%B', '@', '%l', ':', '%c'])
72
-let g:airline_theme = 'base16'
87
+let g:airline_theme = 'base16_monokai'
73 88
 
74 89
 """ AUTOFORMAT """
75 90
 nmap <F3> :Autoformat<CR>
@@ -203,7 +218,14 @@ vmap <Enter> <Esc>
203 218
 nmap <Enter> o<Esc>
204 219
 nmap <C-H> :bp<CR>
205 220
 nmap <C-L> :bn<CR>
206
-nmap <C-K> kkkkkkkkkkkkkkkkkkkkk
207
-nmap <C-J> jjjjjjjjjjjjjjjjjjjjj
221
+if has('nvim')
222
+    " nmap <C-K> 20k
223
+    " nmap <C-J> 20j
224
+    noremap <silent> <C-K> :call smooth_scroll#up(20, 5, 1)<CR>
225
+    noremap <silent> <C-J> :call smooth_scroll#down(20, 5, 1)<CR>
226
+else
227
+    nmap <C-K> kkkkkkkkkkkkkkkkkkkkk
228
+    nmap <C-J> jjjjjjjjjjjjjjjjjjjjj
229
+endif
208 230
 
209 231
 

Loading…
Cancel
Save