diff --git a/power_sequencer/__init__.py b/power_sequencer/__init__.py
index 3cb5d4f2dd424885df017d32eea68be3b0ef4dda..8d2c84dbdfadb225b5959c544c09297d5644f5b0 100755
--- a/power_sequencer/__init__.py
+++ b/power_sequencer/__init__.py
@@ -38,11 +38,11 @@ bl_info = {
     "name": "Power Sequencer",
     "description": "Video editing tools for content creators",
     "author": "Nathan Lovato",
-    "version": (1, 5, 1),
-    "blender": (2, 90, 1),
+    "version": (1, 5, 0),
+    "blender": (2, 81, 0),
     "location": "Sequencer",
     "tracker_url": "https://github.com/GDquest/Blender-power-sequencer/issues",
-    "doc_url": "https://www.gdquest.com/docs/documentation/power-sequencer/",
+    "wiki_url": "https://www.gdquest.com/docs/documentation/power-sequencer/",
     "support": "COMMUNITY",
     "category": "Sequencer",
 }
@@ -80,7 +80,7 @@ def register():
     keymaps = register_shortcuts(classes_operator)
     addon_keymaps += keymaps
 
-    # print("Registered {} with {} modules".format(bl_info["name"], len(modules)))
+    print("Registered {} with {} modules".format(bl_info["name"], len(modules)))
 
 
 def unregister():
@@ -104,4 +104,4 @@ def unregister():
     unregister_properties()
     unregister_handlers()
 
-    # print("Unregistered {}".format(bl_info["name"]))
+    print("Unregistered {}".format(bl_info["name"]))
diff --git a/power_sequencer/addon_preferences.py b/power_sequencer/addon_preferences.py
index 0ca313307c048752903072db63f21dadbd1a4579..014f63ddb7e4b8fbf6a2a4a2220cd18d0125eb26 100644
--- a/power_sequencer/addon_preferences.py
+++ b/power_sequencer/addon_preferences.py
@@ -55,7 +55,7 @@ class PowerSequencerPreferences(bpy.types.AddonPreferences):
         error_message, info = "", ""
         try:
             info: str = subprocess.check_output([path, "-version"]).decode("utf-8")
-            info = info[:info.find("Copyright")]
+            info = info[: info.find("Copyright")]
             print(info)
         except (OSError, subprocess.CalledProcessError):
             error_message = "Path `{}` is not a valid ffmpeg executable".format(path)
diff --git a/power_sequencer/operators/__init__.py b/power_sequencer/operators/__init__.py
index 064ba9b3f694d749fa37d9193942e88a7f44befb..406c86359e7328ddc0ccc914cdae24fd28a53d0a 100755
--- a/power_sequencer/operators/__init__.py
+++ b/power_sequencer/operators/__init__.py
@@ -22,17 +22,13 @@ def get_operator_classes():
     """Returns the list of operators in the add-on"""
     this_file = os.path.dirname(__file__)
     module_files = [
-        f
-        for f in os.listdir(this_file)
-        if f.endswith(".py") and not f.startswith("__init__")
+        f for f in os.listdir(this_file) if f.endswith(".py") and not f.startswith("__init__")
     ]
     module_paths = ["." + os.path.splitext(f)[0] for f in module_files]
     classes = []
     for path in module_paths:
         module = importlib.import_module(path, package="power_sequencer.operators")
-        operator_names = [
-            entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")
-        ]
+        operator_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")]
         classes.extend([getattr(module, name) for name in operator_names])
     return classes
 
@@ -41,9 +37,7 @@ doc = {
     "sequencer.refresh_all": {
         "name": "Refresh All",
         "description": "",
-        "shortcuts": [
-            ({"type": "R", "value": "PRESS", "shift": True}, {}, "Refresh All")
-        ],
+        "shortcuts": [({"type": "R", "value": "PRESS", "shift": True}, {}, "Refresh All")],
         "demo": "",
         "keymap": "Sequencer",
     }
diff --git a/power_sequencer/operators/channel_offset.py b/power_sequencer/operators/channel_offset.py
index 35da1b471f7d2b8dfdc7ef0ea4ec7bfaeff70ddf..48305c4bc5443305a3f5ff352a36cd8e1d011b74 100644
--- a/power_sequencer/operators/channel_offset.py
+++ b/power_sequencer/operators/channel_offset.py
@@ -14,17 +14,12 @@
 # You should have received a copy of the GNU General Public License along with Power Sequencer. If
 # not, see <https://www.gnu.org/licenses/>.
 #
-import bpy
 from operator import attrgetter
 
-from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-from .utils.functions import (
-    slice_selection,
-    get_frame_range,
-    get_channel_range,
-    trim_strips,
-    find_strips_in_range,
-)
+import bpy
+
+from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
+from .utils.functions import find_strips_in_range, move_selection, trim_strips
 
 
 class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
@@ -39,7 +34,7 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
         "shortcuts": [
             (
                 {"type": "UP_ARROW", "value": "PRESS", "alt": True},
-                {"direction": "up"},
+                {"direction": "up", "trim_target_channel": False},
                 "Move to Open Channel Above",
             ),
             (
@@ -49,7 +44,7 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
             ),
             (
                 {"type": "DOWN_ARROW", "value": "PRESS", "alt": True},
-                {"direction": "down"},
+                {"direction": "down", "trim_target_channel": False},
                 "Move to Open Channel Below",
             ),
             (
@@ -79,35 +74,71 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator):
         description="Trim strips to make space in the target channel",
         default=False,
     )
+    keep_selection_offset: bpy.props.BoolProperty(
+        name="Keep selection offset",
+        description="The selected strips preserve their relative positions",
+        default=True,
+    )
 
     @classmethod
     def poll(cls, context):
         return context.selected_sequences
 
     def execute(self, context):
+
+        max_channel = 32
+        min_channel = 1
+
+        if self.direction == "up":
+            channel_offset = +1
+            limit_channel = max_channel
+            comparison_function = min
+
+        if self.direction == "down":
+            channel_offset = -1
+            limit_channel = min_channel
+            comparison_function = max
+
         selection = [s for s in context.selected_sequences if not s.lock]
+
         if not selection:
             return {"FINISHED"}
 
-        selection_blocks = slice_selection(context, selection)
-        for block in selection_blocks:
-            sequences = sorted(block, key=attrgetter("channel", "frame_final_start"))
-            frame_start, frame_end = get_frame_range(sequences)
-            channel_start, channel_end = get_channel_range(sequences)
-
-            if self.trim_target_channel:
-                to_delete, to_trim = find_strips_in_range(frame_start, frame_end, context.sequences)
-                channel_trim = (
-                    channel_end + 1 if self.direction == "up" else max(1, channel_start - 1)
-                )
-                to_trim = [s for s in to_trim if s.channel == channel_trim]
-                to_delete = [s for s in to_delete if s.channel == channel_trim]
-                trim_strips(context, frame_start, frame_end, to_trim, to_delete)
-
-            if self.direction == "up":
-                for s in reversed(sequences):
-                    s.channel += 1
-            elif self.direction == "down":
-                for s in sequences:
-                    s.channel = max(1, s.channel - 1)
+        sequences = sorted(selection, key=attrgetter("channel", "frame_final_start"))
+        if self.direction == "up":
+            sequences = [s for s in reversed(sequences)]
+
+        head = sequences[0]
+        if not self.keep_selection_offset or (
+            head.channel != limit_channel and self.keep_selection_offset
+        ):
+            for s in sequences:
+                if self.trim_target_channel:
+                    channel_trim = s.channel + channel_offset
+                    strips_in_trim_channel = [
+                        sequence
+                        for sequence in context.sequences
+                        if (sequence.channel == channel_trim)
+                    ]
+                    if strips_in_trim_channel:
+                        to_delete, to_trim = find_strips_in_range(
+                            s.frame_final_start, s.frame_final_end, strips_in_trim_channel
+                        )
+                        trim_strips(
+                            context, s.frame_final_start, s.frame_final_end, to_trim, to_delete
+                        )
+
+                if not self.keep_selection_offset:
+                    s.channel = comparison_function(limit_channel, s.channel + channel_offset)
+                    if s.channel == limit_channel:
+                        move_selection(context, [s], 0, 0)
+
+            if self.keep_selection_offset:
+                start_frame = head.frame_final_start
+                x_difference = 0
+                while not head.channel == limit_channel:
+                    move_selection(context, sequences, -x_difference, channel_offset)
+                    x_difference = head.frame_final_start - start_frame
+                    if x_difference == 0:
+                        break
         return {"FINISHED"}
diff --git a/power_sequencer/operators/cut_strips_under_cursor.py b/power_sequencer/operators/cut_strips_under_cursor.py
index d7e1141a1343cc96cbb9e7632f23d8fb4a97eeb4..3b54a17b7e180f1ff1a5bae225cbc09f8f6c1e32 100644
--- a/power_sequencer/operators/cut_strips_under_cursor.py
+++ b/power_sequencer/operators/cut_strips_under_cursor.py
@@ -30,9 +30,7 @@ class POWER_SEQUENCER_OT_split_strips_under_cursor(bpy.types.Operator):
         "name": doc_name(__qualname__),
         "demo": "https://i.imgur.com/ZyEd0jD.gif",
         "description": doc_description(__doc__),
-        "shortcuts": [
-            ({"type": "K", "value": "PRESS"}, {}, "Cut All Strips Under Cursor")
-        ],
+        "shortcuts": [({"type": "K", "value": "PRESS"}, {}, "Cut All Strips Under Cursor")],
         "keymap": "Sequencer",
     }
     bl_idname = doc_idname(__qualname__)
@@ -66,10 +64,5 @@ class POWER_SEQUENCER_OT_split_strips_under_cursor(bpy.types.Operator):
                 deselect = False
         if deselect:
             bpy.ops.sequencer.select_all(action="DESELECT")
-        (
-            context.selected_sequences
-            or bpy.ops.power_sequencer.select_strips_under_cursor()
-        )
-        return bpy.ops.sequencer.split(
-            frame=context.scene.frame_current, side=self.side
-        )
+        (context.selected_sequences or bpy.ops.power_sequencer.select_strips_under_cursor())
+        return bpy.ops.sequencer.split(frame=context.scene.frame_current, side=self.side)
diff --git a/power_sequencer/operators/delete_direct.py b/power_sequencer/operators/delete_direct.py
index b39ae12f055dd520d20cb9f7ea973d351420e283..4c6e4d2dbf89c97080d22f282c2d879058fdf43f 100644
--- a/power_sequencer/operators/delete_direct.py
+++ b/power_sequencer/operators/delete_direct.py
@@ -50,9 +50,7 @@ class POWER_SEQUENCER_OT_delete_direct(bpy.types.Operator):
     bl_description = doc_brief(doc["description"])
     bl_options = {"REGISTER", "UNDO"}
 
-    is_removing_transitions: bpy.props.BoolProperty(
-        name="Remove Transitions", default=False
-    )
+    is_removing_transitions: bpy.props.BoolProperty(name="Remove Transitions", default=False)
 
     @classmethod
     def poll(cls, context):
@@ -61,17 +59,12 @@ class POWER_SEQUENCER_OT_delete_direct(bpy.types.Operator):
     def invoke(self, context, event):
         frame, channel = get_mouse_frame_and_channel(context, event)
         if not context.selected_sequences:
-            bpy.ops.power_sequencer.select_closest_to_mouse(
-                frame=frame, channel=channel
-            )
+            bpy.ops.power_sequencer.select_closest_to_mouse(frame=frame, channel=channel)
         return self.execute(context)
 
     def execute(self, context):
         selection = context.selected_sequences
-        if (
-            self.is_removing_transitions
-            and bpy.ops.power_sequencer.transitions_remove.poll()
-        ):
+        if self.is_removing_transitions and bpy.ops.power_sequencer.transitions_remove.poll():
             bpy.ops.power_sequencer.transitions_remove()
         bpy.ops.sequencer.delete()
 
diff --git a/power_sequencer/operators/expand_to_surrounding_cuts.py b/power_sequencer/operators/expand_to_surrounding_cuts.py
index 1435b23c03a9114f0790804dfce0249eca9e249e..53de8bf72844cf46690281c2d4c0bd52105ed733 100644
--- a/power_sequencer/operators/expand_to_surrounding_cuts.py
+++ b/power_sequencer/operators/expand_to_surrounding_cuts.py
@@ -33,11 +33,7 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
         "demo": "",
         "description": doc_description(__doc__),
         "shortcuts": [
-            (
-                {"type": "E", "value": "PRESS", "ctrl": True},
-                {},
-                "Expand to Surrounding Cuts",
-            )
+            ({"type": "E", "value": "PRESS", "ctrl": True}, {}, "Expand to Surrounding Cuts",)
         ],
         "keymap": "Sequencer",
     }
