import edl, import movie, import audio
parent
2973fd85d8
commit
c75c5469f9
10
constants.py
10
constants.py
|
@ -5,6 +5,16 @@ MODULE_DIR = Path(__file__).parent
|
|||
TRACKERS_DIR = MODULE_DIR / 'resources' / 'trackers'
|
||||
TRACKERS = []
|
||||
PROJECTS = []
|
||||
|
||||
EDITS = [('NONE', 'None', '', 0)]
|
||||
MOVIES = [('NONE', 'None', '', 0)]
|
||||
SOUNDS = [('NONE', 'None', '', 0)]
|
||||
|
||||
EDIT_SUFFIXES = ['.xml', '.edl']
|
||||
MOVIE_SUFFIXES = ['.mov', '.mp4']
|
||||
SOUND_SUFFIXES = ['.mp3', '.aaf', '.flac']
|
||||
|
||||
|
||||
# TRACKER_URL = os.environ.get("TRACKER_URL", "")
|
||||
# TRACKER_LOGIN = os.environ.get("TRACKER_LOGIN", "")
|
||||
# TRACKER_PASSWORD = os.environ.get("TRACKER_PASSWORD", "")
|
|
@ -17,10 +17,21 @@ from bpy.types import (
|
|||
Operator,
|
||||
)
|
||||
from pathlib import Path
|
||||
from vse_toolbox.constants import (
|
||||
EDITS,
|
||||
MOVIES,
|
||||
SOUNDS,
|
||||
EDIT_SUFFIXES,
|
||||
MOVIE_SUFFIXES,
|
||||
SOUND_SUFFIXES,
|
||||
)
|
||||
from vse_toolbox.sequencer_utils import (
|
||||
clean_sequencer,
|
||||
get_shot_sequence,
|
||||
get_strips,
|
||||
import_edl,
|
||||
import_edit,
|
||||
import_movie,
|
||||
import_sound,
|
||||
rename_strips,
|
||||
render_strips,
|
||||
set_channels,
|
||||
|
@ -44,10 +55,55 @@ class VSETB_OT_export_csv(Operator):
|
|||
return {"CANCELLED"}
|
||||
|
||||
|
||||
class VSETB_OT_auto_select_files(Operator):
|
||||
bl_idname = "import.auto_select_files"
|
||||
bl_label = "Auto Select"
|
||||
bl_description = "Auto Select Files"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def get_items(self, items=[]):
|
||||
if not items:
|
||||
return [('NONE', 'None', '', 0)]
|
||||
return [(e, e, '', i) for i, e in enumerate(sorted(items))]
|
||||
|
||||
def execute(self, context):
|
||||
params = context.space_data.params
|
||||
directory = Path(params.directory.decode())
|
||||
|
||||
EDITS.clear()
|
||||
MOVIES.clear()
|
||||
SOUNDS.clear()
|
||||
|
||||
edits = []
|
||||
movies = []
|
||||
sounds = []
|
||||
|
||||
for file_entry in directory.glob('*'):
|
||||
if file_entry.is_dir():
|
||||
continue
|
||||
|
||||
if file_entry.suffix in EDIT_SUFFIXES:
|
||||
edits.append(file_entry.name)
|
||||
elif file_entry.suffix in MOVIE_SUFFIXES:
|
||||
movies.append(file_entry.name)
|
||||
elif file_entry.suffix in SOUND_SUFFIXES:
|
||||
sounds.append(file_entry.name)
|
||||
|
||||
EDITS.extend(self.get_items(items=edits))
|
||||
MOVIES.extend(self.get_items(items=movies))
|
||||
SOUNDS.extend(self.get_items(items=sounds))
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_import(Operator):
|
||||
bl_idname = "sequencer.import"
|
||||
bl_label = "Set Scene"
|
||||
bl_description = "Set Scene for Breakdown"
|
||||
bl_label = "Import"
|
||||
bl_description = "Import Edit"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
directory : StringProperty(subtype='DIR_PATH')
|
||||
|
@ -58,29 +114,103 @@ class VSETB_OT_import(Operator):
|
|||
subtype='FILE_PATH',
|
||||
)
|
||||
files : CollectionProperty(type=bpy.types.OperatorFileListElement)
|
||||
filter_glob: StringProperty(
|
||||
default='*.edl;*.mov;*.wav',
|
||||
options={'HIDDEN'}
|
||||
|
||||
clean_sequencer : BoolProperty(
|
||||
name="Clean Sequencer",
|
||||
default=False,
|
||||
description="Clean all existing strips in sequencer",
|
||||
)
|
||||
|
||||
import_edit : BoolProperty(name='', default=True)
|
||||
edit: EnumProperty(name='', items=lambda s, c: EDITS)
|
||||
|
||||
import_movie : BoolProperty(name='', default=False)
|
||||
movie: EnumProperty(name='', items=lambda s, c: MOVIES)
|
||||
|
||||
import_sound : BoolProperty(name='', default=False)
|
||||
sound: EnumProperty(name='', items=lambda s, c: SOUNDS)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
settings = get_settings()
|
||||
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.operator('import.auto_select_files', text='Auto Select')
|
||||
|
||||
row = self.layout.row(heading="Import Edit", align=True)
|
||||
row.prop(self, 'import_edit')
|
||||
sub = row.row()
|
||||
sub.active = self.import_edit
|
||||
sub.prop(self, 'edit')
|
||||
|
||||
row = self.layout.row(heading="Import Movie", align=True)
|
||||
row.prop(self, 'import_movie')
|
||||
sub = row.row()
|
||||
sub.active = self.import_movie
|
||||
sub.prop(self, 'movie')
|
||||
|
||||
row = self.layout.row(heading="Import Sound", align=True)
|
||||
row.prop(self, 'import_sound')
|
||||
sub = row.row()
|
||||
sub.active = self.import_sound
|
||||
sub.prop(self, 'sound')
|
||||
|
||||
col = layout.column()
|
||||
col.separator()
|
||||
col.prop(self, 'clean_sequencer')
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def execute(self, context):
|
||||
otio = install_module('opentimelineio')
|
||||
for filepath in self.files:
|
||||
filepath = Path(self.directory, filepath.name)
|
||||
if filepath.suffix == '.edl':
|
||||
edl = import_edl(filepath, adapter="cmx_3600")
|
||||
print('edl: ', edl)
|
||||
|
||||
return {"CANCELLED"}
|
||||
edit_filepath = Path(self.directory, self.edit)
|
||||
if not edit_filepath.exists():
|
||||
self.import_edit = False
|
||||
|
||||
movie_filepath = Path(self.directory, self.movie)
|
||||
if not movie_filepath.exists():
|
||||
self.import_movie = False
|
||||
|
||||
sound_filepath = Path(self.directory, self.sound)
|
||||
if not sound_filepath.exists():
|
||||
self.import_sound = False
|
||||
|
||||
if self.clean_sequencer:
|
||||
clean_sequencer(
|
||||
edit=self.import_edit,
|
||||
movie=self.import_movie,
|
||||
sound=self.import_sound,
|
||||
)
|
||||
|
||||
if self.import_edit:
|
||||
print(f'[>.] Loading Edit from: {str(edit_filepath)}')
|
||||
import_edit(edit_filepath, adapter="cmx_3600")
|
||||
|
||||
if self.import_movie:
|
||||
print(f'[>.] Loading Movie from: {str(movie_filepath)}')
|
||||
import_movie(movie_filepath)
|
||||
|
||||
if self.import_sound:
|
||||
print(f'[>.] Loading Audio from: {str(sound_filepath)}')
|
||||
import_sound(sound_filepath)
|
||||
elif not self.import_sound and self.import_movie:
|
||||
print(f'[>.] Loading Audio from Movie: {str(movie_filepath)}')
|
||||
import_sound(movie_filepath)
|
||||
|
||||
context.scene.sequence_editor.sequences.update()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VSETB_OT_load_projects(Operator):
|
||||
|
@ -202,7 +332,8 @@ class VSETB_OT_rename(Operator):
|
|||
scn = context.scene
|
||||
settings = get_settings()
|
||||
|
||||
col = self.layout
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.prop(self, 'template')
|
||||
col.prop(self, 'start_number')
|
||||
|
@ -245,15 +376,17 @@ class VSETB_OT_render(Operator):
|
|||
scn = context.scene
|
||||
settings = get_settings()
|
||||
|
||||
col = self.layout
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.use_property_decorate = False
|
||||
col.prop(settings, 'channels', text='Channel')
|
||||
col.prop(settings, 'channel', text='Channel')
|
||||
col.prop(self, 'selected_only')
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
strips = get_strips(channel=self.channel_name, selected_only=self.selected_only)
|
||||
settings = get_settings()
|
||||
strips = get_strips(channel=settings.channel, selected_only=self.selected_only)
|
||||
|
||||
render_strips(strips)
|
||||
|
||||
|
@ -288,6 +421,7 @@ class VSETB_OT_set_scene(Operator):
|
|||
|
||||
|
||||
classes=(
|
||||
VSETB_OT_auto_select_files,
|
||||
VSETB_OT_export_csv,
|
||||
VSETB_OT_import,
|
||||
VSETB_OT_load_projects,
|
||||
|
|
|
@ -50,7 +50,7 @@ class VSETB_PT_main(VSETB_main, Panel):
|
|||
|
||||
# TODO FAIRE DES VRAIS OPS
|
||||
row = col.row(align=True)
|
||||
row.operator('sequencer.import', text='Import Edit', icon='IMPORT')
|
||||
row.operator('sequencer.import', text='Import', icon='IMPORT')
|
||||
row.operator('sequencer.export_csv', text='Export', icon='EXPORT')
|
||||
|
||||
op = col.operator('sequencer.strips_render', text='Render Strips', icon='RENDER_ANIMATION')
|
||||
|
|
|
@ -11,7 +11,7 @@ from bpy.props import (
|
|||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.types import PropertyGroup, UIList
|
||||
from pprint import pprint as pp
|
||||
from vse_toolbox.bl_utils import get_addon_prefs, get_settings
|
||||
from vse_toolbox.constants import TRACKERS
|
||||
|
@ -69,11 +69,12 @@ class Project(PropertyGroup):
|
|||
name="Episode Name", default="e{index:03d}")
|
||||
|
||||
shot_template : StringProperty(
|
||||
name="Shot Name", default="{episode}_sh{index:04d}")
|
||||
name="Shot Name", default="{episode}sh{index:04d}")
|
||||
|
||||
episode_name : EnumProperty(items=get_episodes_items)
|
||||
episodes : CollectionProperty(type=Episode)
|
||||
|
||||
|
||||
#FIXME Trouver une solution pour mettre des method dans les CollectionProperty
|
||||
class Projects(PropertyGroup):
|
||||
pass
|
||||
|
@ -83,6 +84,27 @@ class Projects(PropertyGroup):
|
|||
# return self.get(settings.project_name)
|
||||
|
||||
|
||||
class VSETB_UL_channels(UIList):
|
||||
"""Demo UIList."""
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
|
||||
items = bpy.types.GPTRACER_OT_add_property.__annotations__['enum'].keywords['items']
|
||||
|
||||
enum_item = next((i for i in items if i[0]==item.name), None)
|
||||
if not enum_item:
|
||||
return
|
||||
|
||||
layout.label(text=enum_item[1], icon=enum_item[-2])
|
||||
|
||||
settings = data.settings
|
||||
prop = item.name
|
||||
if '.' in prop:
|
||||
settings, prop = prop.split('.')
|
||||
settings = getattr(data.settings, settings)
|
||||
|
||||
layout.prop(settings, prop, text='')
|
||||
|
||||
class VSETB_PGT_settings(PropertyGroup):
|
||||
_projects = []
|
||||
|
||||
|
@ -91,7 +113,7 @@ class VSETB_PGT_settings(PropertyGroup):
|
|||
tracker_name : EnumProperty(items=get_tracker_items)
|
||||
toogle_prefs : BoolProperty(description='Toogle VSE ToolBox Preferences')
|
||||
|
||||
channels : EnumProperty(
|
||||
channel : EnumProperty(
|
||||
items=[
|
||||
('AUDIO', 'Audio', '', 0),
|
||||
('MOVIE', 'Movie', '', 1),
|
||||
|
@ -117,11 +139,12 @@ class VSETB_PGT_settings(PropertyGroup):
|
|||
if project:
|
||||
return project.episodes.get(project.episode_name)
|
||||
|
||||
|
||||
classes=(
|
||||
Episode,
|
||||
Project,
|
||||
Projects,
|
||||
VSETB_PGT_settings,
|
||||
# VSETB_UL_channels,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ def get_strips(channel=0, selected_only=False):
|
|||
scn = bpy.context.scene
|
||||
|
||||
if isinstance(channel, str):
|
||||
channel = scn.sequence_editor.channels.keys().index(channel)
|
||||
channel = get_channel(channel)
|
||||
|
||||
strips = [s for s in scn.sequence_editor.sequences_all if s.channel==channel]
|
||||
|
||||
|
@ -19,6 +19,16 @@ def get_strips(channel=0, selected_only=False):
|
|||
|
||||
return sorted(strips, key=lambda x : x.frame_final_start)
|
||||
|
||||
def get_channel(name):
|
||||
scn = bpy.context.scene
|
||||
|
||||
channel_id = 0
|
||||
channel = scn.sequence_editor.channels.get(name)
|
||||
if channel:
|
||||
channel_id = scn.sequence_editor.channels.keys().index(name)
|
||||
|
||||
return channel_id
|
||||
|
||||
def get_shot_sequence(shot):
|
||||
sequences = get_strips(channel='Sequences')
|
||||
return next((s.name for s in sequences if s.frame_final_start<=shot.frame_final_start<s.frame_final_end), '')
|
||||
|
@ -59,7 +69,7 @@ def rename_strips(
|
|||
def set_channels():
|
||||
scn = bpy.context.scene
|
||||
settings = get_settings()
|
||||
items = settings.rna_type.bl_rna.properties['channels'].enum_items
|
||||
items = settings.rna_type.bl_rna.properties['channel'].enum_items
|
||||
|
||||
for i, c in enumerate(items.keys(), start=1):
|
||||
scn.sequence_editor.channels[i].name = c.title()
|
||||
|
@ -68,18 +78,35 @@ def render_strips(strips):
|
|||
for strip in strips:
|
||||
print(strip.name)
|
||||
|
||||
def import_edl(filepath, adapter="cmx_3600"):
|
||||
def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False):
|
||||
import opentimelineio as otio
|
||||
scn = bpy.context.scene
|
||||
from opentimelineio.schema import (
|
||||
Clip,
|
||||
ExternalReference,
|
||||
Gap,
|
||||
ImageSequenceReference,
|
||||
Stack,
|
||||
Timeline,
|
||||
Track,
|
||||
)
|
||||
|
||||
scn = bpy.context.scene
|
||||
sequences = scn.sequence_editor.sequences
|
||||
|
||||
edl = Path(filepath)
|
||||
if clean_sequencer:
|
||||
movie = get_strips(channel='Movie')
|
||||
audio = get_strips(channel='Audio')
|
||||
|
||||
for strip in sequences:
|
||||
if strip not in (movie+audio):
|
||||
sequences.remove(strip)
|
||||
|
||||
edl = Path(filepath)
|
||||
try:
|
||||
timeline = otio.adapters.read_from_file(
|
||||
str(edl), adapter, rate=scn.render.fps, ignore_timecode_mismatch=True)
|
||||
except:
|
||||
print("[>.] read_from_file Failed. Using read_from_string method.")
|
||||
data = edl.read_text(encoding='latin-1')
|
||||
timeline = otio.adapters.read_from_string(
|
||||
data, adapter, rate=scn.render.fps, ignore_timecode_mismatch=True)
|
||||
|
@ -87,46 +114,98 @@ def import_edl(filepath, adapter="cmx_3600"):
|
|||
scn.frame_start = (
|
||||
0 if timeline.global_start_time is None else timeline.global_start_time
|
||||
)
|
||||
scn.frame_end = otio.opentime.to_frames(timeline.duration()) - 1
|
||||
# scn.frame_end = otio.opentime.to_frames(timeline.duration())
|
||||
|
||||
for i, track in enumerate(timeline.tracks, 1):
|
||||
for child in track.each_child(shallow_search=False):
|
||||
for track in timeline.tracks:
|
||||
for child in track.each_child(shallow_search=True):
|
||||
|
||||
# FIXME Exclude Gaps for now. Gaps are Transitions, Blank Spaces...
|
||||
# if type(child) != Clip:
|
||||
if not isinstance(child, Clip):
|
||||
continue
|
||||
|
||||
# FIXME Exclude Audio for now
|
||||
if any(child.name.endswith(ext) for ext in ('.wav', '.mp3')):
|
||||
channel = get_channel('Audio')
|
||||
continue
|
||||
|
||||
channel = get_channel('Shots')
|
||||
frame_start = otio.opentime.to_frames(
|
||||
child.range_in_parent().start_time)
|
||||
|
||||
frame_end = otio.opentime.to_frames(
|
||||
child.range_in_parent().duration) - 1
|
||||
frame_end = frame_start + otio.opentime.to_frames(
|
||||
child.range_in_parent().duration)
|
||||
|
||||
try:
|
||||
strip = sequences.new_effect(
|
||||
name='',
|
||||
name=child.name,
|
||||
type='COLOR',
|
||||
channel=i,
|
||||
channel=channel,
|
||||
frame_start=frame_start,
|
||||
frame_end=frame_end,
|
||||
)
|
||||
strip.blend_alpha = 0.0
|
||||
|
||||
except Exception as e:
|
||||
print('e: ', e)
|
||||
continue
|
||||
|
||||
"""
|
||||
t = otio.adapters.read_from_file(edl, rate=C.scene.render.fps)
|
||||
t.video_tracks() # Video Tracks du fichiers. C'est une liste.
|
||||
Parcourir toutes les video_tracks ?
|
||||
Trouver comment fait Felix David
|
||||
"""
|
||||
print('timeline: ', timeline)
|
||||
|
||||
scn.frame_end = frame_end-1
|
||||
return timeline
|
||||
|
||||
"""
|
||||
def create_strip_effect(name, strip_type, channel, frame_start, frame_end):
|
||||
strip = scn.sequence_editor.sequences.new_effect(
|
||||
name=name,
|
||||
type=strip_type,
|
||||
channel=channel,
|
||||
frame_start=frame_start,
|
||||
frame_end=frame_end,
|
||||
def import_movie(filepath):
|
||||
scn = bpy.context.scene
|
||||
|
||||
res_x = scn.render.resolution_x
|
||||
res_y = scn.render.resolution_y
|
||||
|
||||
strip = scn.sequence_editor.sequences.new_movie(
|
||||
name=filepath.stem,
|
||||
filepath=str(filepath),
|
||||
channel=get_channel('Movie'),
|
||||
frame_start=scn.frame_start
|
||||
)
|
||||
|
||||
elem = strip.strip_elem_from_frame(scn.frame_current)
|
||||
src_width, src_height = elem.orig_width, elem.orig_height
|
||||
|
||||
if src_width != res_x:
|
||||
strip.transform.scale_x = (res_x / src_width)
|
||||
if src_height != res_y:
|
||||
strip.transform.scale_y = (res_y / src_height)
|
||||
|
||||
if bpy.data.is_saved:
|
||||
strip.filepath = bpy.path.relpath(str(filepath))
|
||||
|
||||
return strip
|
||||
"""
|
||||
|
||||
def import_sound(filepath):
|
||||
scn = bpy.context.scene
|
||||
|
||||
strip = scn.sequence_editor.sequences.new_sound(
|
||||
name=filepath.stem,
|
||||
filepath=str(filepath),
|
||||
channel=get_channel('Audio'),
|
||||
frame_start=scn.frame_start
|
||||
)
|
||||
|
||||
if bpy.data.is_saved:
|
||||
strip.sound.filepath = bpy.path.relpath(str(filepath))
|
||||
|
||||
strip.show_waveform = True
|
||||
return strip
|
||||
|
||||
|
||||
def clean_sequencer(edit=False, movie=False, sound=False):
|
||||
scn = bpy.context.scene
|
||||
sequences = []
|
||||
|
||||
if edit:
|
||||
sequences.extend(get_strips('Shots'))
|
||||
if movie:
|
||||
sequences.extend(get_strips('Movie'))
|
||||
if sound:
|
||||
sequences.extend(get_strips('Audio'))
|
||||
|
||||
for sequence in sequences:
|
||||
scn.sequence_editor.sequences.remove(sequence)
|
Loading…
Reference in New Issue