Skip to content

lib

create_bin(name, root=None, set_as_current=True)

Create media pool's folder.

Return folder object and if the name does not exist it will create a new. If the input name is with forward or backward slashes then it will create all parents and return the last child bin object

Parameters:

Name Type Description Default
name str

name of folder / bin, or hierarchycal name "parent/name"

required
root (resolve.Folder)[optional]

root folder / bin object

required
set_as_current (resolve.Folder)[optional]

Whether to set the resulting bin as current folder or not.

required

Returns:

Name Type Description
object object

resolve.Folder

Source code in client/ayon_resolve/api/lib.py
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
258
259
260
261
262
263
264
def create_bin(name: str,
               root: object = None,
               set_as_current: bool = True) -> object:
    """
    Create media pool's folder.

    Return folder object and if the name does not exist it will create a new.
    If the input name is with forward or backward slashes then it will create
    all parents and return the last child bin object

    Args:
        name (str): name of folder / bin, or hierarchycal name "parent/name"
        root (resolve.Folder)[optional]: root folder / bin object
        set_as_current (resolve.Folder)[optional]: Whether to set the
            resulting bin as current folder or not.

    Returns:
        object: resolve.Folder
    """
    # get all variables
    media_pool = get_current_resolve_project().GetMediaPool()
    root_bin = root or media_pool.GetRootFolder()

    # create hierarchy of bins in case there is slash in name
    if "/" in name.replace("\\", "/"):
        child_bin = None
        for bname in name.split("/"):
            child_bin = create_bin(bname,
                                   root=child_bin or root_bin,
                                   set_as_current=set_as_current)
        if child_bin:
            return child_bin
    else:
        # Find existing folder or create it
        for subfolder in root_bin.GetSubFolderList():
            if subfolder.GetName() == name:
                created_bin = subfolder
                break
        else:
            created_bin = media_pool.AddSubFolder(root_bin, name)

        if set_as_current:
            media_pool.SetCurrentFolder(created_bin)

        return created_bin

create_compound_clip(clip_data, name, folder)

Convert timeline object into nested timeline object

Parameters:

Name Type Description Default
clip_data dict

timeline item object packed into dict with project, timeline (sequence)

required
folder Folder

media pool folder object,

required
name str

name for compound clip

required

Returns:

Type Description

resolve.MediaPoolItem: media pool item with compound clip timeline(cct)

Source code in client/ayon_resolve/api/lib.py
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
def create_compound_clip(clip_data, name, folder):
    """
    Convert timeline object into nested timeline object

    Args:
        clip_data (dict): timeline item object packed into dict
                          with project, timeline (sequence)
        folder (resolve.MediaPool.Folder): media pool folder object,
        name (str): name for compound clip

    Returns:
        resolve.MediaPoolItem: media pool item with compound clip timeline(cct)
    """
    # get basic objects form data
    resolve_project = clip_data["project"]
    timeline = clip_data["timeline"]
    clip = clip_data["clip"]

    # get details of objects
    clip_item = clip["item"]

    mp = resolve_project.GetMediaPool()

    # get clip attributes
    clip_attributes = get_clip_attributes(clip_item)

    mp_item = clip_item.GetMediaPoolItem()
    _mp_props = mp_item.GetClipProperty

    mp_first_frame = int(_mp_props("Start"))
    mp_last_frame = int(_mp_props("End"))

    # initialize basic source timing for otio
    ci_l_offset = clip_item.GetLeftOffset()
    ci_duration = clip_item.GetDuration()
    rate = float(_mp_props("FPS"))

    # source rational times
    mp_in_rc = otio.opentime.RationalTime((ci_l_offset), rate)
    mp_out_rc = otio.opentime.RationalTime((ci_l_offset + ci_duration - 1), rate)

    # get frame in and out for clip swapping
    in_frame = otio.opentime.to_frames(mp_in_rc)
    out_frame = otio.opentime.to_frames(mp_out_rc)

    # keep original sequence
    tl_origin = timeline

    # Set current folder to input media_pool_folder:
    mp.SetCurrentFolder(folder)

    # check if clip doesn't exist already:
    clips = folder.GetClipList()
    cct = next((c for c in clips
                if c.GetName() in name), None)

    if cct:
        print(f"Compound clip exists: {cct}")
    else:
        # Create empty timeline in current folder and give name:
        cct = mp.CreateEmptyTimeline(name)

        # check if clip doesn't exist already:
        clips = folder.GetClipList()
        cct = next((c for c in clips
                    if c.GetName() in name), None)
        print(f"Compound clip created: {cct}")

        with maintain_current_timeline(cct, tl_origin):
            # Add input clip to the current timeline:
            mp.AppendToTimeline([{
                "mediaPoolItem": mp_item,
                "startFrame": mp_first_frame,
                "endFrame": mp_last_frame
            }])

    # Add collected metadata and attributes to the compound clip:
    if mp_item.GetMetadata(constants.AYON_TAG_NAME):
        clip_attributes[constants.AYON_TAG_NAME] = mp_item.GetMetadata(
            constants.AYON_TAG_NAME)[constants.AYON_TAG_NAME]

    # stringify
    clip_attributes = json.dumps(clip_attributes)

    # add attributes to metadata
    for k, v in mp_item.GetMetadata().items():
        cct.SetMetadata(k, v)

    # add metadata to cct
    cct.SetMetadata(constants.AYON_TAG_NAME, clip_attributes)

    # reset start timecode of the compound clip
    cct.SetClipProperty("Start TC", _mp_props("Start TC"))

    # swap clips on timeline
    swap_clips(clip_item, cct, in_frame, out_frame)

    cct.SetClipColor("Pink")
    return cct

create_media_pool_item(files, root=None)

Create media pool item.

Parameters:

Name Type Description Default
files list

absolute path to a file

required
root (resolve.Folder)[optional]

root folder / bin object

required

Returns:

Name Type Description
object object

resolve.MediaPoolItem

Source code in client/ayon_resolve/api/lib.py
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
def create_media_pool_item(files: list,
                           root: object = None) -> object:
    """ Create media pool item.

    Args:
        files (list): absolute path to a file
        root (resolve.Folder)[optional]: root folder / bin object

    Returns:
        object: resolve.MediaPoolItem
    """
    # get all variables
    resolve_project = get_current_resolve_project()
    media_pool = resolve_project.GetMediaPool()
    root_bin = root or media_pool.GetRootFolder()

    # make sure files list is not empty and first available file exists
    filepath = next((f for f in files if os.path.isfile(f)), None)
    if not filepath:
        raise FileNotFoundError("No file found in input files list")

    # try to search in bin if the clip does not exist
    existing_mpi = get_media_pool_item(filepath, root_bin)

    if existing_mpi:
        return existing_mpi

    # add media to media-pool
    media_pool_items = media_pool.ImportMedia(files)
    if not media_pool_items:
        raise RuntimeError(f"Failed to import media: {files}")

    # return only first found
    return media_pool_items.pop()

