def scan_strips(context, sub, selected):
    frames = list()
    for obj in Get.objects_nla(context):
        data = getattr(obj, 'data', None)
        shapes = getattr(data, 'shape_keys', None)

        # Poll object
        if pose(context):
            if not pose(obj):
                continue
            elif selected:
                if not Get.selected_pose_bones(context, src=obj):
                    continue
        else:
            if (selected and obj not in selected):
                continue

        for src in (obj, data, shapes):
            frames_fcurve = list()
            frames_strip = list()
            for item in Get.strip_tracks(src, selected=False):
                strip = item.strip

                if strip.mute and not strip.select:
                    continue

                for fc in strip.fcurves:
                    if fc.hide:
                        continue
                    scan_fcurve(context, sub, fc, frames_fcurve)

                if frames_fcurve:
                    # Don't scan strip boxes if have animation keyframes
                    continue

                (fs, fe) = (strip.frame_start, strip.frame_end)
                if not sub:
                    (fs, fe) = (smooth(fs), smooth(fe))
                frames_strip.append(fs)
                frames_strip.append(fe)

                # Get loop ends
                if (strip.action) and (strip.repeat != 1.0):
                    afe = strip.action_frame_end
                    fel = fer = Get.frame_from_strip(context, strip, afe)
                    while fel < strip.frame_end:
                        frames_strip.append([smooth(fel), fel][sub])
                        fel += (fer - strip.frame_start)

            if frames_fcurve:
                frames.extend(frames_fcurve)
            else:
                frames.extend(frames_strip)

    return sorted(set(frames))
def scan_actions(context, sub, selected):
    scn = context.scene
    fc = (int(scn.frame_current_final), scn.frame_current_final)[sub]
    frames = list()

    for obj in selected:
        if Is.gpencil(obj):
            for layer in obj.data.layers:
                for frame in layer.frames:
                    frames.append(frame.frame_number)

    for obj in Get.objects_nla(context):
        # Poll object
        if pose(context):
            if not pose(obj):
                continue
            elif selected:
                bones = [
                    f'pose.bones[\"{b.name}\"]'
                    for b in Get.selected_pose_bones(context, src=obj)
                ]
                if not bones:
                    continue
            else:
                bones = list()
        else:
            if (selected and obj not in selected):
                continue
            else:
                bones = list()

        for anim in Get.animation_datas(obj):
            if (not anim.action):
                continue
            if anim.use_tweak_mode:
                for s in Get.strips(anim.id_data):
                    if s.action == anim.action:
                        strip = s
                        if s.active: break

                # Get loop ends
                cycle = list()
                afe = strip.action_frame_end
                fel = fer = Get.frame_from_strip(context, strip, afe)
                while fel < strip.frame_end:
                    offset = (fer - strip.frame_start)
                    cycle.append(offset)
                    fel += abs(offset)
                    # If offset becomes negative, it "should" create an infinite loop
                    # abs() forces positive, which "should" prevent loop
            else:
                strip = None

            for fcurve in anim.action.fcurves:
                path = fcurve.data_path
                if bones:
                    # Bones are selected, so verify this fcurve if for one of them
                    for bpath in bones:
                        if path.startswith(bpath):
                            break
                    else:
                        continue
                elif path.startswith('pose.bones[\"'):
                    try:
                        eval(repr(obj) + '.' + path)  # Validate path
                        bpath = path.split('\"]', 1)[0] + '\"]'
                        bone = eval(repr(obj) + '.' + bpath)
                        if not Is.visible(context, bone):
                            continue
                    except:
                        # curve points to a removed bone or something
                        continue

                scan_fcurve(context, sub, fcurve, frames, strip=strip)

    return sorted(set(frames))