@@ -68,37 +64,24 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
             sequences_frame_start = min(
                 sequences, key=lambda s: s.frame_final_start
             ).frame_final_start
-            sequences_frame_end = max(
-                sequences, key=lambda s: s.frame_final_end
-            ).frame_final_end
+            sequences_frame_end = max(sequences, key=lambda s: s.frame_final_end).frame_final_end
 
             frame_left, frame_right = find_closest_cuts(
                 context, sequences_frame_start, sequences_frame_end
             )
-            if (
-                sequences_frame_start == frame_left
-                and sequences_frame_end == frame_right
-            ):
+            if sequences_frame_start == frame_left and sequences_frame_end == frame_right:
                 continue
 
-            to_extend_left = [
-                s for s in sequences if s.frame_final_start == sequences_frame_start
-            ]
-            to_extend_right = [
-                s for s in sequences if s.frame_final_end == sequences_frame_end
-            ]
+            to_extend_left = [s for s in sequences if s.frame_final_start == sequences_frame_start]
+            to_extend_right = [s for s in sequences if s.frame_final_end == sequences_frame_end]
 
             for s in to_extend_left:
                 s.frame_final_start = (
-                    frame_left
-                    if frame_left < sequences_frame_start
-                    else sequences_frame_start
+                    frame_left if frame_left < sequences_frame_start else sequences_frame_start
                 )
             for s in to_extend_right:
                 s.frame_final_end = (
-                    frame_right
-                    if frame_right > sequences_frame_end
-                    else sequences_frame_end
+                    frame_right if frame_right > sequences_frame_end else sequences_frame_end
                 )
         return {"FINISHED"}
 
@@ -110,8 +93,6 @@ def find_closest_cuts(context, frame_min, frame_max):
     ).frame_final_end
     frame_right = min(
         context.sequences,
-        key=lambda s: s.frame_final_start
-        if s.frame_final_start >= frame_max
-        else 1000000,
+        key=lambda s: s.frame_final_start if s.frame_final_start >= frame_max else 1000000,
     ).frame_final_start
     return frame_left, frame_right
diff --git a/power_sequencer/operators/fade_add.py b/power_sequencer/operators/fade_add.py
index 1f4d0bfa3d9981a8b2070f2b996ef241b16b0e1b..dd75c8649c3159d7b56cc181a7cd332cd97d8a12 100644
--- a/power_sequencer/operators/fade_add.py
+++ b/power_sequencer/operators/fade_add.py
@@ -51,10 +51,7 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
     bl_options = {"REGISTER", "UNDO"}
 
     duration_seconds: bpy.props.FloatProperty(
-        name="Fade Duration",
-        description="Duration of the fade in seconds",
-        default=1.0,
-        min=0.01,
+        name="Fade Duration", description="Duration of the fade in seconds", default=1.0, min=0.01,
     )
     type: bpy.props.EnumProperty(
         items=[
@@ -98,12 +95,8 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
                 if s.frame_final_start < context.scene.frame_current < s.frame_final_end
             ]
 
-        max_duration = min(
-            sequences, key=lambda s: s.frame_final_duration
-        ).frame_final_duration
-        max_duration = (
-            floor(max_duration / 2.0) if self.type == "IN_OUT" else max_duration
-        )
+        max_duration = min(sequences, key=lambda s: s.frame_final_duration).frame_final_duration
+        max_duration = floor(max_duration / 2.0) if self.type == "IN_OUT" else max_duration
 
         faded_sequences = []
         for sequence in sequences:
@@ -113,15 +106,9 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
             if not self.is_long_enough(sequence, duration):
                 continue
 
-            animated_property = (
-                "volume" if hasattr(sequence, "volume") else "blend_alpha"
-            )
-            fade_fcurve = fade_find_or_create_fcurve(
-                context, sequence, animated_property
-            )
-            fades = self.calculate_fades(
-                sequence, fade_fcurve, animated_property, duration
-            )
+            animated_property = "volume" if hasattr(sequence, "volume") else "blend_alpha"
+            fade_fcurve = fade_find_or_create_fcurve(context, sequence, animated_property)
+            fades = self.calculate_fades(sequence, fade_fcurve, animated_property, duration)
             fade_animation_clear(context, fade_fcurve, fades)
             fade_animation_create(fade_fcurve, fades)
             faded_sequences.append(sequence)
@@ -129,9 +116,7 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
         sequence_string = "sequence" if len(faded_sequences) == 1 else "sequences"
         self.report(
             {"INFO"},
-            "Added fade animation to {} {}.".format(
-                len(faded_sequences), sequence_string
-            ),
+            "Added fade animation to {} {}.".format(len(faded_sequences), sequence_string),
         )
         return {"FINISHED"}
 
@@ -232,13 +217,9 @@ class Fade:
 
         if type == "IN":
             self.start = Vector((sequence.frame_final_start, 0.0))
-            self.end = Vector(
-                (sequence.frame_final_start + self.duration, self.max_value)
-            )
+            self.end = Vector((sequence.frame_final_start + self.duration, self.max_value))
         elif type == "OUT":
-            self.start = Vector(
-                (sequence.frame_final_end - self.duration, self.max_value)
-            )
+            self.start = Vector((sequence.frame_final_end - self.duration, self.max_value))
             self.end = Vector((sequence.frame_final_end, 0.0))
 
     def calculate_max_value(self, sequence, fade_fcurve):
@@ -253,15 +234,11 @@ class Fade:
         else:
             if self.type == "IN":
                 fade_end = sequence.frame_final_start + self.duration
-                keyframes = (
-                    k for k in fade_fcurve.keyframe_points if k.co[0] >= fade_end
-                )
+                keyframes = (k for k in fade_fcurve.keyframe_points if k.co[0] >= fade_end)
             if self.type == "OUT":
                 fade_start = sequence.frame_final_end - self.duration
                 keyframes = (
-                    k
-                    for k in reversed(fade_fcurve.keyframe_points)
-                    if k.co[0] <= fade_start
+                    k for k in reversed(fade_fcurve.keyframe_points) if k.co[0] <= fade_start
                 )
             try:
                 max_value = next(keyframes).co[1]
@@ -275,6 +252,4 @@ class Fade:
 
 
 def calculate_duration_frames(context, duration_seconds):
-    return round(
-        duration_seconds * context.scene.render.fps / context.scene.render.fps_base
-    )
+    return round(duration_seconds * context.scene.render.fps / context.scene.render.fps_base)
diff --git a/power_sequencer/operators/fade_clear.py b/power_sequencer/operators/fade_clear.py
index 06b320b068a88d3e70fbfbb584ca5565402b7dad..0251663315946cb1ac6ad21a203cd018351f520d 100644
--- a/power_sequencer/operators/fade_clear.py
+++ b/power_sequencer/operators/fade_clear.py
@@ -32,11 +32,7 @@ class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator):
         "demo": "",
         "description": doc_description(__doc__),
         "shortcuts": [
-            (
-                {"type": "F", "value": "PRESS", "alt": True, "ctrl": True},
-                {},
-                "Clear Fades",
-            )
+            ({"type": "F", "value": "PRESS", "alt": True, "ctrl": True}, {}, "Clear Fades",)
         ],
         "keymap": "Sequencer",
     }
@@ -53,9 +49,7 @@ class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator):
         fcurves = context.scene.animation_data.action.fcurves
 
         for sequence in context.selected_sequences:
-            animated_property = (
-                "volume" if hasattr(sequence, "volume") else "blend_alpha"
-            )
+            animated_property = "volume" if hasattr(sequence, "volume") else "blend_alpha"
             data_path = sequence.path_from_id() + "." + animated_property
             fcurve_map = {
                 curve.data_path: curve
diff --git a/power_sequencer/operators/gap_remove.py b/power_sequencer/operators/gap_remove.py
index 4a73605150621ea41e08166cad25976d804cb7bd..825bfc0d89ae96e748be0e19cefc5cf0cd8fe8a5 100644
--- a/power_sequencer/operators/gap_remove.py
+++ b/power_sequencer/operators/gap_remove.py
@@ -71,9 +71,7 @@ class POWER_SEQUENCER_OT_gap_remove(bpy.types.Operator):
             else context.sequences
         )
         sequences = [
-            s
-            for s in sequences
-            if s.frame_final_start >= frame or s.frame_final_end > frame
+            s for s in sequences if s.frame_final_start >= frame or s.frame_final_end > frame
         ]
         sequence_blocks = slice_selection(context, sequences)
         if not sequence_blocks:
@@ -100,18 +98,12 @@ class POWER_SEQUENCER_OT_gap_remove(bpy.types.Operator):
         Finds and returns the frame at which the gap starts.
         Takes a list sequences sorted by frame_final_start.
         """
-        strips_start = min(
-            sorted_sequences, key=attrgetter("frame_final_start")
-        ).frame_final_start
-        strips_end = max(
-            sorted_sequences, key=attrgetter("frame_final_end")
-        ).frame_final_end
+        strips_start = min(sorted_sequences, key=attrgetter("frame_final_start")).frame_final_start
+        strips_end = max(sorted_sequences, key=attrgetter("frame_final_end")).frame_final_end
 
         gap_frame = -1
         if strips_start > frame:
-            strips_before_frame_start = [
-                s for s in context.sequences if s.frame_final_end <= frame
-            ]
+            strips_before_frame_start = [s for s in context.sequences if s.frame_final_end <= frame]
             frame_target = 0
             if strips_before_frame_start:
                 frame_target = max(
diff --git a/power_sequencer/operators/make_hold_frame.py b/power_sequencer/operators/make_hold_frame.py
index c6893d045fcaac2e0aa4153a09a97157a103b278..22c2eecff2d7e92ca1f5c9544db4f0d1037dc827 100644
--- a/power_sequencer/operators/make_hold_frame.py
+++ b/power_sequencer/operators/make_hold_frame.py
@@ -77,9 +77,7 @@ class POWER_SEQUENCER_OT_make_hold_frame(bpy.types.Operator):
             try:
                 next_strip_start = next(
                     s
-                    for s in sorted(
-                        context.sequences, key=operator.attrgetter("frame_final_start")
-                    )
+                    for s in sorted(context.sequences, key=operator.attrgetter("frame_final_start"))
                     if s.frame_final_start > active.frame_final_end
                 ).frame_final_start
                 offset = next_strip_start - active.frame_final_end
@@ -90,9 +88,7 @@ class POWER_SEQUENCER_OT_make_hold_frame(bpy.types.Operator):
         source_blend_type = active.blend_type
         sequencer.split(frame=scene.frame_current, type="SOFT", side="RIGHT")
         transform.seq_slide(value=(offset, 0))
-        sequencer.split(
-            frame=scene.frame_current + offset + 1, type="SOFT", side="LEFT"
-        )
+        sequencer.split(frame=scene.frame_current + offset + 1, type="SOFT", side="LEFT")
         transform.seq_slide(value=(-offset, 0))
 
         sequencer.meta_make()
diff --git a/power_sequencer/operators/scene_create_from_selection.py b/power_sequencer/operators/scene_create_from_selection.py
index b615dae9125fc07e40cfc18d8000d636b369c2a9..cd774d0df0b730953c17f1c582dede8384a4a553 100644
--- a/power_sequencer/operators/scene_create_from_selection.py
+++ b/power_sequencer/operators/scene_create_from_selection.py
@@ -67,7 +67,6 @@ class POWER_SEQUENCER_OT_scene_create_from_selection(bpy.types.Operator):
             context.window.scene.name = context.selected_sequences[0].name
             new_scene_name = context.window.scene.name
 
-
             ###after full copy also unselected strips are in the sequencer... Delete those strips
             bpy.ops.sequencer.select_all(action="INVERT")
             bpy.ops.power_sequencer.delete_direct()
@@ -85,8 +84,10 @@ class POWER_SEQUENCER_OT_scene_create_from_selection(bpy.types.Operator):
 
             bpy.ops.power_sequencer.delete_direct()
             bpy.ops.sequencer.scene_strip_add(
-                frame_start=selection_start_frame, channel=selection_start_channel, scene=new_scene_name
+                frame_start=selection_start_frame,
+                channel=selection_start_channel,
+                scene=new_scene_name,
             )
             scene_strip = context.selected_sequences[0]
-           # scene_strip.use_sequence = True
+        # scene_strip.use_sequence = True
         return {"FINISHED"}
diff --git a/power_sequencer/operators/scene_merge_from.py b/power_sequencer/operators/scene_merge_from.py
index 16b2710d919eea9b206fdd3a711758dbb3b77a28..5c3f40b07ad0a0c88db73022de9f5ce9c92cad47 100644
--- a/power_sequencer/operators/scene_merge_from.py
+++ b/power_sequencer/operators/scene_merge_from.py
@@ -73,9 +73,7 @@ class POWER_SEQUENCER_OT_merge_from_scene_strip(bpy.types.Operator):
         context.window.scene = strip_scene
         bpy.ops.scene.delete()
         context.window.scene = start_scene
-        self.report(
-            type={"WARNING"}, message="Merged scenes lose all their animation data."
-        )
+        self.report(type={"WARNING"}, message="Merged scenes lose all their animation data.")
 
         return {"FINISHED"}
 
diff --git a/power_sequencer/operators/select_all_left_or_right.py b/power_sequencer/operators/select_all_left_or_right.py
index b2e08bb80e0de75eafac402deadf0bd065e731ea..01e35306672e85fb24bb1c9fe048468e8102d278 100644
--- a/power_sequencer/operators/select_all_left_or_right.py
+++ b/power_sequencer/operators/select_all_left_or_right.py
@@ -62,4 +62,4 @@ class POWER_SEQUENCER_OT_select_all_left_or_right(bpy.types.Operator):
         return context.sequences
 
     def execute(self, context):
-        return bpy.ops.sequencer.select_side_of_frame("INVOKE_DEFAULT", side=self.side)
+        return bpy.ops.sequencer.select("INVOKE_DEFAULT", left_right=self.side)
diff --git a/power_sequencer/operators/snap.py b/power_sequencer/operators/snap.py
index 42b029d1b88197292af1046778b31c426b6e2964..13d5e66e9052ebf887bf2bb44b07bb3fd2019488 100644
--- a/power_sequencer/operators/snap.py
+++ b/power_sequencer/operators/snap.py
@@ -32,11 +32,7 @@ class POWER_SEQUENCER_OT_snap(bpy.types.Operator):
         "demo": "",
         "description": doc_description(__doc__),
         "shortcuts": [
-            (
-                {"type": "S", "value": "PRESS", "shift": True},
-                {},
-                "Snap sequences to cursor",
-            )
+            ({"type": "S", "value": "PRESS", "shift": True}, {}, "Snap sequences to cursor",)
         ],
         "keymap": "Sequencer",
     }
diff --git a/power_sequencer/operators/snap_selection.py b/power_sequencer/operators/snap_selection.py
index 5900579bc957368bd7d31ca7e3352a8ed02ab9a1..eb2851b5fd7a6633d01e95bf866e52d1b2971061 100644
--- a/power_sequencer/operators/snap_selection.py
+++ b/power_sequencer/operators/snap_selection.py
@@ -15,7 +15,7 @@
 # not, see <https://www.gnu.org/licenses/>.
 #
 import bpy
-from .utils.functions import get_sequences_under_cursor, apply_time_offset
+from .utils.functions import get_sequences_under_cursor, move_selection
 from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
 
 
@@ -32,11 +32,7 @@ class POWER_SEQUENCER_OT_snap_selection(bpy.types.Operator):
         "demo": "",
         "description": doc_description(__doc__),
         "shortcuts": [
-            (
-                {"type": "S", "value": "PRESS", "alt": True},
-                {},
-                "Snap selection to cursor",
-            )
+            ({"type": "S", "value": "PRESS", "alt": True}, {}, "Snap selection to cursor",)
         ],
         "keymap": "Sequencer",
     }
@@ -55,9 +51,7 @@ class POWER_SEQUENCER_OT_snap_selection(bpy.types.Operator):
             if context.selected_sequences
             else get_sequences_under_cursor(context)
         )
-        frame_first = min(
-            sequences, key=lambda s: s.frame_final_start
-        ).frame_final_start
+        frame_first = min(sequences, key=lambda s: s.frame_final_start).frame_final_start
         time_offset = context.scene.frame_current - frame_first
-        apply_time_offset(context, sequences, time_offset)
+        move_selection(context, sequences, time_offset)
         return {"FINISHED"}
diff --git a/power_sequencer/operators/trim_left_or_right_handles.py b/power_sequencer/operators/trim_left_or_right_handles.py
index b93bee5364c04e369cdcd6f033b4cc957ff16a95..bbbb8bd5aa563f173cdecc695af297ba53422549 100644
--- a/power_sequencer/operators/trim_left_or_right_handles.py
+++ b/power_sequencer/operators/trim_left_or_right_handles.py
@@ -78,16 +78,9 @@ class POWER_SEQUENCER_OT_trim_left_or_right_handles(bpy.types.Operator):
         frame_current = context.scene.frame_current
 
         # Only select sequences under the time cursor
-        sequences = (
-            context.selected_sequences
-            if context.selected_sequences
-            else context.sequences
-        )
+        sequences = context.selected_sequences if context.selected_sequences else context.sequences
         for s in sequences:
-            s.select = (
-                s.frame_final_start <= frame_current
-                and s.frame_final_end >= frame_current
-            )
+            s.select = s.frame_final_start <= frame_current and s.frame_final_end >= frame_current
         sequences = [s for s in sequences if s.select]
         if not sequences:
             return {"FINISHED"}
diff --git a/power_sequencer/operators/utils/functions.py b/power_sequencer/operators/utils/functions.py
index 3462b767330f93bc8915e57c3d7b5827127ff217..5b33067caafaef59519f710e7f794a61f685a2c3 100644
--- a/power_sequencer/operators/utils/functions.py
+++ b/power_sequencer/operators/utils/functions.py
@@ -21,6 +21,9 @@ import bpy
 
 from .global_settings import SequenceTypes
 
+max_channel = 32
+min_channel = 1
+
 
 def calculate_distance(x1, y1, x2, y2):
     return sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
@@ -34,10 +37,8 @@ def find_linked(context, sequences, selected_sequences):
     """
     Takes a list of sequences and returns a list of all the sequences
     and effects that are linked in time
-
     Args:
-    - sequences: a list of sequences
-
+        - sequences: a list of sequences
     Returns a list of all the linked sequences, but not the sequences passed to the function
     """
     start, end = get_frame_range(sequences, selected_sequences)
@@ -99,7 +100,7 @@ def find_sequences_after(context, sequence):
     """
     Finds the strips following the sequences passed to the function
     Args:
-    - Sequences, the sequences to check
+        - Sequences, the sequences to check
     Returns all the strips after the sequence in the current context
     """
     return [s for s in context.sequences if s.frame_final_start > sequence.frame_final_start]
@@ -133,12 +134,10 @@ def find_snap_candidate(context, frame=0):
 def find_strips_mouse(context, frame, channel, select_linked=False):
     """
     Finds a list of sequences to select based on the frame and channel the mouse cursor is at
-
     Args:
-    - frame: the frame the mouse or cursor is on
-    - channel: the channel the mouse is hovering
-    - select_linked: find and append the sequences linked in time if True
-
+        - frame: the frame the mouse or cursor is on
+        - channel: the channel the mouse is hovering
+        - select_linked: find and append the sequences linked in time if True
     Returns the sequence(s) under the mouse cursor as a list
     Returns an empty list if nothing found
     """
@@ -201,10 +200,9 @@ def get_mouse_frame_and_channel(context, event):
 def is_in_range(context, sequence, start, end):
     """
     Checks if a single sequence's start or end is in the range
-
     Args:
-    - sequence: the sequence to check for
-    - start, end: the start and end frames
+        - sequence: the sequence to check for
+        - start, end: the start and end frames
     Returns True if the sequence is within the range, False otherwise
     """
     s_start = sequence.frame_final_start
@@ -225,47 +223,32 @@ def set_preview_range(context, start, end):
     scene.frame_preview_end = end
 
 
-def slice_selection(context, sequences):
+def slice_selection(context, sequences, range_block=0):
     """
     Takes a list of sequences and breaks it down
     into multiple lists of connected sequences
-
     Returns a list of lists of sequences,
     each list corresponding to a block of sequences
     that are connected in time and sorted by frame_final_start
     """
-    # Find when 2 sequences are not connected in time
     if not sequences:
         return []
 
-    break_ids = [0]
+    # Indicates the index number of the lists from the "broken_selection" list
+    index = -1
+    block_end = 0
+    broken_selection = []
     sorted_sequences = sorted(sequences, key=attrgetter("frame_final_start"))
-    last_sequence = sorted_sequences[0]
-    last_biggest_frame_end = last_sequence.frame_final_end
-    index = 0
+
     for s in sorted_sequences:
-        if s.frame_final_start > last_biggest_frame_end + 1:
-            break_ids.append(index)
-        last_biggest_frame_end = max(last_biggest_frame_end, s.frame_final_end)
-        last_sequence = s
-        index += 1
-
-    # Create lists
-    break_ids.append(len(sorted_sequences))
-    cuts_count = len(break_ids) - 1
-    broken_selection = []
-    index = 0
-    while index < cuts_count:
-        temp_list = []
-        index_range = range(break_ids[index], break_ids[index + 1] - 1)
-        if len(index_range) == 0:
-            temp_list.append(sorted_sequences[break_ids[index]])
-        else:
-            for counter in range(break_ids[index], break_ids[index + 1]):
-                temp_list.append(sorted_sequences[counter])
-        if temp_list:
-            broken_selection.append(temp_list)
-        index += 1
+        if not broken_selection or (block_end + 1 + range_block < s.frame_final_start):
+            broken_selection.append([s])
+            block_end = s.frame_final_end
+            index += 1
+            continue
+        block_end = max(block_end, s.frame_final_end)
+        broken_selection[index].append(s)
+
     return broken_selection
 
 
@@ -278,8 +261,10 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]):
     trim_end = max(frame_start, frame_end)
 
     to_trim = [s for s in to_trim if s.type in SequenceTypes.CUTABLE]
+    initial_selection = context.selected_sequences
 
     for s in to_trim:
+        strips_in_target_channel = []
         # Cut strip longer than the trim range in three
         is_strip_longer_than_trim_range = (
             s.frame_final_start < trim_start and s.frame_final_end > trim_end
@@ -290,6 +275,13 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]):
             bpy.ops.sequencer.split(frame=trim_start, type="SOFT", side="RIGHT")
             bpy.ops.sequencer.split(frame=trim_end, type="SOFT", side="LEFT")
             to_delete.append(context.selected_sequences[0])
+            
+            for c in context.sequences:
+                if c.channel == s.channel:
+                    strips_in_target_channel.append(c)
+            
+            if s in initial_selection:
+                initial_selection.append(strips_in_target_channel[0])
             continue
 
         # Resize strips that overlap the trim range
@@ -298,20 +290,12 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]):
         elif s.frame_final_end > trim_start and s.frame_final_start < trim_start:
             s.frame_final_end = trim_start
 
-    delete_strips(to_delete)
-    return {"FINISHED"}
-
-
-def delete_strips(to_delete):
-    """
-    Deletes the list of sequences `to_delete`
-    """
-    if not to_delete:
-        return
-    bpy.ops.sequencer.select_all(action="DESELECT")
     for s in to_delete:
+        bpy.context.sequences.remove(s)
+
+    for s in initial_selection:
         s.select = True
-    bpy.ops.sequencer.delete()
+    return {"FINISHED"}
 
 
 def find_closest_surrounding_cuts(context, frame):
@@ -378,30 +362,7 @@ def ripple_move(context, sequences, duration_frames, delete=False):
     else:
         to_ripple = set(to_ripple + sequences)
 
-    # Use the built-in seq_slide operator to move strips, for best performances
-    initial_selection = context.selected_sequences
-    bpy.ops.sequencer.select_all(action="DESELECT")
-    for s in to_ripple:
-        s.select = True
-    bpy.ops.transform.seq_slide(value=(duration_frames, 0))
-    bpy.ops.sequencer.select_all(action="DESELECT")
-    for s in initial_selection:
-        s.select = True
-
-
-def apply_time_offset(context, sequences=[], offset=0):
-    """Offsets a list of sequences in time using bpy.ops.transform.seq_slide. Mutates and restores the
-    user's selection. Use this function to ensure maximum performances and avoid having to figure
-    out the logic to move strips in the right order.
-    """
-    selection = context.selected_sequences
-    bpy.ops.sequencer.select_all(action="DESELECT")
-    for s in sequences:
-        s.select = True
-    bpy.ops.transform.seq_slide(value=(offset, 0))
-    bpy.ops.sequencer.select_all(action="DESELECT")
-    for s in selection:
-        s.select = True
+    move_selection(context, to_ripple, duration_frames, 0)
 
 
 def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=True):
@@ -409,18 +370,18 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru
     Returns a tuple of two lists: (strips_inside_range, strips_overlapping_range)
     strips_inside_range are strips entirely contained in the frame range.
     strips_overlapping_range are strips that only overlap the frame range.
-
     Args:
-    - frame_start, the start of the frame range
-    - frame_end, the end of the frame range
-    - sequences (optional): only work with these sequences.
-    If it doesn't receive any, the function works with all the sequences in the current context
-    - find_overlapping (optional): find and return a list of strips that overlap the
+        - frame_start, the start of the frame range
+        - frame_end, the end of the frame range
+        - sequences (optional): only work with these sequences.
+        If it doesn't receive any, the function works with all the sequences in the current context
+        - find_overlapping (optional): find and return a list of strips that overlap the
         frame range
-
     """
     strips_inside_range = []
     strips_overlapping_range = []