create_timeline_item(media_pool_item, timeline=None, timeline_in=None, source_start=None, source_end=None, track_name=None)

Add media pool item to current or defined timeline.

Parameters:

Name Type Description Default
media_pool_item MediaPoolItem

resolve's object

required
timeline Optional[Timeline]

resolve's object

None
timeline_in Optional[int]

timeline input frame (sequence frame)

None
source_start Optional[int]

media source input frame (sequence frame)

None
source_end Optional[int]

media source output frame (sequence frame)

None
track_name Optional[str]

video track to add the item to. Track is created if it does not exist or is not free in item's range. Default video track is used if not set.

None

Returns:

Name Type Description
object object

resolve.TimelineItem

Source code in client/ayon_resolve/api/lib.py
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
def create_timeline_item(
    media_pool_item: object,
    timeline: object | None = None,
    timeline_in: int | None = None,
    source_start: int | None = None,
    source_end: int | None = None,
    track_name: str | None = None,
) -> object:
    """
    Add media pool item to current or defined timeline.

    Args:
        media_pool_item (resolve.MediaPoolItem): resolve's object
        timeline (Optional[resolve.Timeline]): resolve's object
        timeline_in (Optional[int]): timeline input frame (sequence frame)
        source_start (Optional[int]): media source input frame (sequence frame)
        source_end (Optional[int]): media source output frame (sequence frame)
        track_name (Optional[str]): video track to add the item to. Track is
            created if it does not exist or is not free in item's range.
            Default video track is used if not set.

    Returns:
        object: resolve.TimelineItem
    """
    # get all variables
    resolve_project = get_current_resolve_project()
    media_pool = resolve_project.GetMediaPool()
    clip_name = media_pool_item.GetClipProperty("File Name")
    timeline = timeline or get_current_timeline()

    # timing variables
    if all([
        timeline_in is not None,
        source_start is not None,
        source_end is not None
    ]):
        fps = timeline.GetSetting("timelineFrameRate")
        # number of frames, `source_end` is the last used frame (inclusive)
        duration = source_end - source_start + 1
        timecode_in = frames_to_timecode(timeline_in, fps)
        timecode_out = frames_to_timecode(timeline_in + duration - 1, fps)
    else:
        timecode_in = None
        timecode_out = None

    track_index = None
    if track_name:
        record_range = (None, None)
        if timecode_in:
            record_range = (timeline_in, timeline_in + duration)
        track_index = get_or_create_video_track(
            timeline, track_name, *record_range
        )

    # if timeline was used then switch it to current timeline
    output_timeline_item = None
    with maintain_current_timeline(timeline):
        # Add input mediaPoolItem to clip data
        clip_data = {
            "mediaPoolItem": media_pool_item,
        }

        # Movie media starts at frame 0, so do not test for truthiness.
        # Resolve ignores `endFrame` without `startFrame`.
        if source_start is not None:
            # Note: specifying a startFrame or endFrame will fail to place the
            # timeline item if the item does not have that frame, e.g. a static
            # representation on version that does have frame start and end.
            clip_data["startFrame"] = source_start
        if source_end is not None:
            # `endFrame` of AppendToTimeline is exclusive, while
            # `source_end` is the last used frame
            clip_data["endFrame"] = source_end + 1
        if timecode_in:
            # Note: specifying a recordFrame will fail to place the timeline
            #  item if there's already an existing clip at that time on the
            #  active track.
            clip_data["recordFrame"] = timeline_in
        if track_index:
            clip_data["trackIndex"] = track_index
            # video only, audio would need its own track index
            clip_data["mediaType"] = 1

        # add to timeline
        appended_items = media_pool.AppendToTimeline([clip_data])
        if appended_items:
            output_timeline_item = appended_items[0]

            # Adding the item may fail whilst Resolve will still return a
            # TimelineItem instance - however all `Get*` calls return None
            # Hence, we check whether the result is valid
            if output_timeline_item.GetDuration() is None:
                output_timeline_item = None

    if not output_timeline_item:
        raise RuntimeError(
            f"Clip name '{clip_name}' wasn't created on the timeline: "
            f"'{timeline.GetName()}' \n\n"
            "Please check if correct track position is activated, "
            "or if a clip is not already at the timeline in "
            f"position: '{timecode_in}' out: '{timecode_out}'. \n\n"
            "This may also happen if the loaded media does not have frame "
            "start, end or timecode specified in the clip data.\n\n"
            f"Clip data: {clip_data}"
        )
    return output_timeline_item

detect_project_fps_mismatch(task_entity=None)

Detect potential fps mismatch between project settings and task entity. Return any mismatch as a information string to be displayed to the user.

Source code in client/ayon_resolve/api/lib.py
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
def detect_project_fps_mismatch(task_entity=None):
    """ Detect potential fps mismatch between project settings
    and task entity. Return any mismatch as a information
    string to be displayed to the user.
    """
    task_entity = task_entity or get_current_task_entity()
    attributes = task_entity["attrib"]
    task_fps = attributes["fps"]

    resolve_project = get_current_resolve_project()
    resolve_fps = resolve_project.GetSetting("timelineFrameRate")

    if task_fps != resolve_fps:
        return (
            f"Project current fps is {resolve_fps} "
            f"while task fps is {task_fps}."
        )

    return None

detect_project_resolution_mismatch(task_entity=None)

Detect potential resolution mismatch between project settings and task entity. Return any mismatch as a information string to be displayed to the user.

Source code in client/ayon_resolve/api/lib.py
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
def detect_project_resolution_mismatch(task_entity=None):
    """ Detect potential resolution mismatch between project settings
    and task entity. Return any mismatch as a information
    string to be displayed to the user.
    """
    task_entity = task_entity or get_current_task_entity()
    attributes = task_entity["attrib"]
    width = attributes["resolutionWidth"]
    height = attributes["resolutionHeight"]

    resolve_project = get_current_resolve_project()
    resolve_width = resolve_project.GetSetting("timelineResolutionWidth")
    resolve_height = resolve_project.GetSetting("timelineResolutionHeight")

    if (str(width), str(height)) != (resolve_width, resolve_height):
        return (
            f"Project current resolution is {resolve_width}x{resolve_height} "
            f"while task resolution is {width}x{height}."
        )

    return None

export_timeline_otio(timeline)

Export timeline as otio.

Parameters:

Name Type Description Default
timeline Timeline

