Welcome to mirror list, hosted at ThFree Co, Russian Federation.

import_local_footage.py « operators « power_sequencer - git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 8612210ae4d301e89fe948a81f7387268514d742 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors

# This file is part of Power Sequencer.

import json
import os
import re

import bpy

from .utils.functions import convert_duration_to_frames
from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
from ..addon_preferences import get_preferences
from .utils.global_settings import (
    Extensions,
    EXTENSIONS_ALL,
    EXTENSIONS_AUDIO,
    EXTENSIONS_IMG,
    EXTENSIONS_VIDEO,
)


class POWER_SEQUENCER_OT_import_local_footage(bpy.types.Operator):
    """*brief* Imports video, images, and audio from the project folder

    Finds and imports all valid video, audio files, and pictures in the blend file's folder and
    sub-folders, ignoring folders named BL_proxy.

    If you set it in the add-on preferences, it also sets imported sequences to use proxies. See
    `Preferences -> Add-ons -> Blender Power Sequencer -> Proxy`
    """

    doc = {
        "name": doc_name(__qualname__),
        "demo": "",
        "description": doc_description(__doc__),
        "shortcuts": [
            (
                {"type": "I", "value": "PRESS", "ctrl": True, "shift": True},
                {"keep_audio": True},
                "Import Local Footage",
            )
        ],
        "keymap": "Sequencer",
    }
    bl_idname = doc_idname(__qualname__)
    bl_label = doc["name"]
    bl_description = doc_brief(doc["description"])
    bl_options = {"REGISTER", "UNDO"}

    keep_audio: bpy.props.BoolProperty(
        name="Keep Audio from Video Files",
        description=("If False, the audio that comes with video files will" " not be imported"),
        default=True,
    )
    img_length: bpy.props.FloatProperty(
        name="Image strip Length",
        description="Controls the duration of the imported image strip in seconds",
        default=3.0,
        min=1.0,
    )
    img_padding: bpy.props.FloatProperty(
        name="Image Padding",
        description="Padding added between imported image strips in seconds",
        default=1.0,
        min=0.0,
    )

    sequencer_area = None
    directory = ""

    @classmethod
    def poll(cls, context):
        return bpy.data.is_saved

    def execute(self, context):
        self.sequencer_area = self.get_sequencer_area(context)
        self.directory = os.path.split(bpy.data.filepath)[0]

        filepaths = self.find_local_footage_files()
        files_to_import = [
            os.path.join(self.directory, f) for f in self.find_new_files_to_import(filepaths)
        ]
        if not files_to_import:
            self.report({"INFO"}, "No new files to import found")
            return {"FINISHED"}

        bpy.ops.screen.animation_cancel(restore_frame=True)

        audio = self.import_audios(
            context, [f for f in files_to_import if f.lower().endswith(EXTENSIONS_AUDIO)]
        )
        video = self.import_videos(
            context, [f for f in files_to_import if f.lower().endswith(EXTENSIONS_VIDEO)]
        )
        img = self.import_imgs(
            context, [f for f in files_to_import if f.lower().endswith(EXTENSIONS_IMG)]
        )

        bpy.data.texts["POWER_SEQUENCER_IMPORTS"].from_string(json.dumps(filepaths))

        for s in audio:
            s.show_waveform = True

        imported = audio + video + img
        for s in imported:
            s.select = True
        self.set_selected_strips_proxies(context)
        self.report({"INFO"}, "Imported {!s} strips from newly found files.".format(len(imported)))
        return {"FINISHED"}

    def get_sequencer_area(self, context):
        """
        Returns the sequencer area to use as a context override in
        some operators
        """
        sequencer_area = None
        for window in context.window_manager.windows:
            for area in window.screen.areas:
                if not area.type == "SEQUENCE_EDITOR":
                    continue
                sequencer_area = {
                    "window": window,
                    "screen": window.screen,
                    "area": area,
                    "scene": context.scene,
                }
        return sequencer_area

    def find_local_footage_files(self, ignored_directories=["BL_proxy"]):
        """
        Returns a list of relative filepaths in all subdirectories of the `self.directory`
        for all valid files that can be imported in the Sequencer
        """
        files_list = []
        for root, dirs, files in os.walk(self.directory):
            for directory in ignored_directories:
                if directory in dirs:
                    dirs.remove(directory)

            files = [f for f in sorted(files) if f.lower().endswith(EXTENSIONS_ALL)]
            files = map(lambda name: os.path.join(root, name), files)
            files = map(lambda path: os.path.relpath(path, self.directory), files)
            files_list.extend(list(files))

        return files_list

    def create_import_text_block(self, name):
        """
        Creates a new text data block that contains an empty json list, renames it to `name` and
        returns it
        """
        re.compile(r"^Text.[0-9]{3}$")

        bpy.ops.text.new()

        # Find the newly created text file's identifier
        ids = [text.name for text in bpy.data.texts if text.name.startswith("Text")]
        id = max(ids)

        text_file = bpy.data.texts[id]
        text_file.name = name
        text_file.from_string("[]")
        return text_file

    def find_new_files_to_import(self, filepaths):
        """
        Gets and optionally creates the list of already imported files in this project
        Returns a list of paths from filepaths that are not in the imported text file
        """
        text_file = (
            bpy.data.texts.get("POWER_SEQUENCER_IMPORTS")
            if "POWER_SEQUENCER_IMPORTS" in bpy.data.texts.keys()
            else self.create_import_text_block("POWER_SEQUENCER_IMPORTS")
        )
        imported_files = json.loads(text_file.as_string())
        files_to_import = [p for p in filepaths if p not in imported_files]
        return files_to_import

    def import_videos(self, context, videos_filepaths):
        """
        Imports a list of files using movie_strip_add
        Returns the list of imported sequences
        """
        frame = context.scene.frame_current

        context.window_manager.progress_begin(0, len(videos_filepaths))
        imported = []
        for index, f in enumerate(videos_filepaths):
            is_first_import = index == 0
            bpy.ops.sequencer.movie_strip_add(
                self.sequencer_area,
                filepath=f,
                frame_start=frame,
                sound=self.keep_audio,
                use_framerate=is_first_import,
            )
            imported.extend(context.selected_sequences)
            frame = context.selected_sequences[0].frame_final_end
            context.window_manager.progress_update(index)

        context.window_manager.progress_end()
        return imported

    def import_audios(self, context, audio_filepaths):
        """
        Imports audio files as sound strips from a list of absolute file paths
        Returns the list of newly imported audio files
        """
        frame = context.scene.frame_current
        imported = []
        for f in audio_filepaths:
            bpy.ops.sequencer.sound_strip_add(self.sequencer_area, filepath=f, frame_start=frame)
            imported.extend(context.selected_sequences)
            frame = context.selected_sequences[0].frame_final_end
        return imported

    def import_imgs(self, context, img_filepaths):
        frame = context.scene.frame_current
        strip_length = convert_duration_to_frames(context, self.img_length)
        strip_padding = convert_duration_to_frames(context, self.img_padding)

        new_sequences = []
        for f in img_filepaths:
            head, tail = os.path.split(f)
            bpy.ops.sequencer.image_strip_add(
                self.sequencer_area,
                directory=head,
                files=[{"name": tail}],
                frame_start=frame,
                frame_end=frame + strip_length,
            )
            frame += strip_length + strip_padding
            new_sequences.extend(context.selected_sequences)

        return new_sequences

    def set_selected_strips_proxies(self, context):
        proxy_sizes = ["25", "50", "75", "100"]

        use_proxy = False
        prefs = get_preferences(context)
        for size in proxy_sizes:
            if hasattr(prefs, "proxy_" + size):
                use_proxy = True
                break

        if not use_proxy:
            return

        for s in [s for s in context.selected_sequences if s.type in ["MOVIE", "IMAGE"]]:
            s.use_proxy = True
            s.proxy.build_25 = prefs.proxy_25
            s.proxy.build_50 = prefs.proxy_50
            s.proxy.build_75 = prefs.proxy_75
            s.proxy.build_100 = prefs.proxy_100