+    if not sequences:
+        sequences = bpy.context.sequences
     for s in sequences:
         if (
             frame_start <= s.frame_final_start <= frame_end
@@ -429,8 +390,8 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru
             strips_inside_range.append(s)
         elif find_overlapping:
             if (
-                frame_start <= s.frame_final_end <= frame_end
-                or frame_start <= s.frame_final_start <= frame_end
+                frame_start < s.frame_final_end <= frame_end
+                or frame_start <= s.frame_final_start < frame_end
             ):
                 strips_overlapping_range.append(s)
 
@@ -438,3 +399,19 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru
             if s.frame_final_start < frame_start and s.frame_final_end > frame_end:
                 strips_overlapping_range.append(s)
     return strips_inside_range, strips_overlapping_range
+
+
+def move_selection(context, sequences, frame_offset, channel_offset=0):
+    """Offsets the selected `sequences` horizontally and vertically and preserves
+    the current selected sequences.
+    """
+    if not sequences:
+        return
+    initial_selection = context.selected_sequences
+    bpy.ops.sequencer.select_all(action="DESELECT")
+    for s in sequences:
+        s.select = True
+    bpy.ops.transform.seq_slide(value=(frame_offset, channel_offset))
+    bpy.ops.sequencer.select_all(action="DESELECT")
+    for s in initial_selection:
+        s.select = True
diff --git a/power_sequencer/operators/value_offset.py b/power_sequencer/operators/value_offset.py
new file mode 100644
index 0000000000000000000000000000000000000000..debd2ef6535d207faebb2d5fb0db2cb228a7542c
--- /dev/null
+++ b/power_sequencer/operators/value_offset.py
@@ -0,0 +1,94 @@
+#
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
+#
+# This file is part of Power Sequencer.
+#
+# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
+# GNU General Public License as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with Power Sequencer. If
+# not, see <https://www.gnu.org/licenses/>.
+#
+import bpy
+
+from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
+from .utils.functions import convert_duration_to_frames
+
+
+class POWER_SEQUENCER_OT_value_offset(bpy.types.Operator):
+    """Instantly offset selected strips, either using frames or seconds. Allows to
+    nudge the selection quickly, using keyboard shortcuts.
+    """
+
+    doc = {
+        "name": doc_name(__qualname__),
+        "demo": "",
+        "description": doc_description(__doc__),
+        "shortcuts": [
+            (
+                {"type": "LEFT_ARROW", "value": "PRESS", "shift": True, "alt": True},
+                {"direction": "left"},
+                "Offset the selection to the left.",
+            ),
+            (
+                {"type": "RIGHT_ARROW", "value": "PRESS", "shift": True, "alt": True},
+                {"direction": "right"},
+                "Offset the selection to the right.",
+            ),
+        ],
+        "keymap": "Sequencer",
+    }
+    bl_idname = doc_idname(__qualname__)
+    bl_label = doc["name"]
+    bl_description = doc_brief(doc["description"])
+    bl_options = {"REGISTER", "UNDO"}
+
+    direction: bpy.props.EnumProperty(
+        items=[
+            ("left", "left", "Move the selection to the left"),
+            ("right", "right", "Move the selection to the right"),
+        ],
+        name="Direction",
+        description="Move the selection given frames or seconds",
+        default="right",
+        options={"HIDDEN"},
+    )
+    value_type: bpy.props.EnumProperty(
+        items=[
+            ("seconds", "Seconds", "Move with the value as seconds"),
+            ("frames", "Frames", "Move with the value as frames"),
+        ],
+        name="Value Type",
+        description="Toggle between offset in frames or seconds",
+        default="seconds",
+    )
+    offset: bpy.props.FloatProperty(
+        name="Offset",
+        description="Offset amount to apply",
+        default=1.0,
+        step=5,
+        precision=3,
+    )
+
+    @classmethod
+    def poll(cls, context):
+        return context.selected_sequences
+
+    def invoke(self, context, event):
+        self.offset = abs(self.offset)
+        if self.direction == "left":
+            self.offset *= -1.0
+        return self.execute(context)
+
+    def execute(self, context):
+        offset_frames = (
+            convert_duration_to_frames(context, self.offset)
+            if self.value_type == "seconds"
+            else self.offset
+        )
+        return bpy.ops.transform.seq_slide(value=(offset_frames, 0))
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/__init__.py b/power_sequencer/scripts/BPSProxy/bpsproxy/__init__.py
deleted file mode 100644
index f14cfb6aacdac444a4541e30dbad103f17d168d4..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/__main__.py b/power_sequencer/scripts/BPSProxy/bpsproxy/__main__.py
deleted file mode 100644
index d8f2520405dfb3c5734956a70e4780ccc0958a1d..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/__main__.py
+++ /dev/null
@@ -1,171 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-"""
-Tool to render video proxies using FFMPEG
-Offers mp4 and webm options
-"""
-import argparse as ap
-import glob as g
-import logging as lg
-import os.path as osp
-import sys
-from itertools import compress, starmap, tee
-
-from .call import call, call_makedirs
-from .commands import get_commands, get_commands_vi
-from .config import CONFIG as C
-from .config import LOGGER, LOGLEV
-from .utils import checktools, printw, printd, prints, ToolError
-
-
-def find_files(
-    directory=".", ignored_directory=C["proxy_directory"], extensions=C["extensions"]["all"]
-):
-    """
-    Find files to process.
-
-    Parameters
-    ----------
-    directory: str
-    Working directory.
-    ignored_directory: str
-    Don't check for files in this directory. By default `BL_proxy`.
-    extensions: set(str)
-    Set of file extensions for filtering the directory tree.
-
-    Returns
-    -------
-    out: list(str)
-    List of file paths to be processed.
-    """
-    if not osp.isdir(directory):
-        raise ValueError(("The given path '{}' is not a valid directory.".format(directory)))
-    xs = g.iglob("{}/**".format(osp.abspath(directory)), recursive=True)
-    xs = filter(lambda x: osp.isfile(x), xs)
-    xs = filter(lambda x: ignored_directory not in osp.dirname(x), xs)
-    xs = [x for x in xs if osp.splitext(x)[1].lower() in extensions]
-    return xs
-
-
-def parse_arguments(cfg):
-    """
-    Uses `argparse` to parse the command line arguments.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-
-    Returns
-    -------
-    out: Namespace
-    Command line arguments.
-    """
-    p = ap.ArgumentParser(description="Create proxies for Blender VSE using FFMPEG.")
-    p.add_argument(
-        "working_directory",
-        nargs="?",
-        default=".",
-        help="The directory containing media to create proxies for",
-    )
-    p.add_argument(
-        "-p",
-        "--preset",
-        default="mp4",
-        choices=cfg["presets"],
-        help="a preset name for proxy encoding",
-    )
-    p.add_argument(
-        "-s",
-        "--sizes",
-        nargs="+",
-        type=int,
-        default=[25],
-        choices=cfg["proxy_sizes"],
-        help="A list of sizes of the proxies to render, either 25, 50, or 100",
-    )
-    p.add_argument(
-        "-v", "--verbose", action="count", default=0, help="Increase verbosity level (eg. -vvv)."
-    )
-    p.add_argument(
-        "--dry-run",
-        action="store_true",
-        help=(
-            "Run the script without actual rendering or creating files and"
-            " folders. For DEBUGGING purposes"
-        ),
-    )
-
-    clargs = p.parse_args()
-    # normalize directory
-    clargs.working_directory = osp.abspath(clargs.working_directory)
-    # --dry-run implies maximum verbosity level
-    clargs.verbose = 99999 if clargs.dry_run else clargs.verbose
-    return clargs
-
-
-def main():
-    """
-    Script entry point.
-    """
-    tools = ["ffmpeg", "ffprobe"]
-    try:
-        # get command line arguments and set log level
-        clargs = parse_arguments(C)
-        lg.basicConfig(level=LOGLEV[min(clargs.verbose, len(LOGLEV) - 1)])
-
-        # log basic command line arguments
-        clargs.dry_run and LOGGER.info("DRY-RUN")
-        LOGGER.info("WORKING-DIRECTORY :: {}".format(clargs.working_directory))
-        LOGGER.info("PRESET :: {}".format(clargs.preset))
-        LOGGER.info("SIZES :: {}".format(clargs.sizes))
-
-        # check for external dependencies
-        checktools(tools)
-
-        # find files to process
-        path_i = find_files(clargs.working_directory)
-        kwargs = {"path_i": path_i}
-
-        printw(C, "Creating directories if necessary")
-        call_makedirs(C, clargs, **kwargs)
-
-        printw(C, "Checking for existing proxies")
-        cmds = tee(get_commands(C, clargs, what="check", **kwargs))
-        stdouts = call(C, clargs, cmds=cmds[0], check=False, shell=True, **kwargs)
-        checks = map(lambda s: s.strip().split(), stdouts)
-        checks = starmap(lambda fst, *tail: not all(fst == t for t in tail), checks)
-        kwargs["path_i"] = list(compress(kwargs["path_i"], checks))
-
-        if len(kwargs["path_i"]) != 0:
-            printw(C, "Processing", s="\n")
-            cmds = get_commands_vi(C, clargs, **kwargs)
-            call(C, clargs, cmds=cmds, **kwargs)
-        else:
-            printd(C, "All proxies exist or no files found, nothing to process", s="\n")
-        printd(C, "Done")
-    except (ToolError, ValueError) as e:
-        LOGGER.error(e)
-        prints(C, "Exiting")
-    except KeyboardInterrupt:
-        prints(C, "DirtyInterrupt. Exiting", s="\n\n")
-        sys.exit()
-
-
-# this is so it can be ran as a module: `python3 -m bpsrender` (for testing)
-if __name__ == "__main__":
-    main()
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/call.py b/power_sequencer/scripts/BPSProxy/bpsproxy/call.py
deleted file mode 100644
index dd74e3a8250879cba51ad9d2ac090f4bb9f08102..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/call.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-# import multiprocessing as mp
-import os
-import subprocess as sp
-import sys
-
-from functools import partial
-from itertools import chain, tee
-from tqdm import tqdm
-from .config import LOGGER
-from .utils import get_dir, kickstart
-
-WINDOWS = ("win32", "cygwin")
-
-
-def call_makedirs(cfg, clargs, **kwargs):
-    """
-    Make BL_proxy directories if necessary.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments.
-    kwargs: dict
-    MANDATORY: path_i
-    Dictionary with additional information from previous step.
-    """
-    path_i = kwargs["path_i"]
-    path_d = map(partial(get_dir, cfg, clargs, **kwargs), path_i)
-    path_d = tee(chain(*path_d))
-    kickstart(map(lambda p: LOGGER.info("Directory @ {}".format(p)), path_d[0]))
-    if clargs.dry_run:
-        return
-    path_d = (os.makedirs(p, exist_ok=True) for p in path_d[1])
-    kickstart(path_d)
-
-
-def call(cfg, clargs, *, cmds, **kwargs):
-    """
-    Generic subprocess calls.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments.
-    cmds: iter(tuple(str))
-    kwargs: dict
-    MANDATORY: path_i
-    Dictionary with additional information from previous step.
-
-    Returns
-    -------
-    out: str
-    Stdout & Stderr gathered from subprocess call.
-    """
-    kwargs_s = {
-        "stdout": sp.PIPE,
-        "stderr": sp.STDOUT,
-        "universal_newlines": True,
-        "check": kwargs.get("check", True),
-        "shell": kwargs.get("shell", False),
-        "creationflags": sp.CREATE_NEW_PROCESS_GROUP if sys.platform in WINDOWS else 0,
-    }
-    if kwargs_s["shell"]:
-        cmds = map(lambda cmd: (cmd[0], " ".join(cmd[1])), cmds)
-    cmds = tee(cmds)
-    kickstart(map(lambda cmd: LOGGER.debug("CALL :: {}".format(cmd[1])), cmds[0]))
-    if clargs.dry_run:
-        return []
-    n = len(kwargs["path_i"])
-    ps = tqdm(
-        map(lambda cmd: sp.run(cmd[1], **kwargs_s), cmds[1]),
-        total=n,
-        unit="file" if n == 1 else "files",
-    )
-    return [p.stdout for p in ps]
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/commands.py b/power_sequencer/scripts/BPSProxy/bpsproxy/commands.py
deleted file mode 100644
index 4a28241b9a47cf1dfcbe5662e5a3859d50f5fbd1..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/commands.py
+++ /dev/null
@@ -1,190 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import os.path as osp
-import shlex as sl
-from itertools import chain
-from .utils import get_path
-
-
-def get_commands_check(cfg, clargs, **kwargs):
-    """
-    ffprobe subprocess command generation.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments.
-    cmds: iter(tuple(str))
-    kwargs: dict
-    MANDATORY: path_i_1, path_o_1
-    Dictionary with additional information from previous step.
-
-    Returns
-    -------
-    out: iter(tuple(str))
-    Iterator containing commands.
-    """
-    cmd = (
-        "ffprobe -v error -select_streams v:0 -show_entries stream=nb_frames -of"
-        " default=noprint_wrappers=1:nokey=1 '{file}'"
-    )
-    out = map(lambda s: kwargs["path_o_1"].format(size=s), clargs.sizes)
-    out = map(lambda f: cmd.format(file=f), out)
-    out = sl.split(cmd.format(file=kwargs["path_i_1"]) + " && " + " && ".join(out))
-    return iter((out,))
-
-
-def get_commands_image_1(cfg, clargs, **kwargs):
-    """
-    ffmpeg subprocess command generation for processing an image.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments.
-    cmds: iter(tuple(str))
-    kwargs: dict
-    MANDATORY: path_i_1, path_o_1
-    Dictionary with additional information from previous step.
-
-    Returns
-    -------
-    out: iter(tuple(str))
-    Iterator containing commands.
-    """
-    cmd = "ffmpeg -hwaccel auto -y -v quiet -stats -i '{path_i_1}' {common_all}"
-    common = "-f apng -filter:v scale=iw*{size}:ih*{size} '{path_o_1}'"
-    common_all = map(lambda s: kwargs["path_o_1"].format(size=s), clargs.sizes)
-    common_all = map(
-        lambda s: common.format(size=s[0] / 100.0, path_o_1=s[1]), zip(clargs.sizes, common_all)
-    )
-    common_all = " ".join(common_all)
-    out = sl.split(cmd.format(path_i_1=kwargs["path_i_1"], common_all=common_all))
-    return iter((out,))
-
-
-def get_commands_video_1(cfg, clargs, **kwargs):
-    """
-    ffmpeg subprocess command generation for processing a video.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments.
-    cmds: iter(tuple(str))
-    kwargs: dict
-    MANDATORY: path_i_1, path_o_1
-    Dictionary with additional information from previous step.
-
-    Returns
-    -------
-    out: iter(tuple(str))
-    Iterator containing commands.
-    """
-    cmd = "ffmpeg -hwaccel auto -y -v quiet -stats -noautorotate -i '{path_i_1}' {common_all}"
-    common = (
-        "-pix_fmt yuv420p"
-        " -g 1"
-        " -sn -an"
-        " -vf colormatrix=bt601:bt709"
-        " -vf scale=ceil(iw*{size}/2)*2:ceil(ih*{size}/2)*2"
-        " {preset}"
-        " '{path_o_1}'"
-    )
-    common_all = map(lambda s: kwargs["path_o_1"].format(size=s), clargs.sizes)
-    common_all = map(
-        lambda s: common.format(
-            preset=cfg["presets"][clargs.preset], size=s[0] / 100.0, path_o_1=s[1]
-        ),
-        zip(clargs.sizes, common_all),
-    )
-    common_all = " ".join(common_all)
-    out = sl.split(cmd.format(path_i_1=kwargs["path_i_1"], common_all=common_all))
-    return iter((out,))
-
-
-def get_commands(cfg, clargs, *, what, **kwargs):
-    """
-    Delegates the creation of commands lists to appropriate functions based on `what` parameter.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments.
-    cmds: iter(tuple(str))
-    what: str
-    Determines the returned value (see: Returns[out]).
-    kwargs: dict
-    MANDATORY: path_i
-    Dictionary with additional information from previous step.
-
-    Returns
-    -------
-    out: iter(tuple(str, tuple(str)))
-    An iterator with the 1st element as a tag (the `what` parameter) and the 2nd
-    element as the iterator of the actual commands.
-    """
-    get_commands_f = {
-        "video": get_commands_video_1,
-        "image": get_commands_image_1,
-        "check": get_commands_check,
-    }
-    ps = (
-        kwargs["path_i"]
-        if what not in cfg["extensions"]
-        else filter(
-            lambda p: osp.splitext(p)[1].lower() in cfg["extensions"][what], kwargs["path_i"]
-        )
-    )
-    ps = map(lambda p: (p, get_path(cfg, clargs, p, **kwargs)), ps)
-    out = chain.from_iterable(
-        map(lambda p: get_commands_f[what](cfg, clargs, path_i_1=p[0], path_o_1=p[1], **kwargs), ps)
-    )
-    return map(lambda c: (what, c), out)
-
-
-def get_commands_vi(cfg, clargs, **kwargs):
-    """
-    Delegates the creation of commands lists to appropriate functions for video/image processing.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments.
-    cmds: iter(tuple(str))
-    kwargs: dict
-    MANDATORY: path_i_1, path_o_1
-    Dictionary with additional information from previous step.
-
-    Returns
-    -------
-    out: iter(tuple(str, tuple(str)))
-    An iterator with the 1st element as a tag (the `what` parameter) and the 2nd
-    element as the iterator of the actual commands.
-    """
-    ws = filter(lambda x: x is not "all", cfg["extensions"])
-    return chain.from_iterable(map(lambda w: get_commands(cfg, clargs, what=w, **kwargs), ws))
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/config.py b/power_sequencer/scripts/BPSProxy/bpsproxy/config.py
deleted file mode 100644
index 28554e8580b14af410b6761341ab7cde1823373a..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/config.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import multiprocessing as mp
-from itertools import chain
-import logging as lg
-
-
-CONFIG = {
-    "logger": "BPS",
-    "proxy_directory": "BL_proxy",
-    "proxy_sizes": (25, 50, 100),
-    "extensions": {
-        "video": {".mp4", ".mkv", ".mov", ".flv", ".mts"},
-        "image": {".png", ".jpg", ".jpeg"},
-    },
-    "presets": {
-        "webm": "-c:v libvpx -crf 25 -speed 16 -threads {}".format(str(mp.cpu_count())),
-        "mp4": "-c:v libx264 -crf 25 -preset faster -tune fastdecode",
-        "nvenc": "-c:v h264_nvenc -qp 25 -preset fast",
-    },
-    "pre": {"work": "»", "done": "•", "skip": "~"},
-}
-CONFIG["extensions"]["all"] = set(chain(*CONFIG["extensions"].values()))
-
-LOGGER = lg.getLogger(CONFIG["logger"])
-LOGLEV = [lg.INFO, lg.DEBUG]
-LOGLEV = [None] + sorted(LOGLEV, reverse=True)
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/utils.py b/power_sequencer/scripts/BPSProxy/bpsproxy/utils.py
deleted file mode 100644
index 832a0bebd454073e6e10d48b78c6890d370802ef..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/utils.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-"""
-Collection of utility functions, class-independent
-"""
-import os.path as osp
-from collections import deque
-from shutil import which
-
-
-class ToolError(Exception):
-    """Raised if external dependencies aren't found on system.
-    """
-
-    pass
-
-
-def checktools(tools):
-    tools = [(t, which(t) or "") for t in tools]
-    check = {"tools": tools, "test": all(map(lambda x: x[1], tools))}
-    if not check["test"]:
-        msg = ["BPSProxy couldn't find external dependencies:"]
-        msg += [
-            "[{check}] {tool}: {path}".format(
-                check="v" if path is not "" else "X", tool=tool, path=path or "NOT FOUND"
-            )
-            for tool, path in check["tools"]
-        ]
-        msg += [
-            (
-                "Check if you have them properly installed and available in the PATH"
-                " environemnt variable."
-            )
-        ]
-        raise ToolError("\n".join(msg))
-
-
-def get_path_video(cfg, clargs, path, **kwargs):
-    return osp.join(
-        osp.dirname(path), cfg["proxy_directory"], osp.basename(path), "proxy_{size}.avi"
-    )
-
-
-def get_path_image(cfg, clargs, path, **kwargs):
-    return osp.join(
-        osp.dirname(path),
-        cfg["proxy_directory"],
-        "images",
-        "{size}",
-        "{file}_proxy.jpg".format(file=osp.basename(path)),
-    )
-
-
-def get_path(cfg, clargs, path, **kwargs):
-    get_path_f = {"video": get_path_video, "image": get_path_image}
-    what = what_vi(cfg, clargs, path, **kwargs)
-    return get_path_f[what](cfg, clargs, path, **kwargs)
-
-
-def get_dir_video(cfg, clargs, path, **kwargs):
-    return iter((osp.join(osp.dirname(path), cfg["proxy_directory"], osp.basename(path)),))
-
-
-def get_dir_image(cfg, clargs, path, **kwargs):
-    ps = osp.join(osp.dirname(path), cfg["proxy_directory"], "images", "{size}")
-    return map(lambda s: ps.format(size=s), clargs.sizes)
-
-
-def get_dir(cfg, clargs, path, **kwargs):
-    get_dir_f = {"video": get_dir_video, "image": get_dir_image}
-    what = what_vi(cfg, clargs, path, **kwargs)
-    return get_dir_f[what](cfg, clargs, path, **kwargs)
-
-
-def what_vi(cfg, clargs, p, **kwargs):
-    return "video" if osp.splitext(p)[1].lower() in cfg["extensions"]["video"] else "image"
-
-
-def kickstart(it):
-    deque(it, maxlen=0)
-
-
-def printw(cfg, text, s="\n", e="...", p="", **kwargs):
-    p = p or cfg["pre"]["work"]
-    print("{s}{p} {}{e}".format(text, s=s, e=e, p=p), **kwargs)
-
-
-def printd(cfg, text, s="", e=".", p="", **kwargs):
-    p = p or cfg["pre"]["done"]
-    printw(cfg, text, s=s, e=e, p=p, **kwargs)
-
-
-def prints(cfg, text, s="", e=".", p="", **kwargs):
-    p = p or cfg["pre"]["skip"]
-    printw(cfg, text, s=s, e=e, p=p, **kwargs)
diff --git a/power_sequencer/scripts/BPSProxy/setup.py b/power_sequencer/scripts/BPSProxy/setup.py
deleted file mode 100644
index 9676ef60296747ea61c38f680256dd1bfe2f2065..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSProxy/setup.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-from setuptools import setup
-
-
-def readme():
-    with open("README.md") as f:
-        return f.read()
-
-
-if __name__ == "__main__":
-    setup(
-        name="bpsproxy",
-        version="0.2.1",
-        description="Blender Power Sequencer proxy generator tool",
-        long_description=readme(),
-        long_description_content_type="text/markdown",
-        classifiers=[
-            "Development Status :: 5 - Production/Stable",
-            "Environment :: Console",
-            "Intended Audience :: End Users/Desktop",
-            "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
-            "Natural Language :: English",
-            "Programming Language :: Python :: 3.3",
-            "Programming Language :: Python :: 3.4",
-            "Programming Language :: Python :: 3.5",
-            "Programming Language :: Python :: 3.6",
-            "Programming Language :: Python :: 3.7",
-            "Programming Language :: Python :: 3",
-            "Topic :: Multimedia :: Video",
-            "Topic :: Utilities",
-        ],
-        url="https://github.com/GDquest/BPSProxy",
-        keywords="blender proxy vse sequence editor productivity",
-        author="Răzvan C. Rădulescu",
-        author_email="razcore.art@gmail.com",
-        license="GPLv3",
-        packages=["bpsproxy"],
-        install_requires=["tqdm"],
-        zip_safe=False,
-        entry_points={"console_scripts": ["bpsproxy=bpsproxy.__main__:main"]},
-        include_package_data=True,
-    )
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/__init__.py b/power_sequencer/scripts/BPSRender/bpsrender/__init__.py
deleted file mode 100644
index f14cfb6aacdac444a4541e30dbad103f17d168d4..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/__main__.py b/power_sequencer/scripts/BPSRender/bpsrender/__main__.py
deleted file mode 100644
index 07c84fe2d8ff9891115db3da073758fe2261767b..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/__main__.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-"""
-Renders videos edited in Blender 3D's Video Sequence Editor using multiple CPU
-cores. Original script by Justin Warren:
-https://github.com/sciactive/pulverize/blob/master/pulverize.py
-Modified by sudopluto (Pranav Sharma), gdquest (Nathan Lovato) and
-razcore (Razvan Radulescu)
-
-Under GPLv3 license
-"""
-import argparse as ap
-import os.path as osp
-import sys
-from functools import partial
-
-from .calls import call
-from .config import CONFIG as C
-from .config import LOGGER
-from .helpers import BSError, ToolError, checktools, kickstart, prints
-from .setup import setup
-
-# https://github.com/mikeycal/the-video-editors-render-script-for-blender#configuring-the-script
-# there seems no easy way to grab the ram usage in a mulitplatform way
-# without writing platform dependent code, or by using a python module
-
-# Most popluar config is 4 cores, 8 GB ram, this is the default for the script
-# https://store.steampowered.com/hwsurvey/
-
-
-def parse_arguments(cfg):
-    """
-    Uses `argparse` to parse the command line arguments.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-
-    Returns
-    -------
-    out: Namespace
-    Command line arguments (normalized).
-    """
-    p = ap.ArgumentParser(
-        description="Multi-process Blender VSE rendering - will attempt to"
-        " create a folder called `render` inside of the folder"
-        " containing `blendfile`. Insider `render` another folder called"
-        " `parts` will be created for storing temporary files. These files"
-        " will be joined together as the last step to produce the final"
-        " render which will be stored inside `render` and it will have the"
-        " same name as `blendfile`"
-    )
-    p.add_argument(
-        "-o",
-        "--output",
-        default=".",
-        help="Output folder (will contain a `bpsrender` temp folder for" "rendering parts).",
-    )
-    p.add_argument(
-        "-w",
-        "--workers",
-        type=int,
-        default=cfg["cpu_count"],
-        help="Number of workers in the pool (for video rendering).",
-    )
-    p.add_argument(
-        "-v", "--verbose", action="count", default=0, help="Increase verbosity level (eg. -vvv)."
-    )
-    p.add_argument(
-        "--dry-run",
-        action="store_true",
-        help=(
-            "Run the script without actual rendering or creating files and"
-            " folders. For DEBUGGING purposes"
-        ),
-    )
-    p.add_argument("-s", "--start", type=int, default=None, help="Start frame")
-    p.add_argument("-e", "--end", type=int, default=None, help="End frame")
-    p.add_argument(
-        "-m", "--mixdown-only", action="store_true", help="ONLY render the audio MIXDOWN"
-    )
-    p.add_argument(
-        "-c",
-        "--concatenate-only",
-        action="store_true",
-        help="ONLY CONCATENATE the (already) available video chunks",
-    )
-    p.add_argument(
-        "-d",
-        "--video-only",
-        action="store_true",
-        help="ONLY render the VIDEO (implies --concatenate-only).",
-    )
-    p.add_argument(
-        "-j",
-        "--join-only",
-        action="store_true",
-        help="ONLY JOIN the mixdown with the video. This will produce the" " final render",
-    )
-    p.add_argument("blendfile", help="Blender project file to render.")
-
-    clargs = p.parse_args()
-    clargs.blendfile = osp.abspath(clargs.blendfile)
-    clargs.output = osp.abspath(clargs.output)
-    # --video-only implies --concatenate-only
-    clargs.concatenate_only = clargs.concatenate_only or clargs.video_only
-    # --dry-run implies maximum verbosity level
-    clargs.verbose = 99999 if clargs.dry_run else clargs.verbose
-    return clargs
-
-
-def main():
-    """
-    Script entry point.
-    """
-    tools = ["blender", "ffmpeg"]
-    try:
-        clargs = parse_arguments(C)
-        checktools(tools)
-        cmds, kwargs = setup(C, clargs)
-        kickstart(map(partial(call, C, clargs, **kwargs), cmds))
-    except (BSError, ToolError) as e:
-        LOGGER.error(e)
-    except KeyboardInterrupt:
-        # TODO: add actual clean up code
-        prints(C, "DirtyInterrupt. Exiting", s="\n\n", e="...")
-        sys.exit()
-
-
-# this is so it can be ran as a module: `python3 -m bpsrender` (for testing)
-if __name__ == "__main__":
-    main()
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/bscripts/mixdown.py b/power_sequencer/scripts/BPSRender/bpsrender/bscripts/mixdown.py
deleted file mode 100644
index b324333a64c101a7d68c9d05b68f1c2a92e9ceec..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/bscripts/mixdown.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import bpy
-import os.path as osp
-import sys
-
-if __name__ == "__main__":
-    for strip in bpy.context.scene.sequence_editor.sequences_all:
-        if strip.type == "META":
-            continue
-        if strip.type != "SOUND":
-            strip.mute = True
-
-    path = sys.argv[-1]
-    ext = osp.splitext(path)[1][1:].upper()
-    bpy.ops.sound.mixdown(filepath=path, check_existing=False, container=ext, codec=ext)
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/bscripts/probe.py b/power_sequencer/scripts/BPSRender/bpsrender/bscripts/probe.py
deleted file mode 100644
index 92cffa60b2449f43734d883d8cc41640debd39ff..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/bscripts/probe.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import bpy
-
-EXT = {
-    "AVI_JPEG": ".avi",
-    "AVI_RAW": ".avi",
-    "FFMPEG": {"MKV": ".mkv", "OGG": ".ogv", "QUICKTIME": ".mov", "AVI": ".avi", "MPEG4": ".mp4"},
-}
-
-scene = bpy.context.scene
-
-ext = EXT.get(scene.render.image_settings.file_format, "UNDEFINED")
-if scene.render.image_settings.file_format == "FFMPEG":
-    ext = ext[scene.render.ffmpeg.format]
-print("\nBPS:{} {} {}\n".format(scene.frame_start, scene.frame_end, ext))
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/bscripts/video.py b/power_sequencer/scripts/BPSRender/bpsrender/bscripts/video.py
deleted file mode 100644
index 590e73eb11c7db07e577af03620c75c169b477a3..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/bscripts/video.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import bpy
-
-bpy.context.scene.render.ffmpeg.audio_codec = "NONE"
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/calls.py b/power_sequencer/scripts/BPSRender/bpsrender/calls.py
deleted file mode 100644
index 5a223dd6769a770191a4484a438665c513b400df..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/calls.py
+++ /dev/null
@@ -1,410 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-# IMPURE
-import multiprocessing as mp
-import os
-import signal as sig
-import subprocess as sp
-from functools import partial, reduce
-from itertools import chain, islice, starmap, tee
-from multiprocessing import Queue
-
-from tqdm import tqdm
-
-from .config import LOGGER
-from .helpers import BSError, checkblender, kickstart, printd, prints, printw
-
-
-def chunk_frames(cfg, clargs, cmds, **kwargs):
-    """
-    Recover the chunk start/end frames from the constructed commands for the
-    video step. This is necessary to preserve purity until later steps.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`.
-    kwargs: dict
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter(tuple)
-    Start/end pairs of frames corresponding to the chunk commands created at
-    the video step.
-    """
-    out = map(lambda x: (x, islice(x, 1, None)), cmds)
-    out = map(lambda x: zip(*x), out)
-    out = map(lambda x: filter(lambda y: y[0] in ("-s", "-e"), x), out)
-    out = map(lambda x: map(lambda y: int(y[1]), x), out)
-    out = map(lambda x: reduce(lambda acc, y: acc + (y,), x, ()), out)
-    return out
-
-
-def append_chunks_file(cfg, clargs, cmds, **kwargs):
-    """
-    IMPURE
-    Helper function for creating the chunks file that will be used by `ffmpeg`
-    to concatenate the chunks into one video file.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`.
-    kwargs: dict
-    MANDATORY w_frame_start, w_frame_end, ext
-    Dictionary with additional information from the setup step.
-    """
-    with open(kwargs["chunks_file_path"], "a") as f:
-        for fs, fe in chunk_frames(cfg, clargs, cmds, **kwargs):
-            f.write(
-                "file '{rcp}{fs}-{fe}{ext}'\n".format(
-                    rcp=kwargs["render_chunk_path"].rstrip("#"),
-                    fs="{fs:0{frame_pad}d}".format(fs=fs, **cfg),
-                    fe="{fe:0{frame_pad}d}".format(fe=fe, **cfg),
-                    **kwargs
-                )
-            )
-
-
-def call_probe(cfg, clargs, cmds, **kwargs):
-    """
-    IMPURE
-    Probe `clargs.blendfile` for frame start, frame end and extension (for
-    video only).
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`.
-    kwargs: dict
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: dict
-    Dictionary with info extracted from `clargs.blendfile`, namely: start
-    frame, end frame and extension (only useful for video step).
-    """
-    kwargs_p = {"stdout": sp.PIPE, "stderr": sp.STDOUT, "universal_newlines": True}
-
-    printw(cfg, "Probing")
-    printw(cfg, "Input(blend) @ {}".format(clargs.blendfile), s="")
-    frame_start, frame_end, ext = (0, 0, "")
-    if not clargs.dry_run:
-        with sp.Popen(next(cmds), **kwargs_p) as cp:
-            try:
-                tmp = map(partial(checkblender, "PROBE", [cfg["probe_py"]], cp), cp.stdout)
-                tmp = filter(lambda x: x.startswith("BPS"), tmp)
-                tmp = map(lambda x: x[4:].strip().split(), tmp)
-                frame_start, frame_end, ext = chain(*tmp)
-            except BSError as e:
-                LOGGER.error(e)
-            except KeyboardInterrupt:
-                raise
-            finally:
-                cp.terminate()
-        returncode = cp.poll()
-        if returncode != 0:
-            raise sp.CalledProcessError(returncode, cp.args)
-    frame_start = frame_start if clargs.start is None else clargs.start
-    frame_end = frame_end if clargs.end is None else clargs.end
-    out = {
-        "frame_start": int(frame_start),
-        "frame_end": int(frame_end),
-        "frames_total": int(frame_end) - int(frame_start) + 1,
-        "ext": ext,
-    }
-    if out["ext"] == "UNDEFINED":
-        raise BSError("Video extension is {ext}. Stopping!".format(ext=ext))
-    printd(cfg, "Probing done")
-    return out
-
-
-def call_mixdown(cfg, clargs, cmds, **kwargs):
-    """
-    IMPURE
-    Calls blender to render the audio mixdown.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`.
-    kwargs: dict
-    MANDATORY render_mixdown_path
-    Dictionary with additional information from the setup step.
-    """
-    kwargs_p = {"stdout": sp.PIPE, "stderr": sp.STDOUT, "universal_newlines": True}
-
-    printw(cfg, "Rendering mixdown")
-    printw(cfg, "Output @ {}".format(kwargs["render_mixdown_path"]), s="")
-    if not clargs.dry_run:
-        with sp.Popen(next(cmds), **kwargs_p) as cp:
-            try:
-                tmp = map(partial(checkblender, "MIXDOWN", [cfg["mixdown_py"]], cp), cp.stdout)
-                tmp = filter(lambda x: x.startswith("BPS"), tmp)
-                tmp = map(lambda x: x[4:].strip().split(), tmp)
-                kickstart(tmp)
-            except BSError as e:
-                LOGGER.error(e)
-            except KeyboardInterrupt:
-                raise
-            finally:
-                cp.terminate()
-        returncode = cp.poll()
-        if returncode != 0:
-            raise sp.CalledProcessError(returncode, cp.args)
-    printd(cfg, "Mixdown done")
-
-
-def call_chunk(cfg, clargs, queue, cmd, **kwargs):
-    """
-    IMPURE
-    Calls blender to render one chunk (which part is determined by `cmd`).
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmd: tuple
-    Tuple to be passed to `subprocess`.
-    kwargs: dict
-    Dictionary with additional information from the setup step.
-    """
-    sig.signal(sig.SIGINT, sig.SIG_IGN)
-    kwargs_p = {"stdout": sp.PIPE, "stderr": sp.STDOUT, "universal_newlines": True}
-
-    if not clargs.dry_run:
-        # can't use nice functional syntax if we want to simplify with `with`
-        with sp.Popen(cmd, **kwargs_p) as cp:
-            try:
-                tmp = map(
-                    partial(
-                        checkblender,
-                        "VIDEO",
-                        [cfg["video_py"], "The encoder timebase is not set"],
-                        cp,
-                    ),
-                    cp.stdout,
-                )
-                tmp = filter(lambda x: x.startswith("Append frame"), tmp)
-                tmp = map(lambda x: x.split()[-1], tmp)
-                tmp = map(int, tmp)
-                tmp = map(lambda x: True, tmp)
-                kickstart(map(queue.put, tmp))
-                queue.put(False)
-            except BSError as e:
-                LOGGER.error(e)
-
-
-def call_video(cfg, clargs, cmds, **kwargs):
-    """
-    IMPURE
-    Multi-process call to blender for rendering the (video) chunks.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`.
-    kwargs: dict
-    Dictionary with additional information from the setup step.
-    """
-    printw(cfg, "Rendering video (w/o audio)")
-    printw(cfg, "Output @ {}".format(kwargs["render_chunk_path"]), s="")
-    try:
-        not clargs.dry_run and os.remove(kwargs["chunks_file_path"])
-        LOGGER.info("CALL-VIDEO: generating {}".format(kwargs["chunks_file_path"]))
-    except OSError as e:
-        LOGGER.info("CALL-VIDEO: skipping {}: {}".format(e.filename, e.strerror))
-
-    cmds, cmds_cf = tee(cmds)
-    (not clargs.dry_run and append_chunks_file(cfg, clargs, cmds_cf, **kwargs))
-    # prepare queue/worker
-    queues = queues_close = (Queue(),) * clargs.workers
-    # prpare processes
-    proc = starmap(
-        lambda q, cmd: mp.Process(target=partial(call_chunk, cfg, clargs, **kwargs), args=(q, cmd)),
-        zip(queues, cmds),
-    )
-    # split iterator in 2 for later joining the processes and sum
-    # one of them
-    proc, proc_close = tee(proc)
-    proc = map(lambda p: p.start(), proc)
-    try:
-        not clargs.dry_run and kickstart(proc)
-
-        # communicate with processes through the queues and use tqdm to show a
-        # simple terminal progress bar baesd on video total frames
-        queues = map(lambda q: iter(q.get, False), queues)
-        queues = chain(*queues)
-        queues = tqdm(queues, total=kwargs["frame_end"] - kwargs["frame_start"] + 1, unit="frames")
-        not clargs.dry_run and kickstart(queues)
-    except KeyboardInterrupt:
-        proc_close = map(lambda x: x.terminate(), proc_close)
-        not clargs.dry_run and kickstart(proc_close)
-        raise
-    finally:
-        # close and join processes and queues
-        proc_close = map(lambda x: x.join(), proc_close)
-        not clargs.dry_run and kickstart(proc_close)
-
-        queues_close = map(lambda q: (q, q.close()), queues_close)
-        queues_close = starmap(lambda q, _: q.join_thread(), queues_close)
-        not clargs.dry_run and kickstart(queues_close)
-    printd(cfg, "Video chunks rendering done")
-
-
-def call_concatenate(cfg, clargs, cmds, **kwargs):
-    """
-    IMPURE
-    Calls ffmpeg in order to concatenate the video chunks together.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`.
-    kwargs: dict
-    MANDATORY: render_video_path
-    Dictionary with additional information from the setup step.
-
-    Note
-    ----
-    It expects the video chunk files to already be available.
-    """
-    kwargs_p = {"stdout": sp.DEVNULL, "stderr": sp.DEVNULL}
-    printw(cfg, "Concatenating (video) chunks")
-    printw(cfg, "Output @ {}".format(kwargs["render_video_path"]), s="")
-    if not clargs.dry_run:
-        with sp.Popen(next(cmds), **kwargs_p) as cp:
-            try:
-                returncode = cp.wait()
-                if returncode != 0:
-                    raise sp.CalledProcessError(returncode, cp.args)
-            except KeyboardInterrupt:
-                raise
-            finally:
-                cp.terminate()
-    printd(cfg, "Concatenating done")
-
-
-def call_join(cfg, clargs, cmds, **kwargs):
-    """
-    IMPURE
-    Calls ffmpeg for joining the audio mixdown and the video.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`.
-    kwargs: dict
-    MANDATORY: render_audiovideo_path
-    Dictionary with additional information from the setup step.
-
-    Note
-    ----
-    It expects the audio mixdown and video files to already be available.
-    """
-    kwargs_p = {"stdout": sp.DEVNULL, "stderr": sp.DEVNULL}
-    printw(cfg, "Joining audio/video")
-    printw(cfg, "Output @ {}".format(kwargs["render_audiovideo_path"]), s="")
-    if not clargs.dry_run:
-        with sp.Popen(next(cmds), **kwargs_p) as cp:
-            try:
-                returncode = cp.wait()
-                if returncode != 0:
-                    raise sp.CalledProcessError(returncode, cp.args)
-            except KeyboardInterrupt:
-                raise
-            finally:
-                cp.terminate()
-    printd(cfg, "Joining done")
-
-
-def call(cfg, clargs, cmds, **kwargs):
-    """
-    IMPURE
-    Delegates work to appropriate `call_*` functions.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    cmds: iter(tuple)
-    Iterator of commands to be passed to `subprocess`
-    kwargs: dict
-    MANDATORY: render_audiovideo_path
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: dict or None
-    It passes on the output from the `call_*` functions. See `call_*` for
-    specific details.
-
-    Note
-    ----
-    It tries to be smart and skip steps if child subprocesses give errors.
-    Example if `--join-only` is passed, but the audio mixdown or video file
-    aren't available on hard drive.
-    """
-    calls = {
-        "probe": call_probe,
-        "mixdown": call_mixdown,
-        "video": call_video,
-        "concatenate": call_concatenate,
-        "join": call_join,
-    }
-    try:
-        out = calls[cmds[0]](cfg, clargs, cmds[1], **kwargs)
-        return out
-    except sp.CalledProcessError:
-        prints(
-            cfg,
-            ("WARNING:{}: Something went wrong when calling" " command - SKIPPING").format(cmds[0]),
-        )
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/commands.py b/power_sequencer/scripts/BPSRender/bpsrender/commands.py
deleted file mode 100644
index 910ff021213b7a7ba5853f46e6b8ba603c68ede5..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/commands.py
+++ /dev/null
@@ -1,341 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import math as m
-from collections import OrderedDict
-from itertools import chain, islice
-
-from .config import LOGGER
-
-
-def get_commands_probe(cfg, clargs, **kwargs):
-    """
-    Create the command for probing the `clargs.blendfile`.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter(list)
-    An iterator for which each element is a list to be sent to functions like
-    `subprocess.run`.
-    """
-    out = (
-        "blender",
-        "--background",
-        clargs.blendfile,
-        "--python",
-        kwargs["probe_py_normalized"],
-        "--disable-autoexec",
-    )
-    LOGGER.debug("CMD-PROBE: {cmd}".format(cmd=" ".join(out)))
-    return iter((out,))
-
-
-def get_commands_chunk(cfg, clargs, **kwargs):
-    """
-    Create the command for rendering a (video) chunk from `clargs.blendfile`.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY render_chunk_path, w_frame_start, w_frame_end
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter(list)
-    An iterator for which each element is a list to be sent to functions like
-    `subprocess.run`.
-    """
-    out = (
-        "blender",
-        "--background",
-        clargs.blendfile,
-        "--python",
-        kwargs["video_py_normalized"],
-        "--disable-autoexec",
-        "--render-output",
-        kwargs["render_chunk_path"],
-        "-s",
-        str(kwargs["w_frame_start"]),
-        "-e",
-        str(kwargs["w_frame_end"]),
-        "--render-anim",
-    )
-    LOGGER.debug(
-        "CMD-CHUNK({w_frame_start}-{w_frame_end}): {cmd}".format(cmd=" ".join(out), **kwargs)
-    )
-    return iter((out,))
-
-
-def get_commands_video(cfg, clargs, **kwargs):
-    """
-    Create the list of commands (one command per chunk) for rendering a video
-    from `clargs.blendfile`.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY chunk_file_path, frame_start, frame_end, frames_total
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter(tuple)
-    An iterator for which each element is a tuple to be sent to functions like
-    `subprocess.run`.
-    """
-    LOGGER.debug("CMD-VIDEO:")
-    chunk_length = int(m.floor(kwargs["frames_total"] / clargs.workers))
-    out = map(lambda w: (w, kwargs["frame_start"] + w * chunk_length), range(clargs.workers))
-    out = map(
-        lambda x: (
-            x[1],
-            x[1] + chunk_length - 1 if x[0] != clargs.workers - 1 else kwargs["frame_end"],
-        ),
-        out,
-    )
-    out = map(
-        lambda x: get_commands(
-            cfg, clargs, "chunk", w_frame_start=x[0], w_frame_end=x[1], **kwargs
-        ),
-        out,
-    )
-    out = map(lambda x: x[1], out)
-    out = chain(*out)
-    return tuple(out)
-
-
-def get_commands_mixdown(cfg, clargs, **kwargs):
-    """
-    Create the command to render the mixdown from `clargs.blendfile`.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY render_mixdown_path
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter(tuple)
-    An iterator for which each element is a tuple to be sent to functions like
-    `subprocess.run`.
-    """
-    out = (
-        "blender --background {blendfile} --python {mixdown_py_normalized}"
-        " --disable-autoexec -- {render_mixdown_path}".format(**cfg, **vars(clargs), **kwargs)
-    )
-    out = (
-        "blender",
-        "--background",
-        clargs.blendfile,
-        "--python",
-        kwargs["mixdown_py_normalized"],
-        "--disable-autoexec",
-        "--",
-        kwargs["render_mixdown_path"],
-    )
-    LOGGER.debug("CMD-MIXDOWN: {cmd}".format(cmd=" ".join(out)))
-    return iter((out,))
-
-
-def get_commands_concatenate(cfg, clargs, **kwargs):
-    """
-    Create the command to concatenate the available video chunks generated
-    beforehand.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY chunks_file_path, render_video_path
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter(tuple)
-    An iterator for which each element is a tuple to be sent to functions like
-    `subprocess.run`.
-    """
-    out = (
-        "ffmpeg",
-        "-stats",
-        "-f",
-        "concat",
-        "-safe",
-        "-0",
-        "-i",
-        kwargs["chunks_file_path"],
-        "-c",
-        "copy",
-        "-y",
-        kwargs["render_video_path"],
-    )
-    LOGGER.debug("CMD-CONCATENATE: {cmd}".format(cmd=" ".join(out)))
-    return iter((out,))
-
-
-def get_commands_join(cfg, clargs, **kwargs):
-    """
-    Create the command to join the available audio mixdown and video generated
-    beforehand.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY chunks_file_path, render_video_path
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter(tuple)
-    An iterator for which each element is a tuple to be sent to functions like
-    `subprocess.run`.
-    """
-    out = (
-        "ffmpeg",
-        "-stats",
-        "-i",
-        kwargs["render_video_path"],
-        "-i",
-        kwargs["render_mixdown_path"],
-        "-map",
-        "0:v:0",
-        "-c:v",
-        "copy",
-        "-map",
-        "1:a:0",
-        "-c:a",
-        "aac",
-        "-b:a",
-        "320k",
-        "-y",
-        kwargs["render_audiovideo_path"],
-    )
-    LOGGER.debug("CMD-JOIN: {cmd}".format(cmd=" ".join(out)))
-    return iter((out,))
-
-
-def get_commands(cfg, clargs, what="", **kwargs):
-    """
-    Delegates the creation of commands lists to appropriate functions based on
-    `what` parameter.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    what: str (default = '')
-    Determines the returned value (see: Returns[out]).
-    kwargs: dict
-    MANDATORY -- see individual functions for the list of mandatory keys
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter or (str, iter)
-    |- what == '' is True
-    An iterator with elements of the type (str) for determining the order in
-    which to call the functions in the setup step.
-    NOTE: it skipps the "internal use only" functions.
-    |- else
-    A tuple with the 1st element as a tag (the `what` parameter) and the 2nd
-    element as the iterator of the actual commands.
-    """
-    get_commands_f = OrderedDict(
-        (
-            # internal use only
-            ("probe", get_commands_probe),
-            ("chunk", get_commands_chunk),
-            # direct connection to command line arguments - in order of execution
-            ("mixdown", get_commands_mixdown),
-            ("video", get_commands_video),
-            ("concatenate", get_commands_concatenate),
-            ("join", get_commands_join),
-        )
-    )
-
-    return (
-        islice(get_commands_f, 2, None)
-        if what == ""
-        else (what, get_commands_f[what](cfg, clargs, **kwargs))
-    )
-
-
-def get_commands_all(cfg, clargs, **kwargs):
-    """
-    Prepare the list of commands to be executed depending on the command line
-    arguments.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY -- see individual functions for the list of mandatory keys
-    Dictionary with additional information from the setup step.
-
-    Returns
-    -------
-    out: iter((str, tuple))
-    An iterator for which each element is a (str, iter(tuple)). The string
-    value is for tagging the iterator command list (2nd element) for filtering
-    later based on the given command line arguments.
-    """
-    end = "_only"
-    out = filter(lambda x: x[0].endswith(end), vars(clargs).items())
-    out = map(lambda x: (x[0][: -len(end)], x[1]), out)
-    order = list(get_commands(cfg, clargs))
-    out = sorted(out, key=lambda x: order.index(x[0]))
-    out = (
-        map(lambda k: k[0], out)
-        if all(map(lambda k: not k[1], out))
-        else map(lambda k: k[0], filter(lambda k: k[1], out))
-    )
-    out = map(lambda k: get_commands(cfg, clargs, k, **kwargs), out)
-    return out
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/config.py b/power_sequencer/scripts/BPSRender/bpsrender/config.py
deleted file mode 100644
index b87e3ea3bfbae95f287b2999db7c6a74daadeb4d..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/config.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import logging as lg
-import multiprocessing as mp
-import os.path as osp
-
-CONFIG = {
-    "logger": "BPS",
-    "cpu_count": min(int(mp.cpu_count() / 2), 6),
-    "bs_path": osp.join(osp.dirname(osp.abspath(__file__)), "bscripts"),
-    "frame_pad": 7,
-    "parts_folder": "bpsrender",
-    "chunks_file": "chunks.txt",
-    "video_file": "video{}",
-    "pre": {"work": "»", "done": "•", "skip": "~"},
-    "probe_py": "probe.py",
-    "mixdown_py": "mixdown.py",
-    "video_py": "video.py",
-}
-
-LOGGER = lg.getLogger(CONFIG["logger"])
-LOGLEV = [lg.INFO, lg.DEBUG]
-LOGLEV = [None] + sorted(LOGLEV, reverse=True)
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/helpers.py b/power_sequencer/scripts/BPSRender/bpsrender/helpers.py
deleted file mode 100644
index 9ebcf2b08c8efe52b67dbc511b53ee466aa204d7..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/helpers.py
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-from collections import deque
-from shutil import which
-
-
-class BSError(Exception):
-    """
-    Custom Exception raised if Blender is called with a python script argument
-    and gives error while trying to execute the script.
-    """
-
-    pass
-
-
-class ToolError(Exception):
-    """Raised if external dependencies aren't found on system.
-    """
-
-    pass
-
-
-def checktools(tools):
-    tools = [(t, which(t) or "") for t in tools]
-    check = {"tools": tools, "test": all(map(lambda x: x[1], tools))}
-    if not check["test"]:
-        msg = ["BPSRender couldn't find external dependencies:"]
-        msg += [
-            "[{check}] {tool}: {path}".format(
-                check="v" if path is not "" else "X", tool=tool, path=path or "NOT FOUND"
-            )
-            for tool, path in check["tools"]
-        ]
-        msg += [
-            (
-                "Check if you have them properly installed and available in the PATH"
-                " environemnt variable."
-            ),
-            "Exiting...",
-        ]
-        raise ToolError("\n".join(msg))
-
-
-def checkblender(what, search, cp, s):
-    """
-    IMPURE
-    Check Blender output for python script execution error.
-
-    Parameters
-    ----------
-    what: str
-    A tag used in the exception message.
-    search: iter(str)
-    One or more string(s) to search for in Blender's output.
-    cp: Popen
-    Blender subprocess.
-    s: PIPE
-    Blender's output.
-
-    Returns
-    -------
-    out: PIPE
-    The same pipe `s` is returned so that it can be iterated over on later
-    steps.
-    """
-    if not isinstance(search, list):
-        search = [search]
-    for search_item in search:
-        if search_item in s:
-            message = (
-                "Script {what} was not properly executed in" " Blender".format(what=what),
-                "CMD: {cmd}".format(what=what, cmd=" ".join(cp.args)),
-                "DUMP:".format(what=what),
-                s,
-            )
-            raise BSError("\n".join(message))
-    return s
-
-
-def printw(cfg, text, s="\n", e="...", p="", **kwargs):
-    p = p or cfg["pre"]["work"]
-    print("{s}{p} {}{e}".format(text, s=s, e=e, p=p), **kwargs)
-
-
-def printd(cfg, text, s="", e=".", p="", **kwargs):
-    p = p or cfg["pre"]["done"]
-    printw(cfg, text, s=s, e=e, p=p, **kwargs)
-
-
-def prints(cfg, text, s="", e=".", p="", **kwargs):
-    p = p or cfg["pre"]["skip"]
-    printw(cfg, text, s=s, e=e, p=p, **kwargs)
-
-
-def kickstart(it):
-    deque(it, maxlen=0)
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/setup.py b/power_sequencer/scripts/BPSRender/bpsrender/setup.py
deleted file mode 100644
index aba30d07bb33a91fae3a838a105f7d763b698eb4..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/bpsrender/setup.py
+++ /dev/null
@@ -1,182 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-# IMPURE
-import logging as lg
-import os
-import os.path as osp
-from functools import reduce
-from itertools import starmap
-
-from .calls import call
-from .commands import get_commands, get_commands_all
-from .config import LOGGER, LOGLEV
-from .helpers import kickstart
-
-
-def setup_bspy(cfg, clargs, **kwargs):
-    """
-    Normalize the names of the script to be ran in Blender for certain steps.
-    Eg. the probe step depends on the script located in
-    `bpsrender/cfg['probe_py']`.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-
-    Returns
-    -------
-    out: dict
-    Dictoinary to be used in call steps.
-    """
-    out = filter(lambda x: x[0].endswith("_py"), cfg.items())
-    out = starmap(lambda k, v: ("{}_normalized".format(k), osp.join(cfg["bs_path"], v)), out)
-    return dict(out)
-
-
-def setup_probe(cfg, clargs, **kwargs):
-    """
-    IMPURE
-    Call Blender and extract information that will be necessary for later
-    steps.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY -- see individual functions for the list of mandatory keys
-    Dictionary with additional information from the previous setup step.
-
-    Returns
-    -------
-    out: dict
-    Dictoinary to be used in call steps.
-    """
-    return call(cfg, clargs, get_commands(cfg, clargs, "probe", **kwargs), **kwargs)
-
-
-def setup_paths(cfg, clargs, **kwargs):
-    """
-    Figure out appropriate path locations to store output for parts and final
-    render.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    MANDATORY -- see individual functions for the list of mandatory keys
-    Dictionary with additional information from the previous setup step.
-
-    Returns
-    -------
-    out: dict
-    Dictionary storing all relevant information pertaining to folder and file
-    paths.
-
-    Note
-    ----
-    It also creates the folder structure 'render/parts' where
-    `clargs.blendfile` is stored on disk.
-    """
-    render_parts_path = osp.join(clargs.output, cfg["parts_folder"])
-    name = osp.splitext(osp.basename(clargs.blendfile))[0]
-    render_mixdown_path = osp.join(render_parts_path, "{}_m.flac".format(name))
-    render_chunk_path = osp.join(render_parts_path, "{}_c_{}".format(name, "#" * cfg["frame_pad"]))
-    render_video_path = osp.join(render_parts_path, "{}_v{}".format(name, kwargs["ext"]))
-    render_audiovideo_path = osp.join(clargs.output, "{}{}".format(name, kwargs["ext"]))
-    chunks_file_path = osp.join(render_parts_path, cfg["chunks_file"])
-
-    out = {
-        "render_path": clargs.output,
-        "render_parts_path": render_parts_path,
-        "chunks_file_path": chunks_file_path,
-        "render_chunk_path": render_chunk_path,
-        "render_video_path": render_video_path,
-        "render_mixdown_path": render_mixdown_path,
-        "render_audiovideo_path": render_audiovideo_path,
-    }
-    return out
-
-
-def setup_folders_hdd(cfg, clargs, **kwargs):
-    """
-    IMPURE
-    Prepares the folder structure `cfg['render']/cfg['parts']'`.
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    Dictionary with additional information from the previous setup step.
-
-    Returns
-    -------
-    out: (iter((str, iter(tuple))), dict)
-    1st element: see commands.py:get_commands_all
-    2nd elment: the keyword arguments used by calls.py:call
-    """
-    # create folder structure if it doesn't exist already only if
-    # appropriate command line arguments are given
-    do_it = filter(lambda x: x[0].endswith("_only"), vars(clargs).items())
-    do_it = all(map(lambda x: not x[1], do_it))
-    do_it = not clargs.dry_run and clargs.video_only or clargs.mixdown_only or do_it
-    do_it and os.makedirs(kwargs["render_parts_path"], exist_ok=True)
-    return {}
-
-
-def setup(cfg, clargs):
-    """
-    IMPURE -- setup_paths
-    Prepares the folder structure 'render/parts', the appropriate command lists
-    to be called and the keyword arguments to be passed to call functions
-    (calls.py).
-
-    Parameters
-    ----------
-    cfg: dict
-    Configuration dictionary.
-    clargs: Namespace
-    Command line arguments (normalized).
-    kwargs: dict
-    Dictionary with additional information from the previous setup step.
-
-    Returns
-    -------
-    out: (iter((str, iter(tuple))), dict)
-    1st element: see commands.py:get_commands_all
-    2nd elment: the keyword arguments used by calls.py:call
-    """
-    setups_f = (setup_bspy, setup_probe, setup_paths, setup_folders_hdd)
-    lg.basicConfig(level=LOGLEV[min(clargs.verbose, len(LOGLEV) - 1)])
-
-    kwargs = dict(reduce(lambda acc, sf: {**acc, **sf(cfg, clargs, **acc)}, setups_f, {}))
-
-    LOGGER.info("Setup:")
-    kickstart(starmap(lambda k, v: LOGGER.info("{}: {}".format(k, v)), kwargs.items()))
-    return get_commands_all(cfg, clargs, **kwargs), kwargs
diff --git a/power_sequencer/scripts/BPSRender/setup.py b/power_sequencer/scripts/BPSRender/setup.py
deleted file mode 100644
index 1f17b9bae1483688749eb952e52612c121a422ec..0000000000000000000000000000000000000000
--- a/power_sequencer/scripts/BPSRender/setup.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Razvan Radulescu, Nathan Lovato, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-from setuptools import setup
-
-
-def readme():
-    with open("README.rst") as f:
-        return f.read()
-
-
-if __name__ == "__main__":
-    setup(
-        name="bpsrender",
-        version="0.1.40.post1",
-        description="Blender Power Sequencer Renderer",
-        long_description=readme(),
-        classifiers=[
-            "Development Status :: 4 - Beta",
-            "Environment :: Console",
-            "Intended Audience :: End Users/Desktop",
-            "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
-            "Natural Language :: English",
-            "Programming Language :: Python :: 3.3",
-            "Programming Language :: Python :: 3.4",
-            "Programming Language :: Python :: 3.5",
-            "Programming Language :: Python :: 3.6",
-            "Programming Language :: Python :: 3.7",
-            "Topic :: Text Processing :: Linguistic",
-            "Topic :: Multimedia :: Video",
-            "Topic :: Utilities",
-        ],
-        url="https://gitlab.com/razcore/BPSRender",
-        keywords="blender render parallel multiprocess speedup utility" " productivty",
-        author="Răzvan C. Rădulescu",
-        author_email="razcore.art@gmail.com",
-        license="GPLv3",
-        packages=["bpsrender"],
-        install_requires=["tqdm"],
-        zip_safe=False,
-        entry_points={"console_scripts": ["bpsrender=bpsrender.__main__:main"]},
-        include_package_data=True,
-    )