resolve's timeline

required

Returns:

Name Type Description
otio_timeline Timeline

Otio timeline.

Source code in client/ayon_resolve/api/lib.py
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
def export_timeline_otio(timeline):
    """ Export timeline as otio.

    Args:
        timeline (resolve.Timeline): resolve's timeline

    Returns:
        otio_timeline (otio.Timeline): Otio timeline.
    """
    # DaVinci Resolve <= 18.5
    # Legacy export (slower) through AYON.
    if not hasattr(timeline, "Export"):
        return otio_export.create_otio_timeline(
            get_current_resolve_project(),
            timeline=timeline
        )

    # DaVinci Resolve >= 18.5
    # Force export through a temporary file (native)
    temp_otio_file = _get_otio_temp_file(timeline=timeline)
    export_timeline_otio_to_file(timeline, temp_otio_file)
    otio_timeline = otio.adapters.read_from_file(temp_otio_file)

    return otio_timeline

export_timeline_otio_native(timeline, filepath)

Get timeline otio filepath.

Only supported from Resolve 18.5

Example

Native otio export is available from Resolve 18.5

[major, minor, patch, build, suffix]

resolve_version = bmdvr.GetVersion() if tuple(resolve_version[:2]) < (18, 5): # if it is lower then use ayon's otio exporter otio_timeline = davinci_export.create_otio_timeline( resolve_project, timeline=timeline) davinci_export.write_to_file(otio_timeline, filepath) else: # use native otio export export_timeline_otio_native(timeline, filepath)

Parameters:

Name Type Description Default
timeline Timeline

resolve's object

required
filepath str

otio file path

required

Returns:

Name Type Description
bool

True if success

Source code in client/ayon_resolve/api/lib.py
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
def export_timeline_otio_native(timeline, filepath):
    """Get timeline otio filepath.

    Only supported from Resolve 18.5

    Example:
        # Native otio export is available from Resolve 18.5
        # [major, minor, patch, build, suffix]
        resolve_version = bmdvr.GetVersion()
        if tuple(resolve_version[:2]) < (18, 5):
            # if it is lower then use ayon's otio exporter
            otio_timeline = davinci_export.create_otio_timeline(
                resolve_project, timeline=timeline)
            davinci_export.write_to_file(otio_timeline, filepath)
        else:
            # use native otio export
            export_timeline_otio_native(timeline, filepath)

    Args:
        timeline (resolve.Timeline): resolve's object
        filepath (str): otio file path

    Returns:
        bool: True if success
    """
    from . import bmdvr

    try:
        timeline.Export(filepath, bmdvr.EXPORT_OTIO)
    except Exception as e:
        log.error(f"Failed to export timeline otio: {e}")
        return False
    return True

export_timeline_otio_to_file(timeline, filepath)

Export timeline as otio filepath.

Parameters:

Name Type Description Default
timeline Timeline

resolve's timeline

required
filepath str

otio file path

required

Returns:

Name Type Description
str

temporary otio filepath

Source code in client/ayon_resolve/api/lib.py
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
def export_timeline_otio_to_file(timeline, filepath):
    """Export timeline as otio filepath.

    Args:
        timeline (resolve.Timeline): resolve's timeline
        filepath (str): otio file path

    Returns:
        str: temporary otio filepath
    """
    try:
        from . import bmdvr

        if bmdvr.EXPORT_OTIO is None:
            raise AttributeError("Unsupported native Export OTIO")

        timeline.Export(filepath, bmdvr.EXPORT_OTIO)

    except Exception as error:
        log.debug(
            "Cannot use native OTIO export (%r)."
            "Default to AYON own implementation.",
            error
        )
        otio_timeline = otio_export.create_otio_timeline(
            get_current_resolve_project(),
            timeline=timeline
        )
        otio_export.write_to_file(otio_timeline, filepath)

get_any_timeline()

Get any timeline object.

Returns:

Type Description

object | None: resolve.Timeline

Source code in client/ayon_resolve/api/lib.py
191
192
193
194
195
196
197
198
199
200
def get_any_timeline():
    """Get any timeline object.

    Returns:
        object | None: resolve.Timeline
    """
    resolve_project = get_current_resolve_project()
    timeline_count = resolve_project.GetTimelineCount()
    if timeline_count > 0:
        return resolve_project.GetTimelineByIndex(1)

get_clip_attributes(clip)

Collect basic attributes from resolve timeline item

Parameters:

Name Type Description Default
clip TimelineItem

timeline item object

required

Returns:

Name Type Description
dict

all collected attributres as key: values

Source code in client/ayon_resolve/api/lib.py
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
def get_clip_attributes(clip):
    """
    Collect basic attributes from resolve timeline item

    Args:
        clip (resolve.TimelineItem): timeline item object

    Returns:
        dict: all collected attributres as key: values
    """
    mp_item = clip.GetMediaPoolItem()

    return {
        "clipIn": clip.GetStart(),
        "clipOut": clip.GetEnd(),
        "clipLeftOffset": clip.GetLeftOffset(),
        "clipRightOffset": clip.GetRightOffset(),
        "clipMarkers": clip.GetMarkers(),
        "clipFlags": clip.GetFlagList(),
        "sourceId": mp_item.GetMediaId(),
        "sourceProperties": mp_item.GetClipProperty()
    }

get_clip_resolution_from_media_pool(timeline_item_data)

Return the clip resolution from media pool data.

Parameters:

Name Type Description Default
timeline_item_data dict

Timeline item to investigate.

required

Returns:

Name Type Description
resolution_info dict

The parsed resolution data.

Source code in client/ayon_resolve/api/lib.py
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
def get_clip_resolution_from_media_pool(timeline_item_data):
    """Return the clip resolution from media pool data.

    Args:
        timeline_item_data (dict): Timeline item to investigate.

    Returns:
        resolution_info (dict): The parsed resolution data.
    """
    clip_item = timeline_item_data["clip"]["item"]
    media_pool_item = clip_item.GetMediaPoolItem()
    clip_properties = media_pool_item.GetClipProperty()

    try:
        width, height = clip_properties["Resolution"].split("x")
    except (KeyError, ValueError):
        width = height = None

    try:
        clip_par = clip_properties["PAR"]  # Pixel Aspect Resolution
        pixel_aspect = constants.PAR_VALUES[clip_par]

    except (KeyError, ValueError):  # Unknown or undetected PAR
        pixel_aspect = 1.0

    return {"width": width, "height": height, "pixelAspect": pixel_aspect}

get_current_resolve_project()

Get current resolve project object.

Returns:

Type Description

resolve.Project

Source code in client/ayon_resolve/api/lib.py
159
160
161
162
163
164
165
166
def get_current_resolve_project():
    """Get current resolve project object.

    Returns:
        resolve.Project
    """
    project_manager = get_project_manager()
    return project_manager.GetCurrentProject()

get_current_timeline(new=False)

Get current timeline object.

Parameters:

Name Type Description Default
new (bool)[optional]

[DEPRECATED] if True it will create new timeline if none exists

required

Returns:

Type Description

object | None: resolve.Timeline

Source code in client/ayon_resolve/api/lib.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def get_current_timeline(new=False):
    """Get current timeline object.

    Args:
        new (bool)[optional]: [DEPRECATED] if True it will create
            new timeline if none exists

    Returns:
        object | None: resolve.Timeline
    """
    resolve_project = get_current_resolve_project()
    timeline = resolve_project.GetCurrentTimeline()

    # return current timeline if any
    if timeline:
        return timeline

    # TODO: [deprecated] and will be removed in future
    if new:
        return get_new_timeline()

get_current_timeline_items(filter=False, track_type=None, track_name=None, selecting_color=None)

Get all available current timeline track items

Source code in client/ayon_resolve/api/lib.py
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
def get_current_timeline_items(
        filter: bool = False,
        track_type: str = None,
        track_name: str = None,
        selecting_color: str = None) -> List[Dict[str, Any]]:
    """Get all available current timeline track items"""
    track_type = track_type or "video"
    selecting_color = selecting_color or constants.SELECTED_CLIP_COLOR
    resolve_project = get_current_resolve_project()

    # get timeline anyhow
    timeline = get_current_timeline() or get_any_timeline()
    if not timeline:
        return []

    selected_clips = []

    # get all tracks count filtered by track type
    selected_track_count = timeline.GetTrackCount(track_type)

    # loop all tracks and get items
    _clips = {}
    for track_index in range(1, (int(selected_track_count) + 1)):
        _track_name = timeline.GetTrackName(track_type, track_index)

        # filter out all unmatched track names
        if track_name and _track_name not in track_name:
            continue

        timeline_items = timeline.GetItemListInTrack(track_type, track_index)
        _clips[track_index] = timeline_items

        _data = {
            "project": resolve_project,
            "timeline": timeline,
            "track": {
                "name": _track_name,
                "index": track_index,
                "type": track_type,
            }
        }
        # get track item object and its color
        for clip_index, ti in enumerate(_clips[track_index]):
            data = _data.copy()
            data["clip"] = {
                "item": ti,
                "index": clip_index
            }
            ti_color = ti.GetClipColor()
            if filter and selecting_color in ti_color or not filter:
                selected_clips.append(data)
    return selected_clips

get_media_pool_item(filepath, root=None)

Return clip if found in folder with use of input file path.

Parameters:

Name Type Description Default
filepath str

absolute path to a file

required
root (resolve.Folder)[optional]

root folder / bin object

required

Returns:

Name Type Description
object object

resolve.MediaPoolItem

Source code in client/ayon_resolve/api/lib.py
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
def get_media_pool_item(filepath, root: object = None) -> object:
    """
    Return clip if found in folder with use of input file path.

    Args:
        filepath (str): absolute path to a file
        root (resolve.Folder)[optional]: root folder / bin object

    Returns:
        object: resolve.MediaPoolItem
    """
    resolve_project = get_current_resolve_project()
    media_pool = resolve_project.GetMediaPool()
    root = root or media_pool.GetRootFolder()
    fname = os.path.basename(filepath)

    for _mpi in root.GetClipList():
        _mpi_name = _mpi.GetClipProperty("File Name")
        _mpi_name = get_reformated_path(_mpi_name, first=True)
        if fname in _mpi_name:
            return _mpi
    return None

get_media_storage()

Get media storage object.

Returns:

Type Description

resolve.MediaStorage

Source code in client/ayon_resolve/api/lib.py
147
148
149
150
151
152
153
154
155
156
def get_media_storage():
    """Get media storage object.

    Returns:
        resolve.MediaStorage
    """
    from . import bmdvr, media_storage
    if not media_storage:
        media_storage = bmdvr.GetMediaStorage()
    return media_storage

get_new_timeline(timeline_name=None)

Get new timeline object.

Parameters:

Name Type Description Default
timeline_name str

New timeline name.

None

Returns:

Name Type Description
object

resolve.Timeline

Source code in client/ayon_resolve/api/lib.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def get_new_timeline(timeline_name: str = None):
    """Get new timeline object.

    Arguments:
        timeline_name (str): New timeline name.

    Returns:
        object: resolve.Timeline
    """
    resolve_project = get_current_resolve_project()
    media_pool = resolve_project.GetMediaPool()
    new_timeline = media_pool.CreateEmptyTimeline(
        timeline_name or constants.AYON_TIMELINE_NAME)
    resolve_project.SetCurrentTimeline(new_timeline)
    return new_timeline

get_or_create_video_track(timeline, name, start=None, end=None)

Get video track by name, create it if missing.

Existing track is used only if it is unlocked and, when start and end are given, free in that range. Otherwise the next numbered track (<name>_2, <name>_3, ...) is tried or created.

Parameters:

Name Type Description Default
timeline Timeline

resolve's object

required
name str

base track name

required
start Optional[int]

first timeline frame required to be free

None
end Optional[int]

timeline frame after the last required frame

None

Returns:

Name Type Description
int int

1-based track index

Source code in client/ayon_resolve/api/lib.py
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
def get_or_create_video_track(
        timeline: object,
        name: str,
        start: int | None = None,
        end: int | None = None
) -> int:
    """Get video track by name, create it if missing.

    Existing track is used only if it is unlocked and, when `start` and `end`
    are given, free in that range. Otherwise the next numbered track
    (`<name>_2`, `<name>_3`, ...) is tried or created.

    Args:
        timeline (resolve.Timeline): resolve's object
        name (str): base track name
        start (Optional[int]): first timeline frame required to be free
        end (Optional[int]): timeline frame after the last required frame

    Returns:
        int: 1-based track index
    """
    check_range = start is not None and end is not None
    suffix = 1
    while True:
        track_name = name if suffix == 1 else f"{name}_{suffix}"
        suffix += 1

        track_index = get_video_track_index_by_name(timeline, track_name)
        if track_index is None:
            if not timeline.AddTrack("video"):
                raise RuntimeError(
                    f"Failed to add video track '{track_name}' to timeline "
                    f"'{timeline.GetName()}'"
                )
            track_index = int(timeline.GetTrackCount("video"))
            timeline.SetTrackName("video", track_index, track_name)
            return track_index

        if timeline.GetIsTrackLocked("video", track_index):
            continue

        if check_range and not is_track_range_free(
            timeline, track_index, start, end
        ):
            continue
        return track_index

get_otio_clip_instance_data(otio_timeline, timeline_item_data)

Return otio objects for timeline, track and clip

Parameters:

Name Type Description Default
timeline_item_data dict

timeline_item_data from list returned by resolve.get_current_timeline_items()

required
otio_timeline Timeline

otio object

required

Returns:

Name Type Description
dict

otio clip object

Source code in client/ayon_resolve/api/lib.py
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
def get_otio_clip_instance_data(otio_timeline, timeline_item_data):
    """
    Return otio objects for timeline, track and clip

    Args:
        timeline_item_data (dict): timeline_item_data from list returned by
                                resolve.get_current_timeline_items()
        otio_timeline (otio.schema.Timeline): otio object

    Returns:
        dict: otio clip object

    """

    timeline_item = timeline_item_data["clip"]["item"]
    track_name = timeline_item_data["track"]["name"]
    timeline_range = create_otio_time_range_from_timeline_item_data(
        timeline_item_data)

    try:  # opentimelineio >= 0.16.0
        all_clips = otio_timeline.find_clips()
    except AttributeError:  # legacy
        all_clips = otio_timeline.each_clip()

    for otio_clip in all_clips:
        track_name = otio_clip.parent().name
        parent_range = otio_clip.range_in_parent()
        if track_name not in track_name:
            continue
        if otio_clip.name not in timeline_item.GetName():
            continue
        if is_overlapping_otio_ranges(
                parent_range, timeline_range, strict=True):

            # add pypedata marker to otio_clip metadata
            for marker in otio_clip.markers:
                if constants.AYON_MARKER_NAME in marker.name:
                    otio_clip.metadata.update(marker.metadata)
            return {"otioClip": otio_clip}

    return None

get_project_manager()

Get project manager object.

Returns:

Type Description

resolve.ProjectManager

Source code in client/ayon_resolve/api/lib.py
134
135
136
137
138
139
140
141
142
143
144
def get_project_manager():
    """Get project manager object.

    Returns:
        resolve.ProjectManager
    """
    from . import bmdvr, project_manager
    if not project_manager:
        project_manager = bmdvr.GetProjectManager()

    return project_manager

get_publish_attribute(timeline_item)

Get Publish attribute from marker on timeline item

Attribute

timeline_item (resolve.TimelineItem): resolve's object

Source code in client/ayon_resolve/api/lib.py
788
789
790
791
792
793
794
795
def get_publish_attribute(timeline_item):
    """ Get Publish attribute from marker on timeline item

    Attribute:
        timeline_item (resolve.TimelineItem): resolve's object
    """
    tag_data = get_timeline_item_ayon_tag(timeline_item)
    return tag_data["publish"]

get_pype_clip_metadata(clip)

Get AYON metadata created by creator plugin

Attributes:

Name Type Description
clip TimelineItem

resolve's object

Returns:

Name Type Description
dict

hierarchy, orig clip attributes

Source code in client/ayon_resolve/api/lib.py
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
def get_pype_clip_metadata(clip):
    """
    Get AYON metadata created by creator plugin

    Attributes:
        clip (resolve.TimelineItem): resolve's object

    Returns:
        dict: hierarchy, orig clip attributes
    """
    mp_item = clip.GetMediaPoolItem()
    metadata = mp_item.GetMetadata()

    return metadata.get(constants.AYON_TAG_NAME)

get_reformated_path(path, padded=False, first=False)

Return fixed python expression path

Parameters:

Name Type Description Default
path str

path url or simple file name

required

Returns:

Name Type Description
type

string with reformatted path

Example

get_reformated_path("plate.[0001-1008].exr") > plate.%04d.exr

Source code in client/ayon_resolve/api/lib.py
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
def get_reformated_path(path, padded=False, first=False):
    """
    Return fixed python expression path

    Args:
        path (str): path url or simple file name

    Returns:
        type: string with reformatted path

    Example:
        get_reformated_path("plate.[0001-1008].exr") > plate.%04d.exr

    """
    first_frame_pattern = re.compile(r"\[(\d+)\-\d+\]")

    if "[" in path:
        padding_pattern = r"(\d+)(?=-)"
        padding = len(re.findall(padding_pattern, path).pop())
        num_pattern = r"(\[\d+\-\d+\])"
        if padded:
            path = re.sub(num_pattern, f"%0{padding}d", path)
        elif first:
            first_frame = re.findall(first_frame_pattern, path, flags=0)
            if len(first_frame) >= 1:
                first_frame = first_frame[0]
            path = re.sub(num_pattern, first_frame, path)
        else:
            path = re.sub(num_pattern, "%d", path)
    return path

get_timeline_item(media_pool_item, timeline=None)

Returns clips related to input mediaPoolItem.

Parameters:

Name Type Description Default
media_pool_item MediaPoolItem

resolve's object

required
timeline (resolve.Timeline)[optional]

resolve's object

required

Returns:

Name Type Description
object object

resolve.TimelineItem

Source code in client/ayon_resolve/api/lib.py
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
def get_timeline_item(media_pool_item: object,
                      timeline: object = None) -> object:
    """
    Returns clips related to input mediaPoolItem.

    Args:
        media_pool_item (resolve.MediaPoolItem): resolve's object
        timeline (resolve.Timeline)[optional]: resolve's object

    Returns:
        object: resolve.TimelineItem
    """
    clip_name = media_pool_item.GetClipProperty("File Name")
    output_timeline_item = None
    timeline = timeline or get_current_timeline()

    with maintain_current_timeline(timeline):
        # search the timeline for the added clip

        for ti_data in get_current_timeline_items():
            ti_clip_item = ti_data["clip"]["item"]
            ti_media_pool_item = ti_clip_item.GetMediaPoolItem()

            # Skip items that do not have a media pool item, like for example
            # an "Adjustment Clip" or a "Fusion Composition" from the effects
            # toolbox
            if not ti_media_pool_item:
                continue

            if clip_name in ti_media_pool_item.GetClipProperty("File Name"):
                output_timeline_item = ti_clip_item

    return output_timeline_item

get_timeline_item_ayon_tag(timeline_item)

Get ayon track item tag created by creator or loader plugin.

Parameters:

Name Type Description Default
timeline_item TimelineItem

resolve object

required

Returns:

Name Type Description
dict

ayon tag data

Source code in client/ayon_resolve/api/lib.py
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
def get_timeline_item_ayon_tag(timeline_item):
    """
    Get ayon track item tag created by creator or loader plugin.

    Args:
        timeline_item (resolve.TimelineItem): resolve object

    Returns:
        dict: ayon tag data

    """
    return_tag = None

    if constants.AYON_MARKER_WORKFLOW:
        return_tag = get_ayon_marker(timeline_item)
    else:
        media_pool_item = timeline_item.GetMediaPoolItem()

        # get all tags from track item
        _tags = media_pool_item.GetMetadata()
        if not _tags:
            return None
        for key, data in _tags.items():
            # return only correct tag defined by global name
            if key in constants.AYON_TAG_NAME:
                return_tag = json.loads(data)

    return return_tag

get_timeline_item_by_name(name)

Get timeline item by name.

Parameters:

Name Type Description Default
name str

name of timeline item

required

Returns:

Name Type Description
object object

resolve.TimelineItem

Source code in client/ayon_resolve/api/lib.py
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
def get_timeline_item_by_name(name: str) -> object:
    """Get timeline item by name.

    Args:
        name (str): name of timeline item

    Returns:
        object: resolve.TimelineItem

    """
    for _ti_data in get_current_timeline_items():
        _ti_clip = _ti_data["clip"]["item"]
        tag_data = get_timeline_item_ayon_tag(_ti_clip)
        tag_name = tag_data.get("namespace")
        if not tag_name:
            continue
        if tag_name in name:
            return _ti_clip
    return None

get_timeline_media_pool_item(timeline, root=None)

Return MediaPoolItem from Timeline

Parameters:

Name Type Description Default
timeline Timeline

timeline object

required
root Folder

root folder / bin object

None

Returns:

Type Description
object

resolve.MediaPoolItem: media pool item from timeline

Source code in client/ayon_resolve/api/lib.py
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
def get_timeline_media_pool_item(timeline, root=None) -> object:
    """Return MediaPoolItem from Timeline


    Args:
        timeline (resolve.Timeline): timeline object
        root (resolve.Folder): root folder / bin object

    Returns:
        resolve.MediaPoolItem: media pool item from timeline
    """

    # Due to limitations in the Resolve API we can't get
    # the media pool item directly from the timeline.
    # We can find it by name, however names are not
    # enforced to be unique across bins. So, we give it
    # unique name.
    original_name = timeline.GetName()
    identifier = str(uuid.uuid4().hex)
    try:
        timeline.SetName(identifier)
        for item in iter_all_media_pool_clips(root=root):
            if item.GetName() != identifier:
                continue
            return item
    finally:
        # Revert to original name
        timeline.SetName(original_name)

get_track_end_frame(timeline, track_index, track_type='video')

Return the first timeline frame after the last item of the track.

Returns:

Type Description
int | None

Optional[int]: None if the track has no items.

Source code in client/ayon_resolve/api/lib.py
357
358
359
360
361
362
363
364
365
366
def get_track_end_frame(
        timeline: object, track_index: int, track_type: str = "video"
) -> int | None:
    """Return the first timeline frame after the last item of the track.

    Returns:
        Optional[int]: None if the track has no items.
    """
    items = timeline.GetItemListInTrack(track_type, track_index) or []
    return max((int(item.GetEnd()) for item in items), default=None)

get_video_track_index_by_name(timeline, name)

Return index of the first video track with given name.

Parameters:

Name Type Description Default
timeline Timeline

resolve's object

required
name str

track name

required

Returns:

Type Description
int

Optional[int]: 1-based track index, None if there is no such track.

Source code in client/ayon_resolve/api/lib.py
341
342
343
344
345
346
347
348
349
350
351
352
353
354
def get_video_track_index_by_name(timeline: object, name: str) -> int:
    """Return index of the first video track with given name.

    Args:
        timeline (resolve.Timeline): resolve's object
        name (str): track name

    Returns:
        Optional[int]: 1-based track index, None if there is no such track.
    """
    for track_index in range(1, int(timeline.GetTrackCount("video")) + 1):
        if timeline.GetTrackName("video", track_index) == name:
            return track_index
    return None

imprint(timeline_item, data=None)

Adding AYON data into a timeline item track item tag.

Also including publish attribute into tag.

Parameters:

Name Type Description Default
timeline_item TimelineItem

resolve's object

required
data dict

Any data which needs to be imprinted

None

Examples:

data = { 'folderPath': '/shots/sq020sh0280', 'productBaseType': 'render', 'productName': 'renderMain' }

Source code in client/ayon_resolve/api/lib.py
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
def imprint(timeline_item, data=None):
    """
    Adding `AYON data` into a timeline item track item tag.

    Also including publish attribute into tag.

    Arguments:
        timeline_item (resolve.TimelineItem): resolve's object
        data (dict): Any data which needs to be imprinted

    Examples:
        data = {
            'folderPath': '/shots/sq020sh0280',
            'productBaseType': 'render',
            'productName': 'renderMain'
        }
    """
    data = data or {}

    set_timeline_item_ayon_tag(timeline_item, data)

    # add publish attribute
    set_publish_attribute(timeline_item, True)

is_track_range_free(timeline, track_index, start, end, track_type='video')

Check that no item of the track overlaps with start - end range.

Parameters:

Name Type Description Default
timeline Timeline

resolve's object

required
track_index int

1-based track index

required
start int

first timeline frame of the range

required
end int

timeline frame after the last frame of the range

required
track_type str

resolve's track type

'video'

Returns:

Name Type Description
bool bool

True if the range is not used by any item.

Source code in client/ayon_resolve/api/lib.py
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
def is_track_range_free(
        timeline: object,
        track_index: int,
        start: int,
        end: int,
        track_type: str = "video"
) -> bool:
    """Check that no item of the track overlaps with `start` - `end` range.

    Args:
        timeline (resolve.Timeline): resolve's object
        track_index (int): 1-based track index
        start (int): first timeline frame of the range
        end (int): timeline frame after the last frame of the range
        track_type (str): resolve's track type

    Returns:
        bool: True if the range is not used by any item.
    """
    # item `GetEnd` and `end` are exclusive, so an item ending at `start`
    # is right before the range and does not overlap
    items = timeline.GetItemListInTrack(track_type, track_index) or []
    return not any(
        int(item.GetStart()) < end and int(item.GetEnd()) > start
        for item in items
    )

iter_all_media_pool_clips(root=None)

Recursively iterate all media pool clips in current project

Parameters:

Name Type Description Default
root Optional[Folder]

root folder / bin object. When None, defaults to media pool root folder.

None
Source code in client/ayon_resolve/api/lib.py
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
def iter_all_media_pool_clips(root=None):
    """Recursively iterate all media pool clips in current project

    Args:
        root (Optional[resolve.Folder]): root folder / bin object.
            When None, defaults to media pool root folder.
    """
    root = root or get_current_resolve_project().GetMediaPool().GetRootFolder()
    queue = [root]
    for folder in queue:
        for clip in folder.GetClipList():
            yield clip
        queue.extend(folder.GetSubFolderList())

maintain_current_timeline(to_timeline, from_timeline=None)

Maintain current timeline selection during context

Attributes:

Name Type Description
from_timeline resolve.Timeline)[optional]

Example: >>> print(from_timeline.GetName()) timeline1 >>> print(to_timeline.GetName()) timeline2

>>> with maintain_current_timeline(to_timeline):
...     print(get_current_timeline().GetName())
timeline2

>>> print(get_current_timeline().GetName())
timeline1
Source code in client/ayon_resolve/api/lib.py
 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
@contextlib.contextmanager
def maintain_current_timeline(to_timeline: object,
                              from_timeline: object = None):
    """Maintain current timeline selection during context

    Attributes:
        from_timeline (resolve.Timeline)[optional]:
    Example:
        >>> print(from_timeline.GetName())
        timeline1
        >>> print(to_timeline.GetName())
        timeline2

        >>> with maintain_current_timeline(to_timeline):
        ...     print(get_current_timeline().GetName())
        timeline2

        >>> print(get_current_timeline().GetName())
        timeline1
    """
    project = get_current_resolve_project()
    working_timeline = from_timeline or project.GetCurrentTimeline()

    # search timeline withing project timelines in case the
    # to_timeline is MediaPoolItem
    # Note: this is a hacky way of identifying if object is timeline since
    #   mediapool item is not having AddTrack attribute. API is not providing
    #   any other way to identify the object type. And hasattr is returning
    #   false info.
    if "AddTrack" not in dir(to_timeline):
        tcount = project.GetTimelineCount()
        for idx in range(0, int(tcount)):
            timeline = project.GetTimelineByIndex(idx + 1)
            if timeline.GetName() == to_timeline.GetName():
                to_timeline = timeline
                break

    try:
        # switch to the input timeline
        result = project.SetCurrentTimeline(to_timeline)
        if not result:
            raise ValueError(f"Failed to switch to timeline: {to_timeline}")

        current_timeline = project.GetCurrentTimeline()
        yield current_timeline
    finally:
        # put the original working timeline to context
        project.SetCurrentTimeline(working_timeline)

maintain_page_by_name(page_name)

Maintain specific page by name.

Parameters:

Name Type Description Default
page_name str

name of the page

required
Example

with maintain_page_by_name("Deliver"): ... print("Deliver page is open") Deliver page is open

Source code in client/ayon_resolve/api/lib.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
@contextlib.contextmanager
def maintain_page_by_name(page_name):
    """Maintain specific page by name.

    Args:
        page_name (str): name of the page

    Example:
        >>> with maintain_page_by_name("Deliver"):
        ...     print("Deliver page is open")
        Deliver page is open
    """
    from . import bmdvr
    current_page = bmdvr.GetCurrentPage()

    if not bmdvr.OpenPage(page_name):
        raise ValueError(f"Could not open page {page_name}")

    try:
        yield
    finally:
        bmdvr.OpenPage(current_page)

remove_media_pool_item(media_pool_item)

Remove media pool item.

Parameters:

Name Type Description Default
media_pool_item MediaPoolItem

resolve's object

required

Returns:

Name Type Description
bool bool

True if success

Source code in client/ayon_resolve/api/lib.py
267
268
269
270
271
272
273
274
275
276
277
278
def remove_media_pool_item(media_pool_item: object) -> bool:
    """Remove media pool item.

    Args:
        media_pool_item (resolve.MediaPoolItem): resolve's object

    Returns:
        bool: True if success
    """
    resolve_project = get_current_resolve_project()
    media_pool = resolve_project.GetMediaPool()
    return media_pool.DeleteClips([media_pool_item])

set_project_fps(task_entity=None)

Attempt to set project frame rate from AYON current task. This might not be possible if a timeline already exists within the project. Return a error string if any issue so it can be displayed to the user.

Source code in client/ayon_resolve/api/lib.py
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
def set_project_fps(task_entity=None):
    """ Attempt to set project frame rate from AYON current task.
    This might not be possible if a timeline already exists within the project.
    Return a error string if any issue so it can be displayed to the user.
    """
    task_entity = task_entity or get_current_task_entity()
    attributes = task_entity["attrib"]

    # Set project frame rate and resolution
    resolve_project = get_current_resolve_project()
    project_fps = attributes["fps"]

    SUPPORTED_FPS = {
        16.0: "16",
        18.0: "18",
        23.976: "23.976",
        24.0: "24",
        25.0: "25",
        29.97: "29.97",
        30.0: "30",
        47.952: "47.952",
        48.0: "48",
        50.0: "50"
    }

    if float(project_fps) in SUPPORTED_FPS:
        if not resolve_project.SetSetting(
            "timelineFrameRate",
            SUPPORTED_FPS[float(project_fps)]
        ):
            # Resolve does not allow to edit timeline fps
            # project settings once a timeline has been created.
            return (
                "Cannot override Project fps from AYON."
                " This could be because a timeline already exists."
            )

        return None

    else:
        return (
            "Fps set in AYON project is not supported by Resolve"
            f" attempt to set {project_fps},"
            f" supported are {tuple(SUPPORTED_FPS.keys())}."
        )

set_project_manager_to_folder_name(folder_name)

Sets context of Project manager to given folder by name.

Searching for folder by given name from root folder to nested. If no existing folder by name it will create one in root folder.

Parameters:

Name Type Description Default
folder_name str

name of searched folder

required

Returns:

Name Type Description
bool

True if success

Raises:

Type Description
Exception

Cannot create folder in root

Source code in client/ayon_resolve/api/lib.py
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
def set_project_manager_to_folder_name(folder_name):
    """
    Sets context of Project manager to given folder by name.

    Searching for folder by given name from root folder to nested.
    If no existing folder by name it will create one in root folder.

    Args:
        folder_name (str): name of searched folder

    Returns:
        bool: True if success

    Raises:
        Exception: Cannot create folder in root

    """
    # initialize project manager
    project_manager = get_project_manager()

    set_folder = False

    # go back to root folder
    if project_manager.GotoRootFolder():
        log.info(f"Testing existing folder: {folder_name}")
        folders = _convert_resolve_list_type(
            project_manager.GetFoldersInCurrentFolder())
        log.info(f"Testing existing folders: {folders}")
        # get me first available folder object
        # with the same name as in `folder_name` else return False
        if next((f for f in folders if f in folder_name), False):
            log.info(f"Found existing folder: {folder_name}")
            set_folder = project_manager.OpenFolder(folder_name)

    if set_folder:
        return True

    # if folder by name is not existent then create one
    # go back to root folder
    log.info(f"Folder `{folder_name}` not found and will be created")
    if project_manager.GotoRootFolder():
        try:
            # create folder by given name
            project_manager.CreateFolder(folder_name)
            project_manager.OpenFolder(folder_name)
            return True
        except NameError as e:
            log.error((f"Folder with name `{folder_name}` cannot be created!"
                       f"Error: {e}"))
            return False

set_project_resolution(task_entity=None)

Attempt to set project resolution from AYON current task. Return a error string if any issue so it can be displayed to the user.

Source code in client/ayon_resolve/api/lib.py
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
def set_project_resolution(task_entity=None):
    """ Attempt to set project resolution from AYON current task.
    Return a error string if any issue so it can be displayed to the user.
    """
    task_entity = task_entity or get_current_task_entity()
    attributes = task_entity["attrib"]

    resolve_project = get_current_resolve_project()
    width = attributes["resolutionWidth"]
    height = attributes["resolutionHeight"]

    resolution_params = {
        "timelineResolutionHeight": height,
        "timelineResolutionWidth": width,
    }

    # In order to set vertical resolution in resolve,
    # the "Use vertical resolution" option need to be enabled.
    # This is not exposed from the Python API.
    if height > width:
            return (
                "Cannot override Project resolution from AYON."
                f" Vertical resolution {width}x{height}"
                " is unsupported from the API."
            )

    for resolve_param, value in resolution_params.items():
        if not resolve_project.SetSetting(
            resolve_param,
            str(int(value))
        ):
            return (
                "Cannot override Project resolution from AYON."
                f" trying to set {resolve_param} = {value}"
            )

    SUPPORTED_PIXEL_ASPECTS = {
        1.0: "Square",
        16/9: "16:9 anamorphic",
        4/3: "4:3 standard definition",
        2.0: "Cinemascope",
    }
    pixel_aspect_ratio = round(attributes["pixelAspect"], 2)

    for supported_pa in SUPPORTED_PIXEL_ASPECTS:
        if round(supported_pa, 2) != pixel_aspect_ratio:
            continue

        if not resolve_project.SetSetting(
            "timelinePixelAspectRatio",
            SUPPORTED_PIXEL_ASPECTS[supported_pa]
        ):
            return (
                "Cannot override Project pixel aspect ratio from AYON."
                " trying to set timelinePixelAspectRatio = "
                f"{SUPPORTED_PIXEL_ASPECTS[supported_pa]}"
            )

        return None

    else:
        return (
            "Pixel Aspect Ratio set in AYON project is not supported"
            f" by Resolve, attempt to set {pixel_aspect_ratio},"
            f" supported are {tuple(SUPPORTED_PIXEL_ASPECTS.keys())}."
        )

set_publish_attribute(timeline_item, value)

Set Publish attribute to marker on timeline item

Attribute

timeline_item (resolve.TimelineItem): resolve's object

Source code in client/ayon_resolve/api/lib.py
776
777
778
779
780
781
782
783
784
785
def set_publish_attribute(timeline_item, value):
    """ Set Publish attribute to marker on timeline item

    Attribute:
        timeline_item (resolve.TimelineItem): resolve's object
    """
    tag_data = get_timeline_item_ayon_tag(timeline_item)
    tag_data["publish"] = value
    # set data to the publish attribute
    set_timeline_item_ayon_tag(timeline_item, tag_data)

set_timeline_item_ayon_tag(timeline_item, data=None)

Set ayon track item tag to input timeline_item.

Attributes:

Name Type Description
trackItem TimelineItem

resolve api object

Returns:

Name Type Description
dict

json loaded data

Source code in client/ayon_resolve/api/lib.py
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
def set_timeline_item_ayon_tag(timeline_item, data=None):
    """
    Set ayon track item tag to input timeline_item.

    Attributes:
        trackItem (resolve.TimelineItem): resolve api object

    Returns:
        dict: json loaded data
    """
    data = data or {}

    # get available ayon tag if any
    tag_data = get_timeline_item_ayon_tag(timeline_item)

    if constants.AYON_MARKER_WORKFLOW:
        # delete tag as it is not updatable
        if tag_data:
            delete_ayon_marker(timeline_item)

        tag_data.update(data)
        set_ayon_marker(timeline_item, tag_data)
    else:
        if tag_data:
            media_pool_item = timeline_item.GetMediaPoolItem()
            # it not tag then create one
            tag_data.update(data)
            media_pool_item.SetMetadata(
                constants.AYON_TAG_NAME, json.dumps(tag_data))
        else:
            tag_data = data
            # if ayon tag available then update with input data
            # add it to the input track item
            timeline_item.SetMetadata(
                constants.AYON_TAG_NAME, json.dumps(tag_data))

    return tag_data

swap_clips(from_clip, to_clip, to_in_frame, to_out_frame)

Swapping clips on timeline in timelineItem

It will add take and activate it to the frame range which is inputted

Parameters:

Name Type Description Default
to_clip_name str

name of to_clip

required
to_in_frame float

cut in frame, usually GetLeftOffset()

required
to_out_frame float

cut out frame, usually left offset plus duration

required

Returns:

Name Type Description
bool

True if successfully replaced

Source code in client/ayon_resolve/api/lib.py
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
def swap_clips(from_clip, to_clip, to_in_frame, to_out_frame):
    """
    Swapping clips on timeline in timelineItem

    It will add take and activate it to the frame range which is inputted

    Args:
        from_clip (resolve.TimelineItem)
        to_clip (resolve.mediaPoolItem)
        to_clip_name (str): name of to_clip
        to_in_frame (float): cut in frame, usually `GetLeftOffset()`
        to_out_frame (float): cut out frame, usually left offset plus duration

    Returns:
        bool: True if successfully replaced

    """
    # copy ACES input transform from timeline clip to new media item
    mediapool_item_from_timeline = from_clip.GetMediaPoolItem()
    _idt = mediapool_item_from_timeline.GetClipProperty('IDT')
    to_clip.SetClipProperty('IDT', _idt)

    _clip_prop = to_clip.GetClipProperty
    to_clip_name = _clip_prop("File Name")
    # add clip item as take to timeline
    take = from_clip.AddTake(
        to_clip,
        float(to_in_frame),
        float(to_out_frame)
    )

    if not take:
        return False

    for take_index in range(1, (int(from_clip.GetTakesCount()) + 1)):
        take_item = from_clip.GetTakeByIndex(take_index)
        take_mp_item = take_item["mediaPoolItem"]
        if to_clip_name in take_mp_item.GetName():
            from_clip.SelectTakeByIndex(take_index)
            from_clip.FinalizeTake()
            return True
    return False