[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/workflows/publish_action.yml",
    "content": "name: Publish to Comfy registry\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - main\n    paths:\n      - \"pyproject.toml\"\n\njobs:\n  publish-node:\n    name: Publish Custom Node to registry\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out code\n        uses: actions/checkout@v4\n      - name: Publish Custom Node\n        uses: Comfy-Org/publish-node-action@main\n        with:\n          ## Add your own personal access token to your Github Repository secrets and reference it here.\n          personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintainted in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Deforum LLC.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Deforum for ComfyUI\n\nDeforum integration for ComfyUI.\n\n## Installation\n\nTo get started with Deforum Comfy Nodes, please make sure ComfyUI is installed and you are using Python v3.10 or these nodes will not work. We recommend using a virtual environment.\n\nFollow the steps below depending on your method of preference.\n\n### ComfyUI Manager\n\nLook for `Deforum Nodes` by `XmYx`\n\n### Manual Install \n\nTo install Deforum for ComfyUI we will clone this repo into the `custom_nodes` folder\n```bash\ngit clone https://github.com/XmYx/deforum-comfy-nodes.git\n```\n\n## Recommended Custom Nodes\nHere is a list of extra custom nodes that greatly improves the experience of using Deforum.\n```bash\nhttps://github.com/rgthree/rgthree-comfy\nhttps://github.com/a1lazydog/ComfyUI-AudioScheduler\nhttps://github.com/cubiq/ComfyUI_IPAdapter_plus\nhttps://github.com/Kosinkadink/ComfyUI-VideoHelperSuite\nhttps://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet\nhttps://github.com/WASasquatch/was-node-suite-comfyui\nhttps://github.com/11cafe/comfyui-workspace-manager\nhttps://github.com/cubiq/ComfyUI_essentials\nhttps://github.com/FizzleDorf/ComfyUI_FizzNodes\nhttps://github.com/ltdrdata/ComfyUI-Impact-Pack\nhttps://github.com/Fannovel16/ComfyUI-Frame-Interpolation\nhttps://github.com/Fannovel16/ComfyUI-Video-Matting\nhttps://github.com/crystian/ComfyUI-Crystools\n```\n\n## Usage\n\n1. Launch ComfyUI\n2. Load any of the example workflows from the examples folder.\n3. Queue prompt, this will generate your first frame, you can enable Auto queueing, or batch as many images as long you'd\nlike your animation to be.\n\n## Contribution\n\nWe welcome contributions from the community! If you're interested in improving Deforum Comfy Nodes or have ideas for new features, please follow these steps:\n\n1. Fork the repository on GitHub.\n2. Create a new branch for your feature or fix.\n3. Commit your changes with clear, descriptive messages.\n4. Push your changes to the branch and open a pull request.\n\n## License\n\nDeforum Comfy Nodes is licensed under the MIT License. For more details, see the LICENSE file in the repository.\n\n## Community and Support\n\nJoin our Discord community to discuss Deforum Comfy Nodes, share your creations, and get help from the developers and other users: \n\n[Join Discord](https://discord.gg/deforum)\n\n[Visit our website](https://deforum.art)\n\n![Deforum Website Logo](docs/logo.png)\n"
  },
  {
    "path": "__init__.py",
    "content": "from .deforum_nodes.mapping import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS\nWEB_DIRECTORY = \"./web\"\n\n__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', \"WEB_DIRECTORY\"]"
  },
  {
    "path": "deforum_nodes/__init__.py",
    "content": ""
  },
  {
    "path": "deforum_nodes/exec_hijack.py",
    "content": "import execution, nodes\norig_exec = execution.map_node_over_list\n\ndef map_node_over_list(obj, input_data_all, func, allow_interrupt=False, execution_block_cb=None, pre_execute_cb=None):\n    try:\n        # check if node wants the lists\n        input_is_list = getattr(obj, \"INPUT_IS_LIST\", False)\n\n        if len(input_data_all) == 0:\n            max_len_input = 0\n        else:\n            max_len_input = max([len(x) for x in input_data_all.values() if x is not None])\n        # get a slice of inputs, repeat last input when list isn't long enough\n        def slice_dict(d, i):\n            d_new = dict()\n            for k, v in d.items():\n                if v is None:  # Skip if any of the values is None\n                    return None\n                d_new[k] = v[i if len(v) > i else -1]\n            return d_new\n\n        results = []\n\n        for k, v in input_data_all.items():\n            if v == \"skip\":\n                print(\"[deforum] Skipping execution of\", obj)\n                return []\n        \n        def process_inputs(inputs, index=None):\n            if allow_interrupt:\n                nodes.before_node_execution()\n            execution_block = None\n            for k, v in inputs.items():\n                if isinstance(v, ExecutionBlocker):\n                    execution_block = execution_block_cb(v) if execution_block_cb else v\n                    break\n            if execution_block is None:\n                if pre_execute_cb is not None and index is not None:\n                    pre_execute_cb(index)\n                results.append(getattr(obj, func)(**inputs))\n            else:\n                results.append(execution_block)\n                \n        if input_is_list:\n            process_inputs(input_data_all, 0)\n        elif max_len_input == 0:\n            process_inputs({})\n        else:\n            for i in range(max_len_input):\n                sliced_input = slice_dict(input_data_all, i)\n                if sliced_input is None:  # Skip this iteration if there's a None value after slicing\n                    continue\n                if allow_interrupt:\n                    nodes.before_node_execution()\n                for k, v in sliced_input.items():\n                    if v == \"skip\":\n                        print(\"[deforum] Skipping execution of\", obj)\n                        return []\n                process_inputs(sliced_input, i)\n\n        return results\n    except:\n        print(\"[deforum] Executor HiJack Failed and was deactivated, please report the issue on GitHub\")\n        execution.map_node_over_list = orig_exec\n        return orig_exec(obj, input_data_all, func, allow_interrupt, execution_block_cb, pre_execute_cb)\n\nexecution.map_node_over_list = map_node_over_list\n\nprint(\"[deforum] Execution HiJack Active\")\n"
  },
  {
    "path": "deforum_nodes/mapping.py",
    "content": "import importlib\nimport inspect\nimport sys\n\"\"\"\nDEFORUM STORAGE IMPORT\n\"\"\"\nfrom .modules.deforum_constants import DeforumStorage\n\ngs = DeforumStorage()\n\n\"\"\"\nNODE CLASS IMPORTS\n\"\"\"\nfrom .nodes.deforum_audiosync_nodes import *\nfrom .nodes.deforum_cache_nodes import *\nfrom .nodes.deforum_cnet_nodes import *\nfrom .nodes.deforum_cond_nodes import *\nfrom .nodes.deforum_data_nodes import *\nfrom .nodes.deforum_framewarp_node import *\nfrom .nodes.deforum_hybrid_nodes import *\nfrom .nodes.deforum_interpolation_nodes import *\nfrom .nodes.deforum_image_nodes import *\nfrom .nodes.deforum_iteration_nodes import *\nfrom .nodes.deforum_legacy_nodes import *\nfrom .nodes.deforum_logic_nodes import *\ntry:\n    from .nodes.deforum_noise_nodes import AddCustomNoiseNode\nexcept:\n    pass\ntry:\n    from .nodes.deforum_advnoise_node import AddAdvancedNoiseNode\nexcept:\n    pass\nfrom .nodes.deforum_prompt_nodes import *\nfrom .nodes.redirect_console_node import DeforumRedirectConsole\nfrom .nodes.deforum_sampler_nodes import *\nfrom .nodes.deforum_schedule_visualizer import *\nfrom .nodes.deforum_video_nodes import *\n\nfrom . import exec_hijack\n\n# Create an empty dictionary for class mappings\nNODE_CLASS_MAPPINGS = {}\nNODE_DISPLAY_NAME_MAPPINGS = {}\n\n# Iterate through all classes defined in your code\n# Get a reference to the current module to inspect its imported node classes\ndeforum_node_module = sys.modules[__name__]\n\n# Iterate through all classes defined in the current module\nfor name, obj in inspect.getmembers(deforum_node_module):\n    # Check if the member is a class\n    if inspect.isclass(obj) and hasattr(obj, \"INPUT_TYPES\"):\n        # Extract the class name and display name\n        class_name = name\n        display_name = getattr(obj, \"display_name\", name)  # Use class attribute or default to class name\n        # Add the class to the mappings\n        NODE_CLASS_MAPPINGS[class_name] = obj\n        NODE_DISPLAY_NAME_MAPPINGS[name] = \"(deforum) \" + display_name\n"
  },
  {
    "path": "deforum_nodes/modules/__init__.py",
    "content": ""
  },
  {
    "path": "deforum_nodes/modules/better_resize/__init__.py",
    "content": ""
  },
  {
    "path": "deforum_nodes/modules/better_resize/interp_methods.py",
    "content": "from math import pi\n\ntry:\n    import torch\nexcept ImportError:\n    torch = None\n\ntry:\n    import numpy\nexcept ImportError:\n    numpy = None\n\nif numpy is None and torch is None:\n    raise ImportError(\"Must have either Numpy or PyTorch but both not found\")\n\n\ndef set_framework_dependencies(x):\n    if type(x) is numpy.ndarray:\n        to_dtype = lambda a: a\n        fw = numpy\n    else:\n        to_dtype = lambda a: a.to(x.dtype)\n        fw = torch\n    eps = fw.finfo(fw.float32).eps\n    return fw, to_dtype, eps\n\n\ndef support_sz(sz):\n    def wrapper(f):\n        f.support_sz = sz\n        return f\n    return wrapper\n\n\n@support_sz(4)\ndef cubic(x):\n    fw, to_dtype, eps = set_framework_dependencies(x)\n    absx = fw.abs(x)\n    absx2 = absx ** 2\n    absx3 = absx ** 3\n    return ((1.5 * absx3 - 2.5 * absx2 + 1.) * to_dtype(absx <= 1.) +\n            (-0.5 * absx3 + 2.5 * absx2 - 4. * absx + 2.) *\n            to_dtype((1. < absx) & (absx <= 2.)))\n\n\n@support_sz(4)\ndef lanczos2(x):\n    fw, to_dtype, eps = set_framework_dependencies(x)\n    return (((fw.sin(pi * x) * fw.sin(pi * x / 2) + eps) /\n            ((pi**2 * x**2 / 2) + eps)) * to_dtype(abs(x) < 2))\n\n\n@support_sz(6)\ndef lanczos3(x):\n    fw, to_dtype, eps = set_framework_dependencies(x)\n    return (((fw.sin(pi * x) * fw.sin(pi * x / 3) + eps) /\n            ((pi**2 * x**2 / 3) + eps)) * to_dtype(abs(x) < 3))\n\n\n@support_sz(2)\ndef linear(x):\n    fw, to_dtype, eps = set_framework_dependencies(x)\n    return ((x + 1) * to_dtype((-1 <= x) & (x < 0)) + (1 - x) *\n            to_dtype((0 <= x) & (x <= 1)))\n\n\n@support_sz(1)\ndef box(x):\n    fw, to_dtype, eps = set_framework_dependencies(x)\n    return to_dtype((-1 <= x) & (x < 0)) + to_dtype((0 <= x) & (x <= 1))"
  },
  {
    "path": "deforum_nodes/modules/better_resize/resize_right.py",
    "content": "from typing import Tuple\nimport warnings\nfrom math import ceil\nfrom . import interp_methods\nfrom fractions import Fraction\n\n\nclass NoneClass:\n    pass\n\n\ntry:\n    import torch\n    from torch import nn\n    nnModuleWrapped = nn.Module\nexcept ImportError:\n    warnings.warn('No PyTorch found, will work only with Numpy')\n    torch = None\n    nnModuleWrapped = NoneClass\n\ntry:\n    import numpy\nexcept ImportError:\n    warnings.warn('No Numpy found, will work only with PyTorch')\n    numpy = None\n\n\nif numpy is None and torch is None:\n    raise ImportError(\"Must have either Numpy or PyTorch but both not found\")\n\n\ndef resize(input, scale_factors=None, out_shape=None,\n           interp_method=interp_methods.cubic, support_sz=None,\n           antialiasing=True, by_convs=False, scale_tolerance=None,\n           max_numerator=10, pad_mode='constant'):\n    # get properties of the input tensor\n    in_shape, n_dims = input.shape, input.ndim\n\n    # fw stands for framework that can be either numpy or torch,\n    # determined by the input type\n    fw = numpy if type(input) is numpy.ndarray else torch\n    eps = fw.finfo(fw.float32).eps\n    device = input.device if fw is torch else None\n\n    # set missing scale factors or output shapem one according to another,\n    # scream if both missing. this is also where all the defults policies\n    # take place. also handling the by_convs attribute carefully.\n    scale_factors, out_shape, by_convs = set_scale_and_out_sz(in_shape,\n                                                              out_shape,\n                                                              scale_factors,\n                                                              by_convs,\n                                                              scale_tolerance,\n                                                              max_numerator,\n                                                              eps, fw)\n\n    # sort indices of dimensions according to scale of each dimension.\n    # since we are going dim by dim this is efficient\n    sorted_filtered_dims_and_scales = [(dim, scale_factors[dim], by_convs[dim],\n                                        in_shape[dim], out_shape[dim])\n                                       for dim in sorted(range(n_dims),\n                                       key=lambda ind: scale_factors[ind])\n                                       if scale_factors[dim] != 1.]\n\n    # unless support size is specified by the user, it is an attribute\n    # of the interpolation method\n    if support_sz is None:\n        support_sz = interp_method.support_sz\n\n    # output begins identical to input and changes with each iteration\n    output = input\n\n    # iterate over dims\n    for (dim, scale_factor, dim_by_convs, in_sz, out_sz\n         ) in sorted_filtered_dims_and_scales:\n        # STEP 1- PROJECTED GRID: The non-integer locations of the projection\n        # of output pixel locations to the input tensor\n        projected_grid = get_projected_grid(in_sz, out_sz,\n                                            scale_factor, fw, dim_by_convs,\n                                            device)\n\n        # STEP 1.5: ANTIALIASING- If antialiasing is taking place, we modify\n        # the window size and the interpolation method (see inside function)\n        cur_interp_method, cur_support_sz = apply_antialiasing_if_needed(\n                                                                interp_method,\n                                                                support_sz,\n                                                                scale_factor,\n                                                                antialiasing)\n\n        # STEP 2- FIELDS OF VIEW: for each output pixels, map the input pixels\n        # that influence it. Also calculate needed padding and update grid\n        # accoedingly\n        field_of_view = get_field_of_view(projected_grid, cur_support_sz, fw,\n                                          eps, device)\n\n        # STEP 2.5- CALCULATE PAD AND UPDATE: according to the field of view,\n        # the input should be padded to handle the boundaries, coordinates\n        # should be updated. actual padding only occurs when weights are\n        # aplied (step 4). if using by_convs for this dim, then we need to\n        # calc right and left boundaries for each filter instead.\n        pad_sz, projected_grid, field_of_view = calc_pad_sz(in_sz, out_sz,\n                                                            field_of_view,\n                                                            projected_grid,\n                                                            scale_factor,\n                                                            dim_by_convs, fw,\n                                                            device)\n\n        # STEP 3- CALCULATE WEIGHTS: Match a set of weights to the pixels in\n        # the field of view for each output pixel\n        weights = get_weights(cur_interp_method, projected_grid, field_of_view)\n\n        # STEP 4- APPLY WEIGHTS: Each output pixel is calculated by multiplying\n        # its set of weights with the pixel values in its field of view.\n        # We now multiply the fields of view with their matching weights.\n        # We do this by tensor multiplication and broadcasting.\n        # if by_convs is true for this dim, then we do this action by\n        # convolutions. this is equivalent but faster.\n        if not dim_by_convs:\n            output = apply_weights(output, field_of_view, weights, dim, n_dims,\n                                   pad_sz, pad_mode, fw)\n        else:\n            output = apply_convs(output, scale_factor, in_sz, out_sz, weights,\n                                 dim, pad_sz, pad_mode, fw)\n    return output\n\n\ndef get_projected_grid(in_sz, out_sz, scale_factor, fw, by_convs, device=None):\n    # we start by having the ouput coordinates which are just integer locations\n    # in the special case when usin by_convs, we only need two cycles of grid\n    # points. the first and last.\n    grid_sz = out_sz if not by_convs else scale_factor.numerator\n    out_coordinates = fw_arange(grid_sz, fw, device)\n\n    # This is projecting the ouput pixel locations in 1d to the input tensor,\n    # as non-integer locations.\n    # the following fomrula is derived in the paper\n    # \"From Discrete to Continuous Convolutions\" by Shocher et al.\n    return (out_coordinates / float(scale_factor) +\n            (in_sz - 1) / 2 - (out_sz - 1) / (2 * float(scale_factor)))\n\n\ndef get_field_of_view(projected_grid, cur_support_sz, fw, eps, device):\n    # for each output pixel, map which input pixels influence it, in 1d.\n    # we start by calculating the leftmost neighbor, using half of the window\n    # size (eps is for when boundary is exact int)\n    left_boundaries = fw_ceil(projected_grid - cur_support_sz / 2 - eps, fw)\n\n    # then we simply take all the pixel centers in the field by counting\n    # window size pixels from the left boundary\n    ordinal_numbers = fw_arange(ceil(cur_support_sz - eps), fw, device)\n    return left_boundaries[:, None] + ordinal_numbers\n\n\ndef calc_pad_sz(in_sz, out_sz, field_of_view, projected_grid, scale_factor,\n                dim_by_convs, fw, device):\n    if not dim_by_convs:\n        # determine padding according to neighbor coords out of bound.\n        # this is a generalized notion of padding, when pad<0 it means crop\n        pad_sz = [-field_of_view[0, 0].item(),\n                  field_of_view[-1, -1].item() - in_sz + 1]\n\n        # since input image will be changed by padding, coordinates of both\n        # field_of_view and projected_grid need to be updated\n        field_of_view += pad_sz[0]\n        projected_grid += pad_sz[0]\n\n    else:\n        # only used for by_convs, to calc the boundaries of each filter the\n        # number of distinct convolutions is the numerator of the scale factor\n        num_convs, stride = scale_factor.numerator, scale_factor.denominator\n\n        # calculate left and right boundaries for each conv. left can also be\n        # negative right can be bigger than in_sz. such cases imply padding if\n        # needed. however if# both are in-bounds, it means we need to crop,\n        # practically apply the conv only on part of the image.\n        left_pads = -field_of_view[:, 0]\n\n        # next calc is tricky, explanation by rows:\n        # 1) counting output pixels between the first position of each filter\n        #    to the right boundary of the input\n        # 2) dividing it by number of filters to count how many 'jumps'\n        #    each filter does\n        # 3) multiplying by the stride gives us the distance over the input\n        #    coords done by all these jumps for each filter\n        # 4) to this distance we add the right boundary of the filter when\n        #    placed in its leftmost position. so now we get the right boundary\n        #    of that filter in input coord.\n        # 5) the padding size needed is obtained by subtracting the rightmost\n        #    input coordinate. if the result is positive padding is needed. if\n        #    negative then negative padding means shaving off pixel columns.\n        right_pads = (((out_sz - fw_arange(num_convs, fw, device) - 1)  # (1)\n                      // num_convs)  # (2)\n                      * stride  # (3)\n                      + field_of_view[:, -1]  # (4)\n                      - in_sz + 1)  # (5)\n\n        # in the by_convs case pad_sz is a list of left-right pairs. one per\n        # each filter\n\n        pad_sz = list(zip(left_pads, right_pads))\n\n    return pad_sz, projected_grid, field_of_view\n\n\ndef get_weights(interp_method, projected_grid, field_of_view):\n    # the set of weights per each output pixels is the result of the chosen\n    # interpolation method applied to the distances between projected grid\n    # locations and the pixel-centers in the field of view (distances are\n    # directed, can be positive or negative)\n    weights = interp_method(projected_grid[:, None] - field_of_view)\n\n    # we now carefully normalize the weights to sum to 1 per each output pixel\n    sum_weights = weights.sum(1, keepdims=True)\n    sum_weights[sum_weights == 0] = 1\n    return weights / sum_weights\n\n\ndef apply_weights(input, field_of_view, weights, dim, n_dims, pad_sz, pad_mode,\n                  fw):\n    # for this operation we assume the resized dim is the first one.\n    # so we transpose and will transpose back after multiplying\n    tmp_input = fw_swapaxes(input, dim, 0, fw)\n\n    # apply padding\n    tmp_input = fw_pad(tmp_input, fw, pad_sz, pad_mode)\n\n    # field_of_view is a tensor of order 2: for each output (1d location\n    # along cur dim)- a list of 1d neighbors locations.\n    # note that this whole operations is applied to each dim separately,\n    # this is why it is all in 1d.\n    # neighbors = tmp_input[field_of_view] is a tensor of order image_dims+1:\n    # for each output pixel (this time indicated in all dims), these are the\n    # values of the neighbors in the 1d field of view. note that we only\n    # consider neighbors along the current dim, but such set exists for every\n    # multi-dim location, hence the final tensor order is image_dims+1.\n    neighbors = tmp_input[field_of_view]\n\n    # weights is an order 2 tensor: for each output location along 1d- a list\n    # of weights matching the field of view. we augment it with ones, for\n    # broadcasting, so that when multiplies some tensor the weights affect\n    # only its first dim.\n    tmp_weights = fw.reshape(weights, (*weights.shape, * [1] * (n_dims - 1)))\n\n    # now we simply multiply the weights with the neighbors, and then sum\n    # along the field of view, to get a single value per out pixel\n    tmp_output = (neighbors * tmp_weights).sum(1)\n\n    # we transpose back the resized dim to its original position\n    return fw_swapaxes(tmp_output, 0, dim, fw)\n\n\ndef apply_convs(input, scale_factor, in_sz, out_sz, weights, dim, pad_sz,\n                pad_mode, fw):\n    # for this operations we assume the resized dim is the last one.\n    # so we transpose and will transpose back after multiplying\n    input = fw_swapaxes(input, dim, -1, fw)\n\n    # the stride for all convs is the denominator of the scale factor\n    stride, num_convs = scale_factor.denominator, scale_factor.numerator\n\n    # prepare an empty tensor for the output\n    tmp_out_shape = list(input.shape)\n    tmp_out_shape[-1] = out_sz\n    tmp_output = fw_empty(tuple(tmp_out_shape), fw, input.device)\n\n    # iterate over the conv operations. we have as many as the numerator\n    # of the scale-factor. for each we need boundaries and a filter.\n    for conv_ind, (pad_sz, filt) in enumerate(zip(pad_sz, weights)):\n        # apply padding (we pad last dim, padding can be negative)\n        pad_dim = input.ndim - 1\n        tmp_input = fw_pad(input, fw, pad_sz, pad_mode, dim=pad_dim)\n\n        # apply convolution over last dim. store in the output tensor with\n        # positional strides so that when the loop is comlete conv results are\n        # interwind\n        tmp_output[..., conv_ind::num_convs] = fw_conv(tmp_input, filt, stride)\n\n    return fw_swapaxes(tmp_output, -1, dim, fw)\n\n\ndef set_scale_and_out_sz(in_shape, out_shape, scale_factors, by_convs,\n                         scale_tolerance, max_numerator, eps, fw):\n    # eventually we must have both scale-factors and out-sizes for all in/out\n    # dims. however, we support many possible partial arguments\n    if scale_factors is None and out_shape is None:\n        raise ValueError(\"either scale_factors or out_shape should be \"\n                         \"provided\")\n    if out_shape is not None:\n        # if out_shape has less dims than in_shape, we defaultly resize the\n        # first dims for numpy and last dims for torch\n        out_shape = (list(out_shape) + list(in_shape[len(out_shape):])\n                     if fw is numpy\n                     else list(in_shape[:-len(out_shape)]) + list(out_shape))\n        if scale_factors is None:\n            # if no scale given, we calculate it as the out to in ratio\n            # (not recomended)\n            scale_factors = [out_sz / in_sz for out_sz, in_sz\n                             in zip(out_shape, in_shape)]\n    if scale_factors is not None:\n        # by default, if a single number is given as scale, we assume resizing\n        # two dims (most common are images with 2 spatial dims)\n        scale_factors = (scale_factors\n                         if isinstance(scale_factors, (list, tuple))\n                         else [scale_factors, scale_factors])\n        # if less scale_factors than in_shape dims, we defaultly resize the\n        # first dims for numpy and last dims for torch\n        scale_factors = (list(scale_factors) + [1] *\n                         (len(in_shape) - len(scale_factors)) if fw is numpy\n                         else [1] * (len(in_shape) - len(scale_factors)) +\n                         list(scale_factors))\n        if out_shape is None:\n            # when no out_shape given, it is calculated by multiplying the\n            # scale by the in_shape (not recomended)\n            out_shape = [ceil(scale_factor * in_sz)\n                         for scale_factor, in_sz in\n                         zip(scale_factors, in_shape)]\n        # next part intentionally after out_shape determined for stability\n        # we fix by_convs to be a list of truth values in case it is not\n        if not isinstance(by_convs, (list, tuple)):\n            by_convs = [by_convs] * len(out_shape)\n\n        # next loop fixes the scale for each dim to be either frac or float.\n        # this is determined by by_convs and by tolerance for scale accuracy.\n        for ind, (sf, dim_by_convs) in enumerate(zip(scale_factors, by_convs)):\n            # first we fractionaize\n            if dim_by_convs:\n                frac = Fraction(1/sf).limit_denominator(max_numerator)\n                frac = Fraction(numerator=frac.denominator, denominator=frac.numerator)\n\n            # if accuracy is within tolerance scale will be frac. if not, then\n            # it will be float and the by_convs attr will be set false for\n            # this dim\n            if scale_tolerance is None:\n                scale_tolerance = eps\n            if dim_by_convs and abs(frac - sf) < scale_tolerance:\n                scale_factors[ind] = frac\n            else:\n                scale_factors[ind] = float(sf)\n                by_convs[ind] = False\n\n        return scale_factors, out_shape, by_convs\n\n\ndef apply_antialiasing_if_needed(interp_method, support_sz, scale_factor,\n                                 antialiasing):\n    # antialiasing is \"stretching\" the field of view according to the scale\n    # factor (only for downscaling). this is low-pass filtering. this\n    # requires modifying both the interpolation (stretching the 1d\n    # function and multiplying by the scale-factor) and the window size.\n    scale_factor = float(scale_factor)\n    if scale_factor >= 1.0 or not antialiasing:\n        return interp_method, support_sz\n    cur_interp_method = (lambda arg: scale_factor *\n                         interp_method(scale_factor * arg))\n    cur_support_sz = support_sz / scale_factor\n    return cur_interp_method, cur_support_sz\n\n\ndef fw_ceil(x, fw):\n    if fw is numpy:\n        return fw.int_(fw.ceil(x))\n    else:\n        return x.ceil().long()\n\n\ndef fw_floor(x, fw):\n    if fw is numpy:\n        return fw.int_(fw.floor(x))\n    else:\n        return x.floor().long()\n\n\ndef fw_cat(x, fw):\n    if fw is numpy:\n        return fw.concatenate(x)\n    else:\n        return fw.cat(x)\n\n\ndef fw_swapaxes(x, ax_1, ax_2, fw):\n    if fw is numpy:\n        return fw.swapaxes(x, ax_1, ax_2)\n    else:\n        return x.transpose(ax_1, ax_2)\n\n\ndef fw_pad(x, fw, pad_sz, pad_mode, dim=0):\n    if pad_sz == (0, 0):\n        return x\n    if fw is numpy:\n        pad_vec = [(0, 0)] * x.ndim\n        pad_vec[dim] = pad_sz\n        return fw.pad(x, pad_width=pad_vec, mode=pad_mode)\n    else:\n        if x.ndim < 3:\n            x = x[None, None, ...]\n\n        pad_vec = [0] * ((x.ndim - 2) * 2)\n        pad_vec[0:2] = pad_sz\n        return fw.nn.functional.pad(x.transpose(dim, -1), pad=pad_vec,\n                                    mode=pad_mode).transpose(dim, -1)\n\n\ndef fw_conv(input, filter, stride):\n    # we want to apply 1d conv to any nd array. the way to do it is to reshape\n    # the input to a 4D tensor. first two dims are singeletons, 3rd dim stores\n    # all the spatial dims that we are not convolving along now. then we can\n    # apply conv2d with a 1xK filter. This convolves the same way all the other\n    # dims stored in the 3d dim. like depthwise conv over these.\n    # TODO: numpy support\n    reshaped_input = input.reshape(1, 1, -1, input.shape[-1])\n    reshaped_output = torch.nn.functional.conv2d(reshaped_input,\n                                                 filter.view(1, 1, 1, -1),\n                                                 stride=(1, stride))\n    return reshaped_output.reshape(*input.shape[:-1], -1)\n\n\ndef fw_arange(upper_bound, fw, device):\n    if fw is numpy:\n        return fw.arange(upper_bound)\n    else:\n        return fw.arange(upper_bound, device=device)\n\n\ndef fw_empty(shape, fw, device):\n    if fw is numpy:\n        return fw.empty(shape)\n    else:\n        return fw.empty(size=(*shape,), device=device)"
  },
  {
    "path": "deforum_nodes/modules/deforum_comfy_sampler.py",
    "content": "import secrets\nimport torch\nimport numpy as np\nfrom PIL import Image\n\nfrom deforum import ImageRNGNoise\nfrom deforum.utils.deforum_cond_utils import blend_tensors\n\nrng = None\ndef common_ksampler_with_custom_noise(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent,\n                                      denoise=1.0, disable_noise=False, start_step=None, last_step=None,\n                                      force_full_denoise=False, noise=None):\n    latent_image = latent[\"samples\"]\n    if noise is not None:\n        noise = noise.next()#.detach().cpu()\n        # noise = rng_noise.clone()\n    else:\n        if disable_noise:\n            noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device=\"cpu\")\n        else:\n            batch_inds = latent[\"batch_index\"] if \"batch_index\" in latent else None\n            from comfy.sample import prepare_noise\n            noise = prepare_noise(latent_image, seed, batch_inds)\n\n    noise_mask = None\n    if \"noise_mask\" in latent:\n        noise_mask = latent[\"noise_mask\"]\n\n    # callback = latent_preview.prepare_callback(model, steps)\n    # disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED\n\n    from comfy.sample import sample as sample_k\n\n    samples = sample_k(model, noise, steps, cfg, sampler_name, scheduler, positive, negative, latent_image,\n                       denoise=denoise, disable_noise=disable_noise, start_step=start_step,\n                       last_step=last_step,\n                       force_full_denoise=force_full_denoise, noise_mask=noise_mask, callback=None,\n                       disable_pbar=False, seed=seed)\n    out = latent.copy()\n    out[\"samples\"] = samples\n\n    return (out,)\n\ndef sample_deforum(\n            model,\n            clip,\n            vae,\n            controlnet=None,\n            prompt=\"\",\n            pooled_prompts=None,\n            next_prompt=None,\n            prompt_blend=None,\n            negative_prompt=\"\",\n            steps=25,\n            scale=7.5,\n            sampler_name=\"dpmpp_2m_sde\",\n            scheduler=\"karras\",\n            width=None,\n            height=None,\n            seed=-1,\n            strength=1.0,\n            init_image=None,\n            subseed=-1,\n            subseed_strength=0.6,\n            cnet_image=None,\n            cond=None,\n            n_cond=None,\n            return_latent=None,\n            latent=None,\n            last_step=None,\n            seed_resize_from_h=1024,\n            seed_resize_from_w=1024,\n            reset_noise=False,\n            enable_prompt_blend=True,\n            use_areas= False,\n            areas= None,\n            *args,\n            **kwargs):\n\n    # import comfy.sd\n    # model, clip, vae, clipvision = comfy.sd.load_checkpoint_guess_config(self.model_path,\n    #                                       output_vae=True,\n    #                                       output_clip=True,\n    #                                       embedding_directory=\"models/embeddings\",\n    #                                       output_clipvision=False,\n    #                                       )\n\n    if seed == -1:\n        seed = secrets.randbelow(18446744073709551615)\n\n    # strength = 1 - strength\n\n    if strength <= 0.0 or strength >= 1.0:\n        strength = 1.0\n        reset_noise = True\n        init_image = None\n    if subseed == -1:\n        subseed = secrets.randbelow(18446744073709551615)\n\n    if cnet_image is not None:\n        cnet_image = torch.from_numpy(np.array(cnet_image).astype(np.float32) / 255.0).unsqueeze(0)\n\n    if init_image is None or reset_noise:\n        strength = 1.0\n        if latent is None:\n\n            if width is None:\n                width = 1024\n            if height is None:\n                height = 960\n            latent = generate_latent(width, height, seed, subseed, subseed_strength, seed_resize_from_h,\n                                          seed_resize_from_w, reset_noise)\n        else:\n            if isinstance(latent, torch.Tensor):\n                latent = {\"samples\":latent}\n            elif isinstance(latent, list):\n                latent = {\"samples\":torch.stack(latent, dim=0)}\n            else:\n                latent = latent\n    else:\n        latent = torch.from_numpy(np.array(init_image).astype(np.float32) / 255.0).unsqueeze(0)\n        latent = encode_latent(vae, latent, seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w)\n    assert isinstance(latent, dict), \\\n        \"Our Latents have to be in a dict format with the latent being the 'samples' value\"\n\n    cond = []\n\n    if pooled_prompts is None and prompt is not None:\n        cond = get_conds(clip, prompt)\n    elif pooled_prompts is not None:\n        cond = pooled_prompts\n\n    if use_areas and areas is not None:\n        from nodes import ConditioningSetArea\n        area_setter = ConditioningSetArea()\n        for area in areas:\n            print(\"[deforum] Area Prompt:\", area)\n            prompt = area.get(\"prompt\", None)\n            if prompt:\n\n                new_cond = get_conds(clip, area[\"prompt\"])\n                new_cond = area_setter.append(conditioning=new_cond, width=int(area[\"w\"]), height=int(area[\"h\"]), x=int(area[\"x\"]),\n                                              y=int(area[\"y\"]), strength=area[\"s\"])[0]\n                cond += new_cond\n\n    n_cond = get_conds(clip, negative_prompt)\n\n\n    if next_prompt is not None and enable_prompt_blend:\n        if next_prompt != prompt and next_prompt != \"\":\n            if 0.0 < prompt_blend < 1.0:\n                next_cond = get_conds(clip, next_prompt)\n\n                cond = blend_tensors(cond[0], next_cond[0], blend_value=prompt_blend)\n\n    if cnet_image is not None:\n        cond = apply_controlnet(cond, controlnet, cnet_image, 1.0)\n\n    from nodes import common_ksampler as ksampler\n\n    #steps = int((strength) * steps) if (strength != 1.0 or not reset_noise) else steps\n    last_step = steps# if last_step is None else last_step\n\n\n    sample = common_ksampler_with_custom_noise(model=model,\n                                               seed=seed,\n                                               steps=steps,\n                                               cfg=scale,\n                                               sampler_name=sampler_name,\n                                               scheduler=scheduler,\n                                               positive=cond,\n                                               negative=n_cond,\n                                               latent=latent,\n                                               denoise=strength,\n                                               disable_noise=False,\n                                               start_step=0,\n                                               last_step=last_step,\n                                               force_full_denoise=True,\n                                               noise=rng)\n\n\n    if sample[0][\"samples\"].shape[0] == 1:\n        decoded = decode_sample(vae, sample[0][\"samples\"])\n        np_array = np.clip(255. * decoded.cpu().numpy(), 0, 255).astype(np.uint8)[0]\n        image = Image.fromarray(np_array)\n        # image = Image.fromarray(np.clip(255. * decoded.cpu().numpy(), 0, 255).astype(np.uint8)[0])\n        image = image.convert(\"RGB\")\n\n        image.save('test.png', \"PNG\")\n\n        if return_latent:\n            return sample[0][\"samples\"], image\n        else:\n            return image\n    else:\n        images = []\n        x_samples = vae.decode_tiled(sample[0][\"samples\"])\n        for sample in x_samples:\n            np_array = np.clip(255. * sample.cpu().numpy(), 0, 255).astype(np.uint8)\n            image = Image.fromarray(np_array)\n            # image = Image.fromarray(np.clip(255. * decoded.cpu().numpy(), 0, 255).astype(np.uint8)[0])\n            image = image.convert(\"RGB\")\n            images.append(image)\n        return images\ndef encode_latent(vae, latent, seed, subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, reset_noise=False):\n    global rng\n    subseed_strength = 0.6\n\n    with torch.inference_mode():\n        latent = latent.to(torch.float32)\n        latent = vae.encode_tiled(latent[:, :, :, :3])\n        latent = latent.to(\"cuda\")\n    if rng is None or reset_noise:\n        rng = ImageRNGNoise(shape=latent[0].shape, seeds=[seed], subseeds=[subseed], subseed_strength=subseed_strength,\n                                 seed_resize_from_h=seed_resize_from_h, seed_resize_from_w=seed_resize_from_w)\n    #     noise = self.rng.first()\n    #     noise = slerp(subseed_strength, noise, latent)\n    # else:\n    #     #noise = self.rng.next()\n    #     #noise = slerp(subseed_strength, noise, latent)\n    #     noise = latent\n    return {\"samples\": latent}\n\ndef generate_latent(width, height, seed, subseed, subseed_strength, seed_resize_from_h=None,\n                    seed_resize_from_w=None, reset_noise=False):\n    shape = [4, height // 8, width // 8]\n    global rng\n\n    if rng is None or reset_noise:\n        rng = ImageRNGNoise(shape=shape, seeds=[seed], subseeds=[subseed], subseed_strength=subseed_strength,\n                                 seed_resize_from_h=seed_resize_from_h, seed_resize_from_w=seed_resize_from_w)\n    noise = rng.next()\n    # noise = torch.zeros([1, 4, width // 8, height // 8])\n    return {\"samples\": noise}\n\ndef get_conds(clip, prompt):\n    with torch.inference_mode():\n        # clip_skip = -1\n        # if clip_skip != clip_skip or clip.layer_idx != clip_skip:\n        #     clip.layer_idx = clip_skip\n        #     clip.clip_layer(clip_skip)\n        #     self.clip_skip = clip_skip\n\n        tokens = clip.tokenize(prompt)\n        cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)\n        return [[cond, {\"pooled_output\": pooled}]]\ndef apply_controlnet(conditioning, control_net, image, strength):\n    with torch.inference_mode():\n        if strength == 0:\n            return (conditioning,)\n\n        c = []\n        control_hint = image.movedim(-1, 1)\n        for t in conditioning:\n            n = [t[0], t[1].copy()]\n            c_net = control_net.copy().set_cond_hint(control_hint, strength)\n            if 'control' in t[1]:\n                c_net.set_previous_controlnet(t[1]['control'])\n            n[1]['control'] = c_net\n            n[1]['control_apply_to_uncond'] = True\n            c.append(n)\n    return c\n\ndef decode_sample(vae, sample):\n    # with torch.inference_mode():\n    #     sample = sample.to(torch.float32)\n    #     vae.first_stage_model.cuda()\n    decoded = vae.decode_tiled(sample).detach()\n    return decoded"
  },
  {
    "path": "deforum_nodes/modules/deforum_comfyui_helpers.py",
    "content": "import base64\nimport math\nimport os\nimport random\nimport re\nfrom io import BytesIO\n\nimport cv2\nimport numexpr\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom PIL import Image\nfrom deforum.pipelines.deforum_animation.animation_helpers import DeforumAnimKeys\nfrom deforum.pipelines.deforum_animation.pipeline_deforum_animation import interpolate_areas\n\nblend_methods = [\"linear\", \"sigmoidal\", \"gaussian\", \"pyramid\", \"none\"]\nimport torch\nimport torch.nn.functional as F\nimport math\n\n\ndef pad_to_match(tensor1, tensor2):\n    \"\"\"\n    Pad the smaller tensor with zeros to match the size of the larger tensor.\n    \"\"\"\n    if tensor1.size() == tensor2.size():\n        return tensor1, tensor2\n    else:\n        # Find the size difference between the two tensors\n        diff_dims = [abs(s1 - s2) for s1, s2 in zip(tensor1.size(), tensor2.size())]\n\n        # Padding format (left, right, top, bottom) for 4D tensors, e.g., (batch, channels, height, width)\n        padding = []\n        for diff in reversed(diff_dims):  # Reverse to start padding from last dimensions\n            padding.extend([diff // 2, diff - diff // 2])\n\n        # Apply padding to the smaller tensor\n        if tensor1.numel() < tensor2.numel():\n            tensor1 = F.pad(tensor1, padding)\n        else:\n            tensor2 = F.pad(tensor2, padding)\n\n        return tensor1, tensor2\n\ndef parse_widget(widget_info: dict) -> tuple:\n    parsed_widget = None\n    t = widget_info[\"type\"]\n    if t == \"dropdown\":\n        parsed_widget = (widget_info[\"choices\"],)\n    elif t == \"checkbox\":\n        parsed_widget = (\"BOOLEAN\", {\"default\": widget_info['default']})\n    elif t == \"lineedit\":\n        parsed_widget = (\"STRING\", {\"default\": widget_info['default']})\n    elif t == \"spinbox\":\n        parsed_widget = (\"INT\", {\"default\": widget_info['default']})\n    elif t == \"doublespinbox\":\n        parsed_widget = (\"FLOAT\", {\"default\": widget_info['default']})\n    return parsed_widget\n\n\ndef get_node_params(input_params):\n    data_info = {\"required\": {}, }\n    if input_params:\n        for name, widget_info in input_params.items():\n            data_info[\"required\"][name] = parse_widget(widget_info)\n    data_info[\"optional\"] = {\"deforum_data\": (\"deforum_data\",)}\n    return data_info\n\ndef get_current_keys(anim_args, seed, root, parseq_args=None, video_args=None, area_prompts=None):\n    use_parseq = False if parseq_args == None else True\n    anim_args.max_frames += 2\n    keys = DeforumAnimKeys(anim_args, seed)  # if not use_parseq else ParseqAnimKeys(parseq_args, video_args)\n    areas = None\n    # Always enable pseudo-3d with parseq. No need for an extra toggle:\n    # Whether it's used or not in practice is defined by the schedules\n    if use_parseq:\n        anim_args.flip_2d_perspective = True\n        # expand prompts out to per-frame\n    if use_parseq and keys.manages_prompts():\n        prompt_series = keys.prompts\n    else:\n        prompt_series = None\n        if hasattr(root, 'animation_prompts'):\n            if root.animation_prompts is not None:\n                prompt_series = pd.Series([np.nan for a in range(anim_args.max_frames + 1)])\n                for i, prompt in root.animation_prompts.items():\n                    if str(i).isdigit():\n                        prompt_series[int(i)] = prompt\n                    else:\n                        prompt_series[int(numexpr.evaluate(i))] = prompt\n                prompt_series = prompt_series.ffill().bfill()\n        if area_prompts is not None:\n            areas = interpolate_areas(area_prompts, anim_args.max_frames)\n    anim_args.max_frames -= 2\n    return keys, prompt_series, areas\n\ndef tensor2pil(image):\n    if image is not None:\n        #with torch.inference_mode():\n        return Image.fromarray(np.clip(255. * image.detach().cpu().numpy().squeeze(), 0, 255).astype(np.uint8))\n    else:\n        return None\n\n# PIL to Tensor\ndef pil2tensor(image):\n    return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)\n\n\ndef tensor2np(img):\n    np_img = np.array(tensor2pil(img))\n    return np_img\n\ndef get_latent_with_seed(seed):\n    return torch.randn(generator=torch.manual_seed(seed))\n\ndef pil_image_to_base64(pil_image):\n    buffer = BytesIO()\n    pil_image.save(buffer, format=\"WEBP\")  # Or JPEG\n    return base64.b64encode(buffer.getvalue()).decode()\n\n\ndef tensor_to_webp_base64(tensor):\n    # Ensure tensor is in CPU and detach it from the computation graph\n    tensor = tensor.clone().detach().cpu()\n    # Convert tensor to a numpy array with value range [0, 255]\n    # Transpose the tensor to have the channel in the correct order (H, W, C)\n    np_image = (tensor.numpy().squeeze() * 255).clip(0, 255).astype(np.uint8)\n    if np_image.ndim == 2:  # if it's a grayscale image, convert to RGB\n        np_image = cv2.cvtColor(np_image, cv2.COLOR_GRAY2RGB)\n    elif np_image.shape[0] == 3:  # Convert CHW to HWC\n        np_image = np_image.transpose(1, 2, 0)\n    np_image = cv2.cvtColor(np_image, cv2.COLOR_BGR2RGB)\n    # Encode the numpy array to WEBP using OpenCV\n    compression_quality = 80\n    _, encoded_image = cv2.imencode('.webp', np_image, [cv2.IMWRITE_WEBP_QUALITY, compression_quality])\n    # Convert the encoded image to base64\n    base64_str = base64.b64encode(encoded_image).decode()\n    return base64_str\n\ndef generate_seed_list(max_frames, mode='fixed', start_seed=0, step=1):\n    \"\"\"\n    Generates a list of seed integers compatible with PyTorch in various manners.\n\n    Parameters:\n    - max_frames (int): The maximum number of frames/length of the seed list.\n    - mode (str): The mode of seed generation, one of 'fixed', 'random', 'ladder', 'incrementing', or 'decrementing'.\n    - start_seed (int): The starting seed value for modes other than 'random'.\n    - step (int): The step size for 'incrementing', 'decrementing', and 'ladder' modes.\n\n    Returns:\n    - list: A list of seed integers.\n    \"\"\"\n    if mode == 'fixed':\n        return [start_seed for _ in range(max_frames)]\n    elif mode == 'random':\n        return [random.randint(0, 2**32 - 1) for _ in range(max_frames)]\n    elif mode == 'ladder':\n        # Generate a ladder sequence where the sequence is repeated after reaching the max_frames\n        return [(start_seed + i // 2 * step if i % 2 == 0 else start_seed + (i // 2 + 1) * step) % (2**32) for i in range(max_frames)]\n    elif mode == 'incrementing' or 'iter':\n        return [(start_seed + i * step) % (2**32) for i in range(max_frames)]\n    elif mode == 'decrementing':\n        return [(start_seed - i * step) % (2**32) for i in range(max_frames)]\n    else:\n        raise ValueError(\"Invalid mode specified. Choose among 'fixed', 'random', 'ladder', 'incrementing', 'decrementing'.\")\n\ndef find_next_index(output_dir, filename_prefix, format):\n    \"\"\"\n    Finds the next index for an MP4 file given an output directory and a filename prefix.\n\n    Parameters:\n    - output_dir: The directory where the MP4 files are saved.\n    - filename_prefix: The prefix for the filenames.\n\n    Returns:\n    - An integer representing the next index for a new MP4 file.\n    \"\"\"\n    # Compile a regular expression pattern to match the filenames\n    # This assumes the index is at the end of the filename, before the .mp4 extension\n    pattern = re.compile(rf\"^{re.escape(filename_prefix)}_(\\d+)\\.{format.lower()}$\")\n\n    max_index = -1\n    for filename in os.listdir(output_dir):\n        match = pattern.match(filename)\n        if match:\n            # Extract the current index from the filename\n            current_index = int(match.group(1))\n            # Update the max index found\n            if current_index > max_index:\n                max_index = current_index\n\n    # The next index is one more than the highest index found\n    next_index = max_index + 1\n\n\n    return next_index\n\nimport torch.nn.functional as F\n\ndef pyramid_blend(tensor1, tensor2, blend_value):\n    tensor1, tensor2 = pad_to_match(tensor1, tensor2)\n\n    # For simplicity, we'll use two levels of blending\n    downsampled1 = F.avg_pool2d(tensor1, 2)\n    downsampled2 = F.avg_pool2d(tensor2, 2)\n\n    blended_low = (1 - blend_value) * downsampled1 + blend_value * downsampled2\n    blended_high = tensor1 + tensor2 - F.interpolate(blended_low, scale_factor=2)\n\n    return blended_high\ndef gaussian_blend(tensor2, tensor1, blend_value):\n    tensor1, tensor2 = pad_to_match(tensor1, tensor2)\n\n    sigma = 0.5  # Adjust for desired smoothness\n    weight = math.exp(-((blend_value - 0.5) ** 2) / (2 * sigma ** 2))\n    return (1 - weight) * tensor1 + weight * tensor2\ndef sigmoidal_blend(tensor1, tensor2, blend_value):\n    tensor1, tensor2 = pad_to_match(tensor1, tensor2)\n\n    # Convert blend_value into a tensor with the same shape as tensor1 and tensor2\n    blend_tensor = torch.full_like(tensor1, blend_value)\n    weight = 1 / (1 + torch.exp(-10 * (blend_tensor - 0.5)))  # Sigmoid function centered at 0.5\n    return (1 - weight) * tensor1 + weight * tensor2\ndef blend_tensors(obj1, obj2, blend_value, blend_method=\"linear\"):\n    \"\"\"\n    Blends tensors in two given objects based on a blend value using various blending strategies.\n    \"\"\"\n\n    if blend_method == \"linear\":\n        weight = blend_value\n        obj1[0], obj2[0] = pad_to_match(obj1[0], obj2[0])\n        obj1[1]['pooled_output'], obj2[1]['pooled_output'] = pad_to_match(obj1[1]['pooled_output'], obj2[1]['pooled_output'])\n        blended_cond = (1 - weight) * obj1[0] + weight * obj2[0]\n        blended_pooled = (1 - weight) * obj1[1]['pooled_output'] + weight * obj2[1]['pooled_output']\n\n    elif blend_method == \"sigmoidal\":\n        blended_cond = sigmoidal_blend(obj1[0], obj2[0], blend_value)\n        blended_pooled = sigmoidal_blend(obj1[1]['pooled_output'], obj2[1]['pooled_output'], blend_value)\n\n    elif blend_method == \"gaussian\":\n        blended_cond = gaussian_blend(obj1[0], obj2[0], blend_value)\n        blended_pooled = gaussian_blend(obj1[1]['pooled_output'], obj2[1]['pooled_output'], blend_value)\n\n    elif blend_method == \"pyramid\":\n        blended_cond = pyramid_blend(obj1[0], obj2[0], blend_value)\n        blended_pooled = pyramid_blend(obj1[1]['pooled_output'], obj2[1]['pooled_output'], blend_value)\n\n    return [[blended_cond, {\"pooled_output\": blended_pooled}]]\n"
  },
  {
    "path": "deforum_nodes/modules/deforum_constants.py",
    "content": "class DeforumStorage:\n    _instance = None  # Class attribute that holds the singleton instance\n\n    def __new__(cls, *args, **kwargs):\n        if cls._instance is None:\n            cls._instance = super(DeforumStorage, cls).__new__(cls, *args, **kwargs)\n            # Initialize your object here if needed\n            cls._instance.deforum_models = {}\n            cls._instance.deforum_cache = {}\n            cls._instance.deforum_depth_algo = \"\"\n            cls._instance.reset = None\n        return cls._instance\n\n    def __init__(self):\n        # Initialization code here. If there are attributes that should not be reinitialized,\n        # you can check if they already exist before setting them.\n        pass"
  },
  {
    "path": "deforum_nodes/modules/deforum_node_base.py",
    "content": "class DeforumDataBase:\n\n    # @classmethod\n    # def INPUT_TYPES(s):\n    #     return s.params\n\n    RETURN_TYPES = ((\"deforum_data\",))\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/parameters\"\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n    def get(self, deforum_data=None, *args, **kwargs):\n\n        if deforum_data:\n            deforum_data.update(**kwargs)\n        else:\n            deforum_data = kwargs\n        return (deforum_data,)"
  },
  {
    "path": "deforum_nodes/modules/deforum_ui_data.py",
    "content": "import comfy\ndeforum_base_params = {\n    \"width\": {\n        \"type\": \"spinbox\",\n        \"default\": 512,\n        \"min\": 64,\n        \"max\": 4096,\n        \"step\": 64\n    },\n    \"height\": {\n        \"type\": \"spinbox\",\n        \"default\": 512,\n        \"min\": 64,\n        \"max\": 4096,\n        \"step\": 64\n    },\n    \"seed_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (-1)\",\n    },\n    \"seed_behavior\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"fixed\", \"random\"]\n    },\n    \"sampler_name\": {\n        \"type\": \"dropdown\",\n        \"choices\": comfy.samplers.KSampler.SAMPLERS\n    },\n    \"scheduler\": {\n        \"type\": \"dropdown\",\n        \"choices\": comfy.samplers.KSampler.SCHEDULERS\n    },\n\n    # \"steps\": {\n    #     \"type\": \"spinbox\",\n    #     \"default\": 25,\n    #     \"min\": 0,\n    #     \"max\": 10000,\n    #     \"step\": 1\n    # },\n    # \"scale\": {\n    #     \"type\": \"spinbox\",\n    #     \"default\": 7,\n    #     \"min\": 0,\n    #     \"max\": 10000,\n    #     \"step\": 1\n    # },\n    # \"n_batch\": {\n    #     \"type\": \"spinbox\",\n    #     \"default\": 1,\n    #     \"min\": 0,\n    #     \"max\": 10000,\n    #     \"step\": 1\n    # },\n    # \"batch_name\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"Deforum_{timestring}\"\n    # },\n    #\n    # \"seed_iter_N\": {\n    #     \"type\": \"spinbox\",\n    #     \"default\": 1,\n    #     \"min\": 0,\n    #     \"max\": 10000,\n    #     \"step\": 1\n    # },\n    # \"outdir\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"output/deforum\"\n    # },\n    # \"strength\": {\n    #     \"type\": \"doublespinbox\",\n    #     \"default\": 0.8,\n    #     \"min\": 0,\n    #     \"max\": 1,\n    #     \"step\": 0.01\n    # },\n    # \"save_settings\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": True\n    # },\n    # \"save_sample_per_step\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n    \"prompt_weighting\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"normalize_prompt_weights\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"log_weighted_subprompts\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    # \"prompt\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"\"\n    # },\n    # \"timestring\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"\"\n    # },\n    # \"seed_internal\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"-1\",\n    # },\n}\n\n\ndeforum_anim_params = {\n\n    \"animation_mode\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"None\", \"2D\", \"3D\", \"Video Input\", \"Interpolation\"]\n    },\n    \"max_frames\": {\n        \"type\": \"spinbox\",\n        \"min\": 1,\n        \"max\": 100000,\n        \"default\": 120,\n        \"step\": 1\n    },\n    \"border\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"wrap\", \"replicate\", \"zeros\"]\n    },\n    # \"resume_from_timestring\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n    # \"resume_timestring\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"20230129210106\"\n    # },\n    # \"use_looper\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n\n}\n\n\ndeforum_translation_params = {\n\n    \"angle\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0)\"\n    },\n    \"zoom\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(1.0025+0.002*sin(1.25*3.14*t/30))\"\n    },\n    \"translation_x\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0)\"\n    },\n    \"translation_y\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0)\"\n    },\n    \"translation_z\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(1.75)\"\n    },\n    \"transform_center_x\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0.5)\"\n    },\n    \"transform_center_y\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0.5)\"\n    },\n    \"rotation_3d_x\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0)\"\n    },\n    \"rotation_3d_y\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0)\"\n    },\n    \"rotation_3d_z\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0)\"\n    },\n\n}\n\ndeforum_hybrid_video_params = {\n\n    # \"hybrid_generate_inputframes\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n    # \"hybrid_generate_human_masks\": {\n    #     \"type\": \"dropdown\",\n    #     \"choices\": [\"None\", \"PNGs\", \"Video\", \"Both\"],\n    #     \"default\": \"None\"\n    # },\n    \"hybrid_use_first_frame_as_init_image\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"hybrid_motion\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"None\", \"Optical Flow\", \"Perspective\", \"Affine\"],\n        \"default\": \"None\"\n    },\n    \"hybrid_motion_use_prev_img\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"hybrid_flow_consistency\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"hybrid_consistency_blur\": {\n        \"type\": \"spinbox\",\n        \"min\": 0,\n        \"max\": 10,\n        \"default\": 2,\n        \"step\": 1\n    },\n    \"hybrid_flow_method\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"RAFT\", \"DIS Medium\", \"DIS Fine\", \"Farneback\"],\n        \"default\": \"RAFT\"\n    },\n    \"hybrid_composite\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"None\", \"Normal\", \"Before Motion\", \"After Generation\"],\n        \"default\": \"None\"\n    },\n    # \"hybrid_use_init_image\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n    # \"hybrid_comp_mask_type\": {\n    #     \"type\": \"dropdown\",\n    #     \"choices\": [\"None\", \"Depth\", \"Video Depth\", \"Blend\", \"Difference\"],\n    #     \"default\": \"None\"\n    # },\n    # \"hybrid_comp_mask_inverse\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n    # \"hybrid_comp_mask_equalize\": {\n    #     \"type\": \"dropdown\",\n    #     \"choices\": [\"None\", \"Before\", \"After\", \"Both\"],\n    #     \"default\": \"None\"\n    # },\n    # \"hybrid_comp_mask_auto_contrast\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n    # \"hybrid_comp_save_extra_frames\": {\n    #     \"type\": \"checkbox\",\n    #     \"default\": False\n    # },\n\n}\n\ndeforum_hybrid_video_schedules = {\n\n    # \"hybrid_comp_alpha_schedule\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"0:(0.5)\"\n    # },\n    # \"hybrid_comp_mask_blend_alpha_schedule\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"0:(0.5)\"\n    # },\n    # \"hybrid_comp_mask_contrast_schedule\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"0:(1)\"\n    # },\n    # \"hybrid_comp_mask_auto_contrast_cutoff_high_schedule\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"0:(100)\"\n    # },\n    # \"hybrid_comp_mask_auto_contrast_cutoff_low_schedule\": {\n    #     \"type\": \"lineedit\",\n    #     \"default\": \"0:(0)\"\n    # },\n    \"hybrid_flow_factor_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(1)\"\n    },\n\n}\n\ndeforum_color_coherence_params = {\n\n    \"color_coherence\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"None\", \"HSV\", \"LAB\", \"RGB\", \"Video Input\", \"Image\"]\n    },\n    \"color_coherence_image_path\": {\n        \"type\": \"lineedit\",\n        \"default\": \"\"\n    },\n    \"color_coherence_video_every_N_frames\": {\n        \"type\": \"spinbox\",\n        \"min\": 1,\n        \"max\": 100,\n        \"default\": 1,\n        \"step\": 1\n    },\n    \"color_force_grayscale\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"legacy_colormatch\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n\n}\n\n\ndeforum_depth_params = {\n\n    \"use_depth_warping\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"depth_algorithm\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\n            \"Midas-3-Hybrid\",\n            \"Midas+AdaBins (old)\",\n            \"Midas-3.1-BeitLarge\",\n            \"AdaBins\",\n            \"Leres\",\n            \"Zoe\",\n            \"Zoe+AdaBins (old)\",\n\n        ],\n        \"default\": \"Zoe\"\n    },\n    \"midas_weight\": {\n        \"type\": \"doublespinbox\",\n        \"min\": 0,\n        \"max\": 1,\n        \"default\": 0.2,\n        \"step\": 0.01\n    },\n    \"padding_mode\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"border\", \"reflection\", \"zeros\"],\n        \"default\": \"border\"\n    },\n    \"sampling_mode\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"bicubic\", \"bilinear\", \"nearest\"],\n        \"default\": \"bicubic\"\n    },\n    \"save_depth_maps\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n\n}\n\ndeforum_cadence_params = {\n\n    \"diffusion_cadence\": {\n        \"type\": \"spinbox\",\n        \"min\": 0,\n        \"max\": 100,\n        \"default\": 4,\n        \"step\": 1\n    },\n    \"optical_flow_cadence\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"None\", \"RAFT\", \"DIS Medium\", \"DIS Fine\", \"Farneback\"]\n    },\n    \"cadence_flow_factor_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (1)\"\n    },\n    \"optical_flow_redo_generation\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"None\", \"RAFT\", \"DIS Medium\", \"DIS Fine\", \"Farneback\"]\n    },\n    \"redo_flow_factor_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (1)\"\n    },\n    \"diffusion_redo\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0\"\n    },\n\n}\n\n\ndeforum_video_init_params = {\n\n    \"video_init_path\": {\n        \"type\": \"lineedit\",\n        \"default\": \"https://deforum.github.io/a1/V1.mp4\"\n    },\n    \"extract_nth_frame\": {\n        \"type\": \"spinbox\",\n        \"min\": 1,\n        \"max\": 100,\n        \"default\": 1,\n        \"step\": 1\n    },\n    \"extract_from_frame\": {\n        \"type\": \"spinbox\",\n        \"min\": 0,\n        \"max\": 1000,\n        \"default\": 0,\n        \"step\": 1\n    },\n    \"extract_to_frame\": {\n        \"type\": \"spinbox\",\n        \"min\": -1,\n        \"max\": 1000,\n        \"default\": -1,\n        \"step\": 1\n    },\n    \"overwrite_extracted_frames\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"use_mask_video\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"video_mask_path\": {\n        \"type\": \"lineedit\",\n        \"default\": \"https://deforum.github.io/a1/VM1.mp4\"\n    },\n}\n\n\ndeforum_masking_params = {\n\n    \"use_mask\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"use_alpha_as_mask\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"mask_file\": {\n        \"type\": \"lineedit\",\n        \"default\": \"https://deforum.github.io/a1/M1.jpg\"\n    },\n    \"mask_image\": {\n        \"type\": \"lineedit\",\n        \"default\": None\n    },\n    \"noise_mask\": {\n        \"type\": \"lineedit\",\n        \"default\": None\n    },\n\n    \"invert_mask\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"mask_contrast_adjust\": {\n        \"type\": \"doublespinbox\",\n        \"default\": 1.0,\n        \"min\": 0,\n        \"max\": 10,\n        \"step\": 0.1\n    },\n    \"mask_brightness_adjust\": {\n        \"type\": \"doublespinbox\",\n        \"default\": 1.0,\n        \"min\": 0,\n        \"max\": 10,\n        \"step\": 0.1\n    },\n    \"overlay_mask\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"mask_overlay_blur\": {\n        \"type\": \"spinbox\",\n        \"default\": 4,\n        \"min\": 0,\n        \"max\": 100,\n        \"step\": 1\n    },\n    \"fill\": {\n        \"type\": \"spinbox\",\n        \"default\": 1,\n        \"min\": 0,\n        \"max\": 10000,\n        \"step\": 1\n    },\n    \"full_res_mask\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"full_res_mask_padding\": {\n        \"type\": \"spinbox\",\n        \"default\": 4,\n        \"min\": 0,\n        \"max\": 10000,\n        \"step\": 1\n    },\n\n}\n\ndeforum_diffusion_schedule_params = {\n\n    \"noise_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (0.065)\"\n    },\n    \"strength_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (0.65)\"\n    },\n    \"contrast_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (1.0)\"\n    },\n    \"cfg_scale_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (7)\"\n    },\n    \"enable_steps_scheduling\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"steps_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (25)\"\n    },\n    \"enable_ddim_eta_scheduling\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"ddim_eta_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(0)\"\n    },\n    \"enable_ancestral_eta_scheduling\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n    \"ancestral_eta_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0:(1)\"\n    }\n\n}\n\ndeforum_noise_params = {\n\n    \"enable_noise_multiplier_scheduling\": {\n        \"type\": \"checkbox\",\n        \"default\": True\n    },\n    \"noise_multiplier_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (1.05)\"\n    },\n    \"amount_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (0.1)\"\n    },\n    \"kernel_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (5)\"\n    },\n    \"sigma_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (1.0)\"\n    },\n    \"threshold_schedule\": {\n        \"type\": \"lineedit\",\n        \"default\": \"0: (0.0)\"\n    },\n    \"noise_type\": {\n        \"type\": \"dropdown\",\n        \"choices\": [\"uniform\", \"perlin\"]\n    },\n    \"perlin_w\": {\n        \"type\": \"spinbox\",\n        \"min\": 1,\n        \"max\": 100,\n        \"default\": 8,\n        \"step\": 1\n    },\n    \"perlin_h\": {\n        \"type\": \"spinbox\",\n        \"min\": 1,\n        \"max\": 100,\n        \"default\": 8,\n        \"step\": 1\n    },\n    \"perlin_octaves\": {\n        \"type\": \"spinbox\",\n        \"min\": 1,\n        \"max\": 10,\n        \"default\": 4,\n        \"step\": 1\n    },\n    \"perlin_persistence\": {\n        \"type\": \"doublespinbox\",\n        \"min\": 0.1,\n        \"max\": 1.0,\n        \"default\": 0.5,\n        \"step\": 0.1\n    },\n\n}\n\n\ndeforum_args_layout = {\n                \"enable_perspective_flip\": {\n                    \"type\": \"checkbox\",\n                    \"default\": False\n                },\n                \"perspective_flip_theta\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(0)\"\n                },\n                \"perspective_flip_phi\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(0)\"\n                },\n                \"perspective_flip_gamma\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(0)\"\n                },\n                \"perspective_flip_fv\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(53)\"\n                },\n                \"fov_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (70)\"\n                },\n                \"aspect_ratio_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (1)\"\n                },\n                \"aspect_ratio_use_old_formula\": {\n                    \"type\": \"checkbox\",\n                    \"default\": False\n                },\n                \"near_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (200)\"\n                },\n                \"far_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (10000)\"\n                },\n                \"seed_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(s), 1:(-1), \\\"max_f-2\\\":(-1), \\\"max_f-1\\\":(s)\"\n                },\n                \"pix2pix_img_cfg_scale\": {\n                    \"type\": \"doublespinbox\",\n                    \"min\": 0.0,\n                    \"max\": 10.0,\n                    \"default\": 1.5,\n                    \"step\": 0.1\n                },\n                \"pix2pix_img_cfg_scale_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(1.5)\"\n                },\n                \"enable_subseed_scheduling\": {\n                    \"type\": \"checkbox\",\n                    \"default\": False\n                },\n                \"subseed_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(1)\"\n                },\n                \"subseed_strength_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0:(0)\"\n                },\n                \"enable_sampler_scheduling\": {\n                    \"type\": \"checkbox\",\n                    \"default\": False\n                },\n                \"sampler_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (\\\"Euler a\\\")\"\n                },\n                \"use_noise_mask\": {\n                    \"type\": \"checkbox\",\n                    \"default\": False\n                },\n                \"mask_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (\\\"{video_mask}\\\")\"\n                },\n                \"noise_mask_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (\\\"{video_mask}\\\")\"\n                },\n                \"enable_checkpoint_scheduling\": {\n                    \"type\": \"checkbox\",\n                    \"default\": False\n                },\n                \"checkpoint_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (\\\"model1.ckpt\\\"), 100: (\\\"model2.safetensors\\\")\"\n                },\n                \"enable_clipskip_scheduling\": {\n                    \"type\": \"checkbox\",\n                    \"default\": False\n                },\n                \"clipskip_schedule\": {\n                    \"type\": \"lineedit\",\n                    \"default\": \"0: (2)\"\n                },\n\n\n        }\n\n\n\ndeforum_image_init_params = {\n\n    \"strength_0_no_init\": {\n      \"type\": \"checkbox\",\n      \"default\": True\n    },\n    \"init_image\": {\n      \"type\": \"lineedit\",\n      \"default\": \"https://deforum.github.io/a1/I1.png\"\n    },\n    \"init_sample\": {\n      \"type\": \"lineedit\",\n      \"default\": None\n    },\n    \"use_init\": {\n        \"type\": \"checkbox\",\n        \"default\": False\n    },\n\n}\n\n\n"
  },
  {
    "path": "deforum_nodes/modules/interp.py",
    "content": "import os\nimport random\nimport cv2\nimport numpy as np\n\n\n# def optical_flow_cadence(i1, i2, cadence, method=\"DIS Medium\"):\n#     imgs = []\n#     if i1 is not None and i2 is not None:\n#         imgs.append(i1)\n#         flow = get_flow_from_images(i1, i2, method)\n#         i2 = image_transform_optical_flow(i2, -flow)\n#         for i in range(1, cadence):\n#             weight = i / cadence\n#             flow_inc = flow * weight\n#             img = cv2.addWeighted(i1, 1 - weight, i2, weight, 0)\n#             img = image_transform_optical_flow(img, flow_inc, cv2.BORDER_REPLICATE)\n#             imgs.append(img)\n#         imgs.append(i2)\n#     return imgs\n\n\ndef optical_flow_cadence(i1, i2, cadence, method=\"DIS Medium\"):\n    imgs = []\n    if i1 is not None and i2 is not None:\n        flow = get_flow_from_images(i1, i2, method)\n        # Warp i2 using the negative of the calculated flow to align it with i1\n        i2_warped = image_transform_optical_flow(i2, -flow)\n\n        for i in range(cadence):\n            weight = i / (cadence - 1)  # Adjust weight calculation to include both endpoints\n            # Directly interpolate between i1 and warped i2 for each frame\n            img = cv2.addWeighted(i1, 1 - weight, i2_warped, weight, 0)\n            flow_inc = flow * weight\n            img = image_transform_optical_flow(img, flow_inc, cv2.BORDER_REPLICATE)\n\n            imgs.append(img)\n\n        # Ensure the last frame is exactly i2, to avoid any discrepancies\n        #imgs[-1] = i2\n\n    return imgs\n\ndef get_matrix_for_hybrid_motion(frame_idx, dimensions, inputfiles, hybrid_motion):\n    img1 = cv2.cvtColor(get_resized_image_from_filename(str(inputfiles[frame_idx - 1]), dimensions), cv2.COLOR_BGR2GRAY)\n    img2 = cv2.cvtColor(get_resized_image_from_filename(str(inputfiles[frame_idx]), dimensions), cv2.COLOR_BGR2GRAY)\n    matrix = get_transformation_matrix_from_images(img1, img2, hybrid_motion)\n    print(f\"[deforum] Calculating {hybrid_motion} RANSAC matrix for frames {frame_idx} to {frame_idx + 1}\")\n    return matrix\n\n\ndef get_matrix_for_hybrid_motion_prev(frame_idx, dimensions, inputfiles, prev_img, hybrid_motion):\n    # first handle invalid images from cadence by returning default matrix\n    height, width = prev_img.shape[:2]\n    if height == 0 or width == 0 or prev_img != np.uint8:\n        return get_hybrid_motion_default_matrix(hybrid_motion)\n    else:\n        prev_img_gray = cv2.cvtColor(prev_img, cv2.COLOR_BGR2GRAY)\n        img = cv2.cvtColor(get_resized_image_from_filename(str(inputfiles[frame_idx]), dimensions), cv2.COLOR_BGR2GRAY)\n        matrix = get_transformation_matrix_from_images(prev_img_gray, img, hybrid_motion)\n        print(f\"[deforum] Calculating {hybrid_motion} RANSAC matrix for frames {frame_idx} to {frame_idx + 1}\")\n        return matrix\n\n\ndef get_flow_for_hybrid_motion(frame_idx, dimensions, inputfiles, hybrid_frame_path, method,\n                               do_flow_visualization=False):\n    print(f\"[deforum] Calculating {method} optical flow for frames {frame_idx} to {frame_idx + 1}\")\n    i1 = get_resized_image_from_filename(str(inputfiles[frame_idx]), dimensions)\n    i2 = get_resized_image_from_filename(str(inputfiles[frame_idx + 1]), dimensions)\n    flow = get_flow_from_images(i1, i2, method)\n    if do_flow_visualization:\n        save_flow_visualization(frame_idx, dimensions, flow, inputfiles, hybrid_frame_path)\n    return flow\n\n\ndef get_flow_for_hybrid_motion_prev(frame_idx, dimensions, inputfiles, hybrid_frame_path, prev_img, method,\n                                    do_flow_visualization=False):\n    print(f\"[deforum] Calculating {method} optical flow for frames {frame_idx} to {frame_idx + 1}\")\n    # first handle invalid images from cadence by returning default matrix\n    height, width = prev_img.shape[:2]\n    if height == 0 or width == 0:\n        flow = get_hybrid_motion_default_flow(dimensions)\n    else:\n        i1 = prev_img.astype(np.uint8)\n        i2 = get_resized_image_from_filename(str(inputfiles[frame_idx]), dimensions)\n        flow = get_flow_from_images(i1, i2, method)\n    if do_flow_visualization:\n        save_flow_visualization(frame_idx, dimensions, flow, inputfiles, hybrid_frame_path)\n    return flow\n\ndef get_flow_for_hybrid_motion_prev_imgs(frame_idx, dimensions, current_img, prev_img, method,\n                                    do_flow_visualization=False):\n    print(f\"[deforum] Calculating {method} optical flow for frames {frame_idx} to {frame_idx + 1}\")\n    # first handle invalid images from cadence by returning default matrix\n    height, width = prev_img.shape[:2]\n    if height == 0 or width == 0:\n        flow = get_hybrid_motion_default_flow(dimensions)\n    else:\n        i1 = prev_img.astype(np.uint8)\n        i2 = current_img.astype(np.uint8)\n        flow = get_flow_from_images(i1, i2, method)\n    # if do_flow_visualization:\n    #     save_flow_visualization(frame_idx, dimensions, flow, inputfiles, hybrid_frame_path)\n    return flow\n\n\ndef image_transform_ransac(image_cv2, xform, hybrid_motion, border_mode=cv2.BORDER_REPLICATE):\n    if hybrid_motion == \"Perspective\":\n        return image_transform_perspective(image_cv2, xform, border_mode=border_mode)\n    else:  # Affine\n        return image_transform_affine(image_cv2, xform, border_mode=border_mode)\n\n\ndef image_transform_optical_flow(img, flow, border_mode=cv2.BORDER_REPLICATE, flow_reverse=False):\n    if not flow_reverse:\n        flow = -flow\n    h, w = img.shape[:2]\n    flow[:, :, 0] += np.arange(w)\n    flow[:, :, 1] += np.arange(h)[:, np.newaxis]\n    return remap(img, flow, border_mode)\n\n\ndef image_transform_affine(image_cv2, xform, border_mode=cv2.BORDER_REPLICATE):\n    return cv2.warpAffine(\n        image_cv2,\n        xform,\n        (image_cv2.shape[1], image_cv2.shape[0]),\n        borderMode=border_mode\n    )\n\n\ndef image_transform_perspective(image_cv2, xform, border_mode=cv2.BORDER_REPLICATE):\n    return cv2.warpPerspective(\n        image_cv2,\n        xform,\n        (image_cv2.shape[1], image_cv2.shape[0]),\n        borderMode=border_mode\n    )\n\n\ndef get_hybrid_motion_default_matrix(hybrid_motion):\n    if hybrid_motion == \"Perspective\":\n        arr = np.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])\n    else:\n        arr = np.array([[1., 0., 0.], [0., 1., 0.]])\n    return arr\n\n\ndef get_hybrid_motion_default_flow(dimensions):\n    cols, rows = dimensions\n    flow = np.zeros((rows, cols, 2), np.float32)\n    return flow\n\n\ndef get_transformation_matrix_from_images(img1, img2, hybrid_motion, max_corners=200, quality_level=0.01,\n                                          min_distance=30, block_size=3):\n    # Detect feature points in previous frame\n    prev_pts = cv2.goodFeaturesToTrack(img1,\n                                       maxCorners=max_corners,\n                                       qualityLevel=quality_level,\n                                       minDistance=min_distance,\n                                       blockSize=block_size)\n\n    if prev_pts is None or len(prev_pts) < 8 or img1 is None or img2 is None:\n        return get_hybrid_motion_default_matrix(hybrid_motion)\n\n    # Get optical flow\n    curr_pts, status, err = cv2.calcOpticalFlowPyrLK(img1, img2, prev_pts, None)\n\n    # Filter only valid points\n    idx = np.where(status == 1)[0]\n    prev_pts = prev_pts[idx]\n    curr_pts = curr_pts[idx]\n\n    if len(prev_pts) < 8 or len(curr_pts) < 8:\n        return get_hybrid_motion_default_matrix(hybrid_motion)\n\n    if hybrid_motion == \"Perspective\":  # Perspective - Find the transformation between points\n        transformation_matrix, mask = cv2.findHomography(prev_pts, curr_pts, cv2.RANSAC, 5.0)\n        return transformation_matrix\n    else:  # Affine - Compute a rigid transformation (without depth, only scale + rotation + translation)\n        transformation_rigid_matrix, rigid_mask = cv2.estimateAffinePartial2D(prev_pts, curr_pts)\n        return transformation_rigid_matrix\n\n\ndef get_flow_from_images(i1, i2, method):\n    if method == \"DIS Medium\":\n        r = get_flow_from_images_DIS(i1, i2, cv2.DISOPTICAL_FLOW_PRESET_MEDIUM)\n    elif method == \"DIS Fast\":\n        r = get_flow_from_images_DIS(i1, i2, cv2.DISOPTICAL_FLOW_PRESET_FAST)\n    elif method == \"DIS UltraFast\":\n        r = get_flow_from_images_DIS(i1, i2, cv2.DISOPTICAL_FLOW_PRESET_ULTRAFAST)\n    elif method == \"DenseRLOF\":  # requires running opencv-contrib-python (full opencv) INSTEAD of opencv-python\n        r = get_flow_from_images_Dense_RLOF(i1, i2)\n    elif method == \"SF\":  # requires running opencv-contrib-python (full opencv) INSTEAD of opencv-python\n        r = get_flow_from_images_SF(i1, i2)\n    elif method == \"Farneback Fine\":\n        r = get_flow_from_images_Farneback(i1, i2, 'fine')\n    else:  # Farneback Normal:\n        r = get_flow_from_images_Farneback(i1, i2)\n    return r\n\n\ndef get_flow_from_images_DIS(i1, i2, preset):\n    i1 = cv2.cvtColor(i1, cv2.COLOR_BGR2GRAY)\n    i2 = cv2.cvtColor(i2, cv2.COLOR_BGR2GRAY)\n    dis = cv2.DISOpticalFlow_create(preset)\n    return dis.calc(i1, i2, None)\n\n\ndef get_flow_from_images_Dense_RLOF(i1, i2, last_flow=None):\n    return cv2.optflow.calcOpticalFlowDenseRLOF(i1, i2, flow=last_flow)\n\n\ndef get_flow_from_images_SF(i1, i2, last_flow=None, layers=3, averaging_block_size=2, max_flow=4):\n    return cv2.optflow.calcOpticalFlowSF(i1, i2, layers, averaging_block_size, max_flow)\n\n\ndef get_flow_from_images_Farneback(i1, i2, preset=\"normal\", last_flow=None, pyr_scale=0.5, levels=3, winsize=15,\n                                   iterations=3, poly_n=5, poly_sigma=1.2, flags=0):\n    flags = cv2.OPTFLOW_FARNEBACK_GAUSSIAN  # Specify the operation flags\n    pyr_scale = 0.5  # The image scale (<1) to build pyramids for each image\n    if preset == \"fine\":\n        levels = 13  # The number of pyramid layers, including the initial image\n        winsize = 77  # The averaging window size\n        iterations = 13  # The number of iterations at each pyramid level\n        poly_n = 15  # The size of the pixel neighborhood used to find polynomial expansion in each pixel\n        poly_sigma = 0.8  # The standard deviation of the Gaussian used to smooth derivatives used as a basis for the polynomial expansion\n    else:  # \"normal\"\n        levels = 5  # The number of pyramid layers, including the initial image\n        winsize = 21  # The averaging window size\n        iterations = 5  # The number of iterations at each pyramid level\n        poly_n = 7  # The size of the pixel neighborhood used to find polynomial expansion in each pixel\n        poly_sigma = 1.2  # The standard deviation of the Gaussian used to smooth derivatives used as a basis for the polynomial expansion\n    i1 = cv2.cvtColor(i1, cv2.COLOR_BGR2GRAY)\n    i2 = cv2.cvtColor(i2, cv2.COLOR_BGR2GRAY)\n    flags = 0  # flags = cv2.OPTFLOW_USE_INITIAL_FLOW\n    flow = cv2.calcOpticalFlowFarneback(i1, i2, last_flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma,\n                                        flags)\n    return flow\n\n\ndef save_flow_visualization(frame_idx, dimensions, flow, inputfiles, hybrid_frame_path):\n    flow_img_file = os.path.join(hybrid_frame_path, f\"flow{frame_idx:05}.jpg\")\n    flow_img = cv2.imread(str(inputfiles[frame_idx]))\n    flow_img = cv2.resize(flow_img, (dimensions[0], dimensions[1]), cv2.INTER_AREA)\n    flow_img = cv2.cvtColor(flow_img, cv2.COLOR_RGB2GRAY)\n    flow_img = cv2.cvtColor(flow_img, cv2.COLOR_GRAY2BGR)\n    flow_img = draw_flow_lines_in_grid_in_color(flow_img, flow)\n    flow_img = cv2.cvtColor(flow_img, cv2.COLOR_BGR2RGB)\n    cv2.imwrite(flow_img_file, flow_img)\n    print(f\"[deforum] Saved optical flow visualization: {flow_img_file}\")\n\n\ndef draw_flow_lines_in_grid_in_color(img, flow, step=8, magnitude_multiplier=1, min_magnitude=1, max_magnitude=10000):\n    flow = flow * magnitude_multiplier\n    h, w = img.shape[:2]\n    y, x = np.mgrid[step / 2:h:step, step / 2:w:step].reshape(2, -1).astype(int)\n    fx, fy = flow[y, x].T\n    lines = np.vstack([x, y, x + fx, y + fy]).T.reshape(-1, 2, 2)\n    lines = np.int32(lines + 0.5)\n    vis = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)\n\n    mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])\n    hsv = np.zeros((flow.shape[0], flow.shape[1], 3), dtype=np.uint8)\n    hsv[..., 0] = ang * 180 / np.pi / 2\n    hsv[..., 1] = 255\n    hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)\n    bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)\n    vis = cv2.add(vis, bgr)\n\n    # Iterate through the lines\n    for (x1, y1), (x2, y2) in lines:\n        # Calculate the magnitude of the line\n        magnitude = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)\n\n        # Only draw the line if it falls within the magnitude range\n        if min_magnitude <= magnitude <= max_magnitude:\n            b = int(bgr[y1, x1, 0])\n            g = int(bgr[y1, x1, 1])\n            r = int(bgr[y1, x1, 2])\n            color = (b, g, r)\n            cv2.arrowedLine(vis, (x1, y1), (x2, y2), color, thickness=1, tipLength=0.1)\n    return vis\n\n\ndef draw_flow_lines_in_color(img, flow, threshold=3, magnitude_multiplier=1, min_magnitude=0, max_magnitude=10000):\n    # h, w = img.shape[:2]\n    vis = img.copy()  # Create a copy of the input image\n\n    # Find the locations in the flow field where the magnitude of the flow is greater than the threshold\n    mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])\n    idx = np.where(mag > threshold)\n\n    # Create HSV image\n    hsv = np.zeros((flow.shape[0], flow.shape[1], 3), dtype=np.uint8)\n    hsv[..., 0] = ang * 180 / np.pi / 2\n    hsv[..., 1] = 255\n    hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)\n\n    # Convert HSV image to BGR\n    bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)\n\n    # Add color from bgr\n    vis = cv2.add(vis, bgr)\n\n    # Draw an arrow at each of these locations to indicate the direction of the flow\n    for i, (y, x) in enumerate(zip(idx[0], idx[1])):\n        # Calculate the magnitude of the line\n        x2 = x + magnitude_multiplier * int(flow[y, x, 0])\n        y2 = y + magnitude_multiplier * int(flow[y, x, 1])\n        magnitude = np.sqrt((x2 - x) ** 2 + (y2 - y) ** 2)\n\n        # Only draw the line if it falls within the magnitude range\n        if min_magnitude <= magnitude <= max_magnitude:\n            if i % random.randint(100, 200) == 0:\n                b = int(bgr[y, x, 0])\n                g = int(bgr[y, x, 1])\n                r = int(bgr[y, x, 2])\n                color = (b, g, r)\n                cv2.arrowedLine(vis, (x, y), (x2, y2), color, thickness=1, tipLength=0.25)\n\n    return vis\n\n\ndef autocontrast_grayscale(image, low_cutoff=0, high_cutoff=100):\n    # Perform autocontrast on a grayscale np array image.\n    # Find the minimum and maximum values in the image\n    min_val = np.percentile(image, low_cutoff)\n    max_val = np.percentile(image, high_cutoff)\n\n    # Scale the image so that the minimum value is 0 and the maximum value is 255\n    image = 255 * (image - min_val) / (max_val - min_val)\n\n    # Clip values that fall outside the range [0, 255]\n    image = np.clip(image, 0, 255)\n\n    return image\n\n\ndef get_resized_image_from_filename(im, dimensions):\n    img = cv2.imread(im)\n    return cv2.resize(img, (dimensions[0], dimensions[1]), cv2.INTER_AREA)\n\n\ndef remap(img, flow, border_mode=cv2.BORDER_REFLECT_101):\n    # copyMakeBorder doesn't support wrap, but supports replicate. Replaces wrap with reflect101.\n    if border_mode == cv2.BORDER_WRAP:\n        border_mode = cv2.BORDER_REFLECT_101\n    h, w = img.shape[:2]\n    displacement = int(h * 0.25), int(w * 0.25)\n    larger_img = cv2.copyMakeBorder(img, displacement[0], displacement[0], displacement[1], displacement[1],\n                                    border_mode)\n    lh, lw = larger_img.shape[:2]\n    larger_flow = extend_flow(flow, lw, lh)\n    remapped_img = cv2.remap(larger_img, larger_flow, None, cv2.INTER_LINEAR, border_mode)\n    output_img = center_crop_image(remapped_img, w, h)\n    return output_img\n\n\ndef center_crop_image(img, w, h):\n    y, x, _ = img.shape\n    width_indent = int((x - w) / 2)\n    height_indent = int((y - h) / 2)\n    cropped_img = img[height_indent:y - height_indent, width_indent:x - width_indent]\n    return cropped_img\n\n\ndef extend_flow(flow, w, h):\n    # Get the shape of the original flow image\n    flow_h, flow_w = flow.shape[:2]\n    # Calculate the position of the image in the new image\n    x_offset = int((w - flow_w) / 2)\n    y_offset = int((h - flow_h) / 2)\n    # Generate the X and Y grids\n    x_grid, y_grid = np.meshgrid(np.arange(w), np.arange(h))\n    # Create the new flow image and set it to the X and Y grids\n    new_flow = np.dstack((x_grid, y_grid)).astype(np.float32)\n    # Shift the values of the original flow by the size of the border\n    flow[:, :, 0] += x_offset\n    flow[:, :, 1] += y_offset\n    # Overwrite the middle of the grid with the original flow\n    new_flow[y_offset:y_offset + flow_h, x_offset:x_offset + flow_w, :] = flow\n    # Return the extended image\n    return new_flow\n\n"
  },
  {
    "path": "deforum_nodes/modules/standalone_cadence.py",
    "content": "import os\n\nimport cv2\nimport numpy as np\nimport torch\n\nfrom deforum.generators.deforum_flow_generator import rel_flow_to_abs_flow, abs_flow_to_rel_flow, get_flow_from_images, \\\n    get_flow_for_hybrid_motion_prev_imgs\nfrom deforum.utils.deforum_framewarp_utils import anim_frame_warp\nfrom deforum.utils.deforum_hybrid_animation import get_matrix_for_hybrid_motion_prev_imgs\nfrom deforum.utils.image_utils import image_transform_optical_flow, image_transform_ransac\nimport cv2\nimport numpy as np\nimport PIL.Image\n\nclass CadenceInterpolator:\n    def __init__(self):\n        self.turbo_prev_image, self.turbo_prev_frame_idx = None, 0\n        self.turbo_next_image, self.turbo_next_frame_idx = None, 0\n        self.start_frame = 0\n        self.depth = None\n        self.prev_img = None\n        self.prev_flow = None\n\n    def new_standalone_cadence(self, args, anim_args, root, keys, frame_idx, depth_model, raft_model, depth_strength=1.0, logger=None, hybrid_provider=None):\n        # global self.turbo_prev_image, self.turbo_next_image, self.turbo_prev_frame_idx, self.turbo_next_frame_idx, self.start_frame, self.depth\n        if not hasattr(self, 'prev_flow'):\n            self.prev_flow = None\n        self.prev_img = self.turbo_prev_image\n        goal_img = self.turbo_next_image\n        turbo_steps = int(anim_args.diffusion_cadence)\n        cadence_flow_factor = keys.cadence_flow_factor_schedule_series[frame_idx]\n        hybrid_flow_factor = keys.hybrid_flow_factor_schedule_series[frame_idx]\n        hybrid_comp_schedules = keys.hybrid_comp_alpha_schedule_series[frame_idx]\n        imgs = []\n        x = 0\n        # emit in-between frames\n        if turbo_steps > 1:\n            tween_frame_start_idx = max(self.start_frame, frame_idx - turbo_steps)\n\n            cadence_flow = None\n            for tween_frame_idx in range(tween_frame_start_idx, frame_idx):\n                # update progress during cadence\n                # state.job = f\"frame {tween_frame_idx + 1}/{anim_args.max_frames}\"\n                # state.job_no = tween_frame_idx + 1\n                # cadence vars\n                tween = float(tween_frame_idx - tween_frame_start_idx + 1) / float(frame_idx - tween_frame_start_idx)\n                advance_prev = self.turbo_prev_image is not None and tween_frame_idx > self.turbo_prev_frame_idx\n                advance_next = tween_frame_idx > self.turbo_next_frame_idx\n    \n                # optical flow cadence setup before animation warping\n                if anim_args.animation_mode in ['2D', '3D'] and anim_args.optical_flow_cadence != 'None':\n                    if keys.strength_schedule_series[tween_frame_start_idx] > 0:\n                        if cadence_flow is None and self.turbo_prev_image is not None and self.turbo_next_image is not None:\n\n                            cadence_flow = get_flow_from_images(self.turbo_prev_image, self.turbo_next_image,\n                                                                anim_args.optical_flow_cadence, raft_model) / 2\n                            self.turbo_next_image = image_transform_optical_flow(self.turbo_next_image, -cadence_flow, 1)\n    \n                # if opts.data.get(\"deforum_save_gen_info_as_srt\"):\n                #     params_to_print = opts.data.get(\"deforum_save_gen_info_as_srt_params\", ['Seed'])\n                #     params_string = format_animation_params(keys, prompt_series, tween_frame_idx, params_to_print)\n                #     write_frame_subtitle(srt_filename, tween_frame_idx, srt_frame_duration,\n                #                          f\"F#: {tween_frame_idx}; Cadence: {tween < 1.0}; Seed: {args.seed}; {params_string}\")\n                #     params_string = None\n                # if logger:\n                #     logger.update_row(\"Status\", [\"Cadence\", str(tween_frame_idx), f\"tween:{tween:0.2f}\"])\n\n                    # print(\n                    #     f\"[deforum] Creating in-between {'' if cadence_flow is None else anim_args.optical_flow_cadence + ' optical flow '}cadence frame: {tween_frame_idx}; tween:{tween:0.2f};\")\n    \n                if depth_model is not None:\n                    assert (self.turbo_next_image is not None)\n                    self.depth = depth_model.predict(self.turbo_next_image, anim_args.midas_weight, root.half_precision) * depth_strength\n    \n                if advance_prev:\n                    self.turbo_prev_image, _, _ = anim_frame_warp(self.turbo_prev_image, args, anim_args, keys, tween_frame_idx,\n                                                          depth_model, depth=self.depth, device=root.device,\n                                                          half_precision=root.half_precision)\n                if advance_next:\n                    self.turbo_next_image, _, _ = anim_frame_warp(self.turbo_next_image, args, anim_args, keys, tween_frame_idx,\n                                                          depth_model, depth=self.depth, device=root.device,\n                                                          half_precision=root.half_precision)\n    \n                # hybrid video motion - warps self.turbo_prev_image or self.turbo_next_image to match motion\n                if tween_frame_idx > 0:\n                    if anim_args.hybrid_motion in ['Affine', 'Perspective']:\n                        if anim_args.hybrid_motion_use_prev_img:\n                            matrix = get_matrix_for_hybrid_motion_prev_imgs(tween_frame_idx - 1, (args.width, args.height),\n                                                                       self.turbo_next_image, self.turbo_prev_image, anim_args.hybrid_motion)\n                            if advance_prev:\n                                self.turbo_prev_image = image_transform_ransac(self.turbo_prev_image, matrix, anim_args.hybrid_motion)\n                            if advance_next:\n                                self.turbo_next_image = image_transform_ransac(self.turbo_next_image, matrix, anim_args.hybrid_motion)\n                        else:\n                            if hybrid_provider is not None:\n                                # matrix = get_matrix_for_hybrid_motion(tween_frame_idx - 1, (args.W, args.H), inputfiles,\n                                #                                       anim_args.hybrid_motion)\n                                matrix = get_matrix_for_hybrid_motion_prev_imgs(tween_frame_idx - 1,\n                                                                                (args.width, args.height),\n                                                                                hybrid_provider[x + 1], hybrid_provider[x + 1],\n                                                                                anim_args.hybrid_motion)\n                                if advance_prev:\n                                    self.turbo_prev_image = image_transform_ransac(self.turbo_prev_image, matrix, anim_args.hybrid_motion)\n                                if advance_next:\n                                    self.turbo_next_image = image_transform_ransac(self.turbo_next_image, matrix, anim_args.hybrid_motion)\n                    if anim_args.hybrid_motion in ['Optical Flow']:\n                        if anim_args.hybrid_motion_use_prev_img:\n\n                            flow = get_flow_for_hybrid_motion_prev_imgs(self.turbo_next_image,\n                                        self.prev_flow,\n                                        self.turbo_prev_image,\n                                        anim_args.hybrid_flow_method,\n                                        raft_model)\n\n\n                            if advance_prev:\n                                self.turbo_prev_image = image_transform_optical_flow(self.turbo_prev_image, flow,\n                                                                                #hybrid_comp_schedules['flow_factor'])\n                                                                                hybrid_flow_factor)\n                            if advance_next:\n                                self.turbo_next_image = image_transform_optical_flow(self.turbo_next_image, flow,\n                                                                                #hybrid_comp_schedules['flow_factor'])\n                                                                                hybrid_flow_factor)\n                            self.prev_flow = flow\n                        else:\n                            if hybrid_provider is not None:\n                                flow = get_flow_for_hybrid_motion_prev_imgs(hybrid_provider[x + 1],\n                                                                            self.prev_flow,\n                                                                            hybrid_provider[x],\n                                                                            anim_args.hybrid_flow_method,\n                                                                            raft_model)\n\n                                if advance_prev:\n                                    self.turbo_prev_image = image_transform_optical_flow(self.turbo_prev_image, flow,\n                                                                                    hybrid_flow_factor)\n                                if advance_next:\n                                    self.turbo_next_image = image_transform_optical_flow(self.turbo_next_image, flow,\n                                                                                    hybrid_flow_factor)\n                                self.prev_flow = flow\n    \n                # do optical flow cadence after animation warping\n                if cadence_flow is not None:\n                    cadence_flow = abs_flow_to_rel_flow(cadence_flow, args.width, args.height)\n                    cadence_flow, _, _ = anim_frame_warp(cadence_flow, args, anim_args, keys, tween_frame_idx, depth_model,\n                                                      depth=self.depth, device=root.device, half_precision=root.half_precision)\n                    cadence_flow_inc = rel_flow_to_abs_flow(cadence_flow, args.width, args.height) * tween\n                    if advance_prev:\n                        self.turbo_prev_image = image_transform_optical_flow(self.turbo_prev_image, cadence_flow_inc,\n                                                                        cadence_flow_factor)\n                    if advance_next:\n                        self.turbo_next_image = image_transform_optical_flow(self.turbo_next_image, cadence_flow_inc,\n                                                                        cadence_flow_factor)\n    \n                self.turbo_prev_frame_idx = self.turbo_next_frame_idx = tween_frame_idx\n    \n                if self.turbo_prev_image is not None and tween < 1.0:\n                    img = self.turbo_prev_image * (1.0 - tween) + self.turbo_next_image * tween\n                else:\n                    img = self.turbo_next_image\n    \n                # intercept and override to grayscale\n                if anim_args.color_force_grayscale:\n                    img = cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2GRAY)\n                    img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)\n    \n                    # overlay mask\n                # if args.overlay_mask and (anim_args.use_mask_video or args.use_mask):\n                #     img = do_overlay_mask(args, anim_args, img, tween_frame_idx, True)\n                if logger:\n                    # preview = preview\n                    pil_img = PIL.Image.fromarray(img.astype(np.uint8))\n                    logger.update_absolute(turbo_steps - (frame_idx - tween_frame_idx) + 1, preview=(\"JPEG\", pil_img, 512))\n\n                imgs.append(img)\n                x += 1\n        return imgs\n    \n    \n    \n\n\n\n\n#\n# def standalone_cadence(img, prev_img, frame_idx, cadence_frames, args, anim_args, keys, raft_model=None, depth_model=None):\n#     if img is not None:\n#         if cadence_frames > 1:\n#             if isinstance(img, PIL.Image.Image):\n#                 img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)\n#             if isinstance(prev_img, PIL.Image.Image):\n#                 prev_img = cv2.cvtColor(np.array(prev_img), cv2.COLOR_RGB2BGR)\n#\n#             tween_frame_start_idx = max(0, frame_idx - cadence_frames)\n#             cadence_flow = None\n#             return_frames = []\n#             for tween_frame_idx in range(tween_frame_start_idx, frame_idx):\n#                 tween = float(tween_frame_idx - tween_frame_start_idx + 1) / float(frame_idx - tween_frame_start_idx)\n#                 advance_prev = prev_img is not None and tween_frame_idx > 0\n#                 advance_next = tween_frame_idx > 0\n#\n#                 if anim_args.animation_mode in ['2D', '3D'] and anim_args.optical_flow_cadence != 'None':\n#                     if keys.strength_schedule_series[tween_frame_start_idx] > 0:\n#                         if cadence_flow is None and prev_img is not None and img is not None:\n#                             cadence_flow = get_flow_from_images(prev_img, img, anim_args.optical_flow_cadence, raft_model) / 2\n#                             img = image_transform_optical_flow(img, -cadence_flow, 1)\n#\n#                 if depth_model is not None:\n#                     depth = depth_model.predict(img, anim_args.midas_weight, True)\n#                     if advance_prev:\n#                         prev_img, _, _ = anim_frame_warp(prev_img, args, anim_args, keys, tween_frame_idx, depth_model=depth_model, depth=self.depth, device='cuda', half_precision=True)\n#                     if advance_next:\n#                         img, _, _ = anim_frame_warp(img, args, anim_args, keys, tween_frame_idx, depth_model=depth_model, depth=self.depth, device='cuda', half_precision=True)\n#\n#                 if cadence_flow is not None:\n#                     cadence_flow = abs_flow_to_rel_flow(cadence_flow, args.width, args.height)\n#                     cadence_flow, _, _ = anim_frame_warp(cadence_flow, args, anim_args, keys, tween_frame_idx, depth_model=depth_model, depth=self.depth, device='cuda', half_precision=True)\n#                     cadence_flow_inc = rel_flow_to_abs_flow(cadence_flow, args.width, args.height) * tween\n#                     if advance_prev:\n#                         prev_img = image_transform_optical_flow(prev_img, cadence_flow_inc, 1)\n#                     if advance_next:\n#                         img = image_transform_optical_flow(img, cadence_flow_inc, 1)\n#\n#                 if prev_img is not None and tween < 1.0:\n#                     combined_img = prev_img * (1.0 - tween) + img * tween\n#                 else:\n#                     combined_img = img\n#\n#                 if anim_args.color_force_grayscale:\n#                     combined_img = cv2.cvtColor(combined_img.astype(np.uint8), cv2.COLOR_BGR2GRAY)\n#                     combined_img = cv2.cvtColor(combined_img, cv2.COLOR_GRAY2BGR)\n#                 res = combined_img.astype(np.uint8)\n#                 return_frames.append(res)\n#             return return_frames, prev_img, img\n\n"
  },
  {
    "path": "deforum_nodes/nodes/__init__.py",
    "content": ""
  },
  {
    "path": "deforum_nodes/nodes/deforum_advnoise_node.py",
    "content": "import secrets\n\nimport torch\nimport numpy as np\nimport math\n\nimport pywt\nimport torch.nn.functional as F\nfrom opensimplex import OpenSimplex\n\n\"\"\"\nNoise types to implement:\n\nDithering Noise: Introduces a monochrome or color dithering effect that simulates more colors in images with limited palettes. This can create a retro, pixelated look or be used to prevent banding in gradients.\n\nJPEG Compression Noise: Simulates the artifacts introduced by JPEG compression, such as blockiness and loss of detail, which can be relevant for training models to be robust against compression artifacts.\n\nMoire Pattern Noise: Simulates the moiré pattern, an interference pattern that can occur when an image with repetitive details is digitized. This is particularly relevant for document scanning and OCR applications.\n\nDefocus Blur Noise: Simulates the effect of an out-of-focus lens, which can be used to generate data for depth estimation or autofocus algorithms.\n\nLens Distortion Noise: Introduces barrel or pincushion distortion to simulate lens imperfections, useful for calibration and correction algorithms.\n\nChromatic Aberration: Simulates the color fringing around high-contrast edges caused by lens dispersion, adding realism to synthetic images or for artistic effects.\n\nColor Shift Noise: Randomly shifts the color balance of the image, simulating white balance inaccuracies or artistic color grading.\n\nSpeckle Noise in Color: While speckle noise is typically applied in a monochromatic context, it can be adapted to color images to simulate sensor noise in color imaging devices.\n\nAtmospheric Turbulence Noise: Simulates the effect of atmospheric distortion, relevant for images taken over long distances in outdoor environments, like satellite or surveillance imagery.\n\nHalftone Pattern Noise: Simulates the halftone dots used in printed media, which can be useful for preparing models that need to understand scanned printed documents or for creating retro effects.\n\nHolographic Interference Noise: Simulates the pattern seen in holograms, which could be interesting for security feature recognition or artistic effects.\n\nColor Quantization Noise: While quantization was mentioned, specifically focusing on reducing the color depth of an image can simulate older digital or printed media.\n\nFilm Grain: Simulates the random texture characteristic of film photography, adding a nostalgic or artistic quality to digital images.\n\nPattern Overlay Noise: Overlaying a fixed pattern, such as a grid or text watermark, to simulate watermarked paper or screen overlays.\n\n\"\"\"\n\nclass AddAdvancedNoiseNode:\n    \"\"\"\n    A node to add various types of advanced noise to an image using PyTorch.\n    \"\"\"\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"images\": (\"IMAGE\",),\n                \"noise_type\": ([\"wavelet\", \"value\", \"flow\", \"turbulence\",\n                                \"ridged_multifractal\", \"reaction_diffusion\", \"voronoi\", \"simplex\"],), # \"gabor\"\n                \"amount\": (\"FLOAT\", {\n                    \"default\": 0.1,\n                    \"min\": 0.0,\n                    \"max\": 100.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n            },\n            \"optional\": {\n                \"seed\": (\"INT\", {\n                    \"default\": None,\n                    \"min\": 0,\n                    \"max\": 2 ** 32 - 1,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                # Voronoi specific parameters\n                \"num_points\": (\"INT\", {\n                    \"default\": 100,\n                    \"min\": 10,\n                    \"max\": 1000,\n                    \"step\": 10,\n                    \"display\": \"number\"\n                }),\n                # Simplex Noise specific parameters\n                \"scale\": (\"FLOAT\", {\n                    \"default\": 0.1,\n                    \"min\": 0.01,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"octaves\": (\"INT\", {\n                    \"default\": 1,\n                    \"min\": 1,\n                    \"max\": 10,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                \"persistence\": (\"FLOAT\", {\n                    \"default\": 0.5,\n                    \"min\": 0.1,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"lacunarity\": (\"FLOAT\", {\n                    \"default\": 2.0,\n                    \"min\": 1.0,\n                    \"max\": 3.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                # Wavelet Noise specific parameters\n\n                \"wavelet\": ([\"haar\", \"db1\", \"sym2\", \"coif1\", \"bior1.3\", \"rbio1.3\"],),\n                \"mode\": ([\"symmetric\", \"periodic\", \"reflect\", \"zero-padding\"],),\n                # Additional parameters specific to Gabor noise\n                \"frequency\": (\"FLOAT\", {\n                    \"default\": 0.1,\n                    \"min\": 0.01,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"theta\": (\"FLOAT\", {\n                    \"default\": 0.0,\n                    \"min\": 0.0,\n                    \"max\": 2 * math.pi,\n                    \"step\": 0.01,\n                    \"display\": \"slider\"\n                }),\n                \"sigma_x\": (\"FLOAT\", {\n                    \"default\": 10.0,\n                    \"min\": 1.0,\n                    \"max\": 50.0,\n                    \"step\": 1.0,\n                    \"display\": \"number\"\n                }),\n                \"sigma_y\": (\"FLOAT\", {\n                    \"default\": 10.0,\n                    \"min\": 1.0,\n                    \"max\": 50.0,\n                    \"step\": 1.0,\n                    \"display\": \"number\"\n                }),\n                # Value Noise specific parameters\n                \"res\": (\"INT\", {\n                    \"default\": 16,\n                    \"min\": 4,\n                    \"max\": 64,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                # Additional parameters for Flow Noise\n                \"flow_scale\": (\"FLOAT\", {\n                    \"default\": 0.1,\n                    \"min\": 0.01,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"flow_angle\": (\"FLOAT\", {\n                    \"default\": 0.0,\n                    \"min\": 0.0,\n                    \"max\": 2 * np.pi,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                # Reaction Diffusion Noise\n                \"steps\": (\"INT\", {\n                    \"default\": 10,\n                    \"min\": 1,\n                    \"max\": 100,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                \"Du\": (\"FLOAT\", {\n                    \"default\": 0.16,\n                    \"min\": 0.01,\n                    \"max\": 0.5,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"Dv\": (\"FLOAT\", {\n                    \"default\": 0.08,\n                    \"min\": 0.01,\n                    \"max\": 0.5,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"feed_rate\": (\"FLOAT\", {\n                    \"default\": 0.035,\n                    \"min\": 0.01,\n                    \"max\": 0.1,\n                    \"step\": 0.001,\n                    \"display\": \"slider\"\n                }),\n                \"kill_rate\": (\"FLOAT\", {\n                    \"default\": 0.06,\n                    \"min\": 0.01,\n                    \"max\": 0.1,\n                    \"step\": 0.001,\n                    \"display\": \"slider\"\n                }),\n                # Additional parameters specific to each noise type can be defined here\n            }\n        }\n\n    RETURN_TYPES = (\"IMAGE\",\"IMAGE\",)\n    RETURN_NAMES = (\"NOISED_IMAGE\",\"NOISE\",)\n\n    FUNCTION = \"add_advanced_noise\"\n    CATEGORY = \"deforum/noise\"\n    display_name = \"Add Advanced Noise\"\n\n    def add_advanced_noise(self, images, noise_type, amount, seed=None, **kwargs):\n        B, H, W, C = images.shape\n        if seed is not None:\n            np.random.seed(seed)\n            torch.manual_seed(seed)\n\n        # Adjusted noise function dictionary\n        noise_function = {\n            \"voronoi\": generate_voronoi_noise,\n            \"simplex\": lambda H, W, **kw: generate_simplex_noise(H, W, **kw).unsqueeze(-1).repeat(1, 1, 1, C),\n            \"wavelet\": lambda H, W, **kw: generate_wavelet_noise(H, W, **kw).unsqueeze(-1).repeat(1, 1, 1, C),\n            \"gabor\": lambda H, W, **kw: generate_gabor_noise(H, W, **kw),\n            \"value\": lambda H, W, **kw: generate_value_noise(H, W, **kw).unsqueeze(-1).repeat(1, 1, 1, C),\n            \"flow\": lambda H, W, **kw: generate_flow_noise(H, W, **kw),\n            \"turbulence\": lambda H, W, **kw: generate_turbulence_noise(H, W, **kw),\n            \"ridged_multifractal\": lambda H, W, **kw: generate_ridged_multifractal_noise(H, W, **kw),\n            \"reaction_diffusion\": lambda H, W, **kw: generate_reaction_diffusion_noise(H, W, **kw),\n\n            # Additional noise functions adjusted similarly\n        }.get(noise_type, lambda H, W, **kw: torch.zeros((B, H, W, C)))\n\n        # Generate the noise pattern\n        noise = noise_function(H, W, seed=seed, **kwargs)\n        # Adjust for the case where noise is not returned with a batch dimension or channel dimension\n        if noise.dim() == 2:  # Assuming noise is of shape (H, W)\n            noise = noise.unsqueeze(0).unsqueeze(-1)  # Add batch and channel dimension\n            noise = noise.expand(B, -1, -1, C)  # Expand to match (B, H, W, C)\n        elif noise.dim() == 3:  # Assuming noise is of shape (B, H, W)\n            noise = noise.unsqueeze(-1)  # Add channel dimension\n            noise = noise.expand(-1, -1, -1, C)  # Expand to match (B, H, W, C)\n\n        # Ensure noise tensor device matches the images tensor device\n        noise = noise.to(images.device)\n        # from ..modules.better_resize.resize_right import resize\n        # belnd_img = resize(images, noise.shape, antialiasing=False)\n        # Apply the noise to the images\n        noisy_images = images.clone() + amount * noise.clone()\n\n        return (torch.clamp(noisy_images, 0, 1),noise,)\n\ndef generate_voronoi_noise(H, W, num_points=100, seed=None, *args, **kwargs):\n    \"\"\"\n    Generates a 2D Voronoi (Worley) Noise pattern.\n\n    Args:\n        H, W (int): The height and width of the output noise pattern.\n        num_points (int): Number of seed points to generate.\n        seed (int, optional): Random seed for reproducibility.\n\n    Returns:\n        torch.Tensor: A tensor containing the Voronoi noise pattern.\n    \"\"\"\n    if seed is not None:\n        torch.manual_seed(seed)\n        np.random.seed(seed)\n\n    # Generate random seed points\n    seeds_x = np.random.uniform(0, W, num_points)\n    seeds_y = np.random.uniform(0, H, num_points)\n    seeds = np.stack((seeds_x, seeds_y), axis=-1)\n\n    # Initialize the noise tensor\n    noise = torch.full((H, W), float('inf'))\n\n    # Compute distance from each pixel to the closest seed point\n    for y in range(H):\n        for x in range(W):\n            distances = torch.tensor([((x - sx) ** 2 + (y - sy) ** 2) for sx, sy in seeds])\n            noise[y, x] = torch.min(distances).sqrt()\n\n    # Normalize the noise pattern\n    noise = (noise - noise.min()) / (noise.max() - noise.min())\n\n    return noise\n\ndef generate_simplex_noise(H, W, scale=0.05, octaves=4, persistence=0.5, lacunarity=2.0, seed=None, **kwargs):\n    if seed is not None:\n        gen = OpenSimplex(seed)\n    else:\n        gen = OpenSimplex()\n\n    noise_pattern = np.zeros((H, W), dtype=np.float32)\n\n    for y in range(H):\n        for x in range(W):\n            nx = x / W - 0.5  # Normalize x coordinate to [-0.5, 0.5]\n            ny = y / H - 0.5  # Normalize y coordinate to [-0.5, 0.5]\n            noise_val = 0\n            amplitude = 1.0\n            frequency = 1.0\n            max_value = 0.0\n            for o in range(octaves):\n                noise_val += gen.noise2(x=nx * scale * frequency, y=ny * scale * frequency) * amplitude\n                max_value += amplitude\n                amplitude *= persistence\n                frequency *= lacunarity\n\n            # Normalize each point individually\n            noise_pattern[y, x] = (noise_val / max_value + 1) / 2  # Normalize to [0, 1]\n\n    return torch.from_numpy(noise_pattern)\n\n\ndef generate_wavelet_noise(H, W, octaves=3, wavelet='haar', mode='symmetric', **kwargs):\n    seed = kwargs.get('seed', secrets.randbelow(2 ** 32))\n    if seed is not None:\n        np.random.seed(seed)\n\n    # Generate initial random noise\n    noise = np.random.randn(H, W)\n\n    # Perform wavelet decomposition and reconstruction\n    coeffs = pywt.wavedec2(noise, wavelet=wavelet, mode=mode, level=octaves)\n    wavelet_noise = pywt.waverec2(coeffs, wavelet=wavelet, mode=mode)\n\n    # Normalize the wavelet noise to have values between 0 and 1\n    wavelet_noise = (wavelet_noise - np.min(wavelet_noise)) / (np.max(wavelet_noise) - np.min(wavelet_noise))\n\n    # Convert to a PyTorch tensor and add a channel dimension since input is assumed to be BxHxWxC\n    wavelet_noise_tensor = torch.from_numpy(wavelet_noise).float().unsqueeze(0).unsqueeze(0)\n\n    # Interpolate to match input size (H, W)\n    # Note: 'align_corners=False' is generally used for non-exact sizes but you might want to experiment\n    # with 'align_corners=True' or 'recompute_scale_factor=True' depending on your specific use case\n    wavelet_noise_tensor = F.interpolate(wavelet_noise_tensor, size=(H, W), mode='bilinear', align_corners=False)\n\n    # Remove the extra dimensions added for interpolation if necessary\n    wavelet_noise_tensor = wavelet_noise_tensor.squeeze()\n\n    return wavelet_noise_tensor\n\n\ndef generate_gabor_kernel(frequency, theta, sigma_x, sigma_y, n_stds=3, grid_size=28):\n    \"\"\"\n    Generates a Gabor kernel.\n\n    Args:\n        frequency (float): The frequency of the sinusoidal component.\n        theta (float): Orientation of the Gabor filter in radians.\n        sigma_x, sigma_y (float): Standard deviation of the Gaussian envelope along x and y axes.\n        n_stds (int): Number of standard deviations to consider for the kernel size.\n        grid_size (int): The size of the output tensor.\n\n    Returns:\n        torch.Tensor: A 2D tensor with the Gabor kernel.\n    \"\"\"\n    xmax = ymax = grid_size // 2\n    xmin = ymin = -xmax\n    (y, x) = torch.meshgrid(torch.arange(ymin, ymax + 1), torch.arange(xmin, xmax + 1))\n    x = x.float()\n    y = y.float()\n\n    # Convert theta to a tensor to use with torch.cos and torch.sin\n    theta_tensor = torch.tensor(theta)\n\n    rotx = x * torch.cos(theta_tensor) + y * torch.sin(theta_tensor)\n    roty = -x * torch.sin(theta_tensor) + y * torch.cos(theta_tensor)\n\n    g = torch.zeros(y.shape)\n    g = torch.exp(-0.5 * ((rotx ** 2 / sigma_x ** 2) + (roty ** 2 / sigma_y ** 2))) * torch.cos(\n        2 * np.pi * frequency * rotx)\n\n    return g\n\ndef generate_gabor_noise(H, W, frequency=0.1, theta=0.0, sigma_x=10.0, sigma_y=10.0, batch_size=1, channels=3, **kwargs):\n    \"\"\"\n    Generates a 2D Gabor Noise pattern and scales it to the size of the input images.\n\n    Args:\n        H, W (int): Height and width of the output noise pattern.\n        frequency (float): Frequency of the sinusoidal component.\n        theta (float): Orientation of the Gabor filter in radians.\n        sigma_x, sigma_y (float): Standard deviation of the Gaussian envelope.\n        batch_size (int): Number of images in the batch.\n        channels (int): Number of channels in the images.\n\n    Returns:\n        torch.Tensor: A tensor containing the Gabor noise pattern, resized to match input images dimensions.\n    \"\"\"\n    gabor_kernel = generate_gabor_kernel(frequency, theta, sigma_x, sigma_y, grid_size=min(H, W))\n\n    # Center the kernel in a larger tensor matching the image dimensions\n    pad_height = (H - gabor_kernel.size(0)) // 2\n    pad_width = (W - gabor_kernel.size(1)) // 2\n    pad_height_extra = H - gabor_kernel.size(0) - pad_height * 2\n    pad_width_extra = W - gabor_kernel.size(1) - pad_width * 2\n\n    # Pad the kernel to match the target size\n    gabor_padded = F.pad(gabor_kernel, [pad_width, pad_width + pad_width_extra, pad_height, pad_height_extra], \"constant\", 0)\n\n    # Ensure the pattern has the correct dimensions: batch_size x H x W x channels\n    gabor_padded = gabor_padded.unsqueeze(0)  # Add a batch dimension\n    gabor_padded = gabor_padded.unsqueeze(-1)  # Add a channel dimension\n    gabor_padded = gabor_padded.expand(batch_size, -1, -1, channels)  # Expand to match the full dimensions\n\n    return gabor_padded\n\n\ndef generate_value_noise(H, W, res=16, seed=None, **kwargs):\n    \"\"\"\n    Generates a 2D Value Noise pattern with edge handling.\n\n    Args:\n        H, W (int): Height and width of the output noise pattern.\n        res (int): Resolution of the noise grid (smaller values mean smoother noise).\n        seed (int, optional): Seed for random number generator.\n\n    Returns:\n        torch.Tensor: A tensor containing the Value noise pattern.\n    \"\"\"\n    if seed is not None:\n        torch.manual_seed(seed)\n\n    # Create a grid of random values\n    grid_res_x, grid_res_y = W // res + 1, H // res + 1\n    random_values = torch.rand((grid_res_y, grid_res_x))\n\n    # Initialize output noise pattern\n    noise_pattern = torch.zeros((H, W))\n\n    # Generate the noise\n    for y in range(H):\n        for x in range(W):\n            # Grid cell coordinates in the random grid\n            cell_x, cell_y = x // res, y // res\n\n            # Ensure indices stay within bounds\n            next_cell_x = min(cell_x + 1, grid_res_x - 1)\n            next_cell_y = min(cell_y + 1, grid_res_y - 1)\n\n            # Local x and y in the grid cell\n            local_x, local_y = (x % res) / res, (y % res) / res\n\n            # Corners of the cell in the grid\n            c00 = random_values[cell_y, cell_x]\n            c10 = random_values[cell_y, next_cell_x]\n            c01 = random_values[next_cell_y, cell_x]\n            c11 = random_values[next_cell_y, next_cell_x]\n\n            # Interpolate between grid corner values\n            nx0 = lerp(c00, c10, fade(local_x))\n            nx1 = lerp(c01, c11, fade(local_x))\n            nxy = lerp(nx0, nx1, fade(local_y))\n\n            noise_pattern[y, x] = nxy\n\n    # Normalize the noise pattern\n    noise_pattern = (noise_pattern - noise_pattern.min()) / (noise_pattern.max() - noise_pattern.min())\n\n    return noise_pattern\n\ndef lerp(a, b, t):\n    \"\"\"Linear interpolation between a and b with t in [0, 1].\"\"\"\n    return a + t * (b - a)\n\ndef fade(t):\n    \"\"\"Fade function as defined by Ken Perlin; eases coordinate values.\"\"\"\n    return t * t * t * (t * (t * 6 - 15) + 10)\n\n\ndef generate_flow_noise_pattern(H, W, scale=0.1, angle=0.0, seed=None):\n    \"\"\"\n    Generates a 2D Flow Noise pattern using a base noise to perturb the sampling space of another noise layer.\n\n    Args:\n        H, W (int): Height and width of the output noise pattern.\n        scale (float): Scale of the underlying noise.\n        angle (float): Base flow direction in radians.\n        seed (int, optional): Random seed for reproducibility.\n\n    Returns:\n        torch.Tensor: A tensor containing the Flow noise pattern.\n    \"\"\"\n    # Base noise for perturbation\n    base_noise = generate_simplex_noise(H, W, scale=scale, seed=seed)\n    # Generate gradient from angle for flow direction\n    flow_x = torch.cos(torch.tensor(angle))\n    flow_y = torch.sin(torch.tensor(angle))\n\n    # Apply perturbation based on the base noise and flow direction\n    perturb_x = base_noise * flow_x\n    perturb_y = base_noise * flow_y\n\n    # Another layer of noise for actual flow appearance\n    flow_noise = generate_simplex_noise(H, W, scale=scale / 2, seed=seed if seed is None else seed + 1)\n\n    # Final flow noise is a combination of perturbed coordinates and the flow noise layer\n    final_noise = flow_noise + perturb_x + perturb_y\n\n    # Normalize the noise pattern\n    final_noise = (final_noise - final_noise.min()) / (final_noise.max() - final_noise.min())\n\n    return final_noise\n\n\ndef generate_flow_noise(H, W, flow_scale=0.1, flow_angle=0.0, seed=None, batch_size=1, channels=3, **kwargs):\n    \"\"\"\n    Generates a 2D Flow Noise pattern and resizes it to match the input image size.\n\n    Args:\n        H, W (int): Height and width of the input images.\n        scale, angle: Parameters for the flow noise generation.\n        seed (int, optional): Seed for random number generator.\n        batch_size (int): The batch size of the input images.\n        channels (int): The channel count of the input images.\n\n    Returns:\n        torch.Tensor: A tensor of the Flow noise pattern, matched to input dimensions.\n    \"\"\"\n    # Generate the base noise pattern\n    base_noise = generate_flow_noise_pattern(H, W, flow_scale, flow_angle, seed)\n    # Ensure the noise tensor has the same dimensions as the input: (batch_size, H, W, channels)\n    # Interpolate noise to match the input size if needed\n    noise_resized = F.interpolate(base_noise.unsqueeze(0).unsqueeze(0), size=(H, W), mode='bilinear',\n                                  align_corners=False)\n\n    # Expand to match the batch and channel dimensions\n    noise_resized = noise_resized.repeat(batch_size, channels, 1, 1)\n    noise_resized = noise_resized.permute(0, 2, 3, 1)  # Rearrange dimensions to match (B, H, W, C)\n\n    return noise_resized\n\ndef generate_turbulence_noise(H, W, scale=0.05, octaves=4, persistence=0.5, lacunarity=2.0, seed=None, **kwargs):\n    if seed is not None:\n        gen = OpenSimplex(seed)\n    else:\n        gen = OpenSimplex()\n\n    noise_pattern = np.zeros((H, W), dtype=np.float32)\n\n    for y in range(H):\n        for x in range(W):\n            nx = x / W - 0.5  # Normalize x coordinate to [-0.5, 0.5]\n            ny = y / H - 0.5  # Normalize y coordinate to [-0.5, 0.5]\n            noise_val = 0\n            amplitude = 1.0\n            frequency = 1.0\n            max_value = 0.0\n            for o in range(octaves):\n                # The key change for turbulence: using the absolute value of the noise\n                noise_val += abs(gen.noise2(x=nx * scale * frequency, y=ny * scale * frequency)) * amplitude\n                max_value += amplitude\n                amplitude *= persistence\n                frequency *= lacunarity\n\n            # Normalize each point individually\n            noise_pattern[y, x] = (noise_val / max_value)\n\n    # Normalize the whole pattern to [0, 1] range if desired\n    noise_pattern = (noise_pattern - noise_pattern.min()) / (noise_pattern.max() - noise_pattern.min())\n\n    return torch.from_numpy(noise_pattern)\n\ndef generate_ridged_multifractal_noise(H, W, scale=0.05, octaves=4, persistence=0.5, lacunarity=2.0, seed=None, **kwargs):\n    if seed is not None:\n        gen = OpenSimplex(seed)\n    else:\n        gen = OpenSimplex()\n\n    noise_pattern = np.zeros((H, W), dtype=np.float32)\n\n    for y in range(H):\n        for x in range(W):\n            nx = x / W - 0.5  # Normalize x coordinate to [-0.5, 0.5]\n            ny = y / H - 0.5  # Normalize y coordinate to [-0.5, 0.5]\n            noise_val = 0\n            amplitude = 1.0\n            frequency = 1.0\n            max_value = 0.0\n            for o in range(octaves):\n                signal = gen.noise2(x=nx * scale * frequency, y=ny * scale * frequency)\n                # Transformation for ridged noise: invert and square the signal\n                signal = 1.0 - abs(signal)\n                signal *= signal\n                noise_val += signal * amplitude\n                max_value += amplitude\n                amplitude *= persistence\n                frequency *= lacunarity\n\n            # Normalize each point individually\n            noise_pattern[y, x] = (noise_val / max_value)\n\n    # Normalize the whole pattern to [0, 1] range if desired\n    noise_pattern = (noise_pattern - noise_pattern.min()) / (noise_pattern.max() - noise_pattern.min())\n\n    return torch.from_numpy(noise_pattern)\n\n\ndef generate_reaction_diffusion_noise(H, W, steps=100, Du=0.16, Dv=0.08, feed_rate=0.035, kill_rate=0.06, seed=None, **kwargs):\n    \"\"\"\n    Generates a 2D Reaction-Diffusion pattern using a simplified Gray-Scott model.\n\n    Args:\n        H, W (int): Height and width of the output noise pattern.\n        steps (int): Number of simulation steps.\n        Du, Dv (float): Diffusion rates for substances A and B.\n        feed_rate, kill_rate (float): Rates for feed and kill reactions.\n        seed (int, optional): Seed for initializing the pattern.\n\n    Returns:\n        torch.Tensor: A tensor containing the Reaction-Diffusion noise pattern.\n    \"\"\"\n    if seed is not None:\n        torch.manual_seed(seed)\n        np.random.seed(seed)\n\n    # Initialize A and B concentrations with A being fully saturated and B having a noise pattern\n    A = torch.ones((H, W), dtype=torch.float32)\n    B = torch.rand((H, W), dtype=torch.float32) * 0.25  # Starting with a low concentration of B\n\n    # Laplacian kernel for diffusion\n    laplacian_kernel = torch.tensor([[0.05, 0.2, 0.05],\n                                     [0.2, -1.0, 0.2],\n                                     [0.05, 0.2, 0.05]], dtype=torch.float32)\n\n    for _ in range(steps):\n        LA = torch.nn.functional.conv2d(A.unsqueeze(0).unsqueeze(0), laplacian_kernel.unsqueeze(0).unsqueeze(0), padding='same').squeeze()\n        LB = torch.nn.functional.conv2d(B.unsqueeze(0).unsqueeze(0), laplacian_kernel.unsqueeze(0).unsqueeze(0), padding='same').squeeze()\n\n        # Reaction-diffusion equations\n        AB2 = A * B.pow(2)\n        dA = Du * LA - AB2 + feed_rate * (1 - A)\n        dB = Dv * LB + AB2 - (feed_rate + kill_rate) * B\n\n        A += dA\n        B += dB\n\n    # Normalize the B concentration pattern to be between 0 and 1\n    pattern = (B - B.min()) / (B.max() - B.min())\n\n    return pattern.unsqueeze(0)  # Add a channel dimension\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_audiosync_nodes.py",
    "content": "import math\n\nimport pandas as pd\nimport scipy.signal\nimport scipy.ndimage\n\nimport numpy as np\nfrom pydub import AudioSegment\nfrom scipy.signal import butter, filtfilt, find_peaks\nimport librosa\n\nschedule_types = [\n    \"angle\", \"transform_center_x\", \"transform_center_y\", \"zoom\", \"translation_x\", \"translation_y\", \"translation_z\",\n    \"rotation_3d_x\", \"rotation_3d_y\", \"rotation_3d_z\", \"perspective_flip_theta\", \"perspective_flip_phi\",\n    \"perspective_flip_gamma\", \"perspective_flip_fv\", \"noise_schedule\", \"strength_schedule\", \"contrast_schedule\",\n    \"cfg_scale_schedule\", \"ddim_eta_schedule\", \"ancestral_eta_schedule\", \"pix2pix_img_cfg_scale\", \"subseed_schedule\",\n    \"subseed_strength_schedule\", \"checkpoint_schedule\", \"steps_schedule\", \"seed_schedule\", \"sampler_schedule\",\n    \"clipskip_schedule\", \"noise_multiplier_schedule\", \"mask_schedule\", \"noise_mask_schedule\", \"kernel_schedule\",\n    \"sigma_schedule\", \"amount_schedule\", \"threshold_schedule\", \"aspect_ratio\", \"fov\", \"near\",\n    \"cadence_flow_factor_schedule\", \"redo_flow_factor_schedule\", \"far\", \"hybrid_comp_alpha_schedule\",\n    \"hybrid_comp_mask_blend_alpha_schedule\", \"hybrid_comp_mask_contrast_schedule\",\n    \"hybrid_comp_mask_auto_contrast_cutoff_high_schedule\", \"hybrid_comp_mask_auto_contrast_cutoff_low_schedule\",\n    \"hybrid_flow_factor_schedule\"\n]\n\nimport numpy as np\nfrom scipy.signal import savgol_filter\n\n\nclass ExtractDominantNoteAmplitude:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\n            \"audio_fft\": (\"AUDIO_FFT\",),\n        },\n            \"optional\": {\n                \"min_frequency\": (\"FLOAT\", {\"default\": 20.0, \"min\": 0.0, \"max\": 20000.0, \"step\": 1.0}),\n                \"max_frequency\": (\"FLOAT\", {\"default\": 8000.0, \"min\": 0.0, \"max\": 20000.0, \"step\": 1.0}),\n                \"magnitude_threshold\": (\"FLOAT\", {\"default\": 0.01, \"min\": 0.0, \"max\": 1.0, \"step\": 0.01}),\n                \"smoothing_window_size\": (\"INT\", {\"default\": 5, \"min\": 1, \"max\": 101, \"step\": 1}),\n                # Odd number, savgol_filter constraint\n            },\n        }\n\n    CATEGORY = \"deforum/audio\"\n\n    RETURN_TYPES = (\"AMPLITUDE\",\"AMPLITUDE\",)\n    RETURN_NAMES = (\"dominant_note_amplitude\",\"phase_amplitude\",)\n    FUNCTION = \"extract\"\n\n    def extract(self, audio_fft, min_frequency, max_frequency, magnitude_threshold, smoothing_window_size):\n        dominant_frequencies_amplitude = []\n        dominant_frequencies_phase = []\n\n        for fft_data in audio_fft:\n            indices = fft_data.get_indices_for_frequency_bands(min_frequency, max_frequency)\n            magnitudes = np.abs(fft_data.fft)[indices]\n            phases = np.angle(fft_data.fft)[indices]\n\n            # Optional: Apply smoothing to magnitudes\n            if smoothing_window_size > 1 and len(magnitudes) > smoothing_window_size:\n                try:\n                    magnitudes = savgol_filter(magnitudes, smoothing_window_size, 3)  # window size, polynomial order\n                except ValueError:\n                    # In case the smoothing_window_size is inappropriate for the data length\n                    pass\n\n            # Apply magnitude threshold\n            magnitudes[magnitudes < magnitude_threshold] = 0\n\n            # Identify dominant frequency index, its amplitude, and phase\n            if magnitudes.size > 0:  # Ensure there's data after filtering\n                dominant_index = np.argmax(magnitudes)\n                dominant_frequencies_amplitude.append(magnitudes[dominant_index])\n                dominant_frequencies_phase.append(phases[dominant_index])\n            else:\n                dominant_frequencies_amplitude.append(0)\n                dominant_frequencies_phase.append(0)  # Use a default phase (e.g., 0) when no dominant frequency is found\n\n        # Convert to numpy array for consistency\n        return (np.array(dominant_frequencies_amplitude), np.array(dominant_frequencies_phase),)\n\nclass InverseFFTNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"magnitude\": (\"AMPLITUDE\",),\n                \"phase\": (\"AMPLITUDE\",),\n            },\n        }\n\n    CATEGORY = \"signal_processing\"\n\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"time_domain_signal\",)\n    FUNCTION = \"synthesize\"\n\n    def synthesize(self, magnitude, phase):\n        # Ensure magnitude and phase arrays have the same length\n        if magnitude.shape != phase.shape:\n            raise ValueError(\"Magnitude and phase arrays must have the same length.\")\n\n        # Combine magnitude and phase to form the complex spectrum\n        complex_spectrum = magnitude * np.exp(1j * phase)\n\n        # Perform the inverse FFT to convert the spectrum back to the time domain\n        time_domain_signal = np.fft.ifft(complex_spectrum)\n\n        # Since the input is real, the output should also be real. We take the real part to avoid numerical errors that might introduce imaginary parts.\n        return (np.real(time_domain_signal),)\n\nclass AmplitudeToAudio:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"amplitude\": (\"AMPLITUDE\",),\n                \"phase\": (\"AMPLITUDE\",),\n            },\n            \"optional\": {\n                # You can define additional parameters such as sample rate if needed\n                \"sample_rate\": (\"INT\", {\"default\": 44100, \"min\": 1, \"max\": 192000, \"step\": 1}),\n            },\n        }\n\n    CATEGORY = \"AudioProcessing\"\n\n    RETURN_TYPES = (\"AUDIO\",)\n    RETURN_NAMES = (\"audio_samples\",)\n    FUNCTION = \"convert\"\n\n    def convert(self, amplitude, phase, sample_rate=44100):\n        if len(amplitude) != len(phase):\n            raise ValueError(\"Amplitude and phase sequences must have the same length.\")\n\n        # Reconstruct the complex spectrum from amplitude and phase\n        complex_spectrum = amplitude * np.exp(1j * phase)\n\n        # Perform the inverse FFT\n        audio_samples = np.fft.ifft(complex_spectrum).real  # Taking the real part as the output\n\n        # Convert the normalized audio samples to 16-bit integers\n        audio_samples_int16 = np.int16(audio_samples / np.max(np.abs(audio_samples)) * 32767)\n\n        # Creating an AudioSegment object from the NumPy array\n        audio_segment = AudioSegment(\n            audio_samples_int16.tobytes(),\n            frame_rate=sample_rate,\n            sample_width=audio_samples_int16.dtype.itemsize,\n            channels=1\n        )\n\n        # Create the AudioData object\n        audio_data = AudioData(audio_segment)\n\n        return (audio_data,)\n\nclass AudioData:\n    def __init__(self, audio_segment) -> None:\n        # Extract the sample rate\n        self.sample_rate = audio_segment.frame_rate\n\n        # Get the number of audio channels\n        self.num_channels = audio_segment.channels\n\n        # Extract the audio data as a NumPy array\n        samples = np.array(audio_segment.get_array_of_samples())\n        if audio_segment.channels == 2:\n            self.audio_data = np.reshape(samples, (len(samples)//2, 2))\n        else:\n            self.audio_data = samples\n\n    def get_channel_audio_data(self, channel: int):\n        if channel < 0 or channel >= self.num_channels:\n            raise IndexError(f\"Channel '{channel}' out of range. total channels is '{self.num_channels}'.\")\n        return self.audio_data[channel::self.num_channels]\n\n    def get_channel_fft(self, channel: int):\n        audio_data = self.get_channel_audio_data(channel)\n        return np.fft.fft(audio_data)\n\n\n\ndef xor(a, b):\n    return bool(a) ^ bool(b)\n\n# class DeforumAmplitudeToKeyframeSeriesNode:\n#     @classmethod\n#     def INPUT_TYPES(s):\n#         return {\"required\":\n#                     {\"type_name\": (schedule_types,),\n#                     \"amplitude\": (\"AMPLITUDE\",),\n#                      },\n#                 \"optional\":\n#                     {\n#                         \"max_frames\": (\"INT\", {\"default\": 500, \"min\": 1, \"max\": 16500, \"step\": 1}),\n#                         \"math\": (\"STRING\", {\"default\": \"x/100\"}),\n#                         \"filter_window\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 100, \"step\": 1}),\n#                         \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",)\n#                     }\n#                 }\n#\n#     RETURN_TYPES = (\"DEFORUM_FRAME_DATA\", \"AMPLITUDE\", \"STRING\")\n#     FUNCTION = \"convert\"\n#     display_name = \"Amplitude to Schedule\"\n#     CATEGORY = \"deforum/audio\"\n#\n#     def safe_eval(self, expr, t, x, max_f):\n#         allowed_locals = {\n#             \"sin\": math.sin, \"cos\": math.cos, \"tan\": math.tan,\n#             \"asin\": math.asin, \"acos\": math.acos, \"atan\": math.atan,\n#             \"sinh\": math.sinh, \"cosh\": math.cosh, \"tanh\": math.tanh,\n#             \"asinh\": math.asinh, \"acosh\": math.acosh, \"atanh\": math.atanh,\n#             \"sqrt\": math.sqrt, \"exp\": math.exp, \"log\": math.log,\n#             \"abs\": math.fabs, \"pow\": math.pow, \"floor\": math.floor, \"ceil\": math.ceil,\n#             \"round\": round, \"min\": min, \"max\": max, \"pi\": math.pi, \"e\": math.e,\n#             \"factorial\": math.factorial,\n#             \"xor\": xor,  # Add custom xor function\n#             \"t\": t, \"x\": x, \"max_f\": max_f,\n#             # Adding boolean operations as lambda functions to simulate 'and', 'or', 'not'\n#             \"and\": lambda a, b: a and b,\n#             \"or\": lambda a, b: a or b,\n#             \"not\": lambda a: not a,\n#         }\n#         try:\n#             # Directly using 'if else' in expr is supported by Python syntax\n#             # Logical operations 'and', 'or', 'not', and 'xor' are supported via the above definitions\n#             return eval(expr, {\"__builtins__\": {}}, allowed_locals)\n#         except NameError as e:\n#             raise ValueError(f\"Invalid expression: {e}\")\n#         except Exception as e:\n#             raise ValueError(f\"Error evaluating expression: {repr(e)}\")\n#\n#     @classmethod\n#     def IS_CHANGED(self, *args, **kwargs):\n#         return float(\"NaN\")\n#\n#     def convert(self, type_name, amplitude, max_frames=1500, math=\"x/100\", filter_window=0, deforum_frame_data={}):\n#         max_f = int(max_frames)\n#\n#         # Optionally apply a smoothing filter to the amplitude data\n#         if filter_window > 0:\n#             amplitude_smoothed = np.convolve(amplitude, np.ones(filter_window) / filter_window, mode='same')\n#         else:\n#             amplitude_smoothed = amplitude\n#\n#         frame_index = 0\n#         modified_amplitude_list = []\n#         for x in amplitude_smoothed:\n#             modified_value = self.safe_eval(math, frame_index, x, max_f)\n#             modified_amplitude_list.append(modified_value)\n#             frame_index += 1\n#\n#         # Ensure the modified amplitude list can still be used as an amplitude input\n#         modified_amplitude_series = pd.Series(modified_amplitude_list)\n#\n#         # Format the series for visualization or further processing\n#         formatted_strings = [f\"{idx}:({val})\" for idx, val in modified_amplitude_series.items()]\n#         formatted_string = \", \".join(formatted_strings[:-1]) + \" and \" + formatted_strings[-1] if len(formatted_strings) > 1 else formatted_strings[0]\n#\n#         # Update deforum_frame_data if necessary\n#         if \"keys\" in deforum_frame_data:\n#             deforum_frame_data[\"keys\"][f\"{type_name}_series\"] = modified_amplitude_series.to_dict()\n#\n#         return (deforum_frame_data, modified_amplitude_series.to_numpy(), formatted_string,)\nimport numpy as np\nimport pandas as pd\nimport math\n\nclass DeforumAmplitudeToKeyframeSeriesNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\"required\":\n                    {\"type_name\": (schedule_types,),\n                    \"amplitude\": (\"AMPLITUDE\",),\n                     },\n                \"optional\":\n                    {\n                        \"max_frames\": (\"INT\", {\"default\": 500, \"min\": 1, \"max\": 16500, \"step\": 1}),\n                        \"math\": (\"STRING\", {\"default\": \"x/100\"}),\n                        \"filter_window\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 100, \"step\": 1}),\n                        \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                        \"y\": (\"AMPLITUDE\",),\n                        \"z\": (\"AMPLITUDE\",),\n                    }\n                }\n\n    RETURN_TYPES = (\"DEFORUM_FRAME_DATA\", \"AMPLITUDE\", \"STRING\")\n    FUNCTION = \"convert\"\n    display_name = \"Amplitude to Schedule\"\n    CATEGORY = \"deforum/audio\"\n\n    def safe_eval(self, expr, t, x, max_f, y=None, z=None):\n        allowed_locals = {\n            \"sin\": math.sin, \"cos\": math.cos, \"tan\": math.tan,\n            \"asin\": math.asin, \"acos\": math.acos, \"atan\": math.atan,\n            \"sinh\": math.sinh, \"cosh\": math.cosh, \"tanh\": math.tanh,\n            \"asinh\": math.asinh, \"acosh\": math.acosh, \"atanh\": math.atanh,\n            \"sqrt\": math.sqrt, \"exp\": math.exp, \"log\": math.log,\n            \"abs\": math.fabs, \"pow\": math.pow, \"floor\": math.floor, \"ceil\": math.ceil,\n            \"round\": round, \"min\": min, \"max\": max, \"pi\": math.pi, \"e\": math.e,\n            \"factorial\": math.factorial,\n            \"xor\": lambda a, b: a ^ b,  # Python's `^` operator for XOR\n            \"t\": t, \"x\": x, \"max_f\": max_f, \"y\": y, \"z\": z,\n            # Boolean operations via lambda functions\n            \"and\": lambda a, b: a and b,\n            \"or\": lambda a, b: a or b,\n            \"not\": lambda a: not a,\n        }\n        try:\n            return eval(expr, {\"__builtins__\": {}}, allowed_locals)\n        except NameError as e:\n            raise ValueError(f\"Invalid expression: {e}\")\n        except Exception as e:\n            raise ValueError(f\"Error evaluating expression: {repr(e)}\")\n\n    def convert(self, type_name, amplitude, max_frames=500, math=\"x/100\", filter_window=0, deforum_frame_data={}, y=None, z=None):\n        max_f = int(max_frames)\n\n        # Apply smoothing filter to the amplitude data if needed\n        amplitude_smoothed = np.convolve(amplitude, np.ones(filter_window) / filter_window, mode='same') if filter_window > 0 else amplitude\n        y_smoothed = np.convolve(y, np.ones(filter_window) / filter_window, mode='same') if y is not None and filter_window > 0 else y\n        z_smoothed = np.convolve(z, np.ones(filter_window) / filter_window, mode='same') if z is not None and filter_window > 0 else z\n\n        frame_index = 0\n        modified_amplitude_list = []\n        for idx, x in enumerate(amplitude_smoothed):\n            y_val = y_smoothed[idx] if y is not None else None\n            z_val = z_smoothed[idx] if z is not None else None\n            modified_value = self.safe_eval(math, frame_index, x, max_f, y=y_val, z=z_val)\n            modified_amplitude_list.append(modified_value)\n            frame_index += 1\n\n        modified_amplitude_series = pd.Series(modified_amplitude_list)\n        formatted_strings = [f\"{idx}:({val})\" for idx, val in modified_amplitude_series.items()]\n        formatted_string = \", \".join(formatted_strings[:-1]) + \" and \" + formatted_strings[-1] if len(formatted_strings) > 1 else formatted_strings[0]\n\n        if \"keys\" in deforum_frame_data:\n            deforum_frame_data[\"keys\"][f\"{type_name}_series\"] = modified_amplitude_series.to_dict()\n\n        return (deforum_frame_data, modified_amplitude_series.to_numpy(), formatted_string,)\n\n\nclass DeforumAmplitudeToString:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"amplitude\": (\"AMPLITUDE\",),\n                     },\n                }\n\n    RETURN_TYPES = (\"STRING\",)\n    # RETURN_NAMES = (\"POSITIVE\", \"NEGATIVE\")\n    FUNCTION = \"convert\"\n    display_name = \"Amplitude to String\"\n    CATEGORY = \"deforum/audio\"\n\n\n    @classmethod\n    def IS_CHANGED(self, *args, **kwargs):\n        # Force re-evaluation of the node\n        return float(\"NaN\")\n\n    def convert(self, amplitude):\n\n        return (str(amplitude),)\n\nclass DerivativeOfAmplitude:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\n                    \"amplitude\": (\"AMPLITUDE\",),\n                     },}\n\n    CATEGORY = \"deforum/audio\"\n\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"amplitude_derivative\",)\n    FUNCTION = \"derive\"\n    display_name = \"Derive Amplitude\"\n\n\n    def derive(self, amplitude,):\n        derivative = np.diff(amplitude, prepend=amplitude[0])  # Use np.diff with prepend to maintain array length\n        return (derivative,)\n\nclass SpectralCentroid:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\n                    \"audio_fft\": (\"AUDIO_FFT\",),\n                     },}\n\n    CATEGORY = \"deforum/audio\"\n\n    RETURN_TYPES = (\"FLOAT\",)\n    RETURN_NAMES = (\"spectral_centroid\",)\n    FUNCTION = \"calculate\"\n    display_name = \"Amplitude Spectral Centoid\"\n\n\n    def calculate(self, audio_fft,):\n        magnitudes = np.abs(audio_fft) / len(audio_fft)\n        frequencies = np.linspace(0, audio_fft.sample_rate / 2, len(magnitudes))\n        centroid = np.sum(frequencies * magnitudes) / np.sum(magnitudes)\n        return (centroid,)\n\nclass TimeSmoothing:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\n                    \"amplitude\": (\"AMPLITUDE\",),\n                    },\n                \"optional\": {\n                    \"window_size\": (\"INT\", {\"default\": 5, \"min\": 1}),\n                    }\n                }\n\n    CATEGORY = \"deforum/audio\"\n\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"smoothed_amplitude\",)\n    FUNCTION = \"smooth\"\n    display_name = \"Amplitude Time Smoothing\"\n\n\n    def smooth(self, amplitude, window_size,):\n        smoothed_amplitude = np.convolve(amplitude, np.ones(window_size)/window_size, mode='same')\n        return (smoothed_amplitude,)\n\nclass BeatDetection:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\n                    \"audio\": (\"AUDIO\",),\n                 },\n        }\n\n    CATEGORY = \"deforum/audio\"\n\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"beat_times\",)\n    FUNCTION = \"detect\"\n    display_name = \"Audio to Beat Amplitude\"\n\n\n    def detect(self, audio):\n        beat_times = self.find_beat_times(audio)\n        # Assuming beat_times are indices, we convert these to time values\n        beat_times_in_seconds = beat_times / audio.sample_rate\n        beat_times_series = pd.Series(beat_times_in_seconds)\n        return (beat_times_series,)\n\n    def find_beat_times(self, audio):\n        if audio.num_channels > 1:\n            audio_data = audio.get_channel_audio_data(0)\n        else:\n            audio_data = audio.audio_data\n\n        envelope = self.extract_envelope(audio_data, audio.sample_rate)\n        smoothed_envelope = scipy.ndimage.gaussian_filter1d(envelope, sigma=5)\n        normalized_envelope = (smoothed_envelope - np.min(smoothed_envelope)) / (np.max(smoothed_envelope) - np.min(smoothed_envelope))\n        peaks, _ = scipy.signal.find_peaks(normalized_envelope, height=0.3)  # The height threshold may need adjustment\n\n        return peaks\n\n    def extract_envelope(self, audio_data, sample_rate):\n        analytic_signal = scipy.signal.hilbert(audio_data)\n        envelope = np.abs(analytic_signal)\n        return envelope\n\n\n\nclass FrequencyRangeAmplitude:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\n            \"required\": {\n                \"audio\": (\"AUDIO\",),\n            },\n            \"optional\": {\n                \"frequency_range\": (\"TUPLE\", {\"default\": (20, 20000)}),\n                \"window_size\": (\"INT\", {\"default\": 1}),\n                \"inverted\": (\"BOOLEAN\", {\"default\": False}),\n            },\n        }\n\n    CATEGORY = \"deforum/audio\"\n\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"frequency_range_amplitude\",)\n    FUNCTION = \"analyze_frequency_range\"\n    display_name = \"Frequency Range Amplitude\"\n\n    def analyze_frequency_range(self, audio, frequency_range=(20, 20000), window_size=1, inverted=False):\n        if audio.num_channels > 1:\n            audio_data = audio.get_channel_audio_data(0)\n        else:\n            audio_data = audio.audio_data\n        sample_rate = audio.sample_rate\n        # Apply bandpass filter\n        filtered_audio = self.bandpass_filter(audio_data, frequency_range[0], frequency_range[1], sample_rate)\n        # Calculate FFT\n        fft_result = np.fft.rfft(filtered_audio)\n        fft_freqs = np.fft.rfftfreq(len(filtered_audio), 1 / sample_rate)\n        # Extract amplitude\n        amplitudes = np.abs(fft_result)\n        # Normalize and smooth if necessary\n        normalized_amplitudes = self.normalize(amplitudes, window_size=window_size, inverted=inverted)\n        return (normalized_amplitudes,)\n\n    def bandpass_filter(self, data, lowcut, highcut, sample_rate, order=5):\n        nyquist = 0.5 * sample_rate\n        low = lowcut / nyquist\n        high = highcut / nyquist\n        b, a = butter(order, [low, high], btype='band')\n        y = filtfilt(b, a, data)\n        return y\n\n    def normalize(self, data, min_value=0, max_value=1, window_size=1, inverted=False):\n        # Normalization and smoothing logic as described in the notebook\n        # Return normalized data\n        pass  # Implement normalization and optional smoothing as described\n\nclass BeatDetectionNode:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\n            \"required\": {\n                \"audio\": (\"AUDIO\",),\n                \"sample_rate\": (\"INT\",{\"default\": 44100, \"min\": 8000, \"max\": 200000, \"step\":1}),\n            }\n        }\n\n    CATEGORY = \"deforum/audio\"\n\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"beat_times\",)\n    FUNCTION = \"detect_beats\"\n    display_name = \"Beat Detection v2\"\n\n    def detect_beats(self, audio, sample_rate):\n        if audio.num_channels > 1:\n            audio_data = audio.get_channel_audio_data(0)\n        else:\n            audio_data = audio.audio_data\n        # Assuming audio_data is already pre-processed to be in the correct format\n        tempo, beats = librosa.beat.beat_track(y=audio_data, sr=sample_rate)\n        beat_times = librosa.frames_to_time(beats, sr=sample_rate)\n        return (beat_times,)\n\nclass TempoChangeDetectionNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\"audio\": (\"AUDIO\",)},\n            \"optional\": {\"threshold\": (\"FLOAT\", {\"default\": 0.5, \"min\":0.0, \"max\":1250.0, \"step\":0.1})},\n        }\n\n    CATEGORY = \"deforum/audio\"\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"tempo_change_times\",)\n    FUNCTION = \"detect_tempo_changes\"\n    display_name = \"Tempo Change Detection\"\n\n    def detect_tempo_changes(self, audio, threshold=0.5, fps=24.0):\n        audio_data = audio.get_channel_audio_data(0) if audio.num_channels > 1 else audio.audio_data\n        sample_rate = audio.sample_rate\n\n        # Calculate onset strength\n        onset_env = librosa.onset.onset_strength(y=audio_data.astype(np.float32), sr=sample_rate)\n        tempogram = librosa.feature.tempogram(onset_envelope=onset_env, sr=sample_rate)\n        dynamic_tempo = np.mean(tempogram, axis=1)\n        times = librosa.frames_to_time(np.arange(len(dynamic_tempo)), sr=sample_rate)\n\n        # Detect change points\n        change_points = np.abs(np.diff(dynamic_tempo)) > threshold\n        change_times = times[:-1][change_points]\n\n        # Convert change times to a sequence of amplitudes with the desired fps\n        # First, create a boolean array indicating change points\n        full_length = int(np.ceil(times[-1] * fps))\n        change_points_sequence = np.zeros(full_length, dtype=bool)\n        interpolated_times = np.arange(0, times[-1], step=1.0 / fps)\n\n        for time in interpolated_times:\n            index = int(time * fps)\n            if index < full_length:\n                change_points_sequence[index] = True\n\n        # Optionally, fill in missing values if needed, but here we map detected changes directly\n        # Return the sequence as desired\n        print(len(change_points_sequence))  # Debug print to check the length of the output sequence\n        return (change_points_sequence,)\n\nclass ConvertNormalizedAmplitude:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\"normalized_amp\": (\"NORMALIZED_AMPLITUDE\",)},\n        }\n\n    CATEGORY = \"deforum/audio\"\n    RETURN_TYPES = (\"AMPLITUDE\",)\n    RETURN_NAMES = (\"amplitude\",)\n    FUNCTION = \"convert_normalized_amplitude\"\n    display_name = \"Convert Normalized Amplitude\"\n\n    def convert_normalized_amplitude(self, normalized_amp):\n        return (normalized_amp,)"
  },
  {
    "path": "deforum_nodes/nodes/deforum_cache_nodes.py",
    "content": "\n\nclass DeforumCacheLatentNode:\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"latent\": (\"LATENT\",),\n                \"cache_index\": (\"INT\", {\"default\":0, \"min\": 0, \"max\": 16, \"step\": 1})\n            }\n        }\n\n    RETURN_TYPES = ((\"LATENT\",))\n    FUNCTION = \"cache_it\"\n    CATEGORY = f\"deforum/cache\"\n    display_name = \"Cache Latent\"\n    OUTPUT_NODE = True\n\n    def cache_it(self, latent=None, cache_index=0):\n        from ..mapping import gs\n        from ..mapping import gs\n        if \"latent\" not in gs.deforum_cache:\n\n            gs.deforum_cache[\"latent\"] = {}\n\n        gs.deforum_cache[\"latent\"][cache_index] = latent\n\n        return (latent,)\n\n\nclass DeforumGetCachedLatentNode:\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\"required\": {\n\n            \"cache_index\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 16, \"step\": 1})\n\n        }}\n\n    RETURN_TYPES = ((\"LATENT\",))\n    FUNCTION = \"get_cached_latent\"\n    CATEGORY = f\"deforum/cache\"\n    OUTPUT_NODE = True\n    display_name = \"Load Cached Latent\"\n\n    def get_cached_latent(self, cache_index=0):\n        from ..mapping import gs\n        if gs.reset:\n            return (None,)\n        latent_dict = gs.deforum_cache.get(\"latent\", {})\n        latent = latent_dict.get(cache_index)\n        return (latent,)\n\n\n\nclass DeforumCacheImageNode:\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"image\": (\"IMAGE\",),\n                \"cache_index\": (\"INT\", {\"default\":0, \"min\": 0, \"max\": 16, \"step\": 1})\n            }\n        }\n\n    RETURN_TYPES = ((\"IMAGE\",))\n    FUNCTION = \"cache_it\"\n    CATEGORY = f\"deforum/cache\"\n    display_name = \"Cache Image\"\n    OUTPUT_NODE = True\n\n    def cache_it(self, image=None, cache_index=0):\n        from ..mapping import gs\n\n        if \"image\" not in gs.deforum_cache:\n            gs.deforum_cache[\"image\"] = {}\n        if image is not None:\n            gs.deforum_cache[\"image\"][cache_index] = image.detach().clone()\n\n        return (image,)\n\n\nclass DeforumGetCachedImageNode:\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\"required\": {\n\n            \"cache_index\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 16, \"step\": 1})\n\n        }}\n\n    RETURN_TYPES = ((\"IMAGE\",\"MASK\"))\n    RETURN_NAMES = (\"IMAGE\",\"MASK\")\n    FUNCTION = \"get_cached_latent\"\n    CATEGORY = f\"deforum/cache\"\n    OUTPUT_NODE = True\n    display_name = \"Load Cached Image\"\n\n    def get_cached_latent(self, cache_index=0):\n        from ..mapping import gs\n\n        if gs.reset:\n            return (None, None)\n        img_dict = gs.deforum_cache.get(\"image\", {})\n        image = img_dict.get(cache_index)\n        mask = None\n        if image is not None:\n            mask = image[:, :, :, 0]\n            image = image.detach().clone()\n        return (image ,mask,)\n\n\n\n\nclass DeforumCacheStringNode:\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"input_string\": (\"STRING\",{\"default\":\"\"}),\n                \"cache_index\": (\"INT\", {\"default\":0, \"min\": 0, \"max\": 16, \"step\": 1})\n            }\n        }\n\n    RETURN_TYPES = ((\"STRING\",))\n    FUNCTION = \"cache_it\"\n    CATEGORY = f\"deforum/cache\"\n    display_name = \"Cache String\"\n    OUTPUT_NODE = True\n\n    def cache_it(self, input_string=None, cache_index=0):\n        from ..mapping import gs\n\n        if \"string\" not in gs.deforum_cache:\n            gs.deforum_cache[\"string\"] = {}\n\n        gs.deforum_cache[\"string\"][cache_index] = input_string\n\n        return (input_string,)\n\n\nclass DeforumGetCachedStringNode:\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\"required\": {\n\n            \"cache_index\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 16, \"step\": 1})\n\n        }}\n\n    RETURN_TYPES = ((\"STRING\",))\n    FUNCTION = \"get_cached_string\"\n    CATEGORY = f\"deforum/cache\"\n    OUTPUT_NODE = True\n    display_name = \"Load Cached String\"\n\n    def get_cached_string(self, cache_index=0):\n        from ..mapping import gs\n        img_dict = gs.deforum_cache.get(\"string\", {})\n        string = img_dict.get(cache_index)\n\n        return (str(string),)\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_cnet_nodes.py",
    "content": "\nclass DeforumControlNetApply:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\"conditioning\": (\"CONDITIONING\", ),\n                             \"control_net\": (\"CONTROL_NET\", ),\n                             \"strength\": (\"FLOAT\", {\"default\": 1.0, \"min\": 0.0, \"max\": 10.0, \"step\": 0.01})\n                             },\n                \"optional\": {\"image\": (\"IMAGE\",)}\n                }\n    RETURN_TYPES = (\"CONDITIONING\",)\n    FUNCTION = \"apply_controlnet\"\n    display_name = \"Apply ControlNet [safe]\"\n    CATEGORY = \"deforum/controlnet\"\n\n    def apply_controlnet(self, conditioning, control_net, strength, image=None):\n        if strength == 0 or image is None:\n            return (conditioning, )\n        c = []\n        control_hint = image.movedim(-1,1)\n        for t in conditioning:\n            n = [t[0], t[1].copy()]\n            c_net = control_net.copy().set_cond_hint(control_hint, strength)\n            if 'control' in t[1]:\n                c_net.set_previous_controlnet(t[1]['control'])\n            n[1]['control'] = c_net\n            n[1]['control_apply_to_uncond'] = True\n            c.append(n)\n        return (c, )"
  },
  {
    "path": "deforum_nodes/nodes/deforum_cond_nodes.py",
    "content": "import random\n\nimport torch\n\nfrom ..modules.deforum_comfyui_helpers import blend_tensors, blend_methods\n\nclass DeforumConditioningBlendNode:\n    def __init__(self):\n        self.prompt = None\n        self.n_prompt = None\n        self.cond = None\n        self.n_cond = None\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"clip\": (\"CLIP\",),\n                     \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     \"blend_method\": ([blend_methods]),\n                     }\n                }\n\n    RETURN_TYPES = (\"CONDITIONING\", \"CONDITIONING\")\n    RETURN_NAMES = (\"POSITIVE\", \"NEGATIVE\")\n    FUNCTION = \"fn\"\n    display_name = \"Blend Conditionings\"\n    CATEGORY = \"deforum/conditioning\"\n    def fn(self, clip, deforum_frame_data, blend_method):\n        areas = deforum_frame_data.get(\"areas\")\n        negative_prompt = deforum_frame_data.get(\"negative_prompt\", \"\")\n        n_cond = self.get_conditioning(prompt=negative_prompt, clip=clip)\n\n        if not areas:\n            prompt = deforum_frame_data.get(\"prompt\", \"\")\n            next_prompt = deforum_frame_data.get(\"next_prompt\", None)\n            cond = self.get_conditioning(prompt=prompt, clip=clip)\n            # image = self.getInputData(2)\n            # controlnet = self.getInputData(3)\n\n            prompt_blend = deforum_frame_data.get(\"prompt_blend\", 0.0)\n            #method = self.content.blend_method.currentText()\n            if blend_method != 'none':\n                if next_prompt != prompt and prompt_blend != 0.0 and next_prompt is not None:\n                    next_cond = self.get_conditioning(prompt=next_prompt, clip=clip)\n                    cond = blend_tensors(cond[0], next_cond[0], prompt_blend, blend_method)\n                    print(f\"[deforum] Blending next prompt: {next_prompt}, with alpha: {prompt_blend} ]\")\n        else:\n            from nodes import ConditioningSetArea\n            area_setter = ConditioningSetArea()\n            cond = []\n            for area in areas:\n                prompt = area.get(\"prompt\", None)\n                if prompt:\n\n                    new_cond = self.get_conditioning(clip=clip, prompt=area[\"prompt\"])\n                    new_cond = area_setter.append(conditioning=new_cond, width=int(area[\"w\"]), height=int(area[\"h\"]), x=int(area[\"x\"]),\n                                                  y=int(area[\"y\"]), strength=area[\"s\"])[0]\n                    cond += new_cond\n\n        return (cond, n_cond,)\n\n    def get_conditioning(self, prompt=\"\", clip=None, progress_callback=None):\n\n\n        tokens = clip.tokenize(prompt)\n        cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)\n        return [[cond, {\"pooled_output\": pooled}]]\n\n\nclass DeforumInpaintModelConditioning:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\n                \"positive\": (\"CONDITIONING\", ),\n                \"negative\": (\"CONDITIONING\", ),\n                \"vae\": (\"VAE\", ),},\n                \"optional\": {\n                    \"pixels\": (\"IMAGE\",),\n                    \"mask\": (\"MASK\",),\n                    \"latent\": (\"LATENT\",),\n                    \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n\n                }\n\n                             }\n\n    RETURN_TYPES = (\"CONDITIONING\",\"CONDITIONING\",\"LATENT\")\n    RETURN_NAMES = (\"positive\", \"negative\", \"latent\")\n    FUNCTION = \"encode\"\n\n    display_name = \"InpaintModelConditioning [safe]\"\n    CATEGORY = \"deforum/conditioning\"\n    def encode(self, positive, negative, vae, pixels, mask, latent, deforum_frame_data={}):\n        reset = deforum_frame_data.get(\"reset\", False)\n        if (pixels is not None and mask is not None) and not reset:\n            x = (pixels.shape[1] // 8) * 8\n            y = (pixels.shape[2] // 8) * 8\n            mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(pixels.shape[1], pixels.shape[2]), mode=\"bilinear\")\n\n            orig_pixels = pixels\n            pixels = orig_pixels.clone()\n            if pixels.shape[1] != x or pixels.shape[2] != y:\n                x_offset = (pixels.shape[1] % 8) // 2\n                y_offset = (pixels.shape[2] % 8) // 2\n                pixels = pixels[:,x_offset:x + x_offset, y_offset:y + y_offset,:]\n                mask = mask[:,:,x_offset:x + x_offset, y_offset:y + y_offset]\n\n            m = (1.0 - mask.round()).squeeze(1)\n            for i in range(3):\n                pixels[:,:,:,i] -= 0.5\n                pixels[:,:,:,i] *= m\n                pixels[:,:,:,i] += 0.5\n            concat_latent = vae.encode(pixels)\n            orig_latent = vae.encode(orig_pixels)\n\n            out_latent = {}\n\n            out_latent[\"samples\"] = orig_latent\n            out_latent[\"noise_mask\"] = mask\n\n            out = []\n            for conditioning in [positive, negative]:\n                c = []\n                for t in conditioning:\n                    d = t[1].copy()\n                    d[\"concat_latent_image\"] = concat_latent\n                    d[\"concat_mask\"] = mask\n                    n = [t[0], d]\n                    c.append(n)\n                out.append(c)\n            return (out[0], out[1], out_latent)\n        else:\n            return (positive, negative, latent,)\n\n\nclass DeforumShuffleTokenizer:\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"clip\": (\"CLIP\",),\n                     \"seed\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 0xffffffffffffffff}),\n\n                     }\n                }\n\n    RETURN_TYPES = (\"CLIP\",)\n    FUNCTION = \"fn\"\n    display_name = \"Shuffle Tokenizer\"\n    CATEGORY = \"deforum/conditioning\"\n    def fn(self, clip, seed=42):\n        # Access the tokenizer from the clip object\n        tokenizer = clip.tokenizer\n\n        # Copy the original vocabulary for restoration if needed\n        original_vocab = tokenizer.vocab.copy()\n\n        # Seed the random number generator for reproducibility\n        seeded_random = random.Random(seed)\n\n        # Create a list of (key, value) pairs, shuffle it, then convert it back to a dictionary\n        items = list(original_vocab.items())\n        seeded_random.shuffle(items)\n        shuffled_vocab = dict(items)\n\n        # Update the tokenizer's vocabulary with the shuffled version\n        # This step is highly dependent on the tokenizer's implementation.\n        # If the tokenizer has a method to set its vocab, use it.\n        # Otherwise, you might need to directly set the attribute, if possible.\n        # tokenizer.set_vocab(shuffled_vocab)  # Hypothetical method\n        tokenizer.vocab = shuffled_vocab  # Direct attribute setting, if no method available\n\n        return (clip,)"
  },
  {
    "path": "deforum_nodes/nodes/deforum_data_nodes.py",
    "content": "from ..modules.deforum_ui_data import (deforum_base_params, deforum_anim_params, deforum_translation_params,\n                                       deforum_cadence_params, deforum_depth_params,\n                                       deforum_noise_params, deforum_color_coherence_params, deforum_diffusion_schedule_params,\n                                       deforum_hybrid_video_params, deforum_hybrid_video_schedules)\nfrom ..modules.deforum_node_base import DeforumDataBase\nfrom ..modules.deforum_comfyui_helpers import get_node_params\n\nclass DeforumBaseParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_base_params)\n    display_name = \"Base Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\n\nclass DeforumAnimParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_anim_params)\n    display_name = \"Animation Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\n\nclass DeforumTranslationParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_translation_params)\n    display_name = \"Translate Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\n\nclass DeforumDepthParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_depth_params)\n    display_name = \"Depth Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\n\nclass DeforumNoiseParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_noise_params)\n    display_name = \"Noise Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\n\nclass DeforumColorParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_color_coherence_params)\n    display_name = \"ColorMatch Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\n\nclass DeforumDiffusionParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_diffusion_schedule_params)\n    display_name = \"Diffusion Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\n\nclass DeforumCadenceParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_cadence_params)\n    display_name = \"Cadence Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\nclass DeforumHybridParamsNode(DeforumDataBase):\n    params = get_node_params(deforum_hybrid_video_params)\n    display_name = \"Hybrid Parameters\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\nclass DeforumHybridScheduleNode(DeforumDataBase):\n    params = get_node_params(deforum_hybrid_video_schedules)\n    display_name = \"Hybrid Schedule\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return s.params\n\nclass DeforumFrameDataExtract:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     }\n                }\n    RETURN_TYPES = (\"INT\",\"INT\", \"INT\", \"FLOAT\", \"STRING\", \"STRING\", \"FLOAT\", \"FLOAT\", \"BOOLEAN\")\n    RETURN_NAMES = (\"frame_idx\", \"seed\", \"steps\", \"cfg_scale\", \"sampler_name\", \"scheduler_name\", \"denoise\", \"subseed_strength\", \"first_run\")\n    FUNCTION = \"get_data\"\n    display_name = \"Frame Data Extract\"\n    CATEGORY = \"deforum/parameters\"\n\n    def get_data(self, deforum_frame_data):\n        seed = deforum_frame_data.get(\"seed\", 0)\n        steps = deforum_frame_data.get(\"steps\", 10)\n        cfg = deforum_frame_data.get(\"cfg\", 7.5)\n        sampler_name = deforum_frame_data.get(\"sampler_name\", \"euler_a\")\n        scheduler = deforum_frame_data.get(\"scheduler\", \"normal\")\n        denoise = deforum_frame_data.get(\"denoise\", 1.0)\n        keys = deforum_frame_data.get(\"keys\")\n        frame_idx = deforum_frame_data.get(\"frame_idx\")\n        subseed_str = keys.subseed_strength_schedule_series[frame_idx]\n        first_run = deforum_frame_data.get(\"second_run\", False)\n        return (frame_idx, seed, steps, cfg, sampler_name, scheduler, denoise, subseed_str, first_run, )"
  },
  {
    "path": "deforum_nodes/nodes/deforum_framewarp_node.py",
    "content": "\nimport cv2\nimport numpy as np\nimport torch\nfrom PIL import Image\nfrom einops import rearrange, repeat\n\nimport comfy\nfrom ..modules.deforum_comfyui_helpers import tensor2np, pil2tensor\n\n\nclass DeforumFrameWarpNode:\n    def __init__(self):\n        self.depth_model = None\n        self.depth = None\n        self.algo = \"\"\n        self.depth_min, self.depth_max = 1000, -1000\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     \"warp_depth_image\": (\"BOOLEAN\",{\"default\":False}),\n                     },\n                \"optional\":\n                    {\n                        \"depth_image\":(\"IMAGE\",),\n                    }\n                }\n\n    RETURN_TYPES = (\"IMAGE\",\"IMAGE\",\"IMAGE\")\n    RETURN_NAMES = (\"IMAGE\",\"DEPTH\", \"WARPED_DEPTH\")\n    FUNCTION = \"fn\"\n    display_name = \"Frame Warp\"\n    CATEGORY = \"deforum/image\"\n\n    def fn(self, image, deforum_frame_data, warp_depth_image, depth_image=None):\n        from deforum.models import DepthModel\n        from deforum.utils.deforum_framewarp_utils import anim_frame_warp\n        np_image = None\n        data = deforum_frame_data\n        if image is not None:\n            if image.shape[0] > 1:\n                for img in image:\n                    np_image = tensor2np(img)\n            else:\n                np_image = tensor2np(image)\n\n            np_image = cv2.cvtColor(np_image, cv2.COLOR_RGB2BGR)\n\n            args = data.get(\"args\")\n            anim_args = data.get(\"anim_args\")\n            keys = data.get(\"keys\")\n            frame_idx = data.get(\"frame_idx\")\n\n            if frame_idx == 0:\n                self.depth = None\n            predict_depths = (\n                                     anim_args.animation_mode == '3D' and anim_args.use_depth_warping) or anim_args.save_depth_maps\n            predict_depths = predict_depths or (\n                    anim_args.hybrid_composite and anim_args.hybrid_comp_mask_type in ['Depth', 'Video Depth'])\n\n\n            if depth_image is not None:\n                predict_depths = False\n            if self.depth_model == None or self.algo != anim_args.depth_algorithm:\n                self.vram_state = \"high\"\n                if self.depth_model is not None:\n                    self.depth_model.to(\"cpu\")\n                    del self.depth_model\n                    # torch_gc()\n\n                self.algo = anim_args.depth_algorithm\n                if predict_depths:\n                    keep_in_vram = True if self.vram_state == 'high' else False\n                    # device = ('cpu' if cmd_opts.lowvram or cmd_opts.medvram else self.root.device)\n                    # TODO Set device in root in webui\n                    device = 'cuda'\n                    self.depth_model = DepthModel(\"models/other\", device,\n                                                  keep_in_vram=keep_in_vram,\n                                                  depth_algorithm=anim_args.depth_algorithm, Width=args.width,\n                                                  Height=args.height,\n                                                  midas_weight=anim_args.midas_weight)\n\n                    # depth-based hybrid composite mask requires saved depth maps\n                    if anim_args.hybrid_composite != 'None' and anim_args.hybrid_comp_mask_type == 'Depth':\n                        anim_args.save_depth_maps = True\n                else:\n                    self.depth_model = None\n                    anim_args.save_depth_maps = False\n            if self.depth_model != None and not predict_depths:\n                self.depth_model = None\n            if self.depth_model is not None:\n                self.depth_model.to('cuda')\n            if depth_image is not None:\n\n                depth_image = comfy.utils.common_upscale(depth_image.permute(0,3,1,2), args.width, args.height, upscale_method=\"bislerp\", crop=\"disabled\")\n                depth_image = depth_image.permute(0,2,3,1) * 255.0\n\n                if depth_image.dim() > 2:\n\n                    depth_image = depth_image[0].mean(dim=-1)  # Take the mean across the color channels\n\n            warped_np_img, depth, mask = anim_frame_warp(np_image, args, anim_args, keys, frame_idx,\n                                                              depth_model=self.depth_model, depth=depth_image, device='cuda',\n                                                              half_precision=True)\n            image = Image.fromarray(cv2.cvtColor(warped_np_img, cv2.COLOR_BGR2RGB))\n            tensor = pil2tensor(image)\n            if depth is not None:\n                num_channels = len(depth.shape)\n\n                if num_channels <= 3:\n                    depth_image_pil = self.to_image(depth.detach().cpu())\n                else:\n                    depth_image_pil = self.to_image(depth[0].detach().cpu())\n\n\n                ret_depth = pil2tensor(depth_image_pil).detach().cpu()\n                if warp_depth_image:\n                    warped_depth, _, _ = anim_frame_warp(np.array(depth_image_pil), args, anim_args, keys, frame_idx,\n                                                                      depth_model=self.depth_model, depth=depth_image,\n                                                                      device='cuda',\n                                                                      half_precision=True)\n                    warped_depth_image = Image.fromarray(warped_depth)\n                    warped_ret = pil2tensor(warped_depth_image).detach().cpu()\n                else:\n                    warped_ret = ret_depth\n\n            else:\n                ret_depth = tensor\n                warped_ret = tensor\n            self.depth = depth\n\n            # if gs.vram_state in [\"low\", \"medium\"] and self.depth_model is not None:\n            #     self.depth_model.to('cpu')\n\n\n            # if mask is not None:\n            #     mask = mask.detach().cpu()\n            #     # mask = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).movedim(1, -1).expand(-1, -1, -1, 3)\n            #     mask = mask.mean(dim=0, keepdim=False)\n            #     mask[mask > 1e-05] = 1\n            #     mask[mask < 1e-05] = 0\n            #     mask = mask[0].unsqueeze(0)\n\n\n\n            # from ai_nodes.ainodes_engine_base_nodes.ainodes_backend.resizeRight import resizeright\n            # from ai_nodes.ainodes_engine_base_nodes.ainodes_backend.resizeRight import interp_methods\n            # mask = resizeright.resize(mask, scale_factors=None,\n            #                                     out_shape=[mask.shape[0], int(mask.shape[1] // 8), int(mask.shape[2] // 8)\n            #                                             ],\n            #                                     interp_method=interp_methods.lanczos3, support_sz=None,\n            #                                     antialiasing=True, by_convs=True, scale_tolerance=None,\n            #                                     max_numerator=10, pad_mode='reflect')\n            return (tensor, ret_depth,warped_ret,)\n            # return [data, tensor, mask, ret_depth, self.depth_model]\n        else:\n            return (image, image,image,)\n    def to_image(self, depth: torch.Tensor):\n        depth = depth.cpu().numpy()\n        depth = np.expand_dims(depth, axis=0) if len(depth.shape) == 2 else depth\n        self.depth_min, self.depth_max = min(self.depth_min, depth.min()), max(self.depth_max, depth.max())\n        denom = max(1e-8, self.depth_max - self.depth_min)\n        temp = rearrange((depth - self.depth_min) / denom * 255, 'c h w -> h w c')\n        return Image.fromarray(repeat(temp, 'h w 1 -> h w c', c=3).astype(np.uint8))"
  },
  {
    "path": "deforum_nodes/nodes/deforum_hybrid_nodes.py",
    "content": "import copy\n\nimport cv2\nimport numpy as np\nfrom PIL import Image\nfrom deforum.generators.deforum_flow_generator import get_flow_from_images\nfrom deforum.models import RAFT\nfrom deforum.utils.image_utils import image_transform_optical_flow\n\nfrom ..modules.deforum_comfyui_helpers import tensor2np, tensor2pil, pil2tensor\n# from ..modules.deforum_constants import deforum_models, deforum_depth_algo\n\nfrom ..mapping import gs\n\n\nclass DeforumApplyFlowNode:\n    methods = ['RAFT', 'DIS Medium', 'DIS Fine', 'Farneback']\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"image\": (\"IMAGE\",),\n                \"flow_image\": (\"IMAGE\",),\n                \"flow_method\": ([cls.methods]),\n                \"flow_factor\": (\"FLOAT\", {\"default\": 0.8, \"min\": 0, \"max\": 1.0}),\n            },\n            \"optional\":{\n                \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n            }\n        }\n\n    RETURN_TYPES = ((\"IMAGE\",))\n    FUNCTION = \"apply_flow\"\n    CATEGORY = f\"deforum/image\"\n    OUTPUT_NODE = True\n    display_name = \"Apply Flow\"\n\n    def __init__(self):\n        self.image_cache = []\n\n    def apply_flow(self, image, flow_image, flow_method, flow_factor, deforum_frame_data={}):\n        # global deforum_models\n        if \"raft_model\" not in gs.deforum_models:\n            gs.deforum_models[\"raft_model\"] = RAFT()\n\n        if deforum_frame_data.get(\"reset\", None):\n            self.image_cache.clear()\n        if flow_image is not None:\n            temp_np = tensor2np(flow_image)\n        else:\n            temp_np = tensor2np(image)\n        self.image_cache.append(temp_np)\n\n        if len(self.image_cache) >= 2:\n            flow = get_flow_from_images(self.image_cache[0], self.image_cache[1], flow_method, gs.deforum_models[\"raft_model\"])\n            img = image_transform_optical_flow(tensor2np(image), flow, flow_factor)\n            ret = pil2tensor(img)\n            self.image_cache = [self.image_cache[1]]\n            return (ret,)\n        else:\n            return (image,)\n\n\nclass DeforumHybridMotionNode:\n    raft_model = None\n    methods = ['RAFT', 'DIS Medium', 'DIS Fine', 'Farneback']\n\n    def __init__(self):\n        self.prev_image = None\n        self.flow = None\n        self.image_size = None\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"hybrid_image\": (\"IMAGE\",),\n                     \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     \"hybrid_method\": ([s.methods]),\n                     }\n                }\n\n    RETURN_TYPES = (\"IMAGE\",)\n    FUNCTION = \"fn\"\n    display_name = \"Hybrid Motion\"\n    CATEGORY = \"deforum/image\"\n\n    def fn(self, image, hybrid_image, deforum_frame_data, hybrid_method):\n        if self.raft_model is None:\n            self.raft_model = RAFT()\n\n        flow_factor = deforum_frame_data[\"keys\"].hybrid_flow_factor_schedule_series[deforum_frame_data[\"frame_index\"]]\n        p_img = tensor2pil(image)\n        size = p_img.size\n\n        pil_image = np.array(p_img).astype(np.uint8)\n\n        if self.image_size != size:\n            self.prev_image = None\n            self.flow = None\n            self.image_size = size\n\n        bgr_image = cv2.cvtColor(pil_image, cv2.COLOR_RGB2BGR)\n\n        if hybrid_image is None:\n            if self.prev_image is None:\n                self.prev_image = bgr_image\n                return (image,)\n            else:\n                self.flow = get_flow_from_images(self.prev_image, bgr_image, hybrid_method, self.raft_model, self.flow)\n\n                self.prev_image = copy.deepcopy(bgr_image)\n\n                bgr_image = image_transform_optical_flow(bgr_image, self.flow, flow_factor)\n\n                rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)\n\n                return (pil2tensor(rgb_image),)\n        else:\n\n            pil_image_ref = np.array(tensor2pil(hybrid_image).resize((self.image_size), Image.Resampling.LANCZOS)).astype(np.uint8)\n            bgr_image_ref = cv2.cvtColor(pil_image_ref, cv2.COLOR_RGB2BGR)\n            bgr_image_ref = cv2.resize(bgr_image_ref, (bgr_image.shape[1], bgr_image.shape[0]))\n            if self.prev_image is None:\n                self.prev_image = bgr_image_ref\n                return (image,)\n            else:\n                self.flow = get_flow_from_images(self.prev_image, bgr_image_ref, hybrid_method, self.raft_model,\n                                                 self.flow)\n                bgr_image = image_transform_optical_flow(bgr_image, self.flow, flow_factor)\n                rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)\n                return (pil2tensor(rgb_image),)"
  },
  {
    "path": "deforum_nodes/nodes/deforum_image_nodes.py",
    "content": "import cv2\nfrom PIL import Image\nimport numpy as np\nfrom deforum.generators.deforum_noise_generator import add_noise\nfrom deforum.utils.image_utils import maintain_colors, unsharp_mask, compose_mask_with_check\nfrom ..modules.deforum_comfyui_helpers import tensor2pil, tensor2np, pil2tensor\n\nclass DeforumColorMatchNode:\n\n    def __init__(self):\n        self.depth_model = None\n        self.algo = \"\"\n        self.color_match_sample = None\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     \"force_use_sample\": (\"BOOLEAN\", {\"default\":False},)\n                     },\n                \"optional\":\n                    {\"force_sample_image\":(\"IMAGE\",)}\n                }\n\n    RETURN_TYPES = (\"IMAGE\",)\n    FUNCTION = \"fn\"\n    display_name = \"Color Match\"\n    CATEGORY = \"deforum/image\"\n\n\n\n    def fn(self, image, deforum_frame_data, force_use_sample, force_sample_image=None):\n        if image is not None:\n            anim_args = deforum_frame_data.get(\"anim_args\")\n            image = np.array(tensor2pil(image))\n            # image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)\n            frame_idx = deforum_frame_data.get(\"frame_idx\", 0)\n            if frame_idx == 0 and not force_use_sample:\n                self.color_match_sample = None\n                return (pil2tensor(image),)\n            if force_use_sample:\n                if force_sample_image is not None:\n                    self.color_match_sample = np.array(tensor2pil(force_sample_image)).copy()\n            if anim_args.color_coherence != 'None' and self.color_match_sample is not None:\n                image = maintain_colors(image, self.color_match_sample, anim_args.color_coherence)\n            print(f\"[deforum] ColorMatch: {anim_args.color_coherence}\")\n            if self.color_match_sample is None:\n                self.color_match_sample = image.copy()\n            if anim_args.color_force_grayscale:\n                image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n                image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)\n\n            # image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n            image = pil2tensor(image)\n\n        return (image,)\n\n\nclass DeforumAddNoiseNode:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     }\n                }\n\n    RETURN_TYPES = (\"IMAGE\",)\n    FUNCTION = \"fn\"\n    display_name = \"Add Noise\"\n    CATEGORY = \"deforum/noise\"\n\n    def fn(self, image, deforum_frame_data):\n\n        if image is not None:\n            keys = deforum_frame_data.get(\"keys\")\n            args = deforum_frame_data.get(\"args\")\n            anim_args = deforum_frame_data.get(\"anim_args\")\n            root = deforum_frame_data.get(\"root\")\n            frame_idx = deforum_frame_data.get(\"frame_idx\")\n            noise = keys.noise_schedule_series[frame_idx]\n            kernel = int(keys.kernel_schedule_series[frame_idx])\n            sigma = keys.sigma_schedule_series[frame_idx]\n            amount = keys.amount_schedule_series[frame_idx]\n            threshold = keys.threshold_schedule_series[frame_idx]\n            contrast = keys.contrast_schedule_series[frame_idx]\n            if anim_args.use_noise_mask and keys.noise_mask_schedule_series[frame_idx] is not None:\n                noise_mask_seq = keys.noise_mask_schedule_series[frame_idx]\n            else:\n                noise_mask_seq = None\n            mask_vals = {}\n            noise_mask_vals = {}\n\n            mask_vals['everywhere'] = Image.new('1', (args.width, args.height), 1)\n            noise_mask_vals['everywhere'] = Image.new('1', (args.width, args.height), 1)\n\n            # from ainodes_frontend.nodes.deforum_nodes.deforum_framewarp_node import tensor2np\n            prev_img = tensor2np(image)\n            mask_image = None\n            # apply scaling\n            contrast_image = (prev_img * contrast).round().astype(np.uint8)\n            # anti-blur\n            if amount > 0:\n                contrast_image = unsharp_mask(contrast_image, (kernel, kernel), sigma, amount, threshold,\n                                              mask_image if args.use_mask else None)\n            # apply frame noising\n            if args.use_mask or anim_args.use_noise_mask:\n                root.noise_mask = compose_mask_with_check(root, args, noise_mask_seq,\n                                                          noise_mask_vals,\n                                                          Image.fromarray(\n                                                              cv2.cvtColor(contrast_image, cv2.COLOR_BGR2RGB)))\n            noised_image = add_noise(contrast_image, noise, int(args.seed), anim_args.noise_type,\n                                     (anim_args.perlin_w, anim_args.perlin_h,\n                                      anim_args.perlin_octaves,\n                                      anim_args.perlin_persistence),\n                                     root.noise_mask, args.invert_mask)\n            # image = Image.fromarray(noised_image)\n            print(f\"[deforum] Adding Noise {noise} {anim_args.noise_type}\")\n            image = pil2tensor(noised_image).detach().cpu()\n\n        return (image,)\n\n\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_interpolation_nodes.py",
    "content": "import cv2\nimport torch\nimport numpy as np\n\nfrom deforum import FilmModel\nfrom deforum.models import DepthModel, RAFT\n\nfrom comfy import model_management\nfrom ..modules.standalone_cadence import CadenceInterpolator\nfrom ..modules.deforum_comfyui_helpers import tensor2pil, pil2tensor\n\n# from ..modules.deforum_constants import deforum_models, deforum_depth_algo\n# from .deforum_cache_nodes import deforum_cache\n\n# deforum_models = {}\n# deforum_depth_algo = \"\"\n\nfrom ..mapping import gs\n\n\nclass DeforumFILMInterpolationNode:\n    def __init__(self):\n        self.FILM_temp = []\n        self.model = None\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"inter_amount\": (\"INT\", {\"default\": 2, \"min\": 1, \"max\": 10000},),\n                     \"skip_first\": (\"BOOLEAN\", {\"default\":True}),\n                     \"skip_last\": (\"BOOLEAN\", {\"default\":False}),\n\n                     }\n                }\n\n    RETURN_TYPES = (\"IMAGE\",)\n    # RETURN_NAMES = (\"POSITIVE\", \"NEGATIVE\")\n    FUNCTION = \"fn\"\n    display_name = \"FILM Interpolation\"\n    CATEGORY = \"deforum/interpolation\"\n    @classmethod\n    def IS_CHANGED(self, *args, **kwargs):\n        # Force re-evaluation of the node\n        return float(\"NaN\")\n\n    def interpolate(self, image, inter_frames, skip_first, skip_last):\n\n        if self.model is None:\n            self.model = FilmModel()\n            self.model.model.cuda()\n\n        return_frames = []\n        pil_image = tensor2pil(image.clone().detach())\n        np_image = np.array(pil_image.convert(\"RGB\"))\n        self.FILM_temp.append(np_image)\n        if len(self.FILM_temp) == 2:\n\n            # with torch.inference_mode():\n            with torch.no_grad():\n                frames = self.model.inference(self.FILM_temp[0], self.FILM_temp[1], inter_frames=inter_frames)\n            # skip_first, skip_last = True, False\n            if skip_first:\n                frames.pop(0)\n            if skip_last:\n                frames.pop(-1)\n\n            for frame in frames:\n                tensor = pil2tensor(frame)[0]\n                return_frames.append(tensor.detach().cpu())\n            self.FILM_temp = [self.FILM_temp[1]]\n        print(f\"[deforum] FILM: {len(return_frames)} frames\")\n        if len(return_frames) > 0:\n            return_frames = torch.stack(return_frames, dim=0)\n            return return_frames\n        else:\n            return image.unsqueeze(0)\n\n\n    def fn(self, image, inter_amount, skip_first, skip_last):\n        result = []\n\n        if image.shape[0] > 1:\n            for img in image:\n                interpolated_frames = self.interpolate(img, inter_amount, skip_first, skip_last)\n\n                for f in interpolated_frames:\n                    result.append(f)\n\n            ret = torch.stack(result, dim=0)\n        else:\n            ret = self.interpolate(image[0], inter_amount, skip_first, skip_last)\n        return (ret,)\n\nclass DeforumSimpleInterpolationNode:\n    def __init__(self):\n        self.FILM_temp = []\n        self.model = None\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"method\": ([\"DIS Medium\", \"DIS Fast\", \"DIS UltraFast\", \"Farneback Fine\", \"Normal\"],), # \"DenseRLOF\", \"SF\",\n                     \"inter_amount\": (\"INT\", {\"default\": 2, \"min\": 1, \"max\": 10000},),\n                     \"skip_first\": (\"BOOLEAN\", {\"default\":False}),\n                     \"skip_last\": (\"BOOLEAN\", {\"default\":False}),\n                     },\n                \"optional\":{\n                    \"first_image\": (\"IMAGE\",),\n                    \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n\n                }\n                }\n\n    RETURN_TYPES = (\"IMAGE\", \"IMAGE\")\n    RETURN_NAMES = (\"IMAGES\", \"LAST_IMAGE\")\n    FUNCTION = \"fn\"\n    display_name = \"Simple Interpolation\"\n    CATEGORY = \"deforum/interpolation\"\n    @classmethod\n    def IS_CHANGED(self, *args, **kwargs):\n        # Force re-evaluation of the node\n        return float(\"NaN\")\n\n    def interpolate(self, image, method, inter_frames, skip_first, skip_last):\n\n        return_frames = []\n        pil_image = tensor2pil(image.clone().detach())\n        np_image = np.array(pil_image.convert(\"RGB\"))\n        self.FILM_temp.append(np_image)\n\n        print(len(self.FILM_temp))\n\n        if len(self.FILM_temp) == 2:\n            if inter_frames > 1:\n\n                if method != \"Dyna\":\n\n                    from ..modules.interp import optical_flow_cadence\n\n                    frames = optical_flow_cadence(self.FILM_temp[0], self.FILM_temp[1], inter_frames + 1, method)\n                    # skip_first, skip_last = True, False\n                    if skip_first:\n                        frames.pop(0)\n                    if skip_last:\n                        frames.pop(-1)\n\n                    for frame in frames:\n                        tensor = pil2tensor(frame)[0]\n                        return_frames.append(tensor)\n                else:\n                    if not self.model:\n                        from ..modules.lvdm.i2v_pipeline import Image2Video\n                        self.model = Image2Video()\n                    frames = self.model.get_image(self.FILM_temp[0], \"cat sushi\", steps=50, cfg_scale=7.5, eta=1.0, fs=5, seed=123, image2=self.FILM_temp[1], frames=inter_frames)\n                    for frame in frames:\n                        tensor = pil2tensor(frame)[0]\n                        return_frames.append(tensor)\n            else:\n                return_frames = [i for i in pil2tensor(self.FILM_temp)[0]]\n            self.FILM_temp = [self.FILM_temp[1]]\n        print(f\"[deforum] Simple Interpolation {len(return_frames)} frames\" )\n        if len(return_frames) > 0:\n            return_frames = torch.stack(return_frames, dim=0)\n            return return_frames\n        else:\n            return None\n\n\n    def fn(self, image, method, inter_amount, skip_first, skip_last, first_image=None, deforum_frame_data={}):\n        last = None\n        if deforum_frame_data.get(\"reset\"):\n            print(\"RESETTING DYNA\")\n            self.FILM_temp = []\n        print(image)\n        if image is not None:\n            result = []\n            if image.shape[0] > 1:\n                for img in image:\n                    interpolated_frames = self.interpolate(img, method, inter_amount, skip_first, skip_last)\n\n                    for f in interpolated_frames:\n                        result.append(f)\n\n                ret = torch.stack(result, dim=0)\n            else:\n                ret = self.interpolate(image[0], method, inter_amount, skip_first, skip_last)\n            if ret is not None:\n                last = ret[-1].unsqueeze(0)\n            return (ret, last,)\n\n        else:\n            return (None, None)\n\nclass DeforumCadenceNode:\n    def __init__(self):\n        self.FILM_temp = []\n        self.model = None\n        self.logger = None\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"first_image\": (\"IMAGE\",),\n                     \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     \"depth_strength\": (\"FLOAT\", {\"default\": 1.0, \"min\": 0.0, \"max\": 10.0, \"step\": 0.01}),\n                     \"preview\": (\"BOOLEAN\", {\"default\":False}),\n                     },\n                \"optional\":\n                    {\"hybrid_images\": (\"IMAGE\",),}\n\n                     }\n\n    RETURN_TYPES = (\"IMAGE\", \"IMAGE\")\n    FUNCTION = \"fn\"\n    display_name = \"Cadence Interpolation\"\n    CATEGORY = \"deforum/interpolation\"\n\n    @classmethod\n    def IS_CHANGED(self, *args, **kwargs):\n        # Force re-evaluation of the node\n        return float(\"NaN\")\n\n    def interpolate(self, image, first_image, deforum_frame_data, depth_strength, preview=False, dry_run=False, hybrid_images=None):\n        self.skip_return = False\n        hybrid_provider = None\n        #global deforum_depth_algo, deforum_models\n        # import turbo_prev_image, turbo_next_image, turbo_next_frame_idx, turbo_prev_frame_idx\n        return_frames = []\n        if not dry_run:\n            pil_image = tensor2pil(image.clone().detach())\n            # Convert PIL image to RGB NumPy array and cast to np.float32\n            np_image = np.array(pil_image.convert(\"RGB\")).astype(np.uint8)\n            # Convert from RGB to BGR for OpenCV compatibility\n            #np_image = cv2.cvtColor(np_image, cv2.COLOR_RGB2BGR)\n            np_image = cv2.normalize(np_image, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)\n            np_image = np_image.astype(np.uint8)\n        else:\n            np_image = None\n        args = deforum_frame_data[\"args\"]\n        anim_args = deforum_frame_data[\"anim_args\"]\n        predict_depths = (\n                                 anim_args.animation_mode == '3D' and anim_args.use_depth_warping) or anim_args.save_depth_maps\n        predict_depths = predict_depths or (\n                anim_args.hybrid_composite and anim_args.hybrid_comp_mask_type in ['Depth', 'Video Depth'])\n\n        if \"depth_model\" not in gs.deforum_models or gs.deforum_depth_algo != anim_args.depth_algorithm:\n            self.vram_state = \"high\"\n            if \"depth_model\" in gs.deforum_models:\n                try:\n                    gs.deforum_models[\"depth_model\"].to(\"cpu\")\n                except:\n                    pass\n                del gs.deforum_models[\"depth_model\"]\n\n            deforum_depth_algo = anim_args.depth_algorithm\n            if predict_depths:\n                keep_in_vram = True if self.vram_state == 'high' else False\n                # device = ('cpu' if cmd_opts.lowvram or cmd_opts.medvram else self.root.device)\n                # TODO Set device in root in webui\n                device = 'cuda'\n                gs.deforum_models[\"depth_model\"] = DepthModel(\"models/other\", device,\n                                              keep_in_vram=keep_in_vram,\n                                              depth_algorithm=anim_args.depth_algorithm, Width=args.width,\n                                              Height=args.height,\n                                              midas_weight=anim_args.midas_weight)\n\n                # depth-based hybrid composite mask requires saved depth maps\n                if anim_args.hybrid_composite != 'None' and anim_args.hybrid_comp_mask_type == 'Depth':\n                    anim_args.save_depth_maps = True\n            else:\n                gs.deforum_models[\"depth_model\"] = None\n                anim_args.save_depth_maps = False\n        if gs.deforum_models[\"depth_model\"] != None and not predict_depths:\n            gs.deforum_models[\"depth_model\"] = None\n        if gs.deforum_models[\"depth_model\"] is not None:\n            gs.deforum_models[\"depth_model\"].to('cuda')\n        if \"raft_model\" not in gs.deforum_models:\n            gs.deforum_models[\"raft_model\"] = RAFT()\n        first_gen = False\n        if deforum_frame_data.get(\"reset\") or not hasattr(self, \"interpolator\"):\n            self.interpolator = CadenceInterpolator()\n            #deforum_frame_data[\"frame_idx\"] += anim_args.diffusion_cadence\n            first_gen = True\n        # if self.interpolator.turbo_next_image is not None:\n        self.interpolator.turbo_prev_image, self.interpolator.turbo_prev_frame_idx = self.interpolator.turbo_next_image, self.interpolator.turbo_next_frame_idx\n        self.interpolator.turbo_next_image, self.interpolator.turbo_next_frame_idx = np_image, deforum_frame_data[\"frame_idx\"]\n\n        if self.interpolator.turbo_next_frame_idx == 0 and first_image is not None:\n            pil_image = tensor2pil(first_image.clone().detach())\n            # Convert PIL image to RGB NumPy array and cast to np.float32\n            np_image = np.array(pil_image.convert(\"RGB\")).astype(np.uint8)\n            # Convert from RGB to BGR for OpenCV compatibility\n            #np_image = cv2.cvtColor(np_image, cv2.COLOR_RGB2BGR)\n            np_image = cv2.normalize(np_image, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)\n            self.interpolator.turbo_prev_image = np_image.astype(np.uint8)\n            self.interpolator.turbo_prev_frame_idx = 0\n            self.interpolator.turbo_next_frame_idx = anim_args.diffusion_cadence\n            deforum_frame_data[\"frame_idx\"] = anim_args.diffusion_cadence\n            self.skip_return = True\n        if hybrid_images is not None:\n            # try:\n            hybrid_provider = []\n            for i in hybrid_images:\n                pil_image = tensor2pil(i.clone().detach())\n                np_image = np.array(pil_image.convert(\"RGB\")).astype(np.uint8)\n                np_image = cv2.normalize(np_image, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)\n                hybrid_provider.append(np_image)\n            if len(hybrid_provider) > 0:\n                if len(hybrid_provider) - 1 > anim_args.diffusion_cadence:\n                    anim_args.diffusion_cadence = len(hybrid_provider) - 1\n        # if first_gen:\n        #     self.interpolator.turbo_prev_image = np_image\n        #     return image\n        # with torch.inference_mode():\n        if not dry_run:\n\n            with torch.no_grad():\n                if not self.logger:\n                    import comfy\n\n                    self.logger = comfy.utils.ProgressBar(anim_args.diffusion_cadence)\n                # from ..modules.standalone_cadence import new_standalone_cadence\n                frames = self.interpolator.new_standalone_cadence(deforum_frame_data[\"args\"],\n                                                deforum_frame_data[\"anim_args\"],\n                                                deforum_frame_data[\"root\"],\n                                                deforum_frame_data[\"keys\"],\n                                                deforum_frame_data[\"frame_idx\"],\n                                                gs.deforum_models[\"depth_model\"],\n                                                gs.deforum_models[\"raft_model\"],\n                                                depth_strength,\n                                                self.logger if preview else None,\n                                                hybrid_provider=hybrid_provider)\n\n\n\n            # for frame in frames:\n            #     tensor = pil2tensor(frame)\n            #     return_frames.append(tensor.squeeze(0))\n            # print(f\"[deforum] [rbn] Cadence: {len(return_frames)} frames\")\n            # if len(return_frames) > 0:\n            #     return_frames = torch.stack(return_frames, dim=0)\n                return frames\n            # else:\n            #     return None\n        else:\n            return None\n\n    def fn(self, image, first_image, deforum_frame_data, depth_strength, preview, hybrid_images=None):\n\n        result = []\n        ret = None\n        if image is not None and not deforum_frame_data.get(\"reset\"):\n\n            device = model_management.get_torch_device()\n            free_memory = model_management.get_free_memory(device)\n            if free_memory < 4000000000:\n                print(\"Low current VRam, offloading all models before executing Cadence\")\n                model_management.unload_all_models()\n\n\n            # Check if there are multiple images in the batch\n            if image.shape[0] > 1:\n                for img in image:\n                    # Ensure img has batch dimension of 1 for interpolation\n                    interpolated_frames = self.interpolate(img.unsqueeze(0), first_image, deforum_frame_data, depth_strength, preview=preview, hybrid_images=hybrid_images)\n\n                    # Collect all interpolated frames\n                    for f in interpolated_frames:\n                        result.append(f)\n                # Stack all results into a single tensor, preserving color channels\n                ret = torch.stack(result, dim=0)\n            else:\n                # Directly interpolate if only one image is present\n                ret = self.interpolate(image, first_image, deforum_frame_data, depth_strength, preview=preview, hybrid_images=hybrid_images)\n                #ret = torch.stack([torch.from_numpy(i) / 255.0 for i in ret], dim=0)\n            if ret is not None:\n                #last = ret[-1].unsqueeze(0)  # Preserve the last frame separately with batch dimension\n                last = torch.from_numpy(ret[-1]).unsqueeze(0) / 255.0  # Preserve the last frame separately with batch dimension\n                if self.skip_return:\n                    return (None, last)\n                else:\n                    return (ret, last,)\n        else:\n            _ = self.interpolate(None, None, deforum_frame_data, depth_strength, dry_run=True)\n            return (None, None,)\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_iteration_nodes.py",
    "content": "import math\nimport secrets\nfrom types import SimpleNamespace\nimport torch\nfrom deforum import ImageRNGNoise\nfrom deforum.generators.rng_noise_generator import slerp\nfrom deforum.pipeline_utils import next_seed\nfrom deforum.pipelines.deforum_animation.animation_params import RootArgs, DeforumArgs, DeforumAnimArgs, \\\n    DeforumOutputArgs, LoopArgs\nfrom deforum.utils.string_utils import split_weighted_subprompts\nimport comfy\n\nfrom ..modules.deforum_comfyui_helpers import get_current_keys, generate_seed_list\n\nclass DeforumIteratorNode:\n    def __init__(self):\n        self.first_run = True\n        self.frame_index = 0\n        self.seed = \"\"\n        self.seeds = []\n        self.second_run = True\n        self.logger = None\n\n    @classmethod\n    def IS_CHANGED(cls, *args, **kwargs):\n        # Force re-evaluation of the node\n        # if autorefresh == \"Yes\":\n        return float(\"NaN\")\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"deforum_data\": (\"deforum_data\",),\n                \"latent_type\": ([\"stable_diffusion\", \"stable_cascade\", \"sd3\"],)\n            },\n            \"optional\": {\n                \"latent\": (\"LATENT\",),\n                \"init_latent\": (\"LATENT\",),\n                \"seed\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 0xffffffffffffffff}),\n                \"subseed\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 0xffffffffffffffff}),\n                \"subseed_strength\": (\"FLOAT\", {\"default\": 0.8, \"min\": 0, \"max\": 1.0}),\n                \"slerp_strength\": (\"FLOAT\", {\"default\": 0.1, \"min\": 0, \"max\": 1.0}),\n                \"reset_counter\":(\"BOOLEAN\", {\"default\": False},),\n                \"reset_latent\":(\"BOOLEAN\", {\"default\": False},),\n                \"enable_autoqueue\":(\"BOOLEAN\", {\"default\": False},),\n            }\n        }\n\n    RETURN_TYPES = ((\"DEFORUM_FRAME_DATA\", \"LATENT\", \"STRING\", \"STRING\"))\n    RETURN_NAMES = ((\"deforum_frame_data\", \"latent\", \"positive_prompt\", \"negative_prompt\"))\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/logic\"\n    display_name = \"Iterator Node\"\n\n\n    @torch.inference_mode()\n    def get(self, deforum_data, latent_type, latent=None, init_latent=None, seed=None, subseed=None, subseed_strength=None, slerp_strength=None, reset_counter=False, reset_latent=False, enable_autoqueue=False, *args, **kwargs):\n\n        from ..mapping import gs\n        if gs.reset:\n            reset_counter = True\n            reset_latent = True\n\n        # global deforum_cache\n        root_dict = RootArgs()\n        args_dict = {key: value[\"value\"] for key, value in DeforumArgs().items()}\n        anim_args_dict = {key: value[\"value\"] for key, value in DeforumAnimArgs().items()}\n        output_args_dict = {key: value[\"value\"] for key, value in DeforumOutputArgs().items()}\n        loop_args_dict = {key: value[\"value\"] for key, value in LoopArgs().items()}\n        root = SimpleNamespace(**root_dict)\n        args = SimpleNamespace(**args_dict)\n        anim_args = SimpleNamespace(**anim_args_dict)\n        anim_args.diffusion_cadence = 1\n        video_args = SimpleNamespace(**output_args_dict)\n        parseq_args = None\n        loop_args = SimpleNamespace(**loop_args_dict)\n        controlnet_args = SimpleNamespace(**{\"controlnet_args\": \"None\"})\n\n        for key, value in args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\":\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(args, key, val)\n\n        for key, value in anim_args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\" and \"schedule\" not in key:\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(anim_args, key, val)\n\n        for key, value in video_args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\" and \"schedule\" not in key:\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(anim_args, key, val)\n\n        for key, value in root.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\":\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(root, key, val)\n\n        for key, value in loop_args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\":\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(loop_args, key, val)\n\n        root.animation_prompts = deforum_data.get(\"prompts\")\n        anim_args.fps = 24\n\n        keys, prompt_series, areas = get_current_keys(anim_args, args.seed, root, area_prompts=deforum_data.get(\"area_prompts\"))\n        if self.frame_index >= anim_args.max_frames or reset_counter:\n            # if self.logger:\n            #     self.logger.stop_live_display()\n            # config = {\n            #     \"Header\": {\"type\": \"full\", \"columns\": [\"DEFORUM COMFY ANIMATOR - Logging\"]},\n            #     \"Status\": {\"type\": \"columns\", \"columns\": [\"Frame\", \"Progress\", \"Errors\"]}\n            # }\n            # self.logger = TerminalTableLogger(config)\n            # self.logger.start_live_display()\n            self.reset_counter = False\n            self.frame_index = 0\n            self.first_run = True\n            self.second_run = True\n\n        if not self.logger:\n            self.logger = comfy.utils.ProgressBar(anim_args.max_frames)\n\n\n        self.logger.update_absolute(self.frame_index)\n\n        # else:\n        args.scale = keys.cfg_scale_schedule_series[self.frame_index]\n        if prompt_series is not None:\n            args.prompt = prompt_series[self.frame_index]\n\n        seed = keys.seed_schedule_series[self.frame_index]\n\n        if args.seed_behavior == 'random':\n            seed = secrets.randbelow(2 ** 32 - 1)\n\n        self.seed = seed\n        args.seed = seed\n        self.seeds.append(seed)\n\n        # args.seed = int(args.seed)\n        # root.seed_internal = int(root.seed_internal)\n        # args.seed_iter_N = int(args.seed_iter_N)\n        #\n        # if self.seed == \"\":\n        #     self.seed = args.seed\n        #     self.seed_internal = root.seed_internal\n        #     self.seed_iter_N = args.seed_iter_N\n        #\n        # self.seed = next_seed(args, root)\n        # args.seed = self.seed\n        # self.seeds.append(self.seed)\n\n        blend_value = 0.0\n\n        next_frame = self.frame_index + anim_args.diffusion_cadence\n        next_prompt = None\n\n        def generate_blend_values(distance_to_next_prompt, blend_type=\"linear\"):\n            if blend_type == \"linear\":\n                return [i / distance_to_next_prompt for i in range(distance_to_next_prompt + 1)]\n            elif blend_type == \"exponential\":\n                base = 2\n                return [1 / (1 + math.exp(-8 * (i / distance_to_next_prompt - 0.5))) for i in\n                        range(distance_to_next_prompt + 1)]\n            else:\n                raise ValueError(f\"Unknown blend type: {blend_type}\")\n\n        def find_last_prompt_change(current_index, prompt_series):\n            # Step backward from the current position\n            for i in range(current_index - 1, -1, -1):\n                if prompt_series[i] != prompt_series[current_index]:\n                    return i\n            return 0  # default to the start if no change found\n\n        def find_next_prompt_change(current_index, prompt_series):\n            # Step forward from the current position\n            for i in range(current_index + 1, len(prompt_series) - 1):\n                if i < anim_args.max_frames:\n\n                    if prompt_series[i] != prompt_series[current_index]:\n                        return i\n            return len(prompt_series) - 1  # default to the end if no change found\n\n        if prompt_series is not None:\n            last_prompt_change = find_last_prompt_change(self.frame_index, prompt_series)\n\n            next_prompt_change = find_next_prompt_change(self.frame_index, prompt_series)\n\n            distance_between_changes = next_prompt_change - last_prompt_change\n            current_distance_from_last = self.frame_index - last_prompt_change\n\n            # Generate blend values for the distance between prompt changes\n            blend_values = generate_blend_values(distance_between_changes, blend_type=\"exponential\")\n\n            # Fetch the blend value based on the current frame's distance from the last prompt change\n            blend_value = blend_values[current_distance_from_last]\n\n            if len(prompt_series) - 1 > next_prompt_change:\n                next_prompt = prompt_series[next_prompt_change]\n\n        gen_args = self.get_current_frame(args, anim_args, root, keys, self.frame_index, areas)\n\n        self.args = args\n        self.root = root\n        if next_prompt is not None:\n            gen_args[\"next_prompt\"], _ = split_weighted_subprompts(str(next_prompt))\n            gen_args[\"prompt_blend\"] = blend_value\n        gen_args[\"frame_index\"] = self.frame_index\n        gen_args[\"max_frames\"] = anim_args.max_frames\n\n        seeds = generate_seed_list(anim_args.max_frames + 1, args.seed_behavior, seed, args.seed_iter_N)\n        subseeds = generate_seed_list(anim_args.max_frames + 1, args.seed_behavior, subseed, args.seed_iter_N)\n        if reset_counter:\n            print(\"[deforum] RESET COUNTER\")\n        if reset_latent or not hasattr(self, \"rng\"):\n            print(\"[deforum] RESET LATENT\"  )\n\n            if \"image\" in gs.deforum_cache:\n                gs.deforum_cache[\"image\"].clear()\n            if \"latent\" in gs.deforum_cache:\n                gs.deforum_cache[\"latent\"].clear()\n            gs.reset = True\n            if latent_type == \"stable_diffusion\":\n                channels = 4\n                compression = 8\n            else:\n                channels = 16\n                compression = 42\n            if init_latent is not None:\n                args.height, args.width = init_latent[\"samples\"].shape[2] * 8, init_latent[\"samples\"].shape[3] * 8\n\n\n            self.rng = ImageRNGNoise((channels, args.height // compression, args.width // compression),\n                                     [seeds[self.frame_index]], [subseeds[self.frame_index]],\n                                     0.6, 1024, 1024)\n            if latent_type == \"stable_diffusion\":\n                l = self.rng.first().half().to(comfy.model_management.intermediate_device())\n            elif latent_type == \"stable_cascade\":\n                l = torch.zeros([1, 16, args.height // 42, args.width // 42]).to(comfy.model_management.intermediate_device())\n            elif latent_type == \"sd3\":\n                l = torch.ones([1, 16, args.height // 8, args.width // 8], device=comfy.model_management.intermediate_device()) * 0.0609\n            latent = {\"samples\": l}\n            gen_args[\"denoise\"] = 1.0\n        else:\n            if latent_type == \"stable_diffusion\" and slerp_strength > 0:\n                args.height, args.width = latent[\"samples\"].shape[2] * 8, latent[\"samples\"].shape[3] * 8\n                l = self.rng.next().clone().to(comfy.model_management.intermediate_device())\n                s = latent[\"samples\"].clone().to(comfy.model_management.intermediate_device())\n                latent = {\"samples\":slerp(slerp_strength, s, l)}\n        print(f\"[deforum] Frame: {self.frame_index} of {anim_args.max_frames}\")\n        gen_args[\"noise\"] = self.rng\n        gen_args[\"seed\"] = int(seed)\n\n        if self.frame_index == 0 and init_latent is not None:\n            latent = init_latent\n            gen_args[\"denoise\"] = keys.strength_schedule_series[0]\n\n        #if anim_args.diffusion_cadence > 1:\n        # global turbo_prev_img, turbo_prev_frame_idx, turbo_next_image, turbo_next_frame_idx, opencv_image\n        if anim_args.diffusion_cadence > 1:\n            self.frame_index += anim_args.diffusion_cadence if not self.first_run else 0# if anim_args.diffusion_cadence == 1\n            if not self.first_run:\n                if self.second_run:\n                    self.frame_index = 0\n                    self.second_run = False\n            # if turbo_steps > 1:\n            # turbo_prev_image, turbo_prev_frame_idx = turbo_next_image, turbo_next_frame_idx\n            # turbo_next_image, turbo_next_frame_idx = opencv_image, self.frame_index\n                # frame_idx += turbo_steps\n\n            self.first_run = False\n\n        else:\n            self.frame_index += 1 if not self.first_run else 0\n            self.first_run = False\n            self.second_run = False\n\n        if self.frame_index > anim_args.max_frames:\n            self.frame_index = anim_args.max_frames\n        if latent is not None:\n            latent[\"samples\"] = latent[\"samples\"].float()\n        from ..mapping import gs\n        gs.reset = False if not self.first_run else True\n        enable_autoqueue = enable_autoqueue if self.frame_index == 0 else False\n        gen_args[\"sampler_name\"] = deforum_data.get(\"sampler_name\", \"euler_a\")\n        gen_args[\"scheduler\"] = deforum_data.get(\"scheduler\", \"normal\")\n        gen_args[\"reset\"] = reset_latent or reset_counter\n        gen_args[\"frame_idx\"] = self.frame_index\n        gen_args[\"first_run\"] = self.first_run\n        gen_args[\"second_run\"] = self.second_run\n        gen_args[\"logger\"] = self.logger\n        torch.cuda.synchronize()\n\n        return {\"ui\": {\"counter\":(self.frame_index,), \"max_frames\":(anim_args.max_frames,), \"enable_autoqueue\":(enable_autoqueue,)}, \"result\": (gen_args, latent, gen_args[\"prompt\"], gen_args[\"negative_prompt\"],),}\n        # return (gen_args, latent, gen_args[\"prompt\"], gen_args[\"negative_prompt\"],)\n\n    def get_current_frame(self, args, anim_args, root, keys, frame_idx, areas=None):\n        if hasattr(args, 'prompt'):\n            prompt, negative_prompt = split_weighted_subprompts(args.prompt, frame_idx, anim_args.max_frames)\n        else:\n            prompt = \"\"\n            negative_prompt = \"\"\n        strength = keys.strength_schedule_series[frame_idx]\n\n        return {\"prompt\": prompt,\n                \"negative_prompt\": negative_prompt,\n                \"denoise\": strength,\n                \"cfg\": args.scale,\n                \"steps\": int(keys.steps_schedule_series[self.frame_index]),\n                \"root\": root,\n                \"keys\": keys,\n                \"frame_idx\": frame_idx,\n                \"anim_args\": anim_args,\n                \"args\": args,\n                \"areas\":areas[frame_idx] if areas is not None else None,\n                \"logger\":self.logger}\n\nclass DeforumSeedNode:\n    @classmethod\n    def IS_CHANGED(cls, *args, **kwargs):\n        return float(\"NaN\")\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"seed\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 0xffffffffffffffff}),\n            },\n        }\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/logic\"\n    RETURN_TYPES = ((\"INT\",))\n    display_name = \"Seed Node\"\n\n    @torch.inference_mode()\n    def get(self, seed, *args, **kwargs):\n        return (seed,)\n\n\nclass DeforumBigBoneResetNode:\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        # if autorefresh == \"Yes\":\n        return float(\"NaN\")\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"reset_deforum\":(\"BOOLEAN\", {\"default\": False},),},\n\n        }\n    FUNCTION = \"get\"\n    CATEGORY = f\"deforum/logic\"\n    RETURN_TYPES = ((\"BOOLEAN\",))\n    display_name = \"Big Bone Reset Node\"\n\n    def get(self, reset_deforum, *args, **kwargs):\n        from ..mapping import gs\n        gs.reset = reset_deforum\n        # deforum_frame_data[\"reset\"] = reset_deforum\n        # deforum_frame_data[\"reset_latent\"] = reset_deforum\n        # deforum_frame_data[\"reset_counter\"] = reset_deforum\n        # deforum_frame_data[\"first_run\"] = reset_deforum\n        return {\"ui\":{\"reset\":(reset_deforum,)}, \"result\":(reset_deforum,)}\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_legacy_nodes.py",
    "content": "import os\nimport time\n\nfrom types import SimpleNamespace\nimport torch\nimport numpy as np\n\nfrom deforum import DeforumAnimationPipeline\nfrom deforum.pipelines.deforum_animation.animation_helpers import DeforumAnimKeys\nfrom deforum.pipelines.deforum_animation.animation_params import RootArgs, DeforumArgs, DeforumAnimArgs, \\\n    DeforumOutputArgs, LoopArgs, ParseqArgs\nfrom deforum.utils.string_utils import substitute_placeholders\n\nclass DeforumSingleSampleNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"deforum_data\": (\"deforum_data\",),\n                \"model\": (\"MODEL\",),\n                \"clip\": (\"CLIP\",),\n                \"vae\": (\"VAE\",)\n            },\n        }\n\n    RETURN_TYPES = ((\"IMAGE\",))\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/sampling\"\n    display_name = \"Integrated Pipeline\"\n\n    @torch.inference_mode()\n    def get(self, deforum_data, model, clip, vae, *args, **kwargs):\n\n        root_dict = RootArgs()\n        args_dict = {key: value[\"value\"] for key, value in DeforumArgs().items()}\n        anim_args_dict = {key: value[\"value\"] for key, value in DeforumAnimArgs().items()}\n        output_args_dict = {key: value[\"value\"] for key, value in DeforumOutputArgs().items()}\n        loop_args_dict = {key: value[\"value\"] for key, value in LoopArgs().items()}\n        parseq_args_dict = {key: value[\"value\"] for key, value in ParseqArgs().items()}\n        root = SimpleNamespace(**root_dict)\n        args = SimpleNamespace(**args_dict)\n        anim_args = SimpleNamespace(**anim_args_dict)\n        video_args = SimpleNamespace(**output_args_dict)\n        parseq_args = SimpleNamespace(**parseq_args_dict)\n\n        parseq_args.parseq_manifest = \"\"\n\n        # #parseq_args = None\n        loop_args = SimpleNamespace(**loop_args_dict)\n        controlnet_args = SimpleNamespace(**{\"controlnet_args\": \"None\"})\n\n        for key, value in args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\":\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(args, key, val)\n\n        for key, value in anim_args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\" and \"schedule\" not in key:\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(anim_args, key, val)\n\n        for key, value in video_args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\" and \"schedule\" not in key:\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(anim_args, key, val)\n\n        for key, value in root.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\":\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(root, key, val)\n\n        for key, value in loop_args.__dict__.items():\n            if key in deforum_data:\n                if deforum_data[key] == \"\":\n                    val = None\n                else:\n                    val = deforum_data[key]\n                setattr(loop_args, key, val)\n\n        success = None\n        root.timestring = time.strftime('%Y%m%d%H%M%S')\n        args.timestring = root.timestring\n        args.strength = max(0.0, min(1.0, args.strength))\n\n        root.animation_prompts = deforum_data.get(\"prompts\", {})\n\n\n        if not args.use_init and not anim_args.hybrid_use_init_image:\n            args.init_image = None\n\n        elif anim_args.animation_mode == 'Video Input':\n            args.use_init = True\n\n        current_arg_list = [args, anim_args, video_args, parseq_args, root]\n        full_base_folder_path = os.path.join(os.getcwd(), \"output/deforum\")\n\n        args.batch_name = f\"aiNodes_Deforum_{args.timestring}\"\n        args.outdir = os.path.join(full_base_folder_path, args.batch_name)\n\n        root.raw_batch_name = args.batch_name\n        args.batch_name = substitute_placeholders(args.batch_name, current_arg_list, full_base_folder_path)\n\n        # os.makedirs(args.outdir, exist_ok=True)\n\n        def generate(*args, **kwargs):\n            from ..modules.deforum_comfy_sampler import sample_deforum\n            image = sample_deforum(model, clip, vae, **kwargs)\n\n            return image\n\n        self.deforum = DeforumAnimationPipeline(generate)\n\n        self.deforum.config_dir = os.path.join(os.getcwd(), \"output/_deforum_configs\")\n        os.makedirs(self.deforum.config_dir, exist_ok=True)\n        # self.deforum.generate_inpaint = self.generate_inpaint\n        import comfy\n        pbar = comfy.utils.ProgressBar(deforum_data[\"max_frames\"])\n\n        def datacallback(data=None):\n            if data:\n                if \"image\" in data:\n                    pbar.update_absolute(data[\"frame_idx\"], deforum_data[\"max_frames\"], (\"JPEG\", data[\"image\"], 512))\n\n        self.deforum.datacallback = datacallback\n        deforum_data[\"turbo_steps\"] = deforum_data.get(\"diffusion_cadence\", 0)\n        deforum_data[\"store_frames_in_ram\"] = True\n        deforum_data[\"skip_video_creation\"] = True\n        \n        animation = self.deforum(**deforum_data)\n\n        results = []\n        for i in self.deforum.images:\n            tensor = torch.from_numpy(np.array(i).astype(np.float32) / 255.0)\n\n            results.append(tensor)\n            result = torch.stack(results, dim=0)\n\n        return (result,)\n\n\nclass DeforumSetVAEDownscaleRatioNode:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"vae\": (\"VAE\",),\n                     \"downscale_ratio\": (\"INT\", {\"default\": 42, \"min\": 32, \"max\": 64, \"step\": 1}),\n                     },\n                }\n    RETURN_TYPES = (\"VAE\",)\n    FUNCTION = \"fn\"\n    display_name = \"Set VAE Downscale Ratio\"\n    CATEGORY = \"deforum/_for_testing\"\n\n    def fn(self, vae, downscale_ratio):\n        vae.downscale_ratio = downscale_ratio\n        return (vae,)\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_logic_nodes.py",
    "content": "\n\nclass DeforumImageSwitcherNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n\n                \"option\": (\"BOOLEAN\", {\"default\": False}),\n            },\n            \"optional\":\n                {\n                    \"image_true\": (\"IMAGE\",),\n                    \"image_false\": (\"IMAGE\",),\n\n                }\n               }\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    RETURN_TYPES = (\"IMAGE\",)\n    FUNCTION = \"compare\"\n    display_name = \"Image Switcher\"\n    CATEGORY = \"deforum/logic\"\n    OUTPUT_NODE = True\n\n    def compare(self, option=True, image_true=None, image_false=None):\n\n        if option:\n            return (image_true,)\n        else:\n            return (image_false,)\n\n\nclass DeforumComparatorNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"int_1\": (\"INT\",{\"default\":0, \"min\":-999999, \"max\":2 ** 32, \"step\":1}),\n                \"int_2\": (\"INT\",{\"default\":0, \"min\":-999999, \"max\":2 ** 32, \"step\":1}),\n                \"condition\": ([\"<\", \"<=\", \">\", \">=\", \"==\" ],),\n                        }\n               }\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    RETURN_TYPES = (\"BOOLEAN\",)\n    FUNCTION = \"compare\"\n    display_name = \"INT Comparator\"\n    CATEGORY = \"deforum/logic\"\n    OUTPUT_NODE = True\n\n    def compare(self, int_1, int_2, condition):\n        if condition == \"<\":\n            return (int_1 < int_2,)\n        elif condition == \"<=\":\n            return (int_1 <= int_2,)\n        elif condition == \">\":\n            return (int_1 > int_2,)\n        elif condition == \">=\":\n            return (int_1 >= int_2,)\n        elif condition == \"==\":\n            return (int_1 == int_2,)\n        else:\n            raise ValueError(\"Invalid condition\")\n\nclass DeforumFloatComparatorNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"float_1\": (\"FLOAT\",{\"default\":0.00, \"min\":-999999.00, \"max\":2 ** 32, \"step\":0.01}),\n                \"float_2\": (\"FLOAT\",{\"default\":0.00, \"min\":-999999.00, \"max\":2 ** 32, \"step\":0.01}),\n                \"condition\": ([\"<\", \"<=\", \">\", \">=\", \"==\" ],),\n                        }\n               }\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    RETURN_TYPES = (\"BOOLEAN\",)\n    FUNCTION = \"compare\"\n    display_name = \"FLOAT Comparator\"\n    CATEGORY = \"deforum/logic\"\n    OUTPUT_NODE = True\n\n    def compare(self, float_1, float_2, condition):\n        if condition == \"<\":\n            return (float_1 < float_2,)\n        elif condition == \"<=\":\n            return (float_1 <= float_2,)\n        elif condition == \">\":\n            return (float_1 > float_2,)\n        elif condition == \">=\":\n            return (float_1 >= float_2,)\n        elif condition == \"==\":\n            return (float_1 == float_2,)\n        else:\n            raise ValueError(\"Invalid condition\")\n\n\nclass DeforumAndNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"condition_1\": (\"BOOLEAN\",),\n                \"condition_2\": (\"BOOLEAN\",),\n                # Add more conditions if needed\n            }\n        }\n\n    RETURN_TYPES = (\"BOOLEAN\",)\n    FUNCTION = \"logical_and\"\n    display_name = \"Logical AND\"\n    CATEGORY = f\"deforum/logic\"\n\n    def logical_and(self, condition_1, condition_2, *additional_conditions):\n        return (all([condition_1, condition_2] + list(additional_conditions)),)\n\nclass DeforumOrNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"condition_1\": (\"BOOLEAN\",),\n                \"condition_2\": (\"BOOLEAN\",),\n                # Add more conditions if needed\n            }\n        }\n\n    RETURN_TYPES = (\"BOOLEAN\",)\n    FUNCTION = \"logical_or\"\n    display_name = \"Logical OR\"\n    CATEGORY = f\"deforum/logic\"\n\n    def logical_or(self, condition_1, condition_2, *additional_conditions):\n        return (any([condition_1, condition_2] + list(additional_conditions)),)\n\nclass DeforumNotNode:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"condition\": (\"BOOLEAN\",),\n            }\n        }\n\n    RETURN_TYPES = (\"BOOLEAN\",)\n    FUNCTION = \"logical_not\"\n    display_name = \"Logical NOT\"\n    CATEGORY = f\"deforum/logic\"\n\n    def logical_not(self, condition):\n        return (not condition,)\n\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_noise_nodes.py",
    "content": "import secrets\n\nimport torch\nimport numpy as np\n\n\nclass AddCustomNoiseNode:\n    \"\"\"\n    A node to add various types of noise to an image using PyTorch.\n    \"\"\"\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"images\": (\"IMAGE\",),\n                \"noise_type\": (\n                [\"speckle\", \"uniform\", \"rayleigh\", \"exponential\",\n                 \"gamma\", \"random_valued_impulse\", \"laplace\", \"perlin\", \"brownian\", \"quantization\", \"shot\",\n                 \"multiplicative\", \"flicker\", \"fractal\", \"cellular\", \"gaussian\", \"thermal\", \"salt_pepper\", \"poisson\",],), #TODO [\"blue\", \"anisotropic\"]\n                \"amount\": (\"FLOAT\", {\n                    \"default\": 0.1,\n                    \"min\": 0.0,\n                    \"max\": 100.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n            },\n            \"optional\": {\n                \"seed\": (\"INT\", {\n                    \"default\": None,\n                    \"min\": 0,\n                    \"max\": 2 ** 32 - 1,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                \"temperature_map\": (\"IMAGE\",),\n                \"mean\": (\"FLOAT\", {\n                    \"default\": 0.0,\n                    \"min\": -1.0,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"std\": (\"FLOAT\", {\n                    \"default\": 0.1,\n                    \"min\": 0.01,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"prob\": (\"FLOAT\", {\n                    \"default\": 0.05,\n                    \"min\": 0.0,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"slider\"\n                }),\n                \"scale\": (\"FLOAT\", {\n                    \"default\": 0.1,\n                    \"min\": 0.01,\n                    \"max\": 10.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"temp_scale\": (\"FLOAT\", {\n                    \"default\": 1.0,\n                    \"min\": 0.1,\n                    \"max\": 10.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"scale_factor\": (\"FLOAT\", {\n                    \"default\": 1.0,\n                    \"min\": 0.1,\n                    \"max\": 10.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"location\": (\"FLOAT\", {\n                    \"default\": 0.0,\n                    \"min\": -10.0,\n                    \"max\": 10.0,\n                    \"step\": 0.01,\n                    \"display\": \"number\"\n                }),\n                \"res_x\": (\"INT\", {\n                    \"default\": 10,\n                    \"min\": 1,\n                    \"max\": 100,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                \"res_y\": (\"INT\", {\n                    \"default\": 10,\n                    \"min\": 1,\n                    \"max\": 100,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                \"octaves\": (\"INT\", {\n                    \"default\": 5,\n                    \"min\": 1,\n                    \"max\": 10,\n                    \"step\": 1,\n                    \"display\": \"number\"\n                }),\n                \"persistence\": (\"FLOAT\", {\n                    \"default\": 0.5,\n                    \"min\": 0.01,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"slider\"\n                }),\n                \"num_points\": (\"INT\", {\n                    \"default\": 100,\n                    \"min\": 10,\n                    \"max\": 1000,\n                    \"step\": 10,\n                    \"display\": \"number\"\n                }),\n                \"direction\": (\"FLOAT\", {\n                    \"default\": 1.0,\n                    \"min\": 0.00,\n                    \"max\": 1.0,\n                    \"step\": 0.01,\n                    \"display\": \"slider\"\n                }),\n            }\n        }\n\n    RETURN_TYPES = (\"IMAGE\",\"IMAGE\",)\n    RETURN_NAMES = (\"NOISED_IMAGE\",\"NOISE\",)\n    FUNCTION = \"add_noise\"\n    display_name = \"Add Custom Noise\"\n    CATEGORY = \"deforum/noise\"\n    def add_noise(self, images, noise_type, amount, seed=None, temperature_map=None, **kwargs):\n\n        image, noise = add_noise_torch(images.clone(), noise_type, seed, amount, temperature_map, **kwargs)\n        return (image,noise,)\n\n\n\ndef add_noise_torch(images, noise_type='gaussian', seed=None, amount=0.1, temperature_map=None, **kwargs):\n    \"\"\"\n    Add various types of noise to an image tensor using PyTorch.\n\n    Parameters:\n        images (torch.Tensor): The input images tensor of shape (B, C, H, W).\n        noise_type (str): Type of noise to add. Supports 'gaussian', 'salt_pepper', 'poisson', 'speckle',\n                          'uniform', 'rayleigh', 'exponential', 'gamma', 'random_valued_impulse'.\n        seed (int): Seed value for randomness. Default is None.\n        amount (float): General parameter to control noise amount, interpretation depends on noise_type.\n        **kwargs: Additional noise-specific parameters.\n\n    Returns:\n        torch.Tensor: The noisy images tensor of the same shape as input.\n    \"\"\"\n    B, C, H, W = images.shape\n\n    noise = images\n\n    if seed is not None:\n        torch.manual_seed(seed)\n    else:\n        seed = secrets.randbelow(2 ** 32)\n    if noise_type == 'gaussian':\n        mean = kwargs.get('mean', 0.0)\n        std = kwargs.get('std', 0.1)\n        noisy_images = images + amount * torch.randn_like(images) * std + mean\n\n    elif noise_type == 'thermal':\n        if temperature_map is None:\n            raise ValueError(\"Temperature map must be provided for thermal noise type.\")\n        # Normalize temperature map to have a meaningful scale for noise\n        temp_min = temperature_map.min()\n        temp_max = temperature_map.max()\n        normalized_temp_map = (temperature_map - temp_min) / (temp_max - temp_min)\n        # Scale temperature map to control the amount of thermal noise\n        temp_scale = kwargs.get('temp_scale', 1.0)  # Scale factor for temperature effect\n        std_map = amount * normalized_temp_map * temp_scale\n        # Generate thermal noise based on temperature map\n        noise = torch.randn_like(images) * std_map.unsqueeze(\n            1)  # Ensure std_map matches the channel dimension of images\n        noisy_images = images + noise\n\n    elif noise_type == 'salt_pepper':\n        prob = kwargs.get('prob', 0.05)  # Probability for both salt and pepper\n        mask = torch.rand_like(images) < prob\n        images[mask] = torch.rand(1).item() * mask[mask]  # Salt\n        mask = torch.rand_like(images) < prob\n        images[mask] = 0  # Pepper\n        noisy_images = images\n    elif noise_type == 'poisson':\n        noisy_images = torch.poisson(images * amount) / amount\n\n    elif noise_type == 'speckle':\n        noise = torch.randn_like(images)\n        noisy_images = images + images * noise * amount\n\n    elif noise_type == 'uniform':\n        noise = torch.rand_like(images)\n        noisy_images = images + amount * (noise - 0.5)\n\n    elif noise_type == 'rayleigh':\n        scale = kwargs.get('scale', 0.1)\n        noise = torch.sqrt(-2 * scale * torch.log(1 - torch.rand_like(images)))\n        noisy_images = images + amount * noise\n\n    elif noise_type == 'exponential':\n        scale = kwargs.get('scale', 0.1)\n        noise = torch.distributions.Exponential(scale).sample(images.shape)\n        noisy_images = images + amount * noise\n        noise = torch.clamp(1 / noise, 0, 1.0) * amount\n    elif noise_type == 'gamma':\n        shape = kwargs.get('shape', 2.0)\n        scale = kwargs.get('scale', 0.1)\n        noise = torch.distributions.Gamma(shape, scale).sample(images.shape)\n        noisy_images = images + amount * noise\n        noise = torch.clamp(1 / noise, 0, 1.0) * amount\n\n    elif noise_type == 'random_valued_impulse':\n        prob = kwargs.get('prob', 0.05)\n        mask = torch.rand_like(images) < prob\n        # Fix: Use logical NOT operator on the mask\n        inverted_mask = ~mask\n        noise = torch.rand_like(images) * mask\n        noisy_images = images * inverted_mask.float() + noise\n        noise = (torch.clamp(noise * 255.0, 0, 1.0) * mask) * amount\n    elif noise_type == 'laplace':\n        location = kwargs.get('location', 0.0)\n        scale = kwargs.get('scale', 0.1)\n        noise = torch.distributions.Laplace(location, scale).sample(images.shape)\n        noisy_images = images + amount * noise\n        noise = torch.clamp(noise * 255.0, 0, 1.0) * amount\n\n    elif noise_type == 'perlin':\n        # Hypothetical function call - you need to implement or integrate a Perlin noise generator.\n        noise = generate_perlin_noise(B, C, H, W, **kwargs)\n        noisy_images = images + amount * noise\n\n    elif noise_type == 'brownian':\n        scale = kwargs.get('scale', 0.1)\n        noise = generate_brownian_noise(images.shape, scale, seed)\n        noisy_images = images + amount * noise / torch.max(noise)\n    elif noise_type == 'quantization':\n        # Quantization levels\n        levels = kwargs.get('levels', 256)  # Default is 256 levels for an 8-bit image\n        # Quantize the image\n        max_val = images.max()\n        quantized_images = torch.round(images * (levels - 1) / max_val) * max_val / (levels - 1)\n        # Calculate quantization noise as the difference between original and quantized images\n        noise = images - quantized_images\n        # Optionally scale the noise\n        noisy_images = images + amount * noise\n        noise = torch.clamp(noise, 0, 1.0)\n    elif noise_type == 'shot':\n        # Scale images to simulate intensity as photon counts\n        scale_factor = kwargs.get('scale_factor', 1.0)  # Scale factor to adjust the intensity\n        scaled_images = images * scale_factor\n\n        # Apply Poisson distribution to simulate shot noise\n        # Since Poisson function expects lambda > 0, ensure scaled_images is positive\n        noisy_images = torch.poisson(scaled_images) / scale_factor\n    elif noise_type == 'multiplicative':\n        # Amount parameter controls the variance of the multiplicative noise\n        mean = kwargs.get('mean', 1.0)  # Multiplicative noise mean. Typically, it should be around 1.\n        std = kwargs.get('std', 0.1)  # Standard deviation for the multiplicative noise\n\n        # Generate multiplicative noise\n        noise = torch.randn_like(images) * std + mean\n\n        # Apply the multiplicative noise\n        noisy_images = images * noise\n    elif noise_type == 'blue':\n        # Generate approximate Blue Noise\n        noise = generate_approx_blue_noise((B, C, H, W), seed=seed, **kwargs)\n\n        # Apply the Blue Noise pattern to the images\n        # The amount parameter controls the visibility of the blue noise\n        noisy_images = images + amount * noise\n    elif noise_type == 'flicker':\n        # Create frequency domain noise\n        # Create frequency domain noise\n        real_part = torch.randn((B, C, H, W // 2 + 1), device=images.device, generator=torch.manual_seed(seed))\n        imag_part = torch.randn((B, C, H, W // 2 + 1), device=images.device, generator=torch.manual_seed(seed))\n        freq_domain_noise = real_part + 1j * imag_part\n\n        # Correctly compute frequencies for 1/f scaling\n        frequencies_x = torch.fft.rfftfreq(W, d=1 / W).to(images.device)\n        frequencies_y = torch.fft.fftfreq(H, d=1 / H).to(images.device)\n\n        # Use broadcasting to create a meshgrid of frequencies\n        freq_mesh_x = frequencies_x[None, None, None, :]  # Add dimensions for B, C, and H for broadcasting\n        freq_mesh_y = frequencies_y[None, None, :, None]  # Add dimensions for B, C, and W for broadcasting\n\n        # Calculate the hypotenuse of the frequency meshgrid to apply 1/f scaling\n        freq_mesh = torch.sqrt(freq_mesh_x ** 2 + freq_mesh_y ** 2)\n\n        # Avoid division by zero by adding a small value, no need to expand as broadcasting handles it\n        freq_domain_noise /= (freq_mesh + 1e-5)\n\n        # Convert back to the spatial domain\n        noise = torch.fft.irfft(freq_domain_noise, n=W, dim=-1)\n\n        # Apply noise to images\n        noisy_images = images + amount * noise\n    elif noise_type == 'anisotropic':\n        # Generate base noise\n        base_noise = torch.randn_like(images)\n        # Apply a directional gradient\n        dir_min = kwargs.get('dir_min', 0.0)\n        dir_max = kwargs.get('dir_min', 1.0)\n        direction = torch.tensor([dir_max, dir_min])\n        gradient = torch.outer(torch.arange(H), direction[0]) + torch.outer(torch.arange(W), direction[1])\n        gradient = gradient.unsqueeze(0).unsqueeze(0).to(images.device)\n        # Apply directional bias to the noise\n        noise = base_noise * gradient\n        noisy_images = images + amount * noise\n    elif noise_type == 'fractal':\n        res_x = kwargs.get('res_x', 10)  # Base resolution for Perlin noise\n        res_y = kwargs.get('res_y', 10)\n        octaves = kwargs.get('octaves', 5)  # Number of noise layers\n        persistence = kwargs.get('persistence', 0.5)  # Amplitude decay per layer\n        noise = generate_fractal_noise(B, C, H, W, (res_x, res_y), octaves, persistence)\n        noisy_images = images + amount * noise\n    elif noise_type == 'cellular':\n        num_points = kwargs.get('num_points', 100)  # Default number of points\n        cellular_noise = generate_cellular_noise(H, W, num_points=num_points)\n        # Expand Cellular noise to match input dimensions\n        noise = cellular_noise.unsqueeze(0).unsqueeze(0).repeat(B, C, 1, 1)\n        noisy_images = images + amount * noise\n    else:\n        raise ValueError(f\"Unsupported noise type: {noise_type}\")\n\n    return torch.clamp(noisy_images, 0, 1), noise\n\n\ndef generate_perlin_noise_2d(shape, res, seed=None):\n    \"\"\"\n    Generate a 2D numpy array of Perlin noise.\n\n    Args:\n        shape: The shape of the generated array (height, width).\n        res: The resolution of the noise in terms of number of cells in each dimension.\n\n    Returns:\n        A 2D numpy array of Perlin noise.\n    \"\"\"\n\n    if seed is not None:\n        np.random.seed(seed)  # Set the seed if provided\n\n    def interpolate(t):\n        return t * t * t * (t * (t * 6 - 15) + 10)\n\n    def gradient(h, x, y):\n        vectors = np.array([[0,1],[0,-1],[1,0],[-1,0]])\n        g = vectors[h % 4]\n        return g[:, :, 0] * x + g[:, :, 1] * y\n\n    grid_x, grid_y = np.meshgrid(np.arange(shape[1]), np.arange(shape[0]))\n\n    # Rescale grid to fit res\n    grid_x = grid_x * res[0] / shape[1]\n    grid_y = grid_y * res[1] / shape[0]\n\n    # Determine grid cell coordinates\n    x0 = grid_x.astype(int)\n    y0 = grid_y.astype(int)\n\n    # Relative x and y coordinates within each cell\n    x_rel = grid_x - x0\n    y_rel = grid_y - y0\n\n    # Random gradients\n    np.random.seed(0)  # Optional: for reproducibility\n    gradients = np.random.randint(0, 4, (res[0] + 1, res[1] + 1))\n\n    # Compute the dot-product\n    n0 = gradient(gradients[y0, x0], x_rel, y_rel)\n    n1 = gradient(gradients[y0, x0 + 1], x_rel - 1, y_rel)\n    ix0 = interpolate(n0) + (interpolate(n1) - interpolate(n0)) * interpolate(x_rel)\n\n    n0 = gradient(gradients[y0 + 1, x0], x_rel, y_rel - 1)\n    n1 = gradient(gradients[y0 + 1, x0 + 1], x_rel - 1, y_rel - 1)\n    ix1 = interpolate(n0) + (interpolate(n1) - interpolate(n0)) * interpolate(x_rel)\n\n    value = ix0 + (ix1 - ix0) * interpolate(y_rel)\n    return (value - np.min(value)) / (np.max(value) - np.min(value))\n\ndef generate_perlin_noise(B, C, H, W, res_x, res_y, seed=None, *args, **kwargs):\n    \"\"\"\n    Generates Perlin noise for a batch of images in PyTorch.\n\n    Args:\n        B, C, H, W: Dimensions of the output tensor.\n        res_x, res_y: Resolution of the Perlin noise grid.\n\n    Returns:\n        A PyTorch tensor of shape (B, C, H, W) containing Perlin noise.\n    \"\"\"\n    noise = np.zeros((B, C, H, W))\n    for b in range(B):\n        for c in range(C):\n            noise[b, c] = generate_perlin_noise_2d((H, W), (res_x, res_y), seed=seed)\n    return torch.FloatTensor(noise)\n\n\ndef generate_brownian_noise(shape, scale=1.0, seed=None):\n    \"\"\"\n    Generate Brownian (Red) noise for a given tensor shape.\n\n    Parameters:\n    - shape (tuple): The shape of the output tensor (e.g., (B, C, H, W)).\n    - scale (float): Scaling factor for noise intensity. Default is 1.0.\n    - seed (int): Optional seed for reproducibility. Default is None.\n\n    Returns:\n    - torch.Tensor: Tensor containing Brownian noise of the specified shape.\n    \"\"\"\n    if seed is not None:\n        torch.manual_seed(seed)\n\n    # Initialize a tensor of random noise\n    random_noise = torch.randn(shape)\n\n    # Cumulatively sum the noise along the last dimension to simulate Brownian motion\n    brownian_noise = torch.cumsum(random_noise, dim=-1) * scale\n\n    # Normalize the noise to have values between 0 and 1\n    brownian_noise -= brownian_noise.min()\n    brownian_noise /= brownian_noise.max()\n\n    return brownian_noise\n\n\ndef generate_approx_blue_noise(shape, seed=None, min_dist=1.0, sample_fraction=0.1, *args, **kwargs):\n    \"\"\"\n    Generates an approximate Blue Noise pattern using a simplified, naive approach.\n    Note: This is computationally expensive and not optimized for performance or quality.\n\n    Parameters:\n        shape (tuple): Shape of the output tensor (B, C, H, W).\n        seed (int): Random seed for reproducibility.\n        min_dist (float): Minimum distance between points. Controls the density.\n        sample_fraction (float): Fraction of points to sample, lower means sparser.\n\n    Returns:\n        torch.Tensor: Tensor containing an approximate Blue Noise pattern.\n    \"\"\"\n    if seed is not None:\n        torch.manual_seed(seed)\n\n    _, _, H, W = shape\n    pattern = torch.zeros((H, W))\n\n    # Number of points to sample\n    num_points = int(H * W * sample_fraction)\n\n    # Randomly sample points with minimum distance\n    for _ in range(num_points):\n        x, y = torch.randint(0, H, (1,)), torch.randint(0, W, (1,))\n\n        # Check if any existing point is within min_dist\n        y_grid, x_grid = torch.meshgrid(torch.arange(H), torch.arange(W), indexing='ij')\n        distances = torch.sqrt((x_grid - x) ** 2 + (y_grid - y) ** 2)\n        if torch.min(distances * (pattern > 0).float()) >= min_dist or torch.sum(pattern) == 0:\n            pattern[x, y] = 1\n\n    # Convert the pattern to match the input shape, assuming binary pattern applied across all channels\n    blue_noise_pattern = pattern.repeat(shape[1], 1, 1).unsqueeze(0).repeat(shape[0], 1, 1, 1)\n\n    return blue_noise_pattern.float()\n\n\ndef generate_fractal_noise(B, C, H, W, res, octaves=5, persistence=0.5, *args, **kwargs):\n    \"\"\"\n    Generates fractal noise for a batch of images in PyTorch.\n\n    Args:\n        B (int): Batch size.\n        C (int): Number of channels.\n        H, W (int): Height and Width of the images.\n        res (tuple): Resolution of the base Perlin noise (res_x, res_y).\n        octaves (int): Number of layers of noise to generate.\n        persistence (float): Amplitude decay per octave (determines \"roughness\").\n\n    Returns:\n        A PyTorch tensor of shape (B, C, H, W) containing fractal noise.\n    \"\"\"\n    noise = torch.zeros((B, C, H, W))\n    amplitude = 1.0\n    frequency = 1.0\n    max_amplitude = 0.0\n\n    for _ in range(octaves):\n        perlin_noise = generate_perlin_noise(B, C, H, W, res_x=(int(res[0] * frequency)), res_y=int(res[1] * frequency))\n        noise += perlin_noise * amplitude\n        max_amplitude += amplitude\n        amplitude *= persistence\n        frequency *= 2\n\n    # Normalize the noise\n    noise /= max_amplitude\n\n    return noise\n\n\ndef generate_cellular_noise(H, W, num_points=100, *args, **kwargs):\n    \"\"\"\n    Generates a 2D Cellular (Worley) Noise pattern using PyTorch.\n\n    Args:\n        H, W (int): The height and width of the output noise pattern.\n        num_points (int): Number of seed points to generate.\n\n    Returns:\n        torch.Tensor: A tensor containing the Cellular noise pattern.\n    \"\"\"\n    # Ensure PyTorch is using the same device as the input images\n    device = kwargs.get('device', 'cpu')\n\n    # Generate random points (the seeds of the Cellular noise)\n    points = torch.rand(num_points, 2, device=device) * torch.tensor([W, H], device=device)\n\n    # Initialize the noise pattern\n    noise = torch.full((H, W), float('inf'), device=device)\n\n    # Compute distance from each pixel to the nearest point using broadcasting\n    y_coords, x_coords = torch.meshgrid(torch.arange(H, device=device), torch.arange(W, device=device), indexing='ij')\n    for point in points:\n        dist = torch.sqrt((x_coords - point[0]) ** 2 + (y_coords - point[1]) ** 2)\n        noise = torch.min(noise, dist)\n    # Normalize the noise pattern\n    noise = (noise - torch.min(noise)) / (torch.max(noise) - torch.min(noise))\n    return noise\n\n\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_prompt_nodes.py",
    "content": "import torch\n\nfrom nodes import MAX_RESOLUTION\nfrom ..modules.deforum_node_base import DeforumDataBase\n\nclass DeforumPromptNode(DeforumDataBase):\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"prompts\": (\"STRING\", {\"forceInput\": False, \"multiline\": True, \"default\": \"0:'Cat Sushi'\"}),\n            },\n            \"optional\": {\n                \"deforum_data\": (\"deforum_data\",),\n            },\n        }\n\n    RETURN_TYPES = ((\"deforum_data\",))\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/prompt\"\n    display_name = \"Prompt\"\n\n    @torch.inference_mode()\n    def get(self, prompts, deforum_data=None):\n        if deforum_data and \"prompts\" in deforum_data:\n            # Convert the formatted prompts back to a string for editing\n            formatted_prompts = deforum_data[\"prompts\"]\n            prompts = \"\\n\".join([f\"{key}:'{value}'\" for key, value in formatted_prompts.items()])\n        else:\n            # Splitting the data into rows\n            rows = prompts.split('\\n')\n\n            # Creating an empty dictionary\n            prompts_dict = {}\n\n            # Parsing each row\n            for row in rows:\n                key, value = row.split(':', 1)\n                key = int(key)\n                value = value.strip('\"')\n                prompts_dict[key] = value\n\n            if deforum_data:\n                deforum_data[\"prompts\"] = prompts_dict\n            else:\n                deforum_data = {\"prompts\": prompts_dict}\n\n        return (deforum_data,)\n\n\nclass DeforumAreaPromptNode(DeforumDataBase):\n\n    default_area_prompt = '[{\"0\": [{\"prompt\": \"a vast starscape with distant nebulae and galaxies\", \"x\": 0, \"y\": 0, \"w\": 1024, \"h\": 1024, \"s\": 0.7}, {\"prompt\": \"detailed sci-fi spaceship\", \"x\": 512, \"y\": 512, \"w\": 50, \"h\": 50, \"s\": 0.7}]}, {\"50\": [{\"prompt\": \"a vast starscape with distant nebulae and galaxies\", \"x\": 0, \"y\": 0, \"w\": 1024, \"h\": 1024, \"s\": 0.7}, {\"prompt\": \"detailed sci-fi spaceship\", \"x\": 412, \"y\": 412, \"w\": 200, \"h\": 200, \"s\": 0.7}]}, {\"100\": [{\"prompt\": \"a vast starscape with distant nebulae and galaxies\", \"x\": 0, \"y\": 0, \"w\": 1024, \"h\": 1024, \"s\": 0.7}, {\"prompt\": \"detailed sci-fi spaceship\", \"x\": 112, \"y\": 112, \"w\": 800, \"h\": 800, \"s\": 0.7}]}]'\n    default_prompt = \"Alien landscape\"\n\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"keyframe\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 8192, \"step\": 1}),\n                \"mode\":([\"default\", \"percentage\", \"strength\"],),\n                \"prompt\": (\"STRING\", {\"forceInput\": False, \"multiline\": True, 'default': cls.default_prompt,}),\n                \"width\": (\"INT\", {\"default\": 64, \"min\": 64, \"max\": MAX_RESOLUTION, \"step\": 8}),\n                \"height\": (\"INT\", {\"default\": 64, \"min\": 64, \"max\": MAX_RESOLUTION, \"step\": 8}),\n                \"x\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": MAX_RESOLUTION, \"step\": 8}),\n                \"y\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": MAX_RESOLUTION, \"step\": 8}),\n                \"strength\": (\"FLOAT\", {\"default\": 1.0, \"min\": 0.0, \"max\": 10.0, \"step\": 0.01}),\n            },\n            \"optional\": {\n                \"deforum_data\": (\"deforum_data\",),\n            },\n        }\n\n    RETURN_TYPES = ((\"deforum_data\",))\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/prompt\"\n    display_name = \"Area Prompt\"\n\n    @torch.inference_mode()\n    def get(self, keyframe, mode, prompt, width, height, x, y, strength, deforum_data=None):\n\n        area_prompt = {\"prompt\": prompt, \"x\": x, \"y\": y, \"w\": width, \"h\": height, \"s\": strength, \"mode\":mode}\n        area_prompt_dict = {f\"{keyframe}\": [area_prompt]}\n\n        if not deforum_data:\n            deforum_data = {\"area_prompts\":[area_prompt_dict]}\n\n        if \"area_prompts\" not in deforum_data:\n            deforum_data[\"area_prompts\"] = [area_prompt_dict]\n        else:\n\n            added = None\n\n            for item in deforum_data[\"area_prompts\"]:\n                for k, v in item.items():\n                    if int(k) == keyframe:\n                        if area_prompt not in v:\n                            v.append(area_prompt)\n                            added = True\n                        else:\n                            added = True\n            if not added:\n                deforum_data[\"area_prompts\"].append(area_prompt_dict)\n\n        deforum_data[\"prompts\"] = None\n\n        return (deforum_data,)\n\n\nclass DeforumUnformattedPromptNode(DeforumDataBase):\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"unformatted_prompts\": (\"STRING\", {\"forceInput\": False, \"multiline\": True}),\n                \"keyframe_interval\": (\"INT\", {\"default\": 50, \"min\": 1, \"max\": 8192, \"step\": 1}),\n            },\n            \"optional\": {\n                \"deforum_data\": (\"deforum_data\",),\n            },\n        }\n\n    RETURN_TYPES = ((\"deforum_data\",))\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/prompt\"\n    display_name = \"Unformatted Prompt\"\n\n    @torch.inference_mode()\n    def get(self, unformatted_prompts, keyframe_interval, deforum_data=None):\n        # Splitting the unformatted prompts into lines\n        lines = unformatted_prompts.split('\\n')\n\n        # Creating an empty dictionary for formatted prompts\n        formatted_prompts = {}\n\n        # Parsing each line and formatting the prompts\n        for i, line in enumerate(lines):\n            keyframe = i * keyframe_interval\n            formatted_prompts[keyframe] = line.strip()\n\n        if deforum_data:\n            deforum_data[\"prompts\"] = formatted_prompts\n        else:\n            deforum_data = {\"prompts\": formatted_prompts}\n\n        return (deforum_data,)\n\nclass DeforumAppendNode(DeforumDataBase):\n    def __init__(self):\n        super().__init__()\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n                \"append_text\": (\"STRING\", {\"multiline\": True, \"default\": \"\"}),\n                \"keyframe_interval\": (\"INT\", {\"default\": 50, \"min\": 1, \"max\": 8192, \"step\": 1}),\n            },\n            \"optional\": {\n                \"deforum_data\": (\"deforum_data\",),\n                \"append_to_all\": ([\"No\", \"Yes\"], {\"default\": \"No\"}),\n                \"use_neg\": ([\"No\", \"Yes\"], {\"default\": \"No\"}),\n            },\n        }\n\n    RETURN_TYPES = ((\"deforum_data\",))\n    FUNCTION = \"get\"\n    OUTPUT_NODE = True\n    CATEGORY = f\"deforum/prompt\"\n    display_name = \"Append\"\n\n    @torch.inference_mode()\n    def get(self, append_text, keyframe_interval, deforum_data=None, append_to_all=\"No\", use_neg=\"No\"):\n        print(\"Append Text:\", append_text)\n        print(\"Keyframe Interval:\", keyframe_interval)\n        print(\"Deforum Data:\", deforum_data)\n        print(\"Append to All:\", append_to_all)\n        print(\"Use --neg:\", use_neg)\n        \n        if deforum_data and \"prompts\" in deforum_data:\n            formatted_prompts = deforum_data[\"prompts\"]\n            print(\"Formatted Prompts (Before Append):\", formatted_prompts)\n            \n            neg_prefix = \"--neg \" if use_neg == \"Yes\" else \"\"\n            \n            if append_to_all == \"Yes\":\n                # Append the first line of append_text to every prompt\n                first_line = append_text.split('\\n')[0]\n                for key in formatted_prompts:\n                    formatted_prompts[key] = f\"{formatted_prompts[key]} {neg_prefix}{first_line}\"\n            else:\n                # Append the append_text to prompts based on keyframe interval\n                lines = append_text.split('\\n')\n                for i, line in enumerate(lines):\n                    keyframe = i * keyframe_interval\n                    if keyframe in formatted_prompts:\n                        formatted_prompts[keyframe] = f\"{formatted_prompts[keyframe]} {neg_prefix}{line}\"\n            \n            print(\"Formatted Prompts (After Append):\", formatted_prompts)\n            deforum_data[\"prompts\"] = formatted_prompts\n        else:\n            deforum_data = {\"prompts\": {}}\n\n        return (deforum_data,)\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_sampler_nodes.py",
    "content": "class DeforumKSampler:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"model\": (\"MODEL\",),\n                     \"latent\": (\"LATENT\",),\n                     \"positive\": (\"CONDITIONING\",),\n                     \"negative\": (\"CONDITIONING\",),\n\n                     \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                     }\n                }\n\n    RETURN_TYPES = (\"LATENT\",)\n    FUNCTION = \"sample\"\n    display_name = \"KSampler\"\n    CATEGORY = \"deforum/sampling\"\n\n    def sample(self, model, latent, positive, negative, deforum_frame_data):\n        from nodes import common_ksampler\n        seed = deforum_frame_data.get(\"seed\", 0)\n        steps = deforum_frame_data.get(\"steps\", 10)\n        cfg = deforum_frame_data.get(\"cfg\", 7.5)\n        sampler_name = deforum_frame_data.get(\"sampler_name\", \"euler_a\")\n        scheduler = deforum_frame_data.get(\"scheduler\", \"normal\")\n        denoise = deforum_frame_data.get(\"denoise\", 1.0)\n\n        latent[\"samples\"] = latent[\"samples\"].float()\n        return common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent,\n                               denoise=denoise)\n\n\nclass DeforumVAEEncode:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": {\n                              \"vae\": (\"VAE\", )},\n                \"optional\": {\n                            \"pixels\": (\"IMAGE\",),\n                            \"latent\": (\"LATENT\",)\n                }\n\n\n                }\n    RETURN_TYPES = (\"LATENT\",)\n    FUNCTION = \"encode\"\n    display_name = \"VAEEncode [safe]\"\n\n    CATEGORY = \"deforum/latent\"\n\n    def encode(self, vae, pixels, latent):\n        if pixels is not None:\n            t = vae.encode(pixels[:,:,:,:3])\n            return ({\"samples\":t}, )\n        else:\n            return (latent,)\n"
  },
  {
    "path": "deforum_nodes/nodes/deforum_schedule_visualizer.py",
    "content": "import random\n\nfrom matplotlib.figure import Figure\nfrom matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas\nfrom PIL import Image\nimport io\n\nfrom deforum.pipelines.deforum_animation.animation_helpers import FrameInterpolator\nfrom ..modules.deforum_comfyui_helpers import tensor2pil, tensor2np, pil2tensor\n\n\ntemplates = [\n    \"0:(0), max_f:(100)\", # Linearly interpolates from 0 to 100.\n    \"0:(100), max_f:(0)\", # Linearly interpolates from 100 to 0.\n    \"0:(sin(2*3.14*t/max_f))\", # Sinusoidal wave across the frame range.\n    \"0:(exp(-t/max_f)*sin(4*3.14*t/max_f))\", # Damped sinusoidal wave.\n    \"0:(s%100)\", # Outputs a constant value based on the seed modulo 100.\n    \"0:(sin(2*3.14*(t+s)/max_f))\", # Sinusoidal wave with phase shift based on seed.\n    \"0:(t**2/max_f**2)\", # Quadratic growth over frames.\n    \"0:(-t**2/max_f**2 + 1)\", # Inverted quadratic growth.\n    \"0:(0.5*sin(2*3.14*t/max_f) + 0.5*sin(4*3.14*t/max_f))\", # Superposition of two sinusoidal waves.\n    \"0:(0.5*cos(2*3.14*t/max_f) + t/max_f)\", # Combination of cosine wave and linear growth.\n    \"0:(sin(2*3.14*t/max_f + s))\", # Sinusoidal wave with phase offset by seed.\n    \"0:(sin(2*3.14*t/max_f) * (s % 10))\", # Sinusoidal wave amplitude modulated by seed.\n    \"0:(cos(2*3.14*t/max_f))\", # Cosine wave over the frame range.\n    \"0:(1 - abs(2*t/max_f - 1))\", # Triangle wave that peaks at mid-frame.\n    \"0:(sin(2*3.14*t/max_f) * cos(2*3.14*t/max_f))\", # Product of sine and cosine waves.\n    \"0:(t % 20)\", # Modulo operation creates a sawtooth wave.\n    \"0:(50 + 50*sin(2*3.14*t/max_f))\", # Sinusoidal oscillation around 50.\n    \"0:(s % 10 + sin(2*3.14*t/max_f))\", # Sinusoidal wave with base level determined by seed.\n    \"0:(log(t+1))\", # Logarithmic growth.\n    \"0:(-cos(2*3.14*t/max_f) + 2)\", # Inverted cosine wave shifted upwards.\n    \"0:(sin(2*3.14*t/max_f) if t < max_f/2 else cos(2*3.14*t/max_f))\", # Sinusoidal that switches to cosine at halfway.\n    \"0:(sin(4*3.14*t/max_f) + cos(2*3.14*t/max_f))\", # Sum of two waves with different frequencies.\n    \"0:(tan(3.14*t/max_f))\", # Tangent wave, potentially extreme values.\n    \"0:(1 - t/max_f)\", # Linear decrease from 1 to 0.\n    \"0:(sqrt(t/max_f))\", # Square root growth.\n    \"0:(-sqrt(t/max_f) + 1)\", # Inverted square root curve.\n    \"0:(abs(sin(2*3.14*t/max_f)))\", # Absolute value of a sinusoidal wave, creating peaks.\n    \"0:(abs(cos(2*3.14*t/max_f)))\", # Absolute value of a cosine wave, creating peaks.\n    \"0:(sin(2*3.14*t/max_f)**2)\", # Square of a sinusoidal wave, smoothing negative values.\n    \"0:(cos(2*3.14*t/max_f)**2)\", # Square of a cosine wave, smoothing negative values.\n    \"0:(sin(2*3.14*t/(max_f+s)))\", # Sinusoidal wave with frequency modulated by seed.\n    \"0:(exp(t/max_f) - 1)\", # Exponential growth.\n    \"0:(2**(t/max_f) - 1)\", # Exponential growth with base 2.\n    \"0:(-2**(t/max_f) + 2)\", # Inverted exponential curve with base 2.\n    \"0:(s % 10 * t/max_f)\", # Linear growth modulated by seed.\n    \"0:(sin(2*3.14*(t+s)%max_f/max_f))\", # Sinusoidal wave with frequency and phase modulated by seed.\n    \"0:(cos(2*3.14*(t+s)%max_f/max_f))\", # Cosine wave with frequency and phase modulated by seed.\n    \"0:(s * sin(2*3.14*t/max_f))\", # Sinusoidal wave with amplitude modulated by seed.\n    \"0:(cos(t/max_f * 3.14 * (s%5)))\", # Cosine wave with frequency modulated by seed.\n    \"0:(sin(t**2/max_f**2))\", # Sinusoidal wave applied to quadratic growth.\n    \"0:(cos(s + t/max_f))\", # Cosine wave with phase shift linearly increasing and offset by seed.\n    \"0:(tan(s * t/max_f))\", # Tangent wave with slope modulated by seed.\n    \"0:(-1*sin(3.14*t/max_f))\", # Inverted sinusoidal wave.\n    \"0:(exp(-t/max_f)*cos(2*3.14*t/max_f))\", # Damped cosine wave.\n    \"0:(sin(2*3.14*t/max_f)**3)\", # Cubed sinusoidal wave for enhanced contrast.\n    \"0:(cos(2*3.14*t/max_f)**3)\", # Cubed cosine wave for enhanced contrast.\n    \"0:(log(t+1)*sin(2*3.14*t/max_f))\", # Sinusoidal wave amplitude modulated by a logarithmic function.\n    \"0:(sin(t/max_f)*cos(t/max_f))\", # Product of sine and cosine for varying wave patterns.\n    \"0:(t**3/max_f**3)\", # Cubic growth for accelerated change.\n    \"0:(-t**3/max_f**3 + 1)\", # Inverted cubic curve for decelerated change towards the end.\n    \"0:(abs(sin(4*3.14*t/max_f)))\", # Absolute sinusoidal wave with higher frequency.\n    \"0:(abs(cos(4*3.14*t/max_f)))\", # Absolute cosine wave with higher frequency.\n    \"0:(sin(2*3.14*t/max_f)*s)\", # Sinusoidal wave with amplitude directly proportional to seed.\n    \"0:(cos(2*3.14*t/max_f)*s)\", # Cosine wave with amplitude directly proportional to seed.\n    \"0:(tan(2*3.14*t/max_f + s))\", # Tangent wave with phase shift based on seed.\n    \"0:(exp(t/max_f)*sin(2*3.14*t/max_f))\", # Exponentially growing sinusoidal wave.\n    \"0:(2**(t/max_f)*sin(2*3.14*t/max_f))\", # Sinusoidal wave with exponentially growing amplitude.\n    \"0:(-2**(t/max_f)*cos(2*3.14*t/max_f) + 2)\", # Exponentially damped cosine wave, shifted up.\n    \"0:(s%20 * sin(2*3.14*t/max_f))\", # Sinusoidal wave with amplitude modulated by seed modulo 20.\n    \"0:(cos(2*3.14*(t+s)%max_f/max_f)*s)\", # Cosine wave with frequency, phase modulated by seed, and amplitude scaling.\n    \"0:(sin(2*3.14*(t+s)%max_f/max_f)*s)\", # Sinusoidal wave with frequency, phase modulated by seed, and amplitude scaling.\n    \"0:(sin(2*3.14*t/max_f)/(t+1))\", # Sinusoidal wave with amplitude inversely proportional to frame.\n    \"0:(cos(2*3.14*t/max_f)/(t+1))\", # Cosine wave with amplitude inversely proportional to frame.\n    \"0:(sin(2*3.14*t/max_f)*t/max_f)\", # Sinusoidal wave with linearly increasing amplitude.\n    \"0:(cos(2*3.14*t/max_f)*t/max_f)\", # Cosine wave with linearly increasing amplitude.\n    \"0:(sin(2*3.14*t/max_f)*sqrt(t/max_f))\", # Sinusoidal wave with amplitude modulated by square root of frame.\n    \"0:(cos(2*3.14*t/max_f)*sqrt(t/max_f))\", # Cosine wave with amplitude modulated by square root of frame.\n    \"0:(tan(2*3.14*t/max_f)*sqrt(t/max_f))\", # Tangent wave with amplitude modulated by square root of frame.\n    \"0:(sin(2*3.14*t/max_f)*log(t+1))\", # Sinusoidal wave with logarithmically increasing amplitude.\n    \"0:(cos(2*3.14*t/max_f)*log(t+1))\", # Cosine wave with logarithmically increasing amplitude.\n    \"0:(tan(2*3.14*t/max_f)*log(t+1))\" # Tangent wave with logarithmically increasing amplitude.\n    \"0:((exp(t/max_f)) * (log(1+abs(t))) + (exp(1000/max_f)) + 1)\",  # Something Random\n    \"0:((exp(t/max_f)) / (cos(2*3.14*t/max_f)) / (log(1+abs(t))) / (tan(2*3.14*t/max_f)) + 1)\",\n    \"0:((log(1+abs(t))) + (log(1+abs(1000))) - (cos(2*3.14*1000/max_f)) + (cos(2*3.14*t/max_f)) + (sin(2*3.14*1000/max_f)) + 1)\"\n]\n\n# audio_templates = [\n#     \"x * t / max_f\",  # Linear scaling of x with respect to time t.\n#     \"x * sin(2 * pi * t / max_f)\",  # Sinusoidal modulation of x based on frame time.\n#     \"x * (1 - exp(-t / max_f))\",  # Exponential approach of x to its maximum over time.\n#     \"x * exp(-t / max_f)\",  # Exponential decay of x over time.\n#     \"x * sin(2 * pi * e * t / max_f)\",  # Sinusoidal modulation with base e to vary frequency.\n#     \"x * (1 if t < max_f / 2 else 0)\",  # Binary step function in time, for a sudden change.\n#     \"x * log(e + t / max_f)\",  # Logarithmic scaling with base e, gentle growth over time.\n#     \"x * cos(2 * pi * t / max_f) + e ** (t / max_f)\",  # Cosine wave plus exponential growth factor e.\n#     \"x * pow(t / max_f, 2)\",  # Quadratic growth of x over time.\n#     \"x * sqrt(t / max_f)\",  # Square root scaling of x, slower increase over time.\n#     \"x * tan(pi * t / max_f)\",  # Tangent modulation, note potential for extreme values.\n#     \"x * asin(sin(2 * pi * t / max_f))\",  # Arcsine of a sinusoidal wave, for harmonic effects.\n#     \"x * (2 ** (t / max_f) - 1)\",  # Exponential growth based on power of 2.\n#     \"x * factorial(int(t) % 5)\",  # Modulating x by the factorial of t modulo 5, for periodic jumps.\n#     \"x * (1 - abs(2 * t / max_f - 1))\",  # Triangle wave shaping of x over time.\n#     \"x * abs(sin(2 * pi * t / max_f))\",  # Absolute value of a sinusoidal wave, ensuring positive values.\n#     \"x * sin(2 * pi * t / max_f) * cos(2 * pi * t / max_f)\",  # Product of sine and cosine for complex modulation.\n#     \"x * (e ** (cos(2 * pi * t / max_f)) - 1)\",  # Exponential function modulated by a cosine wave.\n#     \"x * if(t < max_f / 2, sin(2 * pi * t / max_f), cos(2 * pi * t / max_f))\",  # Conditional modulation with half period sine, half period cosine.\n#     \"x * sin(4 * pi * t / max_f) + x * cos(2 * pi * t / max_f)\",  # Combination of sine and cosine waves with different frequencies.\n#     \"x * (sin(2 * pi * t / max_f) if t < max_f / 3 else sin(4 * pi * t / max_f))\",  # Conditional frequency change in sine wave.\n#     \"x * (exp(t / max_f) - 1) / log(e + t)\",  # Exponential increase tempered by a logarithmic factor.\n#     \"(x * cos(2 * pi * t / max_f)) / (1 + log(t + 1))\",  # Cosine wave scaled by x with a logarithmic denominator to moderate growth.\n#     \"x * sin(2 * pi * t / max_f) ** 2\",  # Square of a sinusoidal wave, creating a smoothed, non-negative waveform.\n#     \"x * cos(2 * pi * t / max_f) ** 2\",  # Square of a cosine wave, similar effect as above but phase-shifted.\n# ]\n\n\naudio_templates = [\n    # Simple Amplitude Modulations\n    \"x * 2\",  # Doubling the amplitude.\n    \"x / 2\",  # Halving the amplitude.\n    \"abs(x)\",  # Absolute value of the amplitude.\n    \"x * x\",  # Squaring the amplitude.\n    \"sqrt(abs(x))\",  # Square root of the absolute amplitude.\n    \"log(abs(x) + 1)\",  # Logarithmic scaling of the amplitude.\n    \"x * sin(x)\",  # Sine modulation based on amplitude itself.\n    \"x * cos(x)\",  # Cosine modulation based on amplitude itself.\n    \"-x\",  # Inverting the amplitude.\n    \"x * (exp(x) - 1)\",  # Exponential scaling based on the amplitude.\n    \"x * pow(e, x - 1)\",  # Exponential growth using base e, adjusted for amplitude.\n    \"x * factorial(int(abs(x)) % 5)\",  # Factorial modulation based on the absolute amplitude modulo 5.\n    \"x if x > 0.5 else x * 2\",  # Conditional scaling for amplitudes greater than 0.5.\n    \"min(x, 0.5)\",  # Clipping amplitude to a maximum of 0.5.\n    \"max(x, -0.5)\",  # Ensuring amplitude is not less than -0.5.\n    \"x % 0.5\",  # Amplitude modulo 0.5, creating a repeating pattern.\n    \"tan(x)\",  # Tangent modulation of amplitude.\n    \"asin(min(1, max(-1, x))) / pi\",  # Arcsine of amplitude normalized to [-1, 1], scaled by π.\n    \"1 / (abs(x) + 1)\",  # Inverse scaling of amplitude, avoiding division by zero.\n    \"pow(e, -abs(x))\",  # Exponential decay based on the absolute amplitude.\n\n    # Temporal Expressions Involving Time and Maximum Frame\n    \"x * t / max_f\",  # Linear scaling of amplitude with respect to time.\n    \"x * sin(2 * pi * t / max_f)\",  # Sinusoidal modulation over time.\n    \"x * exp(-t / max_f)\",  # Exponential decay over time.\n    \"x * (1 - exp(-t / max_f))\",  # Inverse exponential growth.\n    \"x * cos(2 * pi * e * t / max_f)\",  # Cosine wave modulation with base e to alter frequency.\n    \"x * if(t < max_f / 2, 1, 0)\",  # Binary switch based on time, for sudden change.\n    \"x * pow(t / max_f, 2)\",  # Quadratic growth of amplitude over time.\n    \"x * sqrt(t / max_f)\",  # Square root scaling over time.\n    \"x * tan(pi * t / max_f)\",  # Tangent modulation over time.\n    \"x * sin(2 * pi * t / max_f) * cos(2 * pi * t / max_f)\",  # Product of sine and cosine for complex temporal modulation.\n    \"x * sin(2 * pi * t / max_f) if t < max_f / 2 else x * cos(2 * pi * t / max_f)\",  # Conditional wave modulation.\n    \"x * sin(4 * pi * t / max_f) + x * cos(2 * pi * t / max_f)\",  # Sum of waves with different frequencies.\n    \"x * (exp(t / max_f) - 1) / log(e + t)\",  # Exponential growth tempered by logarithm of time.\n    \"(x * cos(2 * pi * t / max_f)) / (1 + log(t + 1))\",  # Cosine wave moderated by logarithmic factor.\n\n    # Complex and Combined Expressions\n    \"x * (sin(2 * pi * t / max_f) ** 2 + cos(2 * pi * t / max_f) ** 2)\",  # Sum of squared sine and cosine waves.\n    \"x * (1 - abs(2 * t / max_f - 1))\",  # Triangle wave shaping over time.\n    \"x * abs(sin(2 * pi * t / max_f))\",  # Absolute sine wave for positive values.\n    \"x * pow(e, cos(2 * pi * t / max_f))\",  # Exponential function modulated by cosine.\n    \"x * asin(sin(2 * pi * t / max_f)) / pi\",  # Arcsine of sine wave normalized and scaled.\n    \"x * pow(2, sin(2 * pi * t / max_f))\",  # Exponential growth with base 2, modulated by sine wave.\n    \"x * log(1 + abs(sin(2 * pi * t / max_f)))\",  # Logarithmic scaling modulated by absolute sine wave.\n    \"x * (sin(2 * pi * t / (max_f + t)))\",  # Sinusoidal frequency modulation by time.\n    \"(x * cos(2 * pi * t / max_f)) / (1 + log(t + 1)) * sin(2 * pi * t / max_f)\",  # Combined cosine and sine waves with logarithmic softening.\n    \"x * (sin(2 * pi * t / max_f) * (t % 10))\",  # Sinusoidal wave amplitude modulated by frame modulo.\n    \"x * exp(t / max_f) * sin(2 * pi * t / max_f)\",  # Exponential growth combined with sinusoidal modulation.\n    \"x * (if(t < max_f / 3, sin(2 * pi * t / max_f), cos(2 * pi * t / max_f)))\",  # Conditional frequency modulation.\n    \"x * (1 / (1 + exp(-t / max_f)))\",  # Logistic sigmoid function for smooth transitions.\n    \"x * pow(e, -t / max_f) * cos(2 * pi * t / max_f)\",  # Exponential decay combined with cosine modulation.\n    \"x * sin(2 * pi * t / max_f) / (1 + exp(-t / max_f))\",  # Sinusoidal wave with logistic growth control.\n    \"x * (e ** (t / max_f) - e ** (-t / max_f)) / 2\",  # Hyperbolic sine function for symmetric exponential growth and decay.\n    \"x * (1 - (t / max_f) ** 2)\",  # Parabolic decrease towards the end of the timeline.\n    \"x * tanh(2 * pi * t / max_f)\",  # Hyperbolic tangent for smooth transitions between -1 and 1.\n    \"x * factorial((int(t) % 5) + 1)\",  # Factorial modulation based on time modulo 5.\n]\ndef generate_complex_random_expression(max_frames, seed=None, max_parts=3):\n    \"\"\"\n    Generates a complex random mathematical expression using a variety of functions, operators, and globals.\n\n    Parameters:\n    - max_frames: The maximum number of frames, for normalizing 't' in expressions.\n    - seed: Optional seed for random number generator for reproducibility.\n    - max_parts: Maximum number of parts (expressions) to combine.\n\n    Returns:\n    A string formatted like \"0:(expression), max_f:(expression)\".\n    \"\"\"\n    if seed is not None:\n        random.seed(seed)\n\n    funcs = ['sin', 'cos', 'tan', 'exp', 'log', 'abs']\n    operators = ['+', '-', '*', '/']\n    globals = ['t', 'max_f', 's']\n    parts = []\n\n    for _ in range(random.randint(1, max_parts)):\n        func = random.choice(funcs)\n        operator = random.choice(operators) if parts else ''  # No leading operator for the first part\n        global_var = random.choice(globals)\n        if global_var == 'max_f':\n            global_value = str(max_frames)\n        else:\n            global_value = global_var\n\n        if func in ['sin', 'cos', 'tan']:\n            part = f\"{func}(2*3.14*{global_value}/max_f)\"\n        elif func == 'log':\n            part = f\"{func}(1+abs({global_value}))\"\n        elif func == 'exp':\n            part = f\"{func}({global_value}/max_f)\"\n        else:  # abs or other functions without specific handling\n            part = f\"{func}({global_value})\"\n\n        parts.append(f\"{operator} ({part})\")\n\n    # Combine parts with randomly chosen operators\n    expression = ' '.join(parts).strip()\n\n    # Final check to make the expression safer for division and ensure it's not empty\n    if '/' in expression:\n        expression += \" + 1\"  # Avoid division by zero\n    if not expression:\n        expression = \"0\"  # Fallback to a simple zero expression if somehow it ends up empty\n\n    return f\"0:({expression})\"\n\n\nclass DeforumScheduleTemplate:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n\n                \"expression\": (templates,),\n\n            }\n               }\n\n    # @classmethod\n    # def IS_CHANGED(cls, text, autorefresh):\n    #     # Force re-evaluation of the node\n    #     if autorefresh == \"Yes\":\n    #         return float(\"NaN\")\n\n    RETURN_TYPES = (\"STRING\",)\n    FUNCTION = \"show\"\n    display_name = \"Schedule Templates\"\n    CATEGORY = \"deforum/help\"\n    OUTPUT_NODE = True\n\n    def show(self, expression):\n        return(str(expression),)\n\nclass DeforumAudioScheduleTemplate:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n\n                \"expression\": (audio_templates,),\n\n            }\n               }\n\n    # @classmethod\n    # def IS_CHANGED(cls, text, autorefresh):\n    #     # Force re-evaluation of the node\n    #     if autorefresh == \"Yes\":\n    #         return float(\"NaN\")\n\n    RETURN_TYPES = (\"STRING\",)\n    FUNCTION = \"show\"\n    display_name = \"Audio Schedule Expression Templates\"\n    CATEGORY = \"deforum/help\"\n    OUTPUT_NODE = True\n\n    def show(self, expression):\n        return(str(expression),)\n\n\nclass DeforumScheduleTemplateRandomizer:\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n\n                \"seed\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 0xffffffffffffffff}),\n                \"max_frames\": (\"INT\", {\"default\": 1, \"min\": 1, \"max\": 4096, \"step\": 1}),\n                \"max_parts\": (\"INT\", {\"default\": 1, \"min\": 1, \"max\": 4096, \"step\": 1}),\n\n            }\n               }\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    RETURN_TYPES = (\"STRING\",)\n    FUNCTION = \"show\"\n    display_name = \"Schedule Randomizer\"\n    CATEGORY = \"deforum/utils\"\n    OUTPUT_NODE = True\n\n    def show(self, seed, max_frames, max_parts):\n        return(str(generate_complex_random_expression(max_frames, seed, max_parts)),)\n\n\nclass DeforumScheduleVisualizer:\n\n    @classmethod\n    def INPUT_TYPES(cls):\n        return {\n            \"required\": {\n\n                \"schedule\": (\"STRING\", {\"default\": \"0: (1.0)\"}),\n                \"max_frames\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 128000, \"step\": 1}),\n                \"grid\": (\"BOOLEAN\", {\"default\": False}),\n\n            }\n               }\n\n    # @classmethod\n    # def IS_CHANGED(cls, text, autorefresh):\n    #     # Force re-evaluation of the node\n    #     if autorefresh == \"Yes\":\n    #         return float(\"NaN\")\n\n    RETURN_TYPES = (\"IMAGE\",)\n    FUNCTION = \"show\"\n    display_name = \"Schedule Visualizer\"\n    CATEGORY = \"deforum/utils\"\n    OUTPUT_NODE = True\n\n    def show(self, schedule, max_frames, grid):\n        if max_frames == 0:\n            max_frames = len(schedule.split(','))\n        fi = FrameInterpolator(max_frames, -1)\n        series = fi.get_inbetweens(fi.parse_key_frames(schedule))\n\n        # Create a figure for plotting\n        fig = Figure()\n        canvas = FigureCanvas(fig)\n        ax = fig.add_subplot(111)\n        if grid:\n            ax.grid(True, which='both', linestyle='--', linewidth=0.5, color='gray')\n\n        # Plot the series\n        ax.plot(series)\n        ax.set_title(\"Schedule Visualization\")\n        ax.set_xlabel(\"Frame\")\n        ax.set_ylabel(\"Value\")\n\n        # Convert the Matplotlib figure to a PIL Image\n        buf = io.BytesIO()\n        fig.savefig(buf, format='png')\n        buf.seek(0)\n        pil_img = Image.open(buf)\n\n\n        return(pil2tensor(pil_img),)"
  },
  {
    "path": "deforum_nodes/nodes/deforum_video_nodes.py",
    "content": "import base64\nimport gc\nimport os\nimport shutil\nfrom io import BytesIO\nfrom aiohttp import web\nimport hashlib\n\nimport server\nimport cv2\nimport imageio\nimport numpy as np\nimport torch\nfrom PIL import Image\nfrom tqdm import tqdm\n\nimport folder_paths\nfrom ..modules.deforum_comfyui_helpers import tensor2pil, pil2tensor, find_next_index, pil_image_to_base64, tensor_to_webp_base64\n\nvideo_extensions = ['webm', 'mp4', 'mkv', 'gif']\n\nimport moviepy.editor as mp\nfrom scipy.io.wavfile import write\nimport tempfile\n\n\ndef save_to_file(data, filepath: str):\n    # Ensure the audio data is reshaped properly for mono/stereo\n    if data.num_channels > 1:\n        audio_data_reshaped = data.audio_data.reshape((-1, data.num_channels))\n    else:\n        audio_data_reshaped = data.audio_data\n    write(filepath, data.sample_rate, audio_data_reshaped.astype(np.int16))\n    return True\n\nclass DeforumLoadVideo:\n\n    def __init__(self):\n        self.video_path = None\n\n    @classmethod\n    def INPUT_TYPES(s):\n        input_dir = folder_paths.get_input_directory()\n        files = []\n        for f in os.listdir(input_dir):\n            if os.path.isfile(os.path.join(input_dir, f)):\n                file_parts = f.split('.')\n                if len(file_parts) > 1 and (file_parts[-1] in video_extensions):\n                    files.append(f)\n        return {\"required\": {\n                    \"video\": (sorted(files),),\n                    \"reset\": (\"BOOLEAN\", {\"default\": False},),\n                    \"iterative\": (\"BOOLEAN\", {\"default\": True},),\n                    \"start_frame\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 1000000},),\n                    \"return_frames\": (\"INT\", {\"default\": 1, \"min\": 1, \"max\": 1000000},),\n\n        },}\n\n    CATEGORY = \"deforum/video\"\n    display_name = \"Load Video\"\n\n    RETURN_TYPES = (\"IMAGE\",\"INT\",\"INT\")\n    RETURN_NAMES = (\"IMAGE\",\"FRAME_IDX\",\"MAX_FRAMES\")\n    FUNCTION = \"load_video_frame\"\n\n    def __init__(self):\n        self.cap = None\n        self.current_frame = None\n\n    # def load_video_frame(self, video, reset, iterative, start_frame, return_frames):\n    #     video_path = folder_paths.get_annotated_filepath(video)\n    #\n    #     # Initialize or reset video capture\n    #     if self.cap is None or self.cap.get(cv2.CAP_PROP_POS_FRAMES) >= self.cap.get(cv2.CAP_PROP_FRAME_COUNT) or self.video_path != video_path or reset:\n    #         try:\n    #             self.cap.release()\n    #         except:\n    #             pass\n    #         self.cap = cv2.VideoCapture(video_path)\n    #\n    #         self.cap = cv2.VideoCapture(video_path)\n    #         self.current_frame = -1\n    #         self.video_path = video_path\n    #     success, frame = self.cap.read()\n    #     if success:\n    #         self.current_frame += 1\n    #         frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n    #         frame = np.array(frame).astype(np.float32)\n    #         frame = pil2tensor(frame)  # Convert to torch tensor\n    #     else:\n    #         # Reset if reached the end of the video\n    #         self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)\n    #         success, frame = self.cap.read()\n    #         self.current_frame = 0\n    #         frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n    #         frame = np.array(frame).astype(np.float32)\n    #         frame = pil2tensor(frame)  # Convert to torch tensor\n    #\n    #     return (frame,self.current_frame,self.cap.get(cv2.CAP_PROP_POS_FRAMES),)\n    def load_video_frame(self, video, reset, iterative, start_frame, return_frames):\n        video_path = folder_paths.get_annotated_filepath(video)\n        max_frames = 0\n        frames = []\n\n        # Initialize or reset video capture\n        if self.cap is None or self.cap.get(cv2.CAP_PROP_POS_FRAMES) >= self.cap.get(cv2.CAP_PROP_FRAME_COUNT) or self.video_path != video_path or reset:\n            try:\n                self.cap.release()\n            except:\n                pass\n            self.cap = cv2.VideoCapture(video_path)\n            self.current_frame = -1\n            self.video_path = video_path\n\n        if not iterative:\n            self.cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)\n            self.current_frame = start_frame - 1\n\n        max_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))\n\n        for _ in range(return_frames):\n            success, frame = self.cap.read()\n\n            if not success:\n                self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)\n                success, frame = self.cap.read()\n                self.current_frame = -1\n\n            if success:\n                self.current_frame += 1\n                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n                frame = np.array(frame).astype(np.float32)\n                frame = pil2tensor(frame)  # Convert to torch tensor\n                frames.append(frame[0])\n            else:\n                break\n        if len(frames) <= 1:\n            frame = torch.stack(frames) if frames else None\n            return (frame, self.current_frame, max_frames)\n        else:\n            # Stack frames along a new dimension (simulate batch dimension)\n            frame = torch.stack(frames)\n\n        return (frame, self.current_frame, max_frames)\n\n    @classmethod\n    def IS_CHANGED(cls, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n    @classmethod\n    def VALIDATE_INPUTS(cls, video):\n        if not folder_paths.exists_annotated_filepath(video):\n            return \"Invalid video file: {}\".format(video)\n        return True\n\ntemp_dir = tempfile.mkdtemp()\nhash_object = hashlib.md5(temp_dir.encode())\nhex_dig = hash_object.hexdigest()\nendpoint = f\"/tmp/{hex_dig}/{{filename}}\"\n\n\n@server.PromptServer.instance.routes.get(endpoint)\nasync def serve_temp_file(request):\n    filename = request.match_info['filename']\n\n    if '..' in filename or filename.startswith('/'):\n        return web.Response(status=400, text=\"Invalid file path.\")\n\n    file_path = os.path.join(temp_dir, filename)\n\n    if os.path.isfile(file_path):\n        return web.FileResponse(file_path)\n    else:\n        return web.Response(status=404, text=\"File not found.\")\n\n# Dynamically add the route to the server's router\n# server.PromptServer.instance.routes.get(endpoint)(serve_temp_file)\n\nprint(f\"Endpoint {endpoint} registered for serving files from {temp_dir}\")\n\nclass DeforumVideoSaveNode:\n    def __init__(self):\n        self.output_dir = folder_paths.get_output_directory()\n        self.images = []\n        self.size = None\n        self.temp_dir = temp_dir\n        self.hex_dig = hex_dig\n        self.audio_path = None\n        self.filepath = \"\"\n\n    def clear_cache_directory(self):\n        for filename in os.listdir(self.temp_dir):\n            file_path = os.path.join(self.temp_dir, filename)\n            try:\n                if os.path.isfile(file_path) or os.path.islink(file_path):\n                    os.unlink(file_path)\n                elif os.path.isdir(file_path):\n                    shutil.rmtree(file_path)\n            except Exception as e:\n                print(f'Failed to delete {file_path}. Reason: {e}')\n\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"image\": (\"IMAGE\",),\n                     \"filename_prefix\": (\"STRING\",{\"default\":\"Deforum\"}),\n                     \"fps\": (\"INT\", {\"default\": 24, \"min\": 1, \"max\": 10000},),\n                     \"codec\": ([\"libx265\", \"libx264\", \"libvpx-vp9\", \"libaom-av1\", \"mpeg4\", \"libvpx\"],),\n                     \"pixel_format\": ([\"yuv420p\", \"yuv422p\", \"yuv444p\", \"yuvj420p\", \"yuvj422p\", \"yuvj444p\", \"rgb24\", \"rgba\", \"nv12\", \"nv21\"],),\n                     \"format\": ([\"mp4\", \"mov\", \"gif\", \"avi\"],),\n                     \"quality\": (\"INT\", {\"default\": 10, \"min\": 1, \"max\": 10},),\n                     \"dump_by\": ([\"max_frames\", \"per_N_frames\"],),\n                     \"dump_every\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 4096},),\n                     \"dump_now\": (\"BOOLEAN\", {\"default\": False},),\n                     \"skip_save\": (\"BOOLEAN\", {\"default\": False},),\n                     \"skip_return\": (\"BOOLEAN\", {\"default\": True},),\n                     \"enable_preview\": (\"BOOLEAN\", {\"default\": True},),\n                     \"restore\": (\"BOOLEAN\", {\"default\": False},),\n                     \"clear_cache\": (\"BOOLEAN\", {\"default\": False},),\n                     },\n                \"optional\": {\n                    \"deforum_frame_data\": (\"DEFORUM_FRAME_DATA\",),\n                    \"audio\": (\"AUDIO\",),\n                    \"waveform_image\": (\"IMAGE\",),\n                },\n                \"hidden\": {\n                    \"js_frames\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 9999999999},),\n                }\n\n                }\n\n    RETURN_TYPES = (\"IMAGE\",\"STRING\",)\n    RETURN_NAMES = (\"IMAGES\",\"VIDEOPATH\",)\n    OUTPUT_NODE = True\n\n    FUNCTION = \"fn\"\n    display_name = \"Save Video\"\n    CATEGORY = \"deforum/video\"\n    def add_image(self, image):\n        frame_path = os.path.join(self.temp_dir, f\"frame_{len(self.images):05d}.png\")\n        if isinstance(image, torch.Tensor):\n            tensor2pil(image).save(frame_path)\n        else:\n            im = cv2.cvtColor(image.astype(np.uint8), cv2.COLOR_BGR2RGB)\n            cv2.imwrite(frame_path, im)\n        self.images.append(frame_path)\n        # del image\n        return frame_path\n        # self.images.append(image)\n\n    def fn(self,\n           image,\n           filename_prefix,\n           fps,\n           codec,\n           pixel_format,\n           format,\n           quality,\n           dump_by,\n           dump_every,\n           dump_now,\n           skip_save,\n           skip_return,\n           enable_preview,\n           deforum_frame_data={},\n           audio=None,\n           waveform_image=None,\n           restore=False,\n           clear_cache=False):\n        new_images = []\n        base64_audio = \"\"\n        dump = False\n        ret = None\n        full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(\n            filename_prefix, self.output_dir)\n        counter = find_next_index(full_output_folder, filename_prefix, format)\n        anim_args = deforum_frame_data.get(\"anim_args\")\n        if image is not None:\n\n            if anim_args is not None:\n                max_frames = anim_args.max_frames\n            else:\n                max_frames = image.shape[0] + len(self.images) + 2\n            if not deforum_frame_data.get(\"reset\", None):\n                if len(image) > 1:\n                    for img in image:\n                        new_images.append(self.add_image(img))\n                else:\n                    new_images.append(self.add_image(image[0]))\n            print(f\"[deforum] Video Save node cached {len(self.images)} frames\")\n            # print(\"THE CAT IS FINE. SOMETHING WAS False THOUGH THE CAT IS NOT\")\n\n            # When the current frame index reaches the last frame, save the video\n\n            if dump_by == \"max_frames\":\n                dump = len(self.images) >= max_frames\n            else:\n                dump = len(self.images) >= dump_every\n\n            if deforum_frame_data.get(\"reset\", None):\n                dump = True\n                clear_cache = True\n            ret = None\n            if dump or dump_now:  # frame_idx is 0-based\n                if len(self.images) >= 2:\n                    if not skip_save:\n                        self.filepath = self.save_video(full_output_folder, filename, counter, fps, audio, codec, format)\n\n                    if not skip_return:\n                        ret = torch.stack([pil2tensor(i)[0] for i in self.images], dim=0)\n                if clear_cache:\n                    self.images = []\n                    # self.clear_cache_directory()\n                enable_preview = True\n            if deforum_frame_data.get(\"reset\", None):\n                if image.shape[0] > 1:\n                    for img in image:\n                        self.add_image(img)\n                else:\n                    self.add_image(image[0])\n        if enable_preview and image is not None:\n            # if audio is not None:\n            # Delete the previous temporary audio file if it exists\n            if self.audio_path and os.path.exists(os.path.join(self.temp_dir, self.audio_path)):\n                os.remove(os.path.join(self.temp_dir, self.audio_path))\n\n            self.audio_path = self.encode_audio_base64(audio, len(self.images), fps, 0)\n\n            ui_ret = {\"counter\":(len(self.images),),\n                      \"should_dump\":(clear_cache,),\n                      \"frames\":([f\"/tmp/{self.hex_dig}/{os.path.basename(frame_path)}\" for frame_path in self.images] if restore else [f\"/tmp/{self.hex_dig}/{os.path.basename(frame_path)}\" for frame_path in new_images]),\n                      \"fps\":(fps,),\n                      \"audio\":(f\"/tmp/{self.hex_dig}/{self.audio_path}\",)}\n            if waveform_image is not None:\n                ui_ret[\"waveform\"] = (tensor_to_webp_base64(waveform_image),)\n        else:\n            if anim_args is not None:\n                max_frames = anim_args.max_frames\n            else:\n                max_frames = len(self.images) + 5\n            if dump_by == \"max_frames\":\n                dump = len(self.images) >= max_frames\n            else:\n                dump = len(self.images) >= dump_every\n            if deforum_frame_data.get(\"reset\", None):\n                dump = True\n                dump_now = True\n                clear_cache = True\n            if dump or dump_now:  # frame_idx is 0-based\n                if len(self.images) >= 2:\n                    if not skip_save:\n                        self.filepath = self.save_video(full_output_folder, filename, counter, fps, audio, codec, format)\n                    if not skip_return:\n                        ret = torch.stack([pil2tensor(Image.open(i))[0] for i in self.images], dim=0)\n                if clear_cache:\n                    self.images = self.images = []\n                    # self.clear_cache_directory()\n            ui_ret = {\"counter\":(len(self.images),),\n                      \"should_dump\":(clear_cache,),\n                      \"frames\":([]),\n                      \"fps\":(fps,)}\n        del base64_audio, image\n        return {\"ui\": ui_ret, \"result\": (ret,self.filepath,)}\n\n    def encode_audio_base64(self, audio_data, frame_count, fps, start_frame):\n        sample_rate = 44100  # Assuming a default sample rate\n        if audio_data is None:\n            # Generate silent audio data for the specified duration\n            duration_in_seconds = frame_count / float(fps)\n            silence = np.zeros(int(duration_in_seconds * sample_rate), dtype=np.int16)\n            audio_data_reshaped = silence\n        else:\n            # Calculate start and end samples\n            start_sample = 0\n            end_sample = start_sample + int((frame_count / fps) * audio_data.sample_rate)\n\n            # Handle actual audio data\n            if audio_data.num_channels > 1:\n                # Ensure the audio data is properly reshaped for multi-channel data\n                audio_data_reshaped = audio_data.audio_data.reshape((-1, audio_data.num_channels))\n            else:\n                audio_data_reshaped = audio_data.audio_data\n\n            # Loop the audio if the end_sample exceeds the length of the audio data\n            total_samples = audio_data_reshaped.shape[0]\n            if end_sample > total_samples:\n                looped_audio_data = []\n                while end_sample > len(looped_audio_data):\n                    remaining_samples = end_sample - len(looped_audio_data)\n                    looped_audio_data.extend(audio_data_reshaped[:min(remaining_samples, total_samples)])\n                audio_data_reshaped = np.array(looped_audio_data)[:end_sample - start_sample]\n            else:\n                # Slice the audio data from start_sample to end_sample\n                audio_data_reshaped = audio_data_reshaped[start_sample:end_sample]\n\n\n        with tempfile.NamedTemporaryFile(delete=False, dir=self.temp_dir, suffix='.wav') as tmp_file:\n            temp_audio_path = tmp_file.name\n        from scipy.io.wavfile import write as wav_write\n\n        wav_write(temp_audio_path, 48000, audio_data_reshaped)\n\n        # Return the path relative to the temp directory for URL construction\n        return os.path.basename(temp_audio_path)\n\n    def save_video(self, full_output_folder, filename, counter, fps, audio, codec, ext):\n        output_path = os.path.join(full_output_folder, f\"{filename}_{counter}.{ext}\")\n\n        print(\"[deforum] Saving video:\", output_path)\n\n        # writer = imageio.get_writer(output_path, fps=fps, codec=codec, quality=quality, pixelformat=pixel_format, format=format)\n        # for frame in tqdm(self.images, desc=f\"Saving {format} (imageio)\"):\n        #     writer.append_data(np.clip(255. * frame.detach().cpu().numpy().squeeze(), 0, 255).astype(np.uint8))\n        # writer.close()\n        frames = [Image.open(frame_path) for frame_path in self.images]\n        video_clip = mp.ImageSequenceClip([np.array(frame) for frame in frames], fps=fps)\n\n        if audio is not None:\n            # Generate a temporary file for the audio\n            with tempfile.NamedTemporaryFile(delete=False, suffix='.wav') as tmp_audio_file:\n                save_to_file(audio, tmp_audio_file.name)\n                # Load the audio clip\n                audio_clip = mp.AudioFileClip(tmp_audio_file.name)\n                # Calculate video duration\n                video_duration = len(self.images) / fps\n                # Trim or loop the audio clip to match the video length\n                if audio_clip.duration > video_duration:\n                    audio_clip = audio_clip.subclip(0,\n                                                    video_duration)  # Trim the audio to match video length\n                elif audio_clip.duration < video_duration:\n                    # If you want to loop the audio, uncomment the following line\n                    # audio_clip = audio_clip.loop(duration=video_duration)\n                    pass  # If you prefer silence after the audio ends, do nothing\n                # Set the audio on the video clip\n                video_clip = video_clip.set_audio(audio_clip)\n            del tmp_audio_file\n\n        video_clip.write_videofile(output_path, codec=codec, audio_codec='aac')\n        return output_path\n\n    @classmethod\n    def IS_CHANGED(s, text, autorefresh):\n        # Force re-evaluation of the node\n        if autorefresh == \"Yes\":\n            return float(\"NaN\")\n\n\ndef encode_audio_base64(audio_data, frame_count, fps):\n    # Calculate the target duration of the audio in seconds based on the video duration\n    target_audio_duration = frame_count / fps\n\n    # Calculate the number of samples to keep based on the target duration and the sample rate\n    num_samples_to_keep = int(target_audio_duration * audio_data.sample_rate)\n\n    # Reshape the audio data based on the number of channels\n    if audio_data.num_channels > 1:\n        audio_data_reshaped = audio_data.audio_data.reshape((-1, audio_data.num_channels))\n    else:\n        audio_data_reshaped = audio_data.audio_data\n\n    # Trim or pad the audio data to match the target duration\n    actual_samples = audio_data_reshaped.shape[0]\n    if actual_samples > num_samples_to_keep:\n        # Trim the audio data if it's longer than the target duration\n        audio_data_reshaped = audio_data_reshaped[:num_samples_to_keep, ...]\n    elif actual_samples < num_samples_to_keep:\n        # Pad the audio data with zeros if it's shorter than the target duration\n        padding_length = num_samples_to_keep - actual_samples\n        if audio_data.num_channels > 1:\n            padding = np.zeros((padding_length, audio_data.num_channels), dtype=audio_data_reshaped.dtype)\n        else:\n            padding = np.zeros(padding_length, dtype=audio_data_reshaped.dtype)\n        audio_data_reshaped = np.vstack((audio_data_reshaped, padding))\n\n    # Convert the adjusted numpy array to bytes\n    output = BytesIO()\n    write(output, audio_data.sample_rate, audio_data_reshaped.astype(np.int16))\n\n    # Encode bytes to base64\n    base64_audio = base64.b64encode(output.getvalue()).decode('utf-8')\n    return base64_audio\ndef save_to_file(data, filepath: str):\n    # Ensure the audio data is reshaped properly for mono/stereo\n    if data.num_channels > 1:\n        audio_data_reshaped = data.audio_data.reshape((-1, data.num_channels))\n    else:\n        audio_data_reshaped = data.audio_data\n    write(filepath, data.sample_rate, audio_data_reshaped.astype(np.int16))\n    return True"
  },
  {
    "path": "deforum_nodes/nodes/redirect_console_node.py",
    "content": "import sys\nimport asyncio\n\nconsole_redirected = None\nstdout_backup = sys.stdout\nstderr_backup = sys.stderr\nclass StreamToWebSocket:\n    def __init__(self, original_stream, server, stream_type='stdout'):\n        self.original_stream = original_stream\n        self.server = server\n        self.stream_type = stream_type\n\n    def write(self, message):\n        # Write to the original stdout or stderr\n        self.original_stream.write(message)\n\n        # Asynchronously send to the frontend via WebSocket\n        if message.strip():  # Avoid sending empty messages\n            asyncio.run_coroutine_threadsafe(\n                self.server.send('console_output', {'message': message, 'stream': self.stream_type}),\n                self.server.loop\n            )\n\n    def flush(self):\n        self.original_stream.flush()\n\n    def __getattr__(self, attr):\n        # Delegate attribute access to the original stream\n        return getattr(self.original_stream, attr)\n\n\nclass DeforumRedirectConsole:\n\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\":\n                    {\"redirect_console\":  (\"BOOLEAN\", {\"default\": False},),}\n                }\n\n    RETURN_TYPES = (\"BOOLEAN\",)\n    OUTPUT_NODE = True\n\n    FUNCTION = \"fn\"\n    display_name = \"Redirect Console\"\n    CATEGORY = \"deforum/utils\"\n\n    def fn(self, redirect_console):\n        global console_redirected\n\n        if redirect_console:\n            if not console_redirected:\n                try:\n                    import server\n                    server_instance = server.PromptServer.instance\n                    sys.stdout = StreamToWebSocket(sys.stdout, server_instance, 'stdout')\n                    sys.stderr = StreamToWebSocket(sys.stderr, server_instance, 'stderr')\n                    console_redirected = True\n                except:\n                    pass\n            else:\n                sys.stdout = stdout_backup\n                sys.stderr = stderr_backup\n        else:\n            if console_redirected:\n                sys.stdout = stdout_backup\n                sys.stderr = stderr_backup\n                console_redirected = False\n        return (console_redirected,)\n\n\n"
  },
  {
    "path": "examples/deforum_base.json",
    "content": "{\n  \"last_node_id\": 186,\n  \"last_link_id\": 579,\n  \"nodes\": [\n    {\n      \"id\": 141,\n      \"type\": \"Reroute\",\n      \"pos\": {\n        \"0\": 532,\n        \"1\": 972\n      },\n      \"size\": [\n        75,\n        26\n      ],\n      \"flags\": {},\n      \"order\": 17,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"*\",\n          \"link\": 577\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            441,\n            443,\n            447,\n            459\n          ]\n        }\n      ],\n      \"properties\": {\n        \"showOutputText\": false,\n        \"horizontal\": false\n      }\n    },\n    {\n      \"id\": 130,\n      \"type\": \"DeforumKSampler\",\n      \"pos\": {\n        \"0\": 650,\n        \"1\": 120\n      },\n      \"size\": [\n        325.93902587890625,\n        326\n      ],\n      \"flags\": {},\n      \"order\": 18,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 533\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 579,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"positive\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 430\n        },\n        {\n          \"name\": \"negative\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 431,\n          \"slot_index\": 3\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 459\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            416\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumKSampler\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 88,\n      \"type\": \"DeforumLoadVideo\",\n      \"pos\": {\n        \"0\": 83,\n        \"1\": 12\n      },\n      \"size\": {\n        \"0\": 317.5899658203125,\n        \"1\": 294\n      },\n      \"flags\": {},\n      \"order\": 0,\n      \"mode\": 4,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            224,\n            453\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"FRAME_IDX\",\n          \"type\": \"INT\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"MAX_FRAMES\",\n          \"type\": \"INT\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumLoadVideo\"\n      },\n      \"widgets_values\": [\n        \"Recording 2024-02-08 214819.mp4\",\n        \"image\",\n        \"image\",\n        0,\n        1,\n        \"image\"\n      ]\n    },\n    {\n      \"id\": 62,\n      \"type\": \"PreviewImage\",\n      \"pos\": {\n        \"0\": 98,\n        \"1\": 388\n      },\n      \"size\": {\n        \"0\": 309.7149658203125,\n        \"1\": 251.35919189453125\n      },\n      \"flags\": {},\n      \"order\": 5,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 224,\n          \"slot_index\": 0\n        }\n      ],\n      \"outputs\": [],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 170,\n      \"type\": \"Note\",\n      \"pos\": {\n        \"0\": 484,\n        \"1\": -347\n      },\n      \"size\": {\n        \"0\": 633.6494140625,\n        \"1\": 221.03656005859375\n      },\n      \"flags\": {},\n      \"order\": 1,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [],\n      \"properties\": {\n        \"text\": \"\"\n      },\n      \"widgets_values\": [\n        \"Welcome to Deforum!\\n\\nThe below graph provides a pluggable Deforum Animation pipeline in ComfyUI. It works frame-by-frame, so the best practice is to enable Extra options, and Auto Queue.\\n\\nDeforum Parameter and Schedule nodes represent all settings available to consume after chaining up by the Deforum Iterator node, which keeps track of the current frame, generates/gets the cached latent to be denoised in the next pass. The parameters and schedule's are the same as in the auto1111 extension and in the Colab version.\\n\\nHybrid nodes are currently bypassed (purple), enabling them, and selecting a video transforms the pipeline into Deforum Hybrid. Iteration can be reset to frame 0 with the reset value being set to 1 on the iterator node. Don't forget to switch it back to 0 to use the generated image/latent.\\\"\\n\\nDeforum Video Save node dumps its collected frames when the current frame's id reaches/has passed max_frames.\"\n      ],\n      \"color\": \"#432\",\n      \"bgcolor\": \"#653\"\n    },\n    {\n      \"id\": 133,\n      \"type\": \"DeforumConditioningBlendNode\",\n      \"pos\": {\n        \"0\": 640,\n        \"1\": -40\n      },\n      \"size\": {\n        \"0\": 342.5999755859375,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 16,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"clip\",\n          \"type\": \"CLIP\",\n          \"link\": 428\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 576\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"POSITIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            430\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"NEGATIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            431\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumConditioningBlendNode\"\n      },\n      \"widgets_values\": [\n        \"linear\"\n      ]\n    },\n    {\n      \"id\": 175,\n      \"type\": \"DeforumDepthParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 460\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 7,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 557,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            558\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDepthParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"Midas-3-Hybrid\",\n        0.2,\n        \"border\",\n        \"bicubic\",\n        false\n      ]\n    },\n    {\n      \"id\": 176,\n      \"type\": \"DeforumTranslationParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 710\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 8,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 558,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            561\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumTranslationParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0)\",\n        \"0: (1.0025+0.002*sin(1.25*3.14*t/30))\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\"\n      ]\n    },\n    {\n      \"id\": 178,\n      \"type\": \"DeforumDiffusionParamsNode\",\n      \"pos\": {\n        \"0\": -290,\n        \"1\": 530\n      },\n      \"size\": {\n        \"0\": 278.891845703125,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 12,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 573,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            562\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDiffusionParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0: (0.065)\",\n        \"0: (0.48)\",\n        \"0: (1.0)\",\n        \"0: (5)\",\n        false,\n        \"0: (18)\",\n        false,\n        \"0:(0)\",\n        false,\n        \"0:(1)\"\n      ]\n    },\n    {\n      \"id\": 179,\n      \"type\": \"DeforumColorParamsNode\",\n      \"pos\": {\n        \"0\": -750,\n        \"1\": 1040\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 154\n      },\n      \"flags\": {},\n      \"order\": 9,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 561,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            564\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumColorParamsNode\"\n      },\n      \"widgets_values\": [\n        \"None\",\n        \"\",\n        1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 180,\n      \"type\": \"DeforumHybridScheduleNode\",\n      \"pos\": {\n        \"0\": -290,\n        \"1\": 850\n      },\n      \"size\": {\n        \"0\": 274.891845703125,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 13,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 562,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            559\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridScheduleNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0.5)\"\n      ]\n    },\n    {\n      \"id\": 182,\n      \"type\": \"DeforumCadenceParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 1260\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 10,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 564,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            572\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCadenceParamsNode\"\n      },\n      \"widgets_values\": [\n        0,\n        \"None\",\n        \"0: (1)\",\n        \"None\",\n        \"0: (1)\",\n        \"0\"\n      ]\n    },\n    {\n      \"id\": 177,\n      \"type\": \"DeforumNoiseParamsNode\",\n      \"pos\": {\n        \"0\": -290,\n        \"1\": 1080\n      },\n      \"size\": {\n        \"0\": 272.06170654296875,\n        \"1\": 298\n      },\n      \"flags\": {},\n      \"order\": 14,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 559,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            574\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumNoiseParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"0: (0.8)\",\n        \"0: (0.1)\",\n        \"0: (5)\",\n        \"0: (1.0)\",\n        \"0: (0.0)\",\n        \"perlin\",\n        8,\n        8,\n        4,\n        0.5\n      ]\n    },\n    {\n      \"id\": 16,\n      \"type\": \"PreviewImage\",\n      \"pos\": {\n        \"0\": 1120,\n        \"1\": 821\n      },\n      \"size\": {\n        \"0\": 587.271728515625,\n        \"1\": 617.682373046875\n      },\n      \"flags\": {},\n      \"order\": 20,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 462,\n          \"slot_index\": 0\n        }\n      ],\n      \"outputs\": [],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 183,\n      \"type\": \"DeforumPromptNode\",\n      \"pos\": {\n        \"0\": -754,\n        \"1\": -282\n      },\n      \"size\": {\n        \"0\": 335.66650390625,\n        \"1\": 417\n      },\n      \"flags\": {},\n      \"order\": 2,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            556\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumPromptNode\"\n      },\n      \"widgets_values\": [\n        \"0:\\\" tiny cute swamp bunny, highly detailed, intricate, ultra hd, sharp photo, crepuscular rays, in focus, 4k, landscape --neg nsfw, nude\\\",\\n30:\\\" anthropomorphic clean cat, surrounded by mandelbulb fractals, epic angle and pose, symmetrical, 3d, depth of field --neg nsfw, nude\\\",\\n60:\\\" a beautiful coconut --neg photo, realistic  nsfw, nude\\\",\\n90:\\\" a beautiful durian, amazing award winning photography --neg nsfw, nude\\\"\"\n      ]\n    },\n    {\n      \"id\": 174,\n      \"type\": \"DeforumAnimParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 210\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 6,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 556,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            557\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAnimParamsNode\"\n      },\n      \"widgets_values\": [\n        \"3D\",\n        120,\n        \"wrap\"\n      ]\n    },\n    {\n      \"id\": 127,\n      \"type\": \"CheckpointLoaderSimple\",\n      \"pos\": {\n        \"0\": 86,\n        \"1\": 743\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 3,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            533\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": [\n            428\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            417,\n            450\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CheckpointLoaderSimple\"\n      },\n      \"widgets_values\": [\n        \"realismEngineSDXL_v30VAE.safetensors\"\n      ]\n    },\n    {\n      \"id\": 137,\n      \"type\": \"VAEEncode\",\n      \"pos\": {\n        \"0\": 696,\n        \"1\": 1241\n      },\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 25,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"pixels\",\n          \"type\": \"IMAGE\",\n          \"link\": 489\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 450,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            438\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEEncode\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 139,\n      \"type\": \"DeforumAddNoiseNode\",\n      \"pos\": {\n        \"0\": 656,\n        \"1\": 1063\n      },\n      \"size\": {\n        \"0\": 324.6453552246094,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 24,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 488,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 443,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            489\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAddNoiseNode\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 138,\n      \"type\": \"DeforumFrameWarpNode\",\n      \"pos\": {\n        \"0\": 665,\n        \"1\": 888\n      },\n      \"size\": {\n        \"0\": 304.79998779296875,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 23,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 440\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 441\n        },\n        {\n          \"name\": \"depth_image\",\n          \"type\": \"IMAGE\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            488\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"DEPTH\",\n          \"type\": \"IMAGE\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"WARPED_DEPTH\",\n          \"type\": \"IMAGE\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumFrameWarpNode\"\n      },\n      \"widgets_values\": [\n        false\n      ]\n    },\n    {\n      \"id\": 142,\n      \"type\": \"DeforumHybridMotionNode\",\n      \"pos\": {\n        \"0\": 657,\n        \"1\": 676\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 21,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 469\n        },\n        {\n          \"name\": \"hybrid_image\",\n          \"type\": \"IMAGE\",\n          \"link\": 453\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 447\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            440\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridMotionNode\"\n      },\n      \"widgets_values\": [\n        \"RAFT\"\n      ]\n    },\n    {\n      \"id\": 131,\n      \"type\": \"VAEDecode\",\n      \"pos\": {\n        \"0\": 713,\n        \"1\": 545\n      },\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 19,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"samples\",\n          \"type\": \"LATENT\",\n          \"link\": 416\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 417,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            462,\n            469,\n            551\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEDecode\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 185,\n      \"type\": \"DeforumBaseParamsNode\",\n      \"pos\": {\n        \"0\": -309,\n        \"1\": 7\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 442\n      },\n      \"flags\": {},\n      \"order\": 11,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 572,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            573\n          ]\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumBaseParamsNode\"\n      },\n      \"widgets_values\": [\n        512,\n        512,\n        \"0: (-1)\",\n        \"random\",\n        \"euler\",\n        \"normal\",\n        true,\n        true,\n        false\n      ]\n    },\n    {\n      \"id\": 173,\n      \"type\": \"DeforumVideoSaveNode\",\n      \"pos\": {\n        \"0\": 1103,\n        \"1\": -54\n      },\n      \"size\": {\n        \"0\": 573,\n        \"1\": 686.5\n      },\n      \"flags\": {},\n      \"order\": 22,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 551\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 578,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"audio\",\n          \"type\": \"AUDIO\",\n          \"link\": null,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"waveform_image\",\n          \"type\": \"IMAGE\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGES\",\n          \"type\": \"IMAGE\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"VIDEOPATH\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumVideoSaveNode\"\n      },\n      \"widgets_values\": [\n        \"Deforum\",\n        24,\n        \"libx265\",\n        \"yuv420p\",\n        \"mp4\",\n        10,\n        \"max_frames\",\n        0,\n        false,\n        true,\n        true,\n        true,\n        false,\n        false,\n        {\n          \"hidden\": false,\n          \"paused\": false,\n          \"params\": {}\n        }\n      ]\n    },\n    {\n      \"id\": 135,\n      \"type\": \"DeforumGetCachedLatentNode\",\n      \"pos\": {\n        \"0\": -259,\n        \"1\": 1437\n      },\n      \"size\": {\n        \"0\": 235.1999969482422,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 4,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            575\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n      },\n      \"widgets_values\": [\n        0\n      ]\n    },\n    {\n      \"id\": 186,\n      \"type\": \"DeforumIteratorNode\",\n      \"pos\": {\n        \"0\": 67,\n        \"1\": 1041\n      },\n      \"size\": {\n        \"0\": 393,\n        \"1\": 310\n      },\n      \"flags\": {},\n      \"order\": 15,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 574\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 575,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"init_latent\",\n          \"type\": \"LATENT\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            576,\n            577,\n            578\n          ]\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            579\n          ]\n        },\n        {\n          \"name\": \"positive_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null\n        },\n        {\n          \"name\": \"negative_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumIteratorNode\"\n      },\n      \"widgets_values\": [\n        \"stable_diffusion\",\n        287373190076054,\n        \"randomize\",\n        0,\n        0.8,\n        0.1,\n        true,\n        true,\n        true\n      ]\n    },\n    {\n      \"id\": 136,\n      \"type\": \"DeforumCacheLatentNode\",\n      \"pos\": {\n        \"0\": 714,\n        \"1\": 1393\n      },\n      \"size\": {\n        \"0\": 210,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 26,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 438\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCacheLatentNode\"\n      },\n      \"widgets_values\": [\n        0\n      ]\n    }\n  ],\n  \"links\": [\n    [\n      224,\n      88,\n      0,\n      62,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      416,\n      130,\n      0,\n      131,\n      0,\n      \"LATENT\"\n    ],\n    [\n      417,\n      127,\n      2,\n      131,\n      1,\n      \"VAE\"\n    ],\n    [\n      428,\n      127,\n      1,\n      133,\n      0,\n      \"CLIP\"\n    ],\n    [\n      430,\n      133,\n      0,\n      130,\n      2,\n      \"CONDITIONING\"\n    ],\n    [\n      431,\n      133,\n      1,\n      130,\n      3,\n      \"CONDITIONING\"\n    ],\n    [\n      438,\n      137,\n      0,\n      136,\n      0,\n      \"LATENT\"\n    ],\n    [\n      440,\n      142,\n      0,\n      138,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      441,\n      141,\n      0,\n      138,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      443,\n      141,\n      0,\n      139,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      447,\n      141,\n      0,\n      142,\n      2,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      450,\n      127,\n      2,\n      137,\n      1,\n      \"VAE\"\n    ],\n    [\n      453,\n      88,\n      0,\n      142,\n      1,\n      \"IMAGE\"\n    ],\n    [\n      459,\n      141,\n      0,\n      130,\n      4,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      462,\n      131,\n      0,\n      16,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      469,\n      131,\n      0,\n      142,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      488,\n      138,\n      0,\n      139,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      489,\n      139,\n      0,\n      137,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      533,\n      127,\n      0,\n      130,\n      0,\n      \"MODEL\"\n    ],\n    [\n      551,\n      131,\n      0,\n      173,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      556,\n      183,\n      0,\n      174,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      557,\n      174,\n      0,\n      175,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      558,\n      175,\n      0,\n      176,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      559,\n      180,\n      0,\n      177,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      561,\n      176,\n      0,\n      179,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      562,\n      178,\n      0,\n      180,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      564,\n      179,\n      0,\n      182,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      572,\n      182,\n      0,\n      185,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      573,\n      185,\n      0,\n      178,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      574,\n      177,\n      0,\n      186,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      575,\n      135,\n      0,\n      186,\n      1,\n      \"LATENT\"\n    ],\n    [\n      576,\n      186,\n      0,\n      133,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      577,\n      186,\n      0,\n      141,\n      0,\n      \"*\"\n    ],\n    [\n      578,\n      186,\n      0,\n      173,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      579,\n      186,\n      1,\n      130,\n      1,\n      \"LATENT\"\n    ]\n  ],\n  \"groups\": [],\n  \"config\": {},\n  \"extra\": {\n    \"groupNodes\": {},\n    \"workspace_info\": {\n      \"id\": \"FNCJfZPX52ugkXlmp-lm_\"\n    },\n    \"ds\": {\n      \"scale\": 0.5445000000000005,\n      \"offset\": [\n        1153.9036380571622,\n        530.2484150930318\n      ]\n    }\n  },\n  \"version\": 0.4\n}"
  },
  {
    "path": "examples/deforum_cadence.json",
    "content": "{\n  \"last_node_id\": 191,\n  \"last_link_id\": 595,\n  \"nodes\": [\n    {\n      \"id\": 141,\n      \"type\": \"Reroute\",\n      \"pos\": {\n        \"0\": 532,\n        \"1\": 972\n      },\n      \"size\": [\n        75,\n        26\n      ],\n      \"flags\": {},\n      \"order\": 17,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"*\",\n          \"link\": 589\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            459\n          ]\n        }\n      ],\n      \"properties\": {\n        \"showOutputText\": false,\n        \"horizontal\": false\n      }\n    },\n    {\n      \"id\": 157,\n      \"type\": \"DeforumHybridScheduleNode\",\n      \"pos\": {\n        \"0\": -290,\n        \"1\": 950\n      },\n      \"size\": {\n        \"0\": 274.891845703125,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 13,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 540,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            541\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridScheduleNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0.5)\"\n      ]\n    },\n    {\n      \"id\": 170,\n      \"type\": \"Note\",\n      \"pos\": {\n        \"0\": 484,\n        \"1\": -347\n      },\n      \"size\": {\n        \"0\": 633.6494140625,\n        \"1\": 221.03656005859375\n      },\n      \"flags\": {},\n      \"order\": 0,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [],\n      \"properties\": {\n        \"text\": \"\"\n      },\n      \"widgets_values\": [\n        \"Welcome to Deforum!\\n\\nThe below graph provides a pluggable Deforum Animation pipeline in ComfyUI. It works frame-by-frame, so the best practice is to enable Extra options, and Auto Queue.\\n\\nDeforum Parameter and Schedule nodes represent all settings available to consume after chaining up by the Deforum Iterator node, which keeps track of the current frame, generates/gets the cached latent to be denoised in the next pass. The parameters and schedule's are the same as in the auto1111 extension and in the Colab version.\\n\\nHybrid nodes are currently bypassed (purple), enabling them, and selecting a video transforms the pipeline into Deforum Hybrid. Iteration can be reset to frame 0 with the reset value being set to 1 on the iterator node. Don't forget to switch it back to 0 to use the generated image/latent.\\\"\\n\\nDeforum Video Save node dumps its collected frames when the current frame's id reaches/has passed max_frames.\"\n      ],\n      \"color\": \"#432\",\n      \"bgcolor\": \"#653\"\n    },\n    {\n      \"id\": 153,\n      \"type\": \"DeforumNoiseParamsNode\",\n      \"pos\": {\n        \"0\": -290,\n        \"1\": 1180\n      },\n      \"size\": {\n        \"0\": 272.06170654296875,\n        \"1\": 298\n      },\n      \"flags\": {},\n      \"order\": 14,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 541,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            586\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumNoiseParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"0: (0.8)\",\n        \"0: (0.1)\",\n        \"0: (5)\",\n        \"0: (1.0)\",\n        \"0: (0.0)\",\n        \"perlin\",\n        8,\n        8,\n        4,\n        0.5\n      ]\n    },\n    {\n      \"id\": 131,\n      \"type\": \"VAEDecode\",\n      \"pos\": {\n        \"0\": 760,\n        \"1\": 1210\n      },\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 25,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"samples\",\n          \"type\": \"LATENT\",\n          \"link\": 416\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 417,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            556,\n            558,\n            566\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEDecode\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 155,\n      \"type\": \"DeforumColorParamsNode\",\n      \"pos\": {\n        \"0\": -750,\n        \"1\": 1140\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 154\n      },\n      \"flags\": {},\n      \"order\": 9,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 535,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            567\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumColorParamsNode\"\n      },\n      \"widgets_values\": [\n        \"None\",\n        \"\",\n        1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 176,\n      \"type\": \"DeforumFrameDataExtract\",\n      \"pos\": {\n        \"0\": 70,\n        \"1\": 1070\n      },\n      \"size\": {\n        \"0\": 443.4000244140625,\n        \"1\": 186\n      },\n      \"flags\": {},\n      \"order\": 19,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 592,\n          \"slot_index\": 0\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"frame_idx\",\n          \"type\": \"INT\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"seed\",\n          \"type\": \"INT\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"steps\",\n          \"type\": \"INT\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"cfg_scale\",\n          \"type\": \"FLOAT\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"sampler_name\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"scheduler_name\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"denoise\",\n          \"type\": \"FLOAT\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"subseed_strength\",\n          \"type\": \"FLOAT\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"first_run\",\n          \"type\": \"BOOLEAN\",\n          \"links\": [\n            555\n          ],\n          \"shape\": 3,\n          \"slot_index\": 8\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumFrameDataExtract\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 135,\n      \"type\": \"DeforumGetCachedLatentNode\",\n      \"pos\": {\n        \"0\": 162,\n        \"1\": 950\n      },\n      \"size\": {\n        \"0\": 235.1999969482422,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 1,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            587\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n      },\n      \"widgets_values\": [\n        0\n      ]\n    },\n    {\n      \"id\": 173,\n      \"type\": \"DeforumVideoSaveNode\",\n      \"pos\": {\n        \"0\": 1309,\n        \"1\": 10\n      },\n      \"size\": [\n        620,\n        1064\n      ],\n      \"flags\": {},\n      \"order\": 20,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 565\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 590,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"audio\",\n          \"type\": \"AUDIO\",\n          \"link\": null,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"waveform_image\",\n          \"type\": \"IMAGE\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGES\",\n          \"type\": \"IMAGE\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"VIDEOPATH\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumVideoSaveNode\"\n      },\n      \"widgets_values\": [\n        \"Deforum\",\n        24,\n        \"libx265\",\n        \"yuv420p\",\n        \"mp4\",\n        10,\n        \"max_frames\",\n        0,\n        false,\n        false,\n        true,\n        true,\n        false,\n        false,\n        {\n          \"hidden\": false,\n          \"paused\": false,\n          \"params\": {}\n        }\n      ]\n    },\n    {\n      \"id\": 186,\n      \"type\": \"DeforumPromptNode\",\n      \"pos\": {\n        \"0\": -738,\n        \"1\": -31\n      },\n      \"size\": {\n        \"0\": 313.04736328125,\n        \"1\": 276.55352783203125\n      },\n      \"flags\": {},\n      \"order\": 2,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            571\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumPromptNode\"\n      },\n      \"widgets_values\": [\n        \"0:\\\" tiny cute swamp bunny, highly detailed, intricate, ultra hd, sharp photo, crepuscular rays, in focus, 4k, landscape --neg nsfw, nude\\\",\\n30:\\\" anthropomorphic clean cat, surrounded by mandelbulb fractals, epic angle and pose, symmetrical, 3d, depth of field --neg nsfw, nude\\\",\\n60:\\\" a beautiful coconut --neg photo, realistic  nsfw, nude\\\",\\n90:\\\" a beautiful durian, amazing award winning photography --neg nsfw, nude\\\"\"\n      ]\n    },\n    {\n      \"id\": 185,\n      \"type\": \"DeforumCadenceParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 1360\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 10,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 567,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            575\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCadenceParamsNode\"\n      },\n      \"widgets_values\": [\n        12,\n        \"RAFT\",\n        \"0: (1)\",\n        \"None\",\n        \"0: (1)\",\n        \"0\"\n      ]\n    },\n    {\n      \"id\": 152,\n      \"type\": \"DeforumTranslationParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 810\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 8,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 498,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            535\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumTranslationParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0)\",\n        \"0: (1.0025+0.002*sin(1.25*3.14*t/30))\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\"\n      ]\n    },\n    {\n      \"id\": 187,\n      \"type\": \"DeforumAddNoiseNode\",\n      \"pos\": {\n        \"0\": 679,\n        \"1\": 547\n      },\n      \"size\": {\n        \"0\": 304.79998779296875,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 21,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 572\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 593,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            573\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAddNoiseNode\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 151,\n      \"type\": \"DeforumDepthParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 560\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 7,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 497,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            498\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDepthParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"Midas-3-Hybrid\",\n        0.2,\n        \"border\",\n        \"bicubic\",\n        false\n      ]\n    },\n    {\n      \"id\": 154,\n      \"type\": \"DeforumDiffusionParamsNode\",\n      \"pos\": {\n        \"0\": -290,\n        \"1\": 630\n      },\n      \"size\": {\n        \"0\": 278.891845703125,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 12,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 576,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            540\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDiffusionParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0: (0.065)\",\n        \"0: (0.48)\",\n        \"0: (1.0)\",\n        \"0: (5)\",\n        false,\n        \"0: (18)\",\n        false,\n        \"0:(0)\",\n        false,\n        \"0:(1)\"\n      ]\n    },\n    {\n      \"id\": 150,\n      \"type\": \"DeforumAnimParamsNode\",\n      \"pos\": {\n        \"0\": -740,\n        \"1\": 310\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 6,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 571,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            497\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAnimParamsNode\"\n      },\n      \"widgets_values\": [\n        \"3D\",\n        120,\n        \"wrap\"\n      ]\n    },\n    {\n      \"id\": 188,\n      \"type\": \"DeforumBaseParamsNode\",\n      \"pos\": {\n        \"0\": -300,\n        \"1\": 89\n      },\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 442\n      },\n      \"flags\": {},\n      \"order\": 11,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 575,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            576\n          ]\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumBaseParamsNode\"\n      },\n      \"widgets_values\": [\n        512,\n        512,\n        \"0: (-1)\",\n        \"random\",\n        \"euler\",\n        \"normal\",\n        true,\n        true,\n        false\n      ]\n    },\n    {\n      \"id\": 127,\n      \"type\": \"CheckpointLoaderSimple\",\n      \"pos\": {\n        \"0\": 120,\n        \"1\": 380\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 3,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            533\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": [\n            428\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            417,\n            562\n          ],\n          \"shape\": 3,\n          \"slot_index\": 2\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CheckpointLoaderSimple\"\n      },\n      \"widgets_values\": [\n        \"realismEngineSDXL_v30VAE.safetensors\"\n      ]\n    },\n    {\n      \"id\": 178,\n      \"type\": \"DeforumCacheImageNode\",\n      \"pos\": {\n        \"0\": 968,\n        \"1\": 1801\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 27,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 558\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": null,\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCacheImageNode\"\n      },\n      \"widgets_values\": [\n        1\n      ]\n    },\n    {\n      \"id\": 175,\n      \"type\": \"DeforumImageSwitcherNode\",\n      \"pos\": {\n        \"0\": 573,\n        \"1\": 1550\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 26,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image_true\",\n          \"type\": \"IMAGE\",\n          \"link\": 556,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"image_false\",\n          \"type\": \"IMAGE\",\n          \"link\": null,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"option\",\n          \"type\": \"BOOLEAN\",\n          \"link\": 555,\n          \"widget\": {\n            \"name\": \"option\"\n          },\n          \"slot_index\": 2\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            557\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumImageSwitcherNode\"\n      },\n      \"widgets_values\": [\n        false\n      ]\n    },\n    {\n      \"id\": 182,\n      \"type\": \"DeforumGetCachedImageNode\",\n      \"pos\": {\n        \"0\": 813,\n        \"1\": 162\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 4,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            564\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"MASK\",\n          \"type\": \"MASK\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumGetCachedImageNode\"\n      },\n      \"widgets_values\": [\n        1\n      ]\n    },\n    {\n      \"id\": 181,\n      \"type\": \"DeforumGetCachedImageNode\",\n      \"pos\": {\n        \"0\": 386,\n        \"1\": 148\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 5,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            563\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"MASK\",\n          \"type\": \"MASK\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumGetCachedImageNode\"\n      },\n      \"widgets_values\": [\n        0\n      ]\n    },\n    {\n      \"id\": 179,\n      \"type\": \"DeforumCadenceNode\",\n      \"pos\": {\n        \"0\": 705,\n        \"1\": 349\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 142\n      },\n      \"flags\": {},\n      \"order\": 18,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 564\n        },\n        {\n          \"name\": \"first_image\",\n          \"type\": \"IMAGE\",\n          \"link\": 563\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 591\n        },\n        {\n          \"name\": \"hybrid_images\",\n          \"type\": \"IMAGE\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            565\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            572\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCadenceNode\"\n      },\n      \"widgets_values\": [\n        1,\n        false\n      ]\n    },\n    {\n      \"id\": 177,\n      \"type\": \"DeforumCacheImageNode\",\n      \"pos\": {\n        \"0\": 409,\n        \"1\": 1802\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 29,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 557\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCacheImageNode\"\n      },\n      \"widgets_values\": [\n        0\n      ]\n    },\n    {\n      \"id\": 130,\n      \"type\": \"DeforumKSampler\",\n      \"pos\": {\n        \"0\": 709,\n        \"1\": 1118\n      },\n      \"size\": [\n        325.93902587890625,\n        326\n      ],\n      \"flags\": {},\n      \"order\": 23,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 533\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 561,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"positive\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 430\n        },\n        {\n          \"name\": \"negative\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 431,\n          \"slot_index\": 3\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 459\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            416\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumKSampler\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 133,\n      \"type\": \"DeforumConditioningBlendNode\",\n      \"pos\": {\n        \"0\": 700,\n        \"1\": 976\n      },\n      \"size\": {\n        \"0\": 342.5999755859375,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 16,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"clip\",\n          \"type\": \"CLIP\",\n          \"link\": 428\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 588\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"POSITIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            430\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"NEGATIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            431\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumConditioningBlendNode\"\n      },\n      \"widgets_values\": [\n        \"linear\"\n      ]\n    },\n    {\n      \"id\": 191,\n      \"type\": \"DeforumCacheLatentNode\",\n      \"pos\": {\n        \"0\": 698,\n        \"1\": 826\n      },\n      \"size\": {\n        \"0\": 315,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 24,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 595\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": null\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCacheLatentNode\"\n      },\n      \"widgets_values\": [\n        0\n      ]\n    },\n    {\n      \"id\": 180,\n      \"type\": \"DeforumVAEEncode\",\n      \"pos\": {\n        \"0\": 739,\n        \"1\": 675\n      },\n      \"size\": {\n        \"0\": 218.39999389648438,\n        \"1\": 66\n      },\n      \"flags\": {},\n      \"order\": 22,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 562\n        },\n        {\n          \"name\": \"pixels\",\n          \"type\": \"IMAGE\",\n          \"link\": 573,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 594,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            561,\n            595\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumVAEEncode\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 183,\n      \"type\": \"PreviewImage\",\n      \"pos\": {\n        \"0\": 1309,\n        \"1\": 1169\n      },\n      \"size\": {\n        \"0\": 614.161865234375,\n        \"1\": 543.7197265625\n      },\n      \"flags\": {},\n      \"order\": 28,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 566\n        }\n      ],\n      \"outputs\": [],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      },\n      \"widgets_values\": []\n    },\n    {\n      \"id\": 190,\n      \"type\": \"DeforumIteratorNode\",\n      \"pos\": {\n        \"0\": 80,\n        \"1\": 560\n      },\n      \"size\": {\n        \"0\": 393,\n        \"1\": 310\n      },\n      \"flags\": {},\n      \"order\": 15,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 586\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 587,\n          \"shape\": 7\n        },\n        {\n          \"name\": \"init_latent\",\n          \"type\": \"LATENT\",\n          \"link\": null,\n          \"shape\": 7\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            588,\n            589,\n            590,\n            591,\n            592,\n            593\n          ]\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            594\n          ]\n        },\n        {\n          \"name\": \"positive_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null\n        },\n        {\n          \"name\": \"negative_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumIteratorNode\"\n      },\n      \"widgets_values\": [\n        \"stable_diffusion\",\n        478403467058392,\n        \"randomize\",\n        0,\n        0.8,\n        0.1,\n        true,\n        true,\n        true\n      ]\n    }\n  ],\n  \"links\": [\n    [\n      416,\n      130,\n      0,\n      131,\n      0,\n      \"LATENT\"\n    ],\n    [\n      417,\n      127,\n      2,\n      131,\n      1,\n      \"VAE\"\n    ],\n    [\n      428,\n      127,\n      1,\n      133,\n      0,\n      \"CLIP\"\n    ],\n    [\n      430,\n      133,\n      0,\n      130,\n      2,\n      \"CONDITIONING\"\n    ],\n    [\n      431,\n      133,\n      1,\n      130,\n      3,\n      \"CONDITIONING\"\n    ],\n    [\n      459,\n      141,\n      0,\n      130,\n      4,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      497,\n      150,\n      0,\n      151,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      498,\n      151,\n      0,\n      152,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      533,\n      127,\n      0,\n      130,\n      0,\n      \"MODEL\"\n    ],\n    [\n      535,\n      152,\n      0,\n      155,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      540,\n      154,\n      0,\n      157,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      541,\n      157,\n      0,\n      153,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      555,\n      176,\n      8,\n      175,\n      2,\n      \"BOOLEAN\"\n    ],\n    [\n      556,\n      131,\n      0,\n      175,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      557,\n      175,\n      0,\n      177,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      558,\n      131,\n      0,\n      178,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      561,\n      180,\n      0,\n      130,\n      1,\n      \"LATENT\"\n    ],\n    [\n      562,\n      127,\n      2,\n      180,\n      0,\n      \"VAE\"\n    ],\n    [\n      563,\n      181,\n      0,\n      179,\n      1,\n      \"IMAGE\"\n    ],\n    [\n      564,\n      182,\n      0,\n      179,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      565,\n      179,\n      0,\n      173,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      566,\n      131,\n      0,\n      183,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      567,\n      155,\n      0,\n      185,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      571,\n      186,\n      0,\n      150,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      572,\n      179,\n      1,\n      187,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      573,\n      187,\n      0,\n      180,\n      1,\n      \"IMAGE\"\n    ],\n    [\n      575,\n      185,\n      0,\n      188,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      576,\n      188,\n      0,\n      154,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      586,\n      153,\n      0,\n      190,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      587,\n      135,\n      0,\n      190,\n      1,\n      \"LATENT\"\n    ],\n    [\n      588,\n      190,\n      0,\n      133,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      589,\n      190,\n      0,\n      141,\n      0,\n      \"*\"\n    ],\n    [\n      590,\n      190,\n      0,\n      173,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      591,\n      190,\n      0,\n      179,\n      2,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      592,\n      190,\n      0,\n      176,\n      0,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      593,\n      190,\n      0,\n      187,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      594,\n      190,\n      1,\n      180,\n      2,\n      \"LATENT\"\n    ],\n    [\n      595,\n      180,\n      0,\n      191,\n      0,\n      \"LATENT\"\n    ]\n  ],\n  \"groups\": [],\n  \"config\": {},\n  \"extra\": {\n    \"groupNodes\": {},\n    \"workspace_info\": {\n      \"id\": \"6fH6Rsqch6zpTq8oT6FAF\"\n    },\n    \"ds\": {\n      \"scale\": 0.5989500000000006,\n      \"offset\": [\n        868.0037162140221,\n        159.90042862989674\n      ]\n    }\n  },\n  \"version\": 0.4\n}"
  },
  {
    "path": "examples/deforum_integrated.json",
    "content": "{\n  \"last_node_id\": 6,\n  \"last_link_id\": 7,\n  \"nodes\": [\n    {\n      \"id\": 5,\n      \"type\": \"DeforumPromptNode\",\n      \"pos\": [\n        1277,\n        213\n      ],\n      \"size\": {\n        \"0\": 400,\n        \"1\": 200\n      },\n      \"flags\": {},\n      \"order\": 3,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 5\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            6\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumPromptNode\"\n      },\n      \"widgets_values\": [\n        \"0:'Cat Sushi'\"\n      ]\n    },\n    {\n      \"id\": 1,\n      \"type\": \"DeforumSingleSampleNode\",\n      \"pos\": [\n        1841,\n        253\n      ],\n      \"size\": [\n        229.20001220703125,\n        306\n      ],\n      \"flags\": {},\n      \"order\": 4,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 6\n        },\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 1\n        },\n        {\n          \"name\": \"clip\",\n          \"type\": \"CLIP\",\n          \"link\": 2\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 3\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            7\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumSingleSampleNode\"\n      }\n    },\n    {\n      \"id\": 2,\n      \"type\": \"CheckpointLoaderSimple\",\n      \"pos\": [\n        1348,\n        482\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 0,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            1\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": [\n            2\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            3\n          ],\n          \"shape\": 3,\n          \"slot_index\": 2\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CheckpointLoaderSimple\"\n      },\n      \"widgets_values\": [\n        \"protovisionXLHighFidelity3D_releaseV660Bakedvae.safetensors\"\n      ]\n    },\n    {\n      \"id\": 4,\n      \"type\": \"DeforumAnimParamsNode\",\n      \"pos\": [\n        898,\n        214\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 106\n      },\n      \"flags\": {},\n      \"order\": 2,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 4\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            5\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAnimParamsNode\"\n      },\n      \"widgets_values\": [\n        \"None\",\n        16,\n        \"wrap\"\n      ]\n    },\n    {\n      \"id\": 3,\n      \"type\": \"DeforumBaseParamsNode\",\n      \"pos\": [\n        526,\n        214\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 250\n      },\n      \"flags\": {},\n      \"order\": 1,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": null\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            4\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumBaseParamsNode\"\n      },\n      \"widgets_values\": [\n        512,\n        512,\n        \"0: (-1)\",\n        \"random\",\n        \"euler\",\n        \"normal\",\n        true,\n        true,\n        false\n      ]\n    },\n    {\n      \"id\": 6,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        2158,\n        223\n      ],\n      \"size\": [\n        210,\n        246\n      ],\n      \"flags\": {},\n      \"order\": 5,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 7\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    }\n  ],\n  \"links\": [\n    [\n      1,\n      2,\n      0,\n      1,\n      1,\n      \"MODEL\"\n    ],\n    [\n      2,\n      2,\n      1,\n      1,\n      2,\n      \"CLIP\"\n    ],\n    [\n      3,\n      2,\n      2,\n      1,\n      3,\n      \"VAE\"\n    ],\n    [\n      4,\n      3,\n      0,\n      4,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      5,\n      4,\n      0,\n      5,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      6,\n      5,\n      0,\n      1,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      7,\n      1,\n      0,\n      6,\n      0,\n      \"IMAGE\"\n    ]\n  ],\n  \"groups\": [],\n  \"config\": {},\n  \"extra\": {\n    \"ds\": {\n      \"scale\": 0.9090909090909091,\n      \"offset\": [\n        -231.98330528838665,\n        480.9216297942885\n      ]\n    }\n  },\n  \"version\": 0.4\n}"
  },
  {
    "path": "examples/deforum_ip_adapter.json",
    "content": "{\n  \"last_node_id\": 176,\n  \"last_link_id\": 558,\n  \"nodes\": [\n    {\n      \"id\": 141,\n      \"type\": \"Reroute\",\n      \"pos\": [\n        532,\n        972\n      ],\n      \"size\": [\n        75,\n        26\n      ],\n      \"flags\": {},\n      \"order\": 19,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"*\",\n          \"link\": 554\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            441,\n            443,\n            447,\n            459\n          ]\n        }\n      ],\n      \"properties\": {\n        \"showOutputText\": false,\n        \"horizontal\": false\n      }\n    },\n    {\n      \"id\": 157,\n      \"type\": \"DeforumHybridScheduleNode\",\n      \"pos\": [\n        -300,\n        550\n      ],\n      \"size\": {\n        \"0\": 274.891845703125,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 15,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 540\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            541\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridScheduleNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(1)\",\n        \"0:(100)\",\n        \"0:(0)\",\n        \"0:(0.8)\"\n      ]\n    },\n    {\n      \"id\": 155,\n      \"type\": \"DeforumColorParamsNode\",\n      \"pos\": [\n        -660,\n        910\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 154\n      },\n      \"flags\": {},\n      \"order\": 12,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 535\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            548\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumColorParamsNode\"\n      },\n      \"widgets_values\": [\n        \"Image\",\n        \"\",\n        1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 126,\n      \"type\": \"DeforumPromptNode\",\n      \"pos\": [\n        -650,\n        -270\n      ],\n      \"size\": {\n        \"0\": 313.04736328125,\n        \"1\": 276.55352783203125\n      },\n      \"flags\": {},\n      \"order\": 0,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": null\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            537\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumPromptNode\"\n      },\n      \"widgets_values\": [\n        \"0:'the deathstar colliding with a planet in space, explosion'\\n100:'dart vader in the desert, DUNE GIANT WORM, by moebius'\"\n      ]\n    },\n    {\n      \"id\": 150,\n      \"type\": \"DeforumAnimParamsNode\",\n      \"pos\": [\n        -660,\n        70\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 7,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 537\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            497\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAnimParamsNode\"\n      },\n      \"widgets_values\": [\n        \"3D\",\n        100,\n        \"wrap\",\n        false,\n        \"20230129210106\",\n        false\n      ]\n    },\n    {\n      \"id\": 151,\n      \"type\": \"DeforumDepthParamsNode\",\n      \"pos\": [\n        -660,\n        320\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 10,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 497\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            498\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDepthParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"Zoe\",\n        0.2,\n        \"border\",\n        \"bicubic\",\n        false\n      ]\n    },\n    {\n      \"id\": 152,\n      \"type\": \"DeforumTranslationParamsNode\",\n      \"pos\": [\n        -660,\n        570\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 11,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 498\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            535\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumTranslationParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0)\",\n        \"0:(1.0)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\"\n      ]\n    },\n    {\n      \"id\": 130,\n      \"type\": \"DeforumKSampler\",\n      \"pos\": [\n        650,\n        120\n      ],\n      \"size\": {\n        \"0\": 325.93902587890625,\n        \"1\": 106\n      },\n      \"flags\": {},\n      \"order\": 20,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 547\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 555,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"positive\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 430\n        },\n        {\n          \"name\": \"negative\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 431,\n          \"slot_index\": 3\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 459\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            416\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumKSampler\"\n      }\n    },\n    {\n      \"id\": 131,\n      \"type\": \"VAEDecode\",\n      \"pos\": [\n        690,\n        310\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 21,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"samples\",\n          \"type\": \"LATENT\",\n          \"link\": 416\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 417,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            462,\n            469,\n            475\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEDecode\"\n      }\n    },\n    {\n      \"id\": 62,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        98,\n        388\n      ],\n      \"size\": {\n        \"0\": 309.7149658203125,\n        \"1\": 251.35919189453125\n      },\n      \"flags\": {},\n      \"order\": 8,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 556,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 142,\n      \"type\": \"DeforumHybridMotionNode\",\n      \"pos\": [\n        639,\n        428\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 23,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 469\n        },\n        {\n          \"name\": \"hybrid_image\",\n          \"type\": \"IMAGE\",\n          \"link\": 557\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 447\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            440,\n            460\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridMotionNode\"\n      },\n      \"widgets_values\": [\n        \"RAFT\"\n      ]\n    },\n    {\n      \"id\": 47,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        989,\n        391\n      ],\n      \"size\": {\n        \"0\": 192.6160888671875,\n        \"1\": 169.56472778320312\n      },\n      \"flags\": {},\n      \"order\": 26,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 460\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 48,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        992,\n        607\n      ],\n      \"size\": [\n        188.46986389160156,\n        246\n      ],\n      \"flags\": {},\n      \"order\": 27,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 455\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 50,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        995,\n        817\n      ],\n      \"size\": [\n        189.73870849609375,\n        246\n      ],\n      \"flags\": {},\n      \"order\": 29,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 456,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 138,\n      \"type\": \"DeforumFrameWarpNode\",\n      \"pos\": [\n        660,\n        668\n      ],\n      \"size\": {\n        \"0\": 304.79998779296875,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 25,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 440\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 441\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            455,\n            488\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumFrameWarpNode\"\n      }\n    },\n    {\n      \"id\": 139,\n      \"type\": \"DeforumAddNoiseNode\",\n      \"pos\": [\n        642,\n        875\n      ],\n      \"size\": {\n        \"0\": 324.6453552246094,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 28,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 488,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 443,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            456,\n            489\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAddNoiseNode\"\n      }\n    },\n    {\n      \"id\": 137,\n      \"type\": \"VAEEncode\",\n      \"pos\": [\n        691,\n        1001\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 30,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"pixels\",\n          \"type\": \"IMAGE\",\n          \"link\": 489\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 450,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            438\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEEncode\"\n      }\n    },\n    {\n      \"id\": 136,\n      \"type\": \"DeforumCacheLatentNode\",\n      \"pos\": [\n        681,\n        1134\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 31,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 438\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCacheLatentNode\"\n      }\n    },\n    {\n      \"id\": 170,\n      \"type\": \"Note\",\n      \"pos\": [\n        474,\n        -603\n      ],\n      \"size\": {\n        \"0\": 633.6494140625,\n        \"1\": 221.03656005859375\n      },\n      \"flags\": {},\n      \"order\": 1,\n      \"mode\": 0,\n      \"properties\": {\n        \"text\": \"\"\n      },\n      \"widgets_values\": [\n        \"Welcome to Deforum!\\n\\nThe below graph provides a pluggable Deforum Animation pipeline in ComfyUI. It works frame-by-frame, so the best practice is to enable Extra options, and Auto Queue.\\n\\nDeforum Parameter and Schedule nodes represent all settings available to consume after chaining up by the Deforum Iterator node, which keeps track of the current frame, generates/gets the cached latent to be denoised in the next pass. The parameters and schedule's are the same as in the auto1111 extension and in the Colab version.\\n\\nHybrid nodes are currently bypassed (purple), enabling them, and selecting a video transforms the pipeline into Deforum Hybrid. Iteration can be reset to frame 0 with the reset value being set to 1 on the iterator node. Don't forget to switch it back to 0 to use the generated image/latent.\\\"\\n\\nDeforum Video Save node dumps its collected frames when the current frame's id reaches/has passed max_frames.\"\n      ],\n      \"color\": \"#432\",\n      \"bgcolor\": \"#653\"\n    },\n    {\n      \"id\": 133,\n      \"type\": \"DeforumConditioningBlendNode\",\n      \"pos\": [\n        637,\n        -334\n      ],\n      \"size\": {\n        \"0\": 342.5999755859375,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 18,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"clip\",\n          \"type\": \"CLIP\",\n          \"link\": 428\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 553\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"POSITIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            430\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"NEGATIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            431\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumConditioningBlendNode\"\n      },\n      \"widgets_values\": [\n        \"gaussian\"\n      ]\n    },\n    {\n      \"id\": 127,\n      \"type\": \"CheckpointLoaderSimple\",\n      \"pos\": [\n        91,\n        743\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 2,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            546\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": [\n            428\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            417,\n            450\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CheckpointLoaderSimple\"\n      },\n      \"widgets_values\": [\n        \"protovisionXLHighFidelity3D_beta0520Bakedvae.safetensors\"\n      ]\n    },\n    {\n      \"id\": 173,\n      \"type\": \"IPAdapterApply\",\n      \"pos\": [\n        695,\n        -199\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 258\n      },\n      \"flags\": {},\n      \"order\": 9,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"ipadapter\",\n          \"type\": \"IPADAPTER\",\n          \"link\": 543\n        },\n        {\n          \"name\": \"clip_vision\",\n          \"type\": \"CLIP_VISION\",\n          \"link\": 544\n        },\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 558\n        },\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 546\n        },\n        {\n          \"name\": \"attn_mask\",\n          \"type\": \"MASK\",\n          \"link\": null\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            547\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"IPAdapterApply\"\n      },\n      \"widgets_values\": [\n        0.8,\n        0,\n        \"original\",\n        0,\n        1,\n        false\n      ]\n    },\n    {\n      \"id\": 172,\n      \"type\": \"CLIPVisionLoader\",\n      \"pos\": [\n        103,\n        -115\n      ],\n      \"size\": {\n        \"0\": 300,\n        \"1\": 60\n      },\n      \"flags\": {},\n      \"order\": 3,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"CLIP_VISION\",\n          \"type\": \"CLIP_VISION\",\n          \"links\": [\n            544\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CLIPVisionLoader\"\n      },\n      \"widgets_values\": [\n        \"ipadapter_image_encoder_15.safetensors\"\n      ]\n    },\n    {\n      \"id\": 153,\n      \"type\": \"DeforumNoiseParamsNode\",\n      \"pos\": [\n        -300,\n        810\n      ],\n      \"size\": {\n        \"0\": 272.06170654296875,\n        \"1\": 298\n      },\n      \"flags\": {},\n      \"order\": 16,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 541\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            550\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumNoiseParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"0: (0.8)\",\n        \"0: (0.1)\",\n        \"0: (5)\",\n        \"0: (1.0)\",\n        \"0: (0.0)\",\n        \"perlin\",\n        8,\n        8,\n        4,\n        0.5\n      ]\n    },\n    {\n      \"id\": 16,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1270,\n        -263\n      ],\n      \"size\": {\n        \"0\": 894.2135009765625,\n        \"1\": 941.8845825195312\n      },\n      \"flags\": {},\n      \"order\": 22,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 462,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 102,\n      \"type\": \"DeforumVideoSaveNode\",\n      \"pos\": [\n        1524,\n        804\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 150\n      },\n      \"flags\": {},\n      \"order\": 24,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 475\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 552\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumVideoSaveNode\"\n      },\n      \"widgets_values\": [\n        \"deforum_\",\n        12,\n        \"max_frames\",\n        0\n      ]\n    },\n    {\n      \"id\": 174,\n      \"type\": \"DeforumBaseParamsNode\",\n      \"pos\": [\n        -318,\n        -265\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 442\n      },\n      \"flags\": {},\n      \"order\": 13,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 548\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            549\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumBaseParamsNode\"\n      },\n      \"widgets_values\": [\n        1024,\n        1024,\n        \"euler\",\n        \"normal\",\n        25,\n        7,\n        1,\n        \"Deforum_{timestring}\",\n        \"fixed\",\n        1,\n        \"output/deforum\",\n        0.8,\n        true,\n        false,\n        false,\n        true,\n        false\n      ]\n    },\n    {\n      \"id\": 175,\n      \"type\": \"DeforumIteratorNode\",\n      \"pos\": [\n        55,\n        914\n      ],\n      \"size\": {\n        \"0\": 393,\n        \"1\": 286\n      },\n      \"flags\": {},\n      \"order\": 17,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 550\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 551\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            552,\n            553,\n            554\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            555\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"positive_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"negative_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumIteratorNode\"\n      },\n      \"widgets_values\": [\n        \"stable_diffusion\",\n        791126192911641,\n        \"randomize\",\n        0,\n        0.8,\n        0.1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 135,\n      \"type\": \"DeforumGetCachedLatentNode\",\n      \"pos\": [\n        132,\n        1262\n      ],\n      \"size\": {\n        \"0\": 218.39999389648438,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 4,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            551\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n      }\n    },\n    {\n      \"id\": 176,\n      \"type\": \"DeforumLoadVideo\",\n      \"pos\": [\n        94,\n        17\n      ],\n      \"size\": [\n        315,\n        294\n      ],\n      \"flags\": {},\n      \"order\": 5,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            556,\n            557,\n            558\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumLoadVideo\"\n      },\n      \"widgets_values\": [\n        \"2000_FILM (1).mp4\",\n        \"image\"\n      ]\n    },\n    {\n      \"id\": 154,\n      \"type\": \"DeforumDiffusionParamsNode\",\n      \"pos\": [\n        -300,\n        230\n      ],\n      \"size\": {\n        \"0\": 278.891845703125,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 14,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 549\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            540\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDiffusionParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0: (0.03)\",\n        \"0: (0.3)\",\n        \"0: (1.0)\",\n        \"0: (5)\",\n        false,\n        \"0: (25)\",\n        false,\n        \"0:(0)\",\n        false,\n        \"0:(1)\"\n      ]\n    },\n    {\n      \"id\": 171,\n      \"type\": \"IPAdapterModelLoader\",\n      \"pos\": [\n        105,\n        -243\n      ],\n      \"size\": {\n        \"0\": 300,\n        \"1\": 60\n      },\n      \"flags\": {},\n      \"order\": 6,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"IPADAPTER\",\n          \"type\": \"IPADAPTER\",\n          \"links\": [\n            543\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"IPAdapterModelLoader\"\n      },\n      \"widgets_values\": [\n        \"ip-adapter_sdxl_vit-h.safetensors\"\n      ]\n    }\n  ],\n  \"links\": [\n    [\n      416,\n      130,\n      0,\n      131,\n      0,\n      \"LATENT\"\n    ],\n    [\n      417,\n      127,\n      2,\n      131,\n      1,\n      \"VAE\"\n    ],\n    [\n      428,\n      127,\n      1,\n      133,\n      0,\n      \"CLIP\"\n    ],\n    [\n      430,\n      133,\n      0,\n      130,\n      2,\n      \"CONDITIONING\"\n    ],\n    [\n      431,\n      133,\n      1,\n      130,\n      3,\n      \"CONDITIONING\"\n    ],\n    [\n      438,\n      137,\n      0,\n      136,\n      0,\n      \"LATENT\"\n    ],\n    [\n      440,\n      142,\n      0,\n      138,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      441,\n      141,\n      0,\n      138,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      443,\n      141,\n      0,\n      139,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      447,\n      141,\n      0,\n      142,\n      2,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      450,\n      127,\n      2,\n      137,\n      1,\n      \"VAE\"\n    ],\n    [\n      455,\n      138,\n      0,\n      48,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      456,\n      139,\n      0,\n      50,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      459,\n      141,\n      0,\n      130,\n      4,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      460,\n      142,\n      0,\n      47,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      462,\n      131,\n      0,\n      16,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      469,\n      131,\n      0,\n      142,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      475,\n      131,\n      0,\n      102,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      488,\n      138,\n      0,\n      139,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      489,\n      139,\n      0,\n      137,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      497,\n      150,\n      0,\n      151,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      498,\n      151,\n      0,\n      152,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      535,\n      152,\n      0,\n      155,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      537,\n      126,\n      0,\n      150,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      540,\n      154,\n      0,\n      157,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      541,\n      157,\n      0,\n      153,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      543,\n      171,\n      0,\n      173,\n      0,\n      \"IPADAPTER\"\n    ],\n    [\n      544,\n      172,\n      0,\n      173,\n      1,\n      \"CLIP_VISION\"\n    ],\n    [\n      546,\n      127,\n      0,\n      173,\n      3,\n      \"MODEL\"\n    ],\n    [\n      547,\n      173,\n      0,\n      130,\n      0,\n      \"MODEL\"\n    ],\n    [\n      548,\n      155,\n      0,\n      174,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      549,\n      174,\n      0,\n      154,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      550,\n      153,\n      0,\n      175,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      551,\n      135,\n      0,\n      175,\n      1,\n      \"LATENT\"\n    ],\n    [\n      552,\n      175,\n      0,\n      102,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      553,\n      175,\n      0,\n      133,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      554,\n      175,\n      0,\n      141,\n      0,\n      \"*\"\n    ],\n    [\n      555,\n      175,\n      1,\n      130,\n      1,\n      \"LATENT\"\n    ],\n    [\n      556,\n      176,\n      0,\n      62,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      557,\n      176,\n      0,\n      142,\n      1,\n      \"IMAGE\"\n    ],\n    [\n      558,\n      176,\n      0,\n      173,\n      2,\n      \"IMAGE\"\n    ]\n  ],\n  \"groups\": [],\n  \"config\": {},\n  \"extra\": {\n    \"groupNodes\": {\n      \"Deforum Parameters\": {\n        \"nodes\": [\n          {\n            \"type\": \"DeforumBaseParamsNode\",\n            \"pos\": [\n              -325,\n              78\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 490\n            },\n            \"flags\": {},\n            \"order\": 4,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumBaseParamsNode\"\n            },\n            \"widgets_values\": [\n              1366,\n              1024,\n              \"dpmpp_2m_sde\",\n              \"karras\",\n              14,\n              6,\n              1,\n              \"Deforum_{timestring}\",\n              \"iter\",\n              1,\n              \"output/deforum\",\n              0.4,\n              true,\n              false,\n              false,\n              true,\n              false\n            ],\n            \"index\": 0\n          },\n          {\n            \"type\": \"DeforumAnimParamsNode\",\n            \"pos\": [\n              -315,\n              598\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 178\n            },\n            \"flags\": {},\n            \"order\": 5,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumAnimParamsNode\"\n            },\n            \"widgets_values\": [\n              \"3D\",\n              150,\n              \"wrap\",\n              false,\n              \"20230129210106\",\n              false\n            ],\n            \"index\": 1\n          },\n          {\n            \"type\": \"DeforumDepthParamsNode\",\n            \"pos\": [\n              45,\n              68\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 178\n            },\n            \"flags\": {},\n            \"order\": 9,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumDepthParamsNode\"\n            },\n            \"widgets_values\": [\n              true,\n              \"Zoe\",\n              0.2,\n              \"border\",\n              \"bicubic\",\n              false\n            ],\n            \"index\": 2\n          },\n          {\n            \"type\": \"DeforumTranslationParamsNode\",\n            \"pos\": [\n              45,\n              308\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 274\n            },\n            \"flags\": {},\n            \"order\": 10,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumTranslationParamsNode\"\n            },\n            \"widgets_values\": [\n              \"0:(0)\",\n              \"0:(1.0)\",\n              \"0:(0)\",\n              \"0:(0)\",\n              \"0:(0)\",\n              \"0:(0.5)\",\n              \"0:(0.5)\",\n              \"0:(0)\",\n              \"0:(0)\",\n              \"0:(0)\"\n            ],\n            \"index\": 3\n          },\n          {\n            \"type\": \"DeforumNoiseParamsNode\",\n            \"pos\": [\n              45,\n              638\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 298\n            },\n            \"flags\": {},\n            \"order\": 15,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumNoiseParamsNode\"\n            },\n            \"widgets_values\": [\n              true,\n              \"0: (0.4)\",\n              \"0: (0.1)\",\n              \"0: (5)\",\n              \"0: (1.0)\",\n              \"0: (0.0)\",\n              \"perlin\",\n              8,\n              8,\n              4,\n              0.5\n            ],\n            \"index\": 4\n          },\n          {\n            \"type\": \"DeforumDiffusionParamsNode\",\n            \"pos\": [\n              -315,\n              858\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 274\n            },\n            \"flags\": {},\n            \"order\": 16,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumDiffusionParamsNode\"\n            },\n            \"widgets_values\": [\n              \"0: (0.03)\",\n              \"0: (0.45)\",\n              \"0: (1.0)\",\n              \"0: (5)\",\n              false,\n              \"0: (25)\",\n              false,\n              \"0:(0)\",\n              false,\n              \"0:(1)\"\n            ],\n            \"index\": 5\n          },\n          {\n            \"type\": \"DeforumColorParamsNode\",\n            \"pos\": [\n              40,\n              988\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 154\n            },\n            \"flags\": {},\n            \"order\": 17,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumColorParamsNode\"\n            },\n            \"widgets_values\": [\n              \"Image\",\n              \"\",\n              1,\n              false,\n              false\n            ],\n            \"index\": 6\n          }\n        ],\n        \"links\": [\n          [\n            null,\n            1,\n            0,\n            0,\n            112,\n            \"deforum_data\"\n          ],\n          [\n            0,\n            0,\n            1,\n            0,\n            20,\n            \"deforum_data\"\n          ],\n          [\n            1,\n            0,\n            2,\n            0,\n            21,\n            \"deforum_data\"\n          ],\n          [\n            2,\n            0,\n            3,\n            0,\n            42,\n            \"deforum_data\"\n          ],\n          [\n            5,\n            0,\n            4,\n            0,\n            52,\n            \"deforum_data\"\n          ],\n          [\n            3,\n            0,\n            5,\n            0,\n            44,\n            \"deforum_data\"\n          ],\n          [\n            4,\n            0,\n            6,\n            0,\n            51,\n            \"deforum_data\"\n          ]\n        ],\n        \"external\": [\n          [\n            6,\n            0,\n            \"deforum_data\"\n          ]\n        ]\n      },\n      \"Deforum Sampler\": {\n        \"nodes\": [\n          {\n            \"type\": \"DeforumPromptNode\",\n            \"pos\": [\n              845,\n              445\n            ],\n            \"size\": {\n              \"0\": 304.5645751953125,\n              \"1\": 211.12216186523438\n            },\n            \"flags\": {},\n            \"order\": 1,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumPromptNode\"\n            },\n            \"widgets_values\": [\n              \"0:'whirling chaotic tornado, lighthouse, polygons in space, abstract artwork, light illusion'\\n25:'chaotic abstract painting'\"\n            ],\n            \"index\": 0\n          },\n          {\n            \"type\": \"CheckpointLoaderSimple\",\n            \"pos\": [\n              636,\n              -55\n            ],\n            \"size\": {\n              \"0\": 315,\n              \"1\": 98\n            },\n            \"flags\": {},\n            \"order\": 2,\n            \"mode\": 0,\n            \"outputs\": [\n              {\n                \"name\": \"MODEL\",\n                \"type\": \"MODEL\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"CLIP\",\n                \"type\": \"CLIP\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 1\n              },\n              {\n                \"name\": \"VAE\",\n                \"type\": \"VAE\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"CheckpointLoaderSimple\"\n            },\n            \"widgets_values\": [\n              \"protovisionXLHighFidelity3D_beta0520Bakedvae.safetensors\"\n            ],\n            \"index\": 1\n          },\n          {\n            \"type\": \"DeepCache\",\n            \"pos\": [\n              420,\n              100\n            ],\n            \"size\": {\n              \"0\": 315,\n              \"1\": 130\n            },\n            \"flags\": {},\n            \"order\": 5,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"model\",\n                \"type\": \"MODEL\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"MODEL\",\n                \"type\": \"MODEL\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeepCache\"\n            },\n            \"widgets_values\": [\n              4,\n              2,\n              0,\n              1000\n            ],\n            \"index\": 2\n          },\n          {\n            \"type\": \"DeforumIteratorNode\",\n            \"pos\": [\n              420,\n              490\n            ],\n            \"size\": {\n              \"0\": 393,\n              \"1\": 166\n            },\n            \"flags\": {},\n            \"order\": 13,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3\n              },\n              {\n                \"name\": \"positive_prompt\",\n                \"type\": \"STRING\",\n                \"links\": [],\n                \"shape\": 3\n              },\n              {\n                \"name\": \"negative_prompt\",\n                \"type\": \"STRING\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumIteratorNode\"\n            },\n            \"widgets_values\": [\n              282766464392687,\n              \"increment\",\n              0\n            ],\n            \"index\": 3\n          },\n          {\n            \"type\": \"DeforumKSampler\",\n            \"pos\": [\n              460,\n              310\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 106\n            },\n            \"flags\": {},\n            \"order\": 14,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"model\",\n                \"type\": \"MODEL\",\n                \"link\": null\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null,\n                \"slot_index\": 1\n              },\n              {\n                \"name\": \"positive\",\n                \"type\": \"CONDITIONING\",\n                \"link\": null\n              },\n              {\n                \"name\": \"negative\",\n                \"type\": \"CONDITIONING\",\n                \"link\": null,\n                \"slot_index\": 3\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumKSampler\"\n            },\n            \"index\": 4\n          },\n          {\n            \"type\": \"VAEDecode\",\n            \"pos\": [\n              920,\n              319\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 15,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"samples\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              },\n              {\n                \"name\": \"vae\",\n                \"type\": \"VAE\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"VAEDecode\"\n            },\n            \"index\": 5\n          },\n          {\n            \"type\": \"DeforumConditioningBlendNode\",\n            \"pos\": [\n              841,\n              148\n            ],\n            \"size\": {\n              \"0\": 342.5999755859375,\n              \"1\": 78\n            },\n            \"flags\": {},\n            \"order\": 16,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"clip\",\n                \"type\": \"CLIP\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"POSITIVE\",\n                \"type\": \"CONDITIONING\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"NEGATIVE\",\n                \"type\": \"CONDITIONING\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumConditioningBlendNode\"\n            },\n            \"widgets_values\": [\n              \"sigmoidal\"\n            ],\n            \"index\": 6\n          }\n        ],\n        \"links\": [\n          [\n            1,\n            0,\n            2,\n            0,\n            115,\n            \"MODEL\"\n          ],\n          [\n            null,\n            0,\n            3,\n            0,\n            113,\n            \"deforum_data\"\n          ],\n          [\n            null,\n            0,\n            3,\n            1,\n            114,\n            \"LATENT\"\n          ],\n          [\n            2,\n            0,\n            4,\n            0,\n            117,\n            \"MODEL\"\n          ],\n          [\n            3,\n            1,\n            4,\n            1,\n            118,\n            \"LATENT\"\n          ],\n          [\n            6,\n            0,\n            4,\n            2,\n            124,\n            \"CONDITIONING\"\n          ],\n          [\n            6,\n            1,\n            4,\n            3,\n            124,\n            \"CONDITIONING\"\n          ],\n          [\n            null,\n            5,\n            4,\n            4,\n            114,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            4,\n            0,\n            5,\n            0,\n            121,\n            \"LATENT\"\n          ],\n          [\n            1,\n            2,\n            5,\n            1,\n            115,\n            \"VAE\"\n          ],\n          [\n            1,\n            1,\n            6,\n            0,\n            115,\n            \"CLIP\"\n          ],\n          [\n            3,\n            0,\n            6,\n            1,\n            118,\n            \"DEFORUM_FRAME_DATA\"\n          ]\n        ],\n        \"external\": [\n          [\n            0,\n            0,\n            \"deforum_data\"\n          ],\n          [\n            1,\n            2,\n            \"VAE\"\n          ],\n          [\n            3,\n            0,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            5,\n            0,\n            \"IMAGE\"\n          ]\n        ]\n      },\n      \"Deforum Operators\": {\n        \"nodes\": [\n          {\n            \"type\": \"DeforumGetCachedLatentNode\",\n            \"pos\": [\n              1356,\n              1073\n            ],\n            \"size\": {\n              \"0\": 218.39999389648438,\n              \"1\": 26\n            },\n            \"flags\": {},\n            \"order\": 1,\n            \"mode\": 0,\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n            },\n            \"index\": 0\n          },\n          {\n            \"type\": \"DeforumCacheLatentNode\",\n            \"pos\": [\n              1337,\n              1803\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 26\n            },\n            \"flags\": {},\n            \"order\": 4,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": null,\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumCacheLatentNode\"\n            },\n            \"index\": 1\n          },\n          {\n            \"type\": \"VAEEncode\",\n            \"pos\": [\n              1344,\n              1696\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 5,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"pixels\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"vae\",\n                \"type\": \"VAE\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"VAEEncode\"\n            },\n            \"index\": 2\n          },\n          {\n            \"type\": \"DeforumFrameWarpNode\",\n            \"pos\": [\n              1301,\n              1392\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 6,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumFrameWarpNode\"\n            },\n            \"index\": 3\n          },\n          {\n            \"type\": \"DeforumAddNoiseNode\",\n            \"pos\": [\n              1301,\n              1590\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 9,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumAddNoiseNode\"\n            },\n            \"index\": 4\n          },\n          {\n            \"type\": \"DeforumColorMatchNode\",\n            \"pos\": [\n              1303,\n              1157\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 11,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumColorMatchNode\"\n            },\n            \"index\": 5\n          },\n          {\n            \"type\": \"Reroute\",\n            \"pos\": [\n              1079,\n              1292\n            ],\n            \"size\": [\n              75,\n              26\n            ],\n            \"flags\": {},\n            \"order\": 12,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"\",\n                \"type\": \"*\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"\",\n                \"type\": \"*\",\n                \"links\": null\n              }\n            ],\n            \"properties\": {\n              \"showOutputText\": false,\n              \"horizontal\": false\n            },\n            \"index\": 6\n          },\n          {\n            \"type\": \"DeforumHybridMotionNode\",\n            \"pos\": [\n              1298,\n              1249\n            ],\n            \"size\": {\n              \"0\": 315,\n              \"1\": 98\n            },\n            \"flags\": {},\n            \"order\": 14,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"hybrid_image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumHybridMotionNode\"\n            },\n            \"widgets_values\": [\n              \"RAFT\"\n            ],\n            \"index\": 7\n          },\n          {\n            \"type\": \"DeforumColorMatchNode\",\n            \"pos\": [\n              1297,\n              1492\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 15,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumColorMatchNode\"\n            },\n            \"index\": 8\n          }\n        ],\n        \"links\": [\n          [\n            2,\n            0,\n            1,\n            0,\n            36,\n            \"LATENT\"\n          ],\n          [\n            8,\n            0,\n            2,\n            0,\n            109,\n            \"IMAGE\"\n          ],\n          [\n            null,\n            0,\n            2,\n            1,\n            112,\n            \"VAE\"\n          ],\n          [\n            7,\n            0,\n            3,\n            0,\n            108,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            3,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            8,\n            0,\n            4,\n            0,\n            109,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            4,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            null,\n            3,\n            5,\n            0,\n            112,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            5,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            null,\n            2,\n            6,\n            0,\n            112,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            5,\n            0,\n            7,\n            0,\n            95,\n            \"IMAGE\"\n          ],\n          [\n            null,\n            0,\n            7,\n            1,\n            88,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            7,\n            2,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            3,\n            0,\n            8,\n            0,\n            41,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            8,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ]\n        ],\n        \"external\": [\n          [\n            0,\n            0,\n            \"LATENT\"\n          ],\n          [\n            3,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            4,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            5,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            7,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            8,\n            0,\n            \"IMAGE\"\n          ]\n        ]\n      }\n    }\n  },\n  \"version\": 0.4\n}"
  },
  {
    "path": "examples/deforum_simple.json",
    "content": "{\n  \"last_node_id\": 26,\n  \"last_link_id\": 56,\n  \"nodes\": [\n    {\n      \"id\": 7,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1370,\n        230\n      ],\n      \"size\": {\n        \"0\": 733.25,\n        \"1\": 732.75\n      },\n      \"flags\": {},\n      \"order\": 4,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 56\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 14,\n      \"type\": \"Note\",\n      \"pos\": [\n        300,\n        202\n      ],\n      \"size\": {\n        \"0\": 490.25,\n        \"1\": 267.75\n      },\n      \"flags\": {},\n      \"order\": 0,\n      \"mode\": 0,\n      \"properties\": {\n        \"text\": \"\"\n      },\n      \"widgets_values\": [\n        \"Welcome to Deforum Comfy.\\n\\nThis is the simplest example to achieve Deforum-esque animations, right click on Deforum Sampling -> Convert to Nodes to expose the inner logic, and add optional Deforum image processors, or anything you'd like.\\n\\nThe way Deforum works requires a full run of a graph for each frame currently (with the exception of image interpolations [WIP]), thus hitting Queue Prompt generates one frame of your animation. Use Extra Options - Auto Queue for continous generation.\\n\\nThe Deforum Video Save node saves it's cached frames when the current_frame reaches max_frames set in Deforum. The current frame advances with each generation, and resets to 0 when reaching/passing max_frames (i.e. max frames set lower then current frame while running), or by setting the reset parameter of the Iterator node to 1. Don't forget to flip it back to 0, as it is resetting the latents as well.\"\n      ],\n      \"color\": \"#432\",\n      \"bgcolor\": \"#653\"\n    },\n    {\n      \"id\": 1,\n      \"type\": \"CheckpointLoaderSimple\",\n      \"pos\": [\n        800,\n        310\n      ],\n      \"size\": {\n        \"0\": 501.75,\n        \"1\": 108\n      },\n      \"flags\": {},\n      \"order\": 1,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            54\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": [\n            51,\n            52\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            55\n          ],\n          \"shape\": 3,\n          \"slot_index\": 2\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CheckpointLoaderSimple\"\n      },\n      \"widgets_values\": [\n        \"protovisionXLHighFidelity3D_beta0520Bakedvae.safetensors\"\n      ]\n    },\n    {\n      \"id\": 15,\n      \"type\": \"DeforumBaseParamsNode\",\n      \"pos\": [\n        473,\n        530\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 442\n      },\n      \"flags\": {},\n      \"order\": 2,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": null\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            53\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumBaseParamsNode\"\n      },\n      \"widgets_values\": [\n        1024,\n        1024,\n        \"euler\",\n        \"normal\",\n        25,\n        7,\n        1,\n        \"Deforum_{timestring}\",\n        \"fixed\",\n        1,\n        \"output/deforum\",\n        0.4,\n        true,\n        false,\n        false,\n        true,\n        false\n      ]\n    },\n    {\n      \"id\": 26,\n      \"type\": \"workflow/Deforum Sampler\",\n      \"pos\": [\n        839,\n        540\n      ],\n      \"size\": {\n        \"0\": 443.4000244140625,\n        \"1\": 418\n      },\n      \"flags\": {},\n      \"order\": 3,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"clip\",\n          \"type\": \"CLIP\",\n          \"link\": 51\n        },\n        {\n          \"name\": \"CLIPTextEncode clip\",\n          \"type\": \"CLIP\",\n          \"link\": 52\n        },\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 53\n        },\n        {\n          \"name\": \"init_latent\",\n          \"type\": \"LATENT\",\n          \"link\": null\n        },\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 54\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 55\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"positive_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"negative_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            56\n          ],\n          \"shape\": 3,\n          \"slot_index\": 2\n        },\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"workflow/Deforum Sampler\"\n      },\n      \"widgets_values\": [\n        \"\",\n        \"\",\n        \"stable_diffusion\",\n        785978144431937,\n        \"randomize\",\n        0,\n        0.8,\n        0.1,\n        false,\n        false\n      ]\n    }\n  ],\n  \"links\": [\n    [\n      51,\n      1,\n      1,\n      26,\n      0,\n      \"CLIP\"\n    ],\n    [\n      52,\n      1,\n      1,\n      26,\n      1,\n      \"CLIP\"\n    ],\n    [\n      53,\n      15,\n      0,\n      26,\n      2,\n      \"deforum_data\"\n    ],\n    [\n      54,\n      1,\n      0,\n      26,\n      4,\n      \"MODEL\"\n    ],\n    [\n      55,\n      1,\n      2,\n      26,\n      5,\n      \"VAE\"\n    ],\n    [\n      56,\n      26,\n      2,\n      7,\n      0,\n      \"IMAGE\"\n    ]\n  ],\n  \"groups\": [],\n  \"config\": {},\n  \"extra\": {\n    \"groupNodes\": {\n      \"Deforum Sampler\": {\n        \"nodes\": [\n          {\n            \"type\": \"DeforumGetCachedLatentNode\",\n            \"pos\": [\n              926,\n              1437\n            ],\n            \"size\": {\n              \"0\": 239.2784423828125,\n              \"1\": 26\n            },\n            \"flags\": {},\n            \"order\": 2,\n            \"mode\": 0,\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n            },\n            \"index\": 0\n          },\n          {\n            \"type\": \"CLIPTextEncode\",\n            \"pos\": [\n              875,\n              558\n            ],\n            \"size\": {\n              \"0\": 400,\n              \"1\": 200\n            },\n            \"flags\": {},\n            \"order\": 4,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"clip\",\n                \"type\": \"CLIP\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"CONDITIONING\",\n                \"type\": \"CONDITIONING\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"CLIPTextEncode\"\n            },\n            \"widgets_values\": [\n              \"\"\n            ],\n            \"index\": 1\n          },\n          {\n            \"type\": \"CLIPTextEncode\",\n            \"pos\": [\n              865,\n              818\n            ],\n            \"size\": {\n              \"0\": 400,\n              \"1\": 200\n            },\n            \"flags\": {},\n            \"order\": 5,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"clip\",\n                \"type\": \"CLIP\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"CONDITIONING\",\n                \"type\": \"CONDITIONING\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"CLIPTextEncode\"\n            },\n            \"widgets_values\": [\n              \"\"\n            ],\n            \"index\": 2\n          },\n          {\n            \"type\": \"DeforumIteratorNode\",\n            \"pos\": [\n              848,\n              1073\n            ],\n            \"size\": {\n              \"0\": 393,\n              \"1\": 286\n            },\n            \"flags\": {},\n            \"order\": 6,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"links\": [],\n                \"shape\": 3\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3\n              },\n              {\n                \"name\": \"positive_prompt\",\n                \"type\": \"STRING\",\n                \"links\": null,\n                \"shape\": 3\n              },\n              {\n                \"name\": \"negative_prompt\",\n                \"type\": \"STRING\",\n                \"links\": null,\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumIteratorNode\"\n            },\n            \"widgets_values\": [\n              \"stable_diffusion\",\n              923224016066246,\n              \"randomize\",\n              0,\n              0.8,\n              0.1,\n              false,\n              false\n            ],\n            \"index\": 3\n          },\n          {\n            \"type\": \"DeforumKSampler\",\n            \"pos\": [\n              903,\n              1724\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 106\n            },\n            \"flags\": {},\n            \"order\": 7,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"model\",\n                \"type\": \"MODEL\",\n                \"link\": null\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              },\n              {\n                \"name\": \"positive\",\n                \"type\": \"CONDITIONING\",\n                \"link\": null\n              },\n              {\n                \"name\": \"negative\",\n                \"type\": \"CONDITIONING\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumKSampler\"\n            },\n            \"index\": 4\n          },\n          {\n            \"type\": \"VAEDecode\",\n            \"pos\": [\n              949,\n              1613\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 8,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"samples\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              },\n              {\n                \"name\": \"vae\",\n                \"type\": \"VAE\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"VAEDecode\"\n            },\n            \"index\": 5\n          },\n          {\n            \"type\": \"DeforumCacheLatentNode\",\n            \"pos\": [\n              935,\n              1521\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 26\n            },\n            \"flags\": {},\n            \"order\": 9,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": null,\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumCacheLatentNode\"\n            },\n            \"index\": 6\n          }\n        ],\n        \"links\": [\n          [\n            null,\n            1,\n            1,\n            0,\n            1,\n            \"CLIP\"\n          ],\n          [\n            null,\n            1,\n            2,\n            0,\n            1,\n            \"CLIP\"\n          ],\n          [\n            null,\n            0,\n            3,\n            0,\n            15,\n            \"deforum_data\"\n          ],\n          [\n            0,\n            0,\n            3,\n            1,\n            17,\n            \"LATENT\"\n          ],\n          [\n            null,\n            0,\n            4,\n            0,\n            1,\n            \"MODEL\"\n          ],\n          [\n            3,\n            1,\n            4,\n            1,\n            24,\n            \"LATENT\"\n          ],\n          [\n            1,\n            0,\n            4,\n            2,\n            18,\n            \"CONDITIONING\"\n          ],\n          [\n            2,\n            0,\n            4,\n            3,\n            19,\n            \"CONDITIONING\"\n          ],\n          [\n            3,\n            0,\n            4,\n            4,\n            24,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            4,\n            0,\n            5,\n            0,\n            21,\n            \"LATENT\"\n          ],\n          [\n            null,\n            2,\n            5,\n            1,\n            1,\n            \"VAE\"\n          ],\n          [\n            4,\n            0,\n            6,\n            0,\n            21,\n            \"LATENT\"\n          ]\n        ],\n        \"external\": [\n          [\n            5,\n            0,\n            \"IMAGE\"\n          ]\n        ]\n      }\n    }\n  },\n  \"version\": 0.4\n}"
  },
  {
    "path": "examples/deforum_stablecascade.json",
    "content": "{\n  \"last_node_id\": 198,\n  \"last_link_id\": 598,\n  \"nodes\": [\n    {\n      \"id\": 141,\n      \"type\": \"Reroute\",\n      \"pos\": [\n        532,\n        972\n      ],\n      \"size\": [\n        75,\n        26\n      ],\n      \"flags\": {},\n      \"order\": 18,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"*\",\n          \"link\": 589\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            441,\n            443,\n            447,\n            459\n          ]\n        }\n      ],\n      \"properties\": {\n        \"showOutputText\": false,\n        \"horizontal\": false\n      }\n    },\n    {\n      \"id\": 157,\n      \"type\": \"DeforumHybridScheduleNode\",\n      \"pos\": [\n        -310,\n        760\n      ],\n      \"size\": {\n        \"0\": 274.891845703125,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 14,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 540\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            541\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridScheduleNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(1)\",\n        \"0:(100)\",\n        \"0:(0)\",\n        \"0:(0.8)\"\n      ]\n    },\n    {\n      \"id\": 155,\n      \"type\": \"DeforumColorParamsNode\",\n      \"pos\": [\n        -670,\n        1120\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 154\n      },\n      \"flags\": {},\n      \"order\": 11,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 535\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            538\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumColorParamsNode\"\n      },\n      \"widgets_values\": [\n        \"Image\",\n        \"\",\n        1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 150,\n      \"type\": \"DeforumAnimParamsNode\",\n      \"pos\": [\n        -670,\n        280\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 7,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 537\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            497\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAnimParamsNode\"\n      },\n      \"widgets_values\": [\n        \"3D\",\n        100,\n        \"wrap\",\n        false,\n        \"20230129210106\",\n        false\n      ]\n    },\n    {\n      \"id\": 151,\n      \"type\": \"DeforumDepthParamsNode\",\n      \"pos\": [\n        -670,\n        530\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 9,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 497\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            498\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDepthParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"Zoe\",\n        0.2,\n        \"border\",\n        \"bicubic\",\n        false\n      ]\n    },\n    {\n      \"id\": 130,\n      \"type\": \"DeforumKSampler\",\n      \"pos\": [\n        650,\n        152\n      ],\n      \"size\": {\n        \"0\": 325.93902587890625,\n        \"1\": 106\n      },\n      \"flags\": {},\n      \"order\": 20,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 595\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 590,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"positive\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 430\n        },\n        {\n          \"name\": \"negative\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 431,\n          \"slot_index\": 3\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 459\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            568\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumKSampler\"\n      }\n    },\n    {\n      \"id\": 191,\n      \"type\": \"ConditioningZeroOut\",\n      \"pos\": [\n        692,\n        320\n      ],\n      \"size\": {\n        \"0\": 211.60000610351562,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 19,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"conditioning\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 569\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"CONDITIONING\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            570\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"ConditioningZeroOut\"\n      }\n    },\n    {\n      \"id\": 190,\n      \"type\": \"StableCascade_StageB_Conditioning\",\n      \"pos\": [\n        659,\n        392\n      ],\n      \"size\": {\n        \"0\": 277.20001220703125,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 21,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"conditioning\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 570\n        },\n        {\n          \"name\": \"stage_c\",\n          \"type\": \"LATENT\",\n          \"link\": 568\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"CONDITIONING\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            571\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"StableCascade_StageB_Conditioning\"\n      }\n    },\n    {\n      \"id\": 131,\n      \"type\": \"VAEDecode\",\n      \"pos\": [\n        692,\n        858\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 23,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"samples\",\n          \"type\": \"LATENT\",\n          \"link\": 576\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 598,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            462,\n            469,\n            475\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEDecode\"\n      }\n    },\n    {\n      \"id\": 138,\n      \"type\": \"DeforumFrameWarpNode\",\n      \"pos\": [\n        661,\n        1237\n      ],\n      \"size\": {\n        \"0\": 304.79998779296875,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 27,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 440\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 441\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            455,\n            488\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumFrameWarpNode\"\n      }\n    },\n    {\n      \"id\": 142,\n      \"type\": \"DeforumHybridMotionNode\",\n      \"pos\": [\n        650,\n        1028\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 25,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 469\n        },\n        {\n          \"name\": \"hybrid_image\",\n          \"type\": \"IMAGE\",\n          \"link\": 453\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 447\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            440,\n            460\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridMotionNode\"\n      },\n      \"widgets_values\": [\n        \"RAFT\"\n      ]\n    },\n    {\n      \"id\": 133,\n      \"type\": \"DeforumConditioningBlendNode\",\n      \"pos\": [\n        647,\n        17\n      ],\n      \"size\": {\n        \"0\": 342.5999755859375,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 17,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"clip\",\n          \"type\": \"CLIP\",\n          \"link\": 596\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 588\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"POSITIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            430,\n            569\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"NEGATIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            431,\n            572\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumConditioningBlendNode\"\n      },\n      \"widgets_values\": [\n        \"none\"\n      ]\n    },\n    {\n      \"id\": 192,\n      \"type\": \"KSampler\",\n      \"pos\": [\n        631,\n        499\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 262\n      },\n      \"flags\": {},\n      \"order\": 22,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 597,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"positive\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 571\n        },\n        {\n          \"name\": \"negative\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 572,\n          \"slot_index\": 2\n        },\n        {\n          \"name\": \"latent_image\",\n          \"type\": \"LATENT\",\n          \"link\": 573\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            576\n          ],\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"KSampler\"\n      },\n      \"widgets_values\": [\n        361450159878869,\n        \"randomize\",\n        10,\n        1.1,\n        \"ddpm\",\n        \"normal\",\n        1\n      ]\n    },\n    {\n      \"id\": 126,\n      \"type\": \"DeforumPromptNode\",\n      \"pos\": [\n        -660,\n        -60\n      ],\n      \"size\": {\n        \"0\": 313.04736328125,\n        \"1\": 276.55352783203125\n      },\n      \"flags\": {},\n      \"order\": 0,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": null\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            537\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumPromptNode\"\n      },\n      \"widgets_values\": [\n        \"0:'unreal 3d render of a cyberpunk city, the text saying \\\"ComfyUI\\\"'\"\n      ]\n    },\n    {\n      \"id\": 152,\n      \"type\": \"DeforumTranslationParamsNode\",\n      \"pos\": [\n        -670,\n        780\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 10,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 498\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            535\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumTranslationParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0)\",\n        \"0:(1.0)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(10)\",\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\"\n      ]\n    },\n    {\n      \"id\": 153,\n      \"type\": \"DeforumNoiseParamsNode\",\n      \"pos\": [\n        -310,\n        990\n      ],\n      \"size\": {\n        \"0\": 272.06170654296875,\n        \"1\": 298\n      },\n      \"flags\": {},\n      \"order\": 15,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 541\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            585\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumNoiseParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"0: (0.8)\",\n        \"0: (0.4)\",\n        \"0: (5)\",\n        \"0: (1.0)\",\n        \"0: (0.0)\",\n        \"perlin\",\n        8,\n        8,\n        4,\n        0.5\n      ]\n    },\n    {\n      \"id\": 50,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1120,\n        1440\n      ],\n      \"size\": {\n        \"0\": 189.73870849609375,\n        \"1\": 246\n      },\n      \"flags\": {},\n      \"order\": 31,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 456,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 47,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1120,\n        950\n      ],\n      \"size\": {\n        \"0\": 192.6160888671875,\n        \"1\": 169.56472778320312\n      },\n      \"flags\": {},\n      \"order\": 28,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 460\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 48,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1120,\n        1160\n      ],\n      \"size\": {\n        \"0\": 188.46986389160156,\n        \"1\": 246\n      },\n      \"flags\": {},\n      \"order\": 29,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 455\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 179,\n      \"type\": \"StableCascade_EmptyLatentImage\",\n      \"pos\": [\n        160,\n        890\n      ],\n      \"size\": {\n        \"0\": 252,\n        \"1\": 150\n      },\n      \"flags\": {},\n      \"order\": 1,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"stage_c\",\n          \"type\": \"LATENT\",\n          \"links\": [],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"stage_b\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            573\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"StableCascade_EmptyLatentImage\"\n      },\n      \"widgets_values\": [\n        1024,\n        1024,\n        42,\n        1\n      ]\n    },\n    {\n      \"id\": 170,\n      \"type\": \"Note\",\n      \"pos\": [\n        207,\n        -277\n      ],\n      \"size\": {\n        \"0\": 633.6494140625,\n        \"1\": 221.03656005859375\n      },\n      \"flags\": {},\n      \"order\": 2,\n      \"mode\": 0,\n      \"properties\": {\n        \"text\": \"\"\n      },\n      \"widgets_values\": [\n        \"Welcome to Deforum!\\n\\nThe below graph provides a pluggable Deforum Animation pipeline in ComfyUI. It works frame-by-frame, so the best practice is to enable Extra options, and Auto Queue.\\n\\nDeforum Parameter and Schedule nodes represent all settings available to consume after chaining up by the Deforum Iterator node, which keeps track of the current frame, generates/gets the cached latent to be denoised in the next pass. The parameters and schedule's are the same as in the auto1111 extension and in the Colab version.\\n\\nHybrid nodes are currently bypassed (purple), enabling them, and selecting a video transforms the pipeline into Deforum Hybrid. Iteration can be reset to frame 0 with the reset value being set to 1 on the iterator node. Don't forget to switch it back to 0 to use the generated image/latent.\\\"\\n\\nDeforum Video Save node dumps its collected frames when the current frame's id reaches/has passed max_frames.\"\n      ],\n      \"color\": \"#432\",\n      \"bgcolor\": \"#653\"\n    },\n    {\n      \"id\": 62,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        266,\n        1378\n      ],\n      \"size\": {\n        \"0\": 309.7149658203125,\n        \"1\": 251.35919189453125\n      },\n      \"flags\": {},\n      \"order\": 8,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 224,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 88,\n      \"type\": \"DeforumLoadVideo\",\n      \"pos\": [\n        -104,\n        1363\n      ],\n      \"size\": {\n        \"0\": 317.5899658203125,\n        \"1\": 294\n      },\n      \"flags\": {},\n      \"order\": 3,\n      \"mode\": 4,\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            224,\n            453\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumLoadVideo\"\n      },\n      \"widgets_values\": [\n        \"Recording 2024-02-08 214819.mp4\",\n        \"image\"\n      ]\n    },\n    {\n      \"id\": 149,\n      \"type\": \"DeforumBaseParamsNode\",\n      \"pos\": [\n        -310,\n        -60\n      ],\n      \"size\": {\n        \"0\": 281.7219543457031,\n        \"1\": 448.62408447265625\n      },\n      \"flags\": {},\n      \"order\": 12,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 538\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            539\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumBaseParamsNode\"\n      },\n      \"widgets_values\": [\n        1024,\n        1024,\n        \"ddpm\",\n        \"normal\",\n        20,\n        6,\n        1,\n        \"Deforum_{timestring}\",\n        \"random\",\n        1,\n        \"output/deforum\",\n        0.4,\n        true,\n        false,\n        false,\n        true,\n        false\n      ]\n    },\n    {\n      \"id\": 194,\n      \"type\": \"DeforumIteratorNode\",\n      \"pos\": [\n        86,\n        451\n      ],\n      \"size\": {\n        \"0\": 393,\n        \"1\": 286\n      },\n      \"flags\": {},\n      \"order\": 16,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 585\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 586\n        },\n        {\n          \"name\": \"init_latent\",\n          \"type\": \"LATENT\",\n          \"link\": null\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            587,\n            588,\n            589\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            590\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"positive_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"negative_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumIteratorNode\"\n      },\n      \"widgets_values\": [\n        \"stable_cascade\",\n        847600922662897,\n        \"randomize\",\n        0,\n        0.8,\n        0.1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 102,\n      \"type\": \"DeforumVideoSaveNode\",\n      \"pos\": [\n        1383,\n        973\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 150\n      },\n      \"flags\": {},\n      \"order\": 26,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 475\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 587\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumVideoSaveNode\"\n      },\n      \"widgets_values\": [\n        \"deforum_\",\n        12,\n        \"max_frames\",\n        0\n      ]\n    },\n    {\n      \"id\": 16,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1383,\n        -37\n      ],\n      \"size\": {\n        \"0\": 894.2135009765625,\n        \"1\": 941.8845825195312\n      },\n      \"flags\": {},\n      \"order\": 24,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 462,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 154,\n      \"type\": \"DeforumDiffusionParamsNode\",\n      \"pos\": [\n        -309,\n        440\n      ],\n      \"size\": {\n        \"0\": 278.891845703125,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 13,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 539\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            540\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDiffusionParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0: (0.03)\",\n        \"0: (0.35)\",\n        \"0: (1.0)\",\n        \"0: (4)\",\n        false,\n        \"0: (20)\",\n        false,\n        \"0:(0)\",\n        false,\n        \"0:(1)\"\n      ]\n    },\n    {\n      \"id\": 135,\n      \"type\": \"DeforumGetCachedLatentNode\",\n      \"pos\": [\n        169,\n        800\n      ],\n      \"size\": {\n        \"0\": 218.39999389648438,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 4,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            586\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n      }\n    },\n    {\n      \"id\": 139,\n      \"type\": \"DeforumAddNoiseNode\",\n      \"pos\": [\n        644,\n        1355\n      ],\n      \"size\": {\n        \"0\": 324.6453552246094,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 30,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 488,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 443,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            456,\n            593\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAddNoiseNode\"\n      }\n    },\n    {\n      \"id\": 198,\n      \"type\": \"StableCascade_StageC_VAEEncode\",\n      \"pos\": [\n        629,\n        1461\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 32,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 593\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 592\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"stage_c\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            594\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"stage_b\",\n          \"type\": \"LATENT\",\n          \"links\": [],\n          \"shape\": 3,\n          \"slot_index\": 1\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"StableCascade_StageC_VAEEncode\"\n      },\n      \"widgets_values\": [\n        32\n      ]\n    },\n    {\n      \"id\": 136,\n      \"type\": \"DeforumCacheLatentNode\",\n      \"pos\": [\n        683,\n        1616\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 33,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 594\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCacheLatentNode\"\n      }\n    },\n    {\n      \"id\": 196,\n      \"type\": \"CheckpointLoaderSimple\",\n      \"pos\": [\n        -285,\n        -251\n      ],\n      \"size\": {\n        \"0\": 336,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 5,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            595\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": [\n            596\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            592\n          ],\n          \"shape\": 3,\n          \"slot_index\": 2\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CheckpointLoaderSimple\"\n      },\n      \"widgets_values\": [\n        \"stable_cascade_stage_c.safetensors\"\n      ]\n    },\n    {\n      \"id\": 197,\n      \"type\": \"CheckpointLoaderSimple\",\n      \"pos\": [\n        67,\n        228\n      ],\n      \"size\": {\n        \"0\": 349.90911865234375,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 6,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            597\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            598\n          ],\n          \"shape\": 3,\n          \"slot_index\": 2\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CheckpointLoaderSimple\"\n      },\n      \"widgets_values\": [\n        \"stable_cascade_stage_b.safetensors\"\n      ]\n    }\n  ],\n  \"links\": [\n    [\n      224,\n      88,\n      0,\n      62,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      430,\n      133,\n      0,\n      130,\n      2,\n      \"CONDITIONING\"\n    ],\n    [\n      431,\n      133,\n      1,\n      130,\n      3,\n      \"CONDITIONING\"\n    ],\n    [\n      440,\n      142,\n      0,\n      138,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      441,\n      141,\n      0,\n      138,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      443,\n      141,\n      0,\n      139,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      447,\n      141,\n      0,\n      142,\n      2,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      453,\n      88,\n      0,\n      142,\n      1,\n      \"IMAGE\"\n    ],\n    [\n      455,\n      138,\n      0,\n      48,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      456,\n      139,\n      0,\n      50,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      459,\n      141,\n      0,\n      130,\n      4,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      460,\n      142,\n      0,\n      47,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      462,\n      131,\n      0,\n      16,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      469,\n      131,\n      0,\n      142,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      475,\n      131,\n      0,\n      102,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      488,\n      138,\n      0,\n      139,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      497,\n      150,\n      0,\n      151,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      498,\n      151,\n      0,\n      152,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      535,\n      152,\n      0,\n      155,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      537,\n      126,\n      0,\n      150,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      538,\n      155,\n      0,\n      149,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      539,\n      149,\n      0,\n      154,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      540,\n      154,\n      0,\n      157,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      541,\n      157,\n      0,\n      153,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      568,\n      130,\n      0,\n      190,\n      1,\n      \"LATENT\"\n    ],\n    [\n      569,\n      133,\n      0,\n      191,\n      0,\n      \"CONDITIONING\"\n    ],\n    [\n      570,\n      191,\n      0,\n      190,\n      0,\n      \"CONDITIONING\"\n    ],\n    [\n      571,\n      190,\n      0,\n      192,\n      1,\n      \"CONDITIONING\"\n    ],\n    [\n      572,\n      133,\n      1,\n      192,\n      2,\n      \"CONDITIONING\"\n    ],\n    [\n      573,\n      179,\n      1,\n      192,\n      3,\n      \"LATENT\"\n    ],\n    [\n      576,\n      192,\n      0,\n      131,\n      0,\n      \"LATENT\"\n    ],\n    [\n      585,\n      153,\n      0,\n      194,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      586,\n      135,\n      0,\n      194,\n      1,\n      \"LATENT\"\n    ],\n    [\n      587,\n      194,\n      0,\n      102,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      588,\n      194,\n      0,\n      133,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      589,\n      194,\n      0,\n      141,\n      0,\n      \"*\"\n    ],\n    [\n      590,\n      194,\n      1,\n      130,\n      1,\n      \"LATENT\"\n    ],\n    [\n      592,\n      196,\n      2,\n      198,\n      1,\n      \"VAE\"\n    ],\n    [\n      593,\n      139,\n      0,\n      198,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      594,\n      198,\n      0,\n      136,\n      0,\n      \"LATENT\"\n    ],\n    [\n      595,\n      196,\n      0,\n      130,\n      0,\n      \"MODEL\"\n    ],\n    [\n      596,\n      196,\n      1,\n      133,\n      0,\n      \"CLIP\"\n    ],\n    [\n      597,\n      197,\n      0,\n      192,\n      0,\n      \"MODEL\"\n    ],\n    [\n      598,\n      197,\n      2,\n      131,\n      1,\n      \"VAE\"\n    ]\n  ],\n  \"groups\": [],\n  \"config\": {},\n  \"extra\": {\n    \"groupNodes\": {}\n  },\n  \"version\": 0.4\n}"
  },
  {
    "path": "examples/deforum_stablecascade_legacy.json",
    "content": "{\n  \"last_node_id\": 195,\n  \"last_link_id\": 591,\n  \"nodes\": [\n    {\n      \"id\": 141,\n      \"type\": \"Reroute\",\n      \"pos\": [\n        532,\n        972\n      ],\n      \"size\": [\n        75,\n        26\n      ],\n      \"flags\": {},\n      \"order\": 21,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"*\",\n          \"link\": 589\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            441,\n            443,\n            447,\n            459\n          ]\n        }\n      ],\n      \"properties\": {\n        \"showOutputText\": false,\n        \"horizontal\": false\n      }\n    },\n    {\n      \"id\": 157,\n      \"type\": \"DeforumHybridScheduleNode\",\n      \"pos\": [\n        -310,\n        760\n      ],\n      \"size\": {\n        \"0\": 274.891845703125,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 17,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 540\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            541\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridScheduleNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(1)\",\n        \"0:(100)\",\n        \"0:(0)\",\n        \"0:(0.8)\"\n      ]\n    },\n    {\n      \"id\": 155,\n      \"type\": \"DeforumColorParamsNode\",\n      \"pos\": [\n        -670,\n        1120\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 154\n      },\n      \"flags\": {},\n      \"order\": 14,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 535\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            538\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumColorParamsNode\"\n      },\n      \"widgets_values\": [\n        \"Image\",\n        \"\",\n        1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 150,\n      \"type\": \"DeforumAnimParamsNode\",\n      \"pos\": [\n        -670,\n        280\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 10,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 537\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            497\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAnimParamsNode\"\n      },\n      \"widgets_values\": [\n        \"3D\",\n        100,\n        \"wrap\",\n        false,\n        \"20230129210106\",\n        false\n      ]\n    },\n    {\n      \"id\": 151,\n      \"type\": \"DeforumDepthParamsNode\",\n      \"pos\": [\n        -670,\n        530\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 178\n      },\n      \"flags\": {},\n      \"order\": 12,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 497\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            498\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDepthParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"Zoe\",\n        0.2,\n        \"border\",\n        \"bicubic\",\n        false\n      ]\n    },\n    {\n      \"id\": 130,\n      \"type\": \"DeforumKSampler\",\n      \"pos\": [\n        650,\n        152\n      ],\n      \"size\": {\n        \"0\": 325.93902587890625,\n        \"1\": 106\n      },\n      \"flags\": {},\n      \"order\": 23,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 577\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 590,\n          \"slot_index\": 1\n        },\n        {\n          \"name\": \"positive\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 430\n        },\n        {\n          \"name\": \"negative\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 431,\n          \"slot_index\": 3\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 459\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            568\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumKSampler\"\n      }\n    },\n    {\n      \"id\": 191,\n      \"type\": \"ConditioningZeroOut\",\n      \"pos\": [\n        692,\n        320\n      ],\n      \"size\": {\n        \"0\": 211.60000610351562,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 22,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"conditioning\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 569\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"CONDITIONING\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            570\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"ConditioningZeroOut\"\n      }\n    },\n    {\n      \"id\": 190,\n      \"type\": \"StableCascade_StageB_Conditioning\",\n      \"pos\": [\n        659,\n        392\n      ],\n      \"size\": {\n        \"0\": 277.20001220703125,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 24,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"conditioning\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 570\n        },\n        {\n          \"name\": \"stage_c\",\n          \"type\": \"LATENT\",\n          \"link\": 568\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"CONDITIONING\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            571\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"StableCascade_StageB_Conditioning\"\n      }\n    },\n    {\n      \"id\": 181,\n      \"type\": \"CLIPLoader\",\n      \"pos\": [\n        120,\n        300\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 82\n      },\n      \"flags\": {},\n      \"order\": 0,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"CLIP\",\n          \"type\": \"CLIP\",\n          \"links\": [\n            567\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"CLIPLoader\"\n      },\n      \"widgets_values\": [\n        \"cascade_clip.safetensors\",\n        \"stable_cascade\"\n      ]\n    },\n    {\n      \"id\": 131,\n      \"type\": \"VAEDecode\",\n      \"pos\": [\n        692,\n        858\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 26,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"samples\",\n          \"type\": \"LATENT\",\n          \"link\": 576\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 591,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            462,\n            469,\n            475\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEDecode\"\n      }\n    },\n    {\n      \"id\": 137,\n      \"type\": \"VAEEncode\",\n      \"pos\": [\n        686,\n        1459\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 35,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"pixels\",\n          \"type\": \"IMAGE\",\n          \"link\": 489\n        },\n        {\n          \"name\": \"vae\",\n          \"type\": \"VAE\",\n          \"link\": 574,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            438\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAEEncode\"\n      }\n    },\n    {\n      \"id\": 136,\n      \"type\": \"DeforumCacheLatentNode\",\n      \"pos\": [\n        691,\n        1576\n      ],\n      \"size\": {\n        \"0\": 210,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 36,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 438\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumCacheLatentNode\"\n      }\n    },\n    {\n      \"id\": 139,\n      \"type\": \"DeforumAddNoiseNode\",\n      \"pos\": [\n        644,\n        1355\n      ],\n      \"size\": {\n        \"0\": 324.6453552246094,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 33,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 488,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 443,\n          \"slot_index\": 1\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            456,\n            489\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumAddNoiseNode\"\n      }\n    },\n    {\n      \"id\": 138,\n      \"type\": \"DeforumFrameWarpNode\",\n      \"pos\": [\n        661,\n        1237\n      ],\n      \"size\": {\n        \"0\": 304.79998779296875,\n        \"1\": 46\n      },\n      \"flags\": {},\n      \"order\": 30,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 440\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 441\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            455,\n            488\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumFrameWarpNode\"\n      }\n    },\n    {\n      \"id\": 142,\n      \"type\": \"DeforumHybridMotionNode\",\n      \"pos\": [\n        650,\n        1028\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 98\n      },\n      \"flags\": {},\n      \"order\": 28,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 469\n        },\n        {\n          \"name\": \"hybrid_image\",\n          \"type\": \"IMAGE\",\n          \"link\": 453\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 447\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            440,\n            460\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumHybridMotionNode\"\n      },\n      \"widgets_values\": [\n        \"RAFT\"\n      ]\n    },\n    {\n      \"id\": 133,\n      \"type\": \"DeforumConditioningBlendNode\",\n      \"pos\": [\n        647,\n        17\n      ],\n      \"size\": {\n        \"0\": 342.5999755859375,\n        \"1\": 78\n      },\n      \"flags\": {},\n      \"order\": 20,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"clip\",\n          \"type\": \"CLIP\",\n          \"link\": 567\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 588\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"POSITIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            430,\n            569\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"NEGATIVE\",\n          \"type\": \"CONDITIONING\",\n          \"links\": [\n            431,\n            572\n          ],\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumConditioningBlendNode\"\n      },\n      \"widgets_values\": [\n        \"none\"\n      ]\n    },\n    {\n      \"id\": 192,\n      \"type\": \"KSampler\",\n      \"pos\": [\n        631,\n        499\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 262\n      },\n      \"flags\": {},\n      \"order\": 25,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"model\",\n          \"type\": \"MODEL\",\n          \"link\": 578,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"positive\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 571\n        },\n        {\n          \"name\": \"negative\",\n          \"type\": \"CONDITIONING\",\n          \"link\": 572,\n          \"slot_index\": 2\n        },\n        {\n          \"name\": \"latent_image\",\n          \"type\": \"LATENT\",\n          \"link\": 573\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            576\n          ],\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"KSampler\"\n      },\n      \"widgets_values\": [\n        974706146007189,\n        \"randomize\",\n        10,\n        1.1,\n        \"ddpm\",\n        \"normal\",\n        1\n      ]\n    },\n    {\n      \"id\": 126,\n      \"type\": \"DeforumPromptNode\",\n      \"pos\": [\n        -660,\n        -60\n      ],\n      \"size\": {\n        \"0\": 313.04736328125,\n        \"1\": 276.55352783203125\n      },\n      \"flags\": {},\n      \"order\": 1,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": null\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            537\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumPromptNode\"\n      },\n      \"widgets_values\": [\n        \"0:'unreal 3d render of a cyberpunk city, the text saying \\\"ComfyUI\\\"'\"\n      ]\n    },\n    {\n      \"id\": 152,\n      \"type\": \"DeforumTranslationParamsNode\",\n      \"pos\": [\n        -670,\n        780\n      ],\n      \"size\": {\n        \"0\": 317.4000244140625,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 13,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 498\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            535\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumTranslationParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0:(0)\",\n        \"0:(1.0)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(10)\",\n        \"0:(0.5)\",\n        \"0:(0.5)\",\n        \"0:(0)\",\n        \"0:(0)\",\n        \"0:(0)\"\n      ]\n    },\n    {\n      \"id\": 153,\n      \"type\": \"DeforumNoiseParamsNode\",\n      \"pos\": [\n        -310,\n        990\n      ],\n      \"size\": {\n        \"0\": 272.06170654296875,\n        \"1\": 298\n      },\n      \"flags\": {},\n      \"order\": 18,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 541\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            585\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumNoiseParamsNode\"\n      },\n      \"widgets_values\": [\n        true,\n        \"0: (0.8)\",\n        \"0: (0.4)\",\n        \"0: (5)\",\n        \"0: (1.0)\",\n        \"0: (0.0)\",\n        \"perlin\",\n        8,\n        8,\n        4,\n        0.5\n      ]\n    },\n    {\n      \"id\": 50,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1120,\n        1440\n      ],\n      \"size\": {\n        \"0\": 189.73870849609375,\n        \"1\": 246\n      },\n      \"flags\": {},\n      \"order\": 34,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 456,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 47,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1120,\n        950\n      ],\n      \"size\": {\n        \"0\": 192.6160888671875,\n        \"1\": 169.56472778320312\n      },\n      \"flags\": {},\n      \"order\": 31,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 460\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 48,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1120,\n        1160\n      ],\n      \"size\": {\n        \"0\": 188.46986389160156,\n        \"1\": 246\n      },\n      \"flags\": {},\n      \"order\": 32,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 455\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 179,\n      \"type\": \"StableCascade_EmptyLatentImage\",\n      \"pos\": [\n        160,\n        890\n      ],\n      \"size\": [\n        252,\n        150\n      ],\n      \"flags\": {},\n      \"order\": 2,\n      \"mode\": 0,\n      \"inputs\": [],\n      \"outputs\": [\n        {\n          \"name\": \"stage_c\",\n          \"type\": \"LATENT\",\n          \"links\": [],\n          \"shape\": 3,\n          \"slot_index\": 0\n        },\n        {\n          \"name\": \"stage_b\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            573\n          ],\n          \"shape\": 3,\n          \"slot_index\": 1\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"StableCascade_EmptyLatentImage\"\n      },\n      \"widgets_values\": [\n        1024,\n        1024,\n        42,\n        1\n      ]\n    },\n    {\n      \"id\": 195,\n      \"type\": \"VAELoader\",\n      \"pos\": [\n        130,\n        1100\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 3,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            591\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAELoader\"\n      },\n      \"widgets_values\": [\n        \"stage_a.safetensors\"\n      ]\n    },\n    {\n      \"id\": 177,\n      \"type\": \"UNETLoader\",\n      \"pos\": [\n        140,\n        180\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 4,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            578\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"UNETLoader\"\n      },\n      \"widgets_values\": [\n        \"stage_b_bf16.safetensors\"\n      ]\n    },\n    {\n      \"id\": 176,\n      \"type\": \"UNETLoader\",\n      \"pos\": [\n        140,\n        60\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 5,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"MODEL\",\n          \"type\": \"MODEL\",\n          \"links\": [\n            577\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"UNETLoader\"\n      },\n      \"widgets_values\": [\n        \"stage_c_bf16.safetensors\"\n      ]\n    },\n    {\n      \"id\": 184,\n      \"type\": \"VAELoader\",\n      \"pos\": [\n        133,\n        1221\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 58\n      },\n      \"flags\": {},\n      \"order\": 6,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"VAE\",\n          \"type\": \"VAE\",\n          \"links\": [\n            574\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"VAELoader\"\n      },\n      \"widgets_values\": [\n        \"effnet_encoder.safetensors\"\n      ]\n    },\n    {\n      \"id\": 170,\n      \"type\": \"Note\",\n      \"pos\": [\n        207,\n        -277\n      ],\n      \"size\": {\n        \"0\": 633.6494140625,\n        \"1\": 221.03656005859375\n      },\n      \"flags\": {},\n      \"order\": 7,\n      \"mode\": 0,\n      \"properties\": {\n        \"text\": \"\"\n      },\n      \"widgets_values\": [\n        \"Welcome to Deforum!\\n\\nThe below graph provides a pluggable Deforum Animation pipeline in ComfyUI. It works frame-by-frame, so the best practice is to enable Extra options, and Auto Queue.\\n\\nDeforum Parameter and Schedule nodes represent all settings available to consume after chaining up by the Deforum Iterator node, which keeps track of the current frame, generates/gets the cached latent to be denoised in the next pass. The parameters and schedule's are the same as in the auto1111 extension and in the Colab version.\\n\\nHybrid nodes are currently bypassed (purple), enabling them, and selecting a video transforms the pipeline into Deforum Hybrid. Iteration can be reset to frame 0 with the reset value being set to 1 on the iterator node. Don't forget to switch it back to 0 to use the generated image/latent.\\\"\\n\\nDeforum Video Save node dumps its collected frames when the current frame's id reaches/has passed max_frames.\"\n      ],\n      \"color\": \"#432\",\n      \"bgcolor\": \"#653\"\n    },\n    {\n      \"id\": 62,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        266,\n        1378\n      ],\n      \"size\": {\n        \"0\": 309.7149658203125,\n        \"1\": 251.35919189453125\n      },\n      \"flags\": {},\n      \"order\": 11,\n      \"mode\": 4,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 224,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 88,\n      \"type\": \"DeforumLoadVideo\",\n      \"pos\": [\n        -104,\n        1363\n      ],\n      \"size\": {\n        \"0\": 317.5899658203125,\n        \"1\": 294\n      },\n      \"flags\": {},\n      \"order\": 8,\n      \"mode\": 4,\n      \"outputs\": [\n        {\n          \"name\": \"IMAGE\",\n          \"type\": \"IMAGE\",\n          \"links\": [\n            224,\n            453\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumLoadVideo\"\n      },\n      \"widgets_values\": [\n        \"Recording 2024-02-08 214819.mp4\",\n        \"image\"\n      ]\n    },\n    {\n      \"id\": 149,\n      \"type\": \"DeforumBaseParamsNode\",\n      \"pos\": [\n        -310,\n        -60\n      ],\n      \"size\": {\n        \"0\": 281.7219543457031,\n        \"1\": 448.62408447265625\n      },\n      \"flags\": {},\n      \"order\": 15,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 538\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            539\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumBaseParamsNode\"\n      },\n      \"widgets_values\": [\n        1024,\n        1024,\n        \"ddpm\",\n        \"normal\",\n        20,\n        6,\n        1,\n        \"Deforum_{timestring}\",\n        \"random\",\n        1,\n        \"output/deforum\",\n        0.4,\n        true,\n        false,\n        false,\n        true,\n        false\n      ]\n    },\n    {\n      \"id\": 194,\n      \"type\": \"DeforumIteratorNode\",\n      \"pos\": [\n        86,\n        451\n      ],\n      \"size\": {\n        \"0\": 393,\n        \"1\": 286\n      },\n      \"flags\": {},\n      \"order\": 19,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 585\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"link\": 586\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"links\": [\n            587,\n            588,\n            589\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"latent\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            590\n          ],\n          \"shape\": 3\n        },\n        {\n          \"name\": \"positive_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        },\n        {\n          \"name\": \"negative_prompt\",\n          \"type\": \"STRING\",\n          \"links\": null,\n          \"shape\": 3\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumIteratorNode\"\n      },\n      \"widgets_values\": [\n        \"stable_cascade\",\n        828657964052916,\n        \"randomize\",\n        0,\n        0.8,\n        0.1,\n        false,\n        false\n      ]\n    },\n    {\n      \"id\": 102,\n      \"type\": \"DeforumVideoSaveNode\",\n      \"pos\": [\n        1383,\n        973\n      ],\n      \"size\": {\n        \"0\": 315,\n        \"1\": 150\n      },\n      \"flags\": {},\n      \"order\": 29,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"image\",\n          \"type\": \"IMAGE\",\n          \"link\": 475\n        },\n        {\n          \"name\": \"deforum_frame_data\",\n          \"type\": \"DEFORUM_FRAME_DATA\",\n          \"link\": 587\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumVideoSaveNode\"\n      },\n      \"widgets_values\": [\n        \"deforum_\",\n        12,\n        \"max_frames\",\n        0\n      ]\n    },\n    {\n      \"id\": 16,\n      \"type\": \"PreviewImage\",\n      \"pos\": [\n        1383,\n        -37\n      ],\n      \"size\": {\n        \"0\": 894.2135009765625,\n        \"1\": 941.8845825195312\n      },\n      \"flags\": {},\n      \"order\": 27,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"images\",\n          \"type\": \"IMAGE\",\n          \"link\": 462,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"PreviewImage\"\n      }\n    },\n    {\n      \"id\": 154,\n      \"type\": \"DeforumDiffusionParamsNode\",\n      \"pos\": [\n        -309,\n        440\n      ],\n      \"size\": {\n        \"0\": 278.891845703125,\n        \"1\": 274\n      },\n      \"flags\": {},\n      \"order\": 16,\n      \"mode\": 0,\n      \"inputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"link\": 539\n        }\n      ],\n      \"outputs\": [\n        {\n          \"name\": \"deforum_data\",\n          \"type\": \"deforum_data\",\n          \"links\": [\n            540\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumDiffusionParamsNode\"\n      },\n      \"widgets_values\": [\n        \"0: (0.03)\",\n        \"0: (0.35)\",\n        \"0: (1.0)\",\n        \"0: (4)\",\n        false,\n        \"0: (20)\",\n        false,\n        \"0:(0)\",\n        false,\n        \"0:(1)\"\n      ]\n    },\n    {\n      \"id\": 135,\n      \"type\": \"DeforumGetCachedLatentNode\",\n      \"pos\": [\n        169,\n        800\n      ],\n      \"size\": {\n        \"0\": 218.39999389648438,\n        \"1\": 26\n      },\n      \"flags\": {},\n      \"order\": 9,\n      \"mode\": 0,\n      \"outputs\": [\n        {\n          \"name\": \"LATENT\",\n          \"type\": \"LATENT\",\n          \"links\": [\n            586\n          ],\n          \"shape\": 3,\n          \"slot_index\": 0\n        }\n      ],\n      \"properties\": {\n        \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n      }\n    }\n  ],\n  \"links\": [\n    [\n      224,\n      88,\n      0,\n      62,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      430,\n      133,\n      0,\n      130,\n      2,\n      \"CONDITIONING\"\n    ],\n    [\n      431,\n      133,\n      1,\n      130,\n      3,\n      \"CONDITIONING\"\n    ],\n    [\n      438,\n      137,\n      0,\n      136,\n      0,\n      \"LATENT\"\n    ],\n    [\n      440,\n      142,\n      0,\n      138,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      441,\n      141,\n      0,\n      138,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      443,\n      141,\n      0,\n      139,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      447,\n      141,\n      0,\n      142,\n      2,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      453,\n      88,\n      0,\n      142,\n      1,\n      \"IMAGE\"\n    ],\n    [\n      455,\n      138,\n      0,\n      48,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      456,\n      139,\n      0,\n      50,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      459,\n      141,\n      0,\n      130,\n      4,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      460,\n      142,\n      0,\n      47,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      462,\n      131,\n      0,\n      16,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      469,\n      131,\n      0,\n      142,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      475,\n      131,\n      0,\n      102,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      488,\n      138,\n      0,\n      139,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      489,\n      139,\n      0,\n      137,\n      0,\n      \"IMAGE\"\n    ],\n    [\n      497,\n      150,\n      0,\n      151,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      498,\n      151,\n      0,\n      152,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      535,\n      152,\n      0,\n      155,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      537,\n      126,\n      0,\n      150,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      538,\n      155,\n      0,\n      149,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      539,\n      149,\n      0,\n      154,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      540,\n      154,\n      0,\n      157,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      541,\n      157,\n      0,\n      153,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      567,\n      181,\n      0,\n      133,\n      0,\n      \"CLIP\"\n    ],\n    [\n      568,\n      130,\n      0,\n      190,\n      1,\n      \"LATENT\"\n    ],\n    [\n      569,\n      133,\n      0,\n      191,\n      0,\n      \"CONDITIONING\"\n    ],\n    [\n      570,\n      191,\n      0,\n      190,\n      0,\n      \"CONDITIONING\"\n    ],\n    [\n      571,\n      190,\n      0,\n      192,\n      1,\n      \"CONDITIONING\"\n    ],\n    [\n      572,\n      133,\n      1,\n      192,\n      2,\n      \"CONDITIONING\"\n    ],\n    [\n      573,\n      179,\n      1,\n      192,\n      3,\n      \"LATENT\"\n    ],\n    [\n      574,\n      184,\n      0,\n      137,\n      1,\n      \"VAE\"\n    ],\n    [\n      576,\n      192,\n      0,\n      131,\n      0,\n      \"LATENT\"\n    ],\n    [\n      577,\n      176,\n      0,\n      130,\n      0,\n      \"MODEL\"\n    ],\n    [\n      578,\n      177,\n      0,\n      192,\n      0,\n      \"MODEL\"\n    ],\n    [\n      585,\n      153,\n      0,\n      194,\n      0,\n      \"deforum_data\"\n    ],\n    [\n      586,\n      135,\n      0,\n      194,\n      1,\n      \"LATENT\"\n    ],\n    [\n      587,\n      194,\n      0,\n      102,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      588,\n      194,\n      0,\n      133,\n      1,\n      \"DEFORUM_FRAME_DATA\"\n    ],\n    [\n      589,\n      194,\n      0,\n      141,\n      0,\n      \"*\"\n    ],\n    [\n      590,\n      194,\n      1,\n      130,\n      1,\n      \"LATENT\"\n    ],\n    [\n      591,\n      195,\n      0,\n      131,\n      1,\n      \"VAE\"\n    ]\n  ],\n  \"groups\": [],\n  \"config\": {},\n  \"extra\": {\n    \"groupNodes\": {\n      \"Deforum Parameters\": {\n        \"nodes\": [\n          {\n            \"type\": \"DeforumBaseParamsNode\",\n            \"pos\": [\n              -325,\n              78\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 490\n            },\n            \"flags\": {},\n            \"order\": 4,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumBaseParamsNode\"\n            },\n            \"widgets_values\": [\n              1366,\n              1024,\n              \"dpmpp_2m_sde\",\n              \"karras\",\n              14,\n              6,\n              1,\n              \"Deforum_{timestring}\",\n              \"iter\",\n              1,\n              \"output/deforum\",\n              0.4,\n              true,\n              false,\n              false,\n              true,\n              false\n            ],\n            \"index\": 0\n          },\n          {\n            \"type\": \"DeforumAnimParamsNode\",\n            \"pos\": [\n              -315,\n              598\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 178\n            },\n            \"flags\": {},\n            \"order\": 5,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumAnimParamsNode\"\n            },\n            \"widgets_values\": [\n              \"3D\",\n              150,\n              \"wrap\",\n              false,\n              \"20230129210106\",\n              false\n            ],\n            \"index\": 1\n          },\n          {\n            \"type\": \"DeforumDepthParamsNode\",\n            \"pos\": [\n              45,\n              68\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 178\n            },\n            \"flags\": {},\n            \"order\": 9,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumDepthParamsNode\"\n            },\n            \"widgets_values\": [\n              true,\n              \"Zoe\",\n              0.2,\n              \"border\",\n              \"bicubic\",\n              false\n            ],\n            \"index\": 2\n          },\n          {\n            \"type\": \"DeforumTranslationParamsNode\",\n            \"pos\": [\n              45,\n              308\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 274\n            },\n            \"flags\": {},\n            \"order\": 10,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumTranslationParamsNode\"\n            },\n            \"widgets_values\": [\n              \"0:(0)\",\n              \"0:(1.0)\",\n              \"0:(0)\",\n              \"0:(0)\",\n              \"0:(0)\",\n              \"0:(0.5)\",\n              \"0:(0.5)\",\n              \"0:(0)\",\n              \"0:(0)\",\n              \"0:(0)\"\n            ],\n            \"index\": 3\n          },\n          {\n            \"type\": \"DeforumNoiseParamsNode\",\n            \"pos\": [\n              45,\n              638\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 298\n            },\n            \"flags\": {},\n            \"order\": 15,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumNoiseParamsNode\"\n            },\n            \"widgets_values\": [\n              true,\n              \"0: (0.4)\",\n              \"0: (0.1)\",\n              \"0: (5)\",\n              \"0: (1.0)\",\n              \"0: (0.0)\",\n              \"perlin\",\n              8,\n              8,\n              4,\n              0.5\n            ],\n            \"index\": 4\n          },\n          {\n            \"type\": \"DeforumDiffusionParamsNode\",\n            \"pos\": [\n              -315,\n              858\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 274\n            },\n            \"flags\": {},\n            \"order\": 16,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumDiffusionParamsNode\"\n            },\n            \"widgets_values\": [\n              \"0: (0.03)\",\n              \"0: (0.45)\",\n              \"0: (1.0)\",\n              \"0: (5)\",\n              false,\n              \"0: (25)\",\n              false,\n              \"0:(0)\",\n              false,\n              \"0:(1)\"\n            ],\n            \"index\": 5\n          },\n          {\n            \"type\": \"DeforumColorParamsNode\",\n            \"pos\": [\n              40,\n              988\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 154\n            },\n            \"flags\": {},\n            \"order\": 17,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumColorParamsNode\"\n            },\n            \"widgets_values\": [\n              \"Image\",\n              \"\",\n              1,\n              false,\n              false\n            ],\n            \"index\": 6\n          }\n        ],\n        \"links\": [\n          [\n            null,\n            1,\n            0,\n            0,\n            112,\n            \"deforum_data\"\n          ],\n          [\n            0,\n            0,\n            1,\n            0,\n            20,\n            \"deforum_data\"\n          ],\n          [\n            1,\n            0,\n            2,\n            0,\n            21,\n            \"deforum_data\"\n          ],\n          [\n            2,\n            0,\n            3,\n            0,\n            42,\n            \"deforum_data\"\n          ],\n          [\n            5,\n            0,\n            4,\n            0,\n            52,\n            \"deforum_data\"\n          ],\n          [\n            3,\n            0,\n            5,\n            0,\n            44,\n            \"deforum_data\"\n          ],\n          [\n            4,\n            0,\n            6,\n            0,\n            51,\n            \"deforum_data\"\n          ]\n        ],\n        \"external\": [\n          [\n            6,\n            0,\n            \"deforum_data\"\n          ]\n        ]\n      },\n      \"Deforum Sampler\": {\n        \"nodes\": [\n          {\n            \"type\": \"DeforumPromptNode\",\n            \"pos\": [\n              845,\n              445\n            ],\n            \"size\": {\n              \"0\": 304.5645751953125,\n              \"1\": 211.12216186523438\n            },\n            \"flags\": {},\n            \"order\": 1,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumPromptNode\"\n            },\n            \"widgets_values\": [\n              \"0:'whirling chaotic tornado, lighthouse, polygons in space, abstract artwork, light illusion'\\n25:'chaotic abstract painting'\"\n            ],\n            \"index\": 0\n          },\n          {\n            \"type\": \"CheckpointLoaderSimple\",\n            \"pos\": [\n              636,\n              -55\n            ],\n            \"size\": {\n              \"0\": 315,\n              \"1\": 98\n            },\n            \"flags\": {},\n            \"order\": 2,\n            \"mode\": 0,\n            \"outputs\": [\n              {\n                \"name\": \"MODEL\",\n                \"type\": \"MODEL\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"CLIP\",\n                \"type\": \"CLIP\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 1\n              },\n              {\n                \"name\": \"VAE\",\n                \"type\": \"VAE\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"CheckpointLoaderSimple\"\n            },\n            \"widgets_values\": [\n              \"protovisionXLHighFidelity3D_beta0520Bakedvae.safetensors\"\n            ],\n            \"index\": 1\n          },\n          {\n            \"type\": \"DeepCache\",\n            \"pos\": [\n              420,\n              100\n            ],\n            \"size\": {\n              \"0\": 315,\n              \"1\": 130\n            },\n            \"flags\": {},\n            \"order\": 5,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"model\",\n                \"type\": \"MODEL\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"MODEL\",\n                \"type\": \"MODEL\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeepCache\"\n            },\n            \"widgets_values\": [\n              4,\n              2,\n              0,\n              1000\n            ],\n            \"index\": 2\n          },\n          {\n            \"type\": \"DeforumIteratorNode\",\n            \"pos\": [\n              420,\n              490\n            ],\n            \"size\": {\n              \"0\": 393,\n              \"1\": 166\n            },\n            \"flags\": {},\n            \"order\": 13,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"deforum_data\",\n                \"type\": \"deforum_data\",\n                \"link\": null\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3\n              },\n              {\n                \"name\": \"positive_prompt\",\n                \"type\": \"STRING\",\n                \"links\": [],\n                \"shape\": 3\n              },\n              {\n                \"name\": \"negative_prompt\",\n                \"type\": \"STRING\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumIteratorNode\"\n            },\n            \"widgets_values\": [\n              282766464392687,\n              \"increment\",\n              0\n            ],\n            \"index\": 3\n          },\n          {\n            \"type\": \"DeforumKSampler\",\n            \"pos\": [\n              460,\n              310\n            ],\n            \"size\": {\n              \"0\": 317.4000244140625,\n              \"1\": 106\n            },\n            \"flags\": {},\n            \"order\": 14,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"model\",\n                \"type\": \"MODEL\",\n                \"link\": null\n              },\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null,\n                \"slot_index\": 1\n              },\n              {\n                \"name\": \"positive\",\n                \"type\": \"CONDITIONING\",\n                \"link\": null\n              },\n              {\n                \"name\": \"negative\",\n                \"type\": \"CONDITIONING\",\n                \"link\": null,\n                \"slot_index\": 3\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumKSampler\"\n            },\n            \"index\": 4\n          },\n          {\n            \"type\": \"VAEDecode\",\n            \"pos\": [\n              920,\n              319\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 15,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"samples\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              },\n              {\n                \"name\": \"vae\",\n                \"type\": \"VAE\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"VAEDecode\"\n            },\n            \"index\": 5\n          },\n          {\n            \"type\": \"DeforumConditioningBlendNode\",\n            \"pos\": [\n              841,\n              148\n            ],\n            \"size\": {\n              \"0\": 342.5999755859375,\n              \"1\": 78\n            },\n            \"flags\": {},\n            \"order\": 16,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"clip\",\n                \"type\": \"CLIP\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"POSITIVE\",\n                \"type\": \"CONDITIONING\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"NEGATIVE\",\n                \"type\": \"CONDITIONING\",\n                \"links\": [],\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumConditioningBlendNode\"\n            },\n            \"widgets_values\": [\n              \"sigmoidal\"\n            ],\n            \"index\": 6\n          }\n        ],\n        \"links\": [\n          [\n            1,\n            0,\n            2,\n            0,\n            115,\n            \"MODEL\"\n          ],\n          [\n            null,\n            0,\n            3,\n            0,\n            113,\n            \"deforum_data\"\n          ],\n          [\n            null,\n            0,\n            3,\n            1,\n            114,\n            \"LATENT\"\n          ],\n          [\n            2,\n            0,\n            4,\n            0,\n            117,\n            \"MODEL\"\n          ],\n          [\n            3,\n            1,\n            4,\n            1,\n            118,\n            \"LATENT\"\n          ],\n          [\n            6,\n            0,\n            4,\n            2,\n            124,\n            \"CONDITIONING\"\n          ],\n          [\n            6,\n            1,\n            4,\n            3,\n            124,\n            \"CONDITIONING\"\n          ],\n          [\n            null,\n            5,\n            4,\n            4,\n            114,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            4,\n            0,\n            5,\n            0,\n            121,\n            \"LATENT\"\n          ],\n          [\n            1,\n            2,\n            5,\n            1,\n            115,\n            \"VAE\"\n          ],\n          [\n            1,\n            1,\n            6,\n            0,\n            115,\n            \"CLIP\"\n          ],\n          [\n            3,\n            0,\n            6,\n            1,\n            118,\n            \"DEFORUM_FRAME_DATA\"\n          ]\n        ],\n        \"external\": [\n          [\n            0,\n            0,\n            \"deforum_data\"\n          ],\n          [\n            1,\n            2,\n            \"VAE\"\n          ],\n          [\n            3,\n            0,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            5,\n            0,\n            \"IMAGE\"\n          ]\n        ]\n      },\n      \"Deforum Operators\": {\n        \"nodes\": [\n          {\n            \"type\": \"DeforumGetCachedLatentNode\",\n            \"pos\": [\n              1356,\n              1073\n            ],\n            \"size\": {\n              \"0\": 218.39999389648438,\n              \"1\": 26\n            },\n            \"flags\": {},\n            \"order\": 1,\n            \"mode\": 0,\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumGetCachedLatentNode\"\n            },\n            \"index\": 0\n          },\n          {\n            \"type\": \"DeforumCacheLatentNode\",\n            \"pos\": [\n              1337,\n              1803\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 26\n            },\n            \"flags\": {},\n            \"order\": 4,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"latent\",\n                \"type\": \"LATENT\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": null,\n                \"shape\": 3\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumCacheLatentNode\"\n            },\n            \"index\": 1\n          },\n          {\n            \"type\": \"VAEEncode\",\n            \"pos\": [\n              1344,\n              1696\n            ],\n            \"size\": {\n              \"0\": 210,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 5,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"pixels\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"vae\",\n                \"type\": \"VAE\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"LATENT\",\n                \"type\": \"LATENT\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"VAEEncode\"\n            },\n            \"index\": 2\n          },\n          {\n            \"type\": \"DeforumFrameWarpNode\",\n            \"pos\": [\n              1301,\n              1392\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 6,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumFrameWarpNode\"\n            },\n            \"index\": 3\n          },\n          {\n            \"type\": \"DeforumAddNoiseNode\",\n            \"pos\": [\n              1301,\n              1590\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 9,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null,\n                \"slot_index\": 1\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumAddNoiseNode\"\n            },\n            \"index\": 4\n          },\n          {\n            \"type\": \"DeforumColorMatchNode\",\n            \"pos\": [\n              1303,\n              1157\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 11,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumColorMatchNode\"\n            },\n            \"index\": 5\n          },\n          {\n            \"type\": \"Reroute\",\n            \"pos\": [\n              1079,\n              1292\n            ],\n            \"size\": [\n              75,\n              26\n            ],\n            \"flags\": {},\n            \"order\": 12,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"\",\n                \"type\": \"*\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"\",\n                \"type\": \"*\",\n                \"links\": null\n              }\n            ],\n            \"properties\": {\n              \"showOutputText\": false,\n              \"horizontal\": false\n            },\n            \"index\": 6\n          },\n          {\n            \"type\": \"DeforumHybridMotionNode\",\n            \"pos\": [\n              1298,\n              1249\n            ],\n            \"size\": {\n              \"0\": 315,\n              \"1\": 98\n            },\n            \"flags\": {},\n            \"order\": 14,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"hybrid_image\",\n                \"type\": \"IMAGE\",\n                \"link\": null\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumHybridMotionNode\"\n            },\n            \"widgets_values\": [\n              \"RAFT\"\n            ],\n            \"index\": 7\n          },\n          {\n            \"type\": \"DeforumColorMatchNode\",\n            \"pos\": [\n              1297,\n              1492\n            ],\n            \"size\": {\n              \"0\": 304.79998779296875,\n              \"1\": 46\n            },\n            \"flags\": {},\n            \"order\": 15,\n            \"mode\": 0,\n            \"inputs\": [\n              {\n                \"name\": \"image\",\n                \"type\": \"IMAGE\",\n                \"link\": null,\n                \"slot_index\": 0\n              },\n              {\n                \"name\": \"deforum_frame_data\",\n                \"type\": \"DEFORUM_FRAME_DATA\",\n                \"link\": null\n              }\n            ],\n            \"outputs\": [\n              {\n                \"name\": \"IMAGE\",\n                \"type\": \"IMAGE\",\n                \"links\": [],\n                \"shape\": 3,\n                \"slot_index\": 0\n              }\n            ],\n            \"properties\": {\n              \"Node name for S&R\": \"DeforumColorMatchNode\"\n            },\n            \"index\": 8\n          }\n        ],\n        \"links\": [\n          [\n            2,\n            0,\n            1,\n            0,\n            36,\n            \"LATENT\"\n          ],\n          [\n            8,\n            0,\n            2,\n            0,\n            109,\n            \"IMAGE\"\n          ],\n          [\n            null,\n            0,\n            2,\n            1,\n            112,\n            \"VAE\"\n          ],\n          [\n            7,\n            0,\n            3,\n            0,\n            108,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            3,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            8,\n            0,\n            4,\n            0,\n            109,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            4,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            null,\n            3,\n            5,\n            0,\n            112,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            5,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            null,\n            2,\n            6,\n            0,\n            112,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            5,\n            0,\n            7,\n            0,\n            95,\n            \"IMAGE\"\n          ],\n          [\n            null,\n            0,\n            7,\n            1,\n            88,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            7,\n            2,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            3,\n            0,\n            8,\n            0,\n            41,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            8,\n            1,\n            98,\n            \"DEFORUM_FRAME_DATA\"\n          ]\n        ],\n        \"external\": [\n          [\n            0,\n            0,\n            \"LATENT\"\n          ],\n          [\n            3,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            4,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            5,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            6,\n            0,\n            \"DEFORUM_FRAME_DATA\"\n          ],\n          [\n            7,\n            0,\n            \"IMAGE\"\n          ],\n          [\n            8,\n            0,\n            \"IMAGE\"\n          ]\n        ]\n      }\n    }\n  },\n  \"version\": 0.4\n}\n"
  },
  {
    "path": "install.py",
    "content": "import os\nimport shutil\nimport sys\nimport subprocess\nimport threading\nimport locale\nimport traceback\nimport re\nimport os\nimport subprocess\nimport sys\nimport platform\n\nif sys.argv[0] == 'install.py':\n    sys.path.append('.')   # for portable version\n\n\ncomfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))\n\n\nsys.path.append(comfy_path)\n\n\n# ---\ndef handle_stream(stream, is_stdout):\n    stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')\n\n    for msg in stream:\n        if is_stdout:\n            print(msg, end=\"\", file=sys.stdout)\n        else: \n            print(msg, end=\"\", file=sys.stderr)\n            \n\ndef process_wrap(cmd_str, cwd=None, handler=None):\n    print(f\"[Deforum] EXECUTE: {cmd_str} in '{cwd}'\")\n    process = subprocess.Popen(cmd_str, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1)\n\n    if handler is None:\n        handler = handle_stream\n\n    stdout_thread = threading.Thread(target=handler, args=(process.stdout, True))\n    stderr_thread = threading.Thread(target=handler, args=(process.stderr, False))\n\n    stdout_thread.start()\n    stderr_thread.start()\n\n    stdout_thread.join()\n    stderr_thread.join()\n\n    return process.wait()\n# ---\n\n\npip_list = None\n\n\ndef get_installed_packages():\n    global pip_list\n\n    if pip_list is None:\n        try:\n            result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)\n            pip_list = set([line.split()[0].lower() for line in result.split('\\n') if line.strip()])\n        except subprocess.CalledProcessError as e:\n            print(f\"[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.\")\n            return set()\n    \n    return pip_list\n    \n\ndef is_installed(name):\n    name = name.strip()\n    pattern = r'([^<>!=]+)([<>!=]=?)'\n    match = re.search(pattern, name)\n    \n    if match:\n        name = match.group(1)\n        \n    result = name.lower() in get_installed_packages()\n    return result\n    \n\ndef is_requirements_installed(file_path):\n    print(f\"req_path: {file_path}\")\n    if os.path.exists(file_path):\n        with open(file_path, 'r') as file:\n            lines = file.readlines()\n            for line in lines:\n                if not is_installed(line):\n                    return False\n                    \n    return True\ndef find_path(name: str, path: str = None) -> str:\n    \"\"\"\n    Recursively looks at parent folders starting from the given path until it finds the given name.\n    Returns the path as a Path object if found, or None otherwise.\n    \"\"\"\n    # If no path is given, use the current working directory\n    if path is None:\n        path = os.getcwd()\n\n    # Check if the current directory contains the name\n    if name in os.listdir(path):\n        path_name = os.path.join(path, name)\n        print(f\"{name} found: {path_name}\")\n        return path_name\n\n    # Get the parent directory\n    parent_directory = os.path.dirname(path)\n\n    # If the parent directory is the same as the current directory, we've reached the root and stop the search\n    if parent_directory == path:\n        return None\n\n    # Recursively call the function with the parent directory\n    return find_path(name, parent_directory)\n\n\ndef run_git_command(command: str, working_dir: str) -> None:\n    \"\"\"\n    Runs a git command in the specified working directory and handles basic errors.\n    \"\"\"\n    try:\n        subprocess.run(command, shell=True, check=True, cwd=working_dir)\n        print(f\"Successfully executed: {command} in {working_dir}\")\n    except subprocess.CalledProcessError as e:\n        print(f\"Error executing {command} in {working_dir}: {e}\")\n\n\ndef clone_or_pull_repo(repo_url: str, repo_dir: str) -> None:\n    \"\"\"\n    Clones a new repository or updates it if it already exists.\n    \"\"\"\n    if not os.path.exists(repo_dir):\n        os.makedirs(repo_dir, exist_ok=True)\n        run_git_command(f\"git clone {repo_url} .\", repo_dir)\n    else:\n        run_git_command(\"git pull\", repo_dir)\n\ndef install():\n    repositories = [\n        \"https://github.com/ceruleandeep/ComfyUI-LLaVA-Captioner.git\",\n        \"https://github.com/rgthree/rgthree-comfy.git\",\n        \"https://github.com/a1lazydog/ComfyUI-AudioScheduler.git\",\n        \"https://github.com/cubiq/ComfyUI_IPAdapter_plus.git\",\n        \"https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite.git\",\n        \"https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet.git\",\n        \"https://github.com/WASasquatch/was-node-suite-comfyui.git\",\n        \"https://github.com/11cafe/comfyui-workspace-manager.git\",\n        \"https://github.com/cubiq/ComfyUI_essentials.git\",\n        \"https://github.com/FizzleDorf/ComfyUI_FizzNodes.git\",\n        \"https://github.com/ltdrdata/ComfyUI-Impact-Pack.git\",\n        \"https://github.com/Fannovel16/ComfyUI-Frame-Interpolation.git\",\n        \"https://github.com/Fannovel16/ComfyUI-Video-Matting.git\",\n        \"https://github.com/crystian/ComfyUI-Crystools.git\"\n        # Add more repositories as needed\n    ]\n    comfyui_path = find_path(\"ComfyUI\")\n    custom_nodes_path = os.path.join(comfyui_path, 'custom_nodes')\n\n\n\n    for repo_url in repositories:\n        repo_name = repo_url.split('/')[-1].replace('.git', '')\n        repo_dir = os.path.join(custom_nodes_path, repo_name)\n        clone_or_pull_repo(repo_url, repo_dir)\n\n\nimport subprocess\n\n\ndef install_packages():\n    # Install packages from requirements.txt\n    # subprocess.run([\"pip\", \"install\", \"-r\", \"requirements.txt\"], check=True)\n\n    # Force reinstall the deforum-studio package from Git\n    subprocess.run([\"pip\", \"install\", \"--force-reinstall\", \"git+https://github.com/XmYx/deforum-studio.git\"],\n                   check=True)\n\n\ndef get_cuda_version():\n    try:\n        cuda_version = subprocess.check_output([\"nvcc\", \"--version\"]).decode(\"utf-8\")\n        for line in cuda_version.split('\\n'):\n            if \"release\" in line:\n                return line.split('release')[1].split(',')[0].strip().replace('.', '')\n    except Exception as e:\n        print(f\"Error getting CUDA version: {e}\")\n        return None\n\ndef get_torch_version():\n    try:\n        import torch\n        return torch.__version__.split('+')[0]  # Removes the +cuXXX if exists\n    except ImportError:\n        print(\"PyTorch is not installed. Please install PyTorch before proceeding.\")\n        sys.exit(1)\n\ndef construct_wheel_name(cuda_version, py_version, os_name):\n    # cuda_version: e.g., \"110\", \"102\"\n    # py_version: e.g., \"38\", \"37\"\n    # os_name: either \"linux\" or \"win\"\n    os_map = {\"Linux\": \"manylinux2014_x86_64\", \"Windows\": \"win_amd64\"}\n    torch_version = get_torch_version().replace('.', '')\n    cuda_str = f\"cu{cuda_version}\" if cuda_version else \"cpu\"\n    filename = f\"stable_fast-1.0.4+torch{torch_version}{cuda_str}-cp{py_version}-cp{py_version}-{os_map[os_name]}.whl\"\n    return filename\n\ndef install_stable_fast():\n    cuda_version = get_cuda_version()\n    python_version = f\"{sys.version_info.major}{sys.version_info.minor}\"\n    os_name = platform.system()\n    wheel_name = construct_wheel_name(cuda_version, python_version, os_name)\n    url = f\"https://github.com/chengzeyi/stable-fast/releases/download/v1.0.4/{wheel_name}\"\n    print(f\"Attempting to install: {wheel_name}\")\n    subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", url])\n\ndef install_reqs():\n    # Install 'comfy' dependencies\n    subprocess.check_call([\n        'pip', 'install',\n        'einops>=0.6.0',\n        'numexpr>=2.8.4',\n        'matplotlib>=3.7.1',\n        'pandas>=1.5.3',\n        'av>=10.0.0',\n        'pims>=0.6.1',\n        'imageio-ffmpeg>=0.4.8',\n        'rich>=13.3.2',\n        'gdown>=4.7.1',\n        'py3d>=0.0.87',\n        'librosa>=0.10.0.post2',\n        'numpy<2.0.0',\n        'pydub>=0.16.5',\n        'opencv-contrib-python>=4.7.0.68',\n        'loguru>=0.4.0',\n        'python-decouple>=3.8',\n        'timm>=0.6.13',\n        'scikit-image>=0.21.0',\n        'moviepy<2.0.0.dev1'\n    ])\n\n    # Install deforum-studio with 'comfy' extras and no dependencies\n    subprocess.check_call([\n        'pip', 'install', '--no-deps',\n        'git+https://github.com/XmYx/deforum-studio.git#egg=deforum[comfy]'\n    ])\n\n\nif __name__ == \"__main__\":\n    print(\"Installing packages...\")\n    try:\n        install_stable_fast()\n        print(\"Installed Stable Fast 1.0.4\")\n    except:\n        print(\"[warning] Stable Fast Install Failed\")\n\n    try:\n        install_reqs()\n        print(\"Installation complete.\")\n    except Exception as e:\n        print(\"[warning] deforum backend package install failed, if you encounter any issues, please activate your venv and run:\\npip install git+https://github.com/XmYxdeforum-studio.git\\nIf you are using ComfyUI portable, you have to locate your python executable and add that's path before the pip install command.\")\n        pass\n"
  },
  {
    "path": "node_list.json",
    "content": "{\n    \"DeforumBaseParamsNode\": \"\",\n    \"DeforumAnimParamsNode\": \"\",\n    \"DeforumTranslationParamsNode\": \"\",\n    \"DeforumDepthParamsNode\": \"\",\n    \"DeforumNoiseParamsNode\": \"\",\n    \"DeforumColorParamsNode\": \"\",\n    \"DeforumDiffusionParamsNode\": \"\",\n    \"DeforumCadenceParamsNode\": \"\",\n    \"DeforumHybridParamsNode\": \"\",\n    \"DeforumHybridScheduleNode\": \"\",\n    \"DeforumPromptNode\": \"\",\n    \"DeforumAreaPromptNode\": \"\",\n    \"DeforumSingleSampleNode\": \"\",\n    \"DeforumCacheLatentNode\": \"\",\n    \"DeforumGetCachedLatentNode\": \"\",\n    \"DeforumSeedNode\": \"\",\n    \"DeforumIteratorNode\": \"\",\n    \"DeforumKSampler\": \"\",\n    \"DeforumFrameWarpNode\": \"\",\n    \"DeforumColorMatchNode\": \"\",\n    \"DeforumAddNoiseNode\": \"\",\n    \"DeforumHybridMotionNode\": \"\",\n    \"DeforumSetVAEDownscaleRatioNode\": \"\",\n    \"DeforumLoadVideo\": \"\",\n    \"DeforumVideoSaveNode\": \"\",\n    \"DeforumFILMInterpolationNode\": \"\",\n    \"DeforumSimpleInterpolationNode\": \"\",\n    \"DeforumCadenceNode\": \"\",\n    \"DeforumConditioningBlendNode\": \"\"\n}\n\n\n"
  },
  {
    "path": "web/js/deforumIterateNode.js",
    "content": "import { app } from \"../../../scripts/app.js\";\nimport { api } from '../../../scripts/api.js'\nimport { ComfyWidgets } from \"../../../scripts/widgets.js\";\n\n// Many functions copied from:\n// https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite\n// Thank you!\n\n\n// Hijack LiteGraph to sort categories and nodes alphabetically (case-insensitive)\n(function(LiteGraph) {\n    var originalGetNodeTypesCategories = LiteGraph.getNodeTypesCategories;\n    LiteGraph.getNodeTypesCategories = function(filter) {\n        var categories = originalGetNodeTypesCategories.call(this, filter);\n        // Sort categories case-insensitively\n        return categories.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));\n    };\n\n    var originalGetNodeTypesInCategory = LiteGraph.getNodeTypesInCategory;\n    LiteGraph.getNodeTypesInCategory = function(category, filter) {\n        var nodeTypes = originalGetNodeTypesInCategory.call(this, category, filter);\n        // Sort node types case-insensitively by title\n        return nodeTypes.sort((a, b) => a.title.toLowerCase().localeCompare(b.title.toLowerCase()));\n    };\n})(LiteGraph || global.LiteGraph); // Ensure LiteGraph is defined; use global.LiteGraph if it's not directly accessible\n\n\n\ndocument.getElementById(\"comfy-file-input\").accept += \",video/webm,video/mp4\";\n\nfunction chainCallback(object, property, callback) {\n    if (object == undefined) {\n        //This should not happen.\n        console.error(\"Tried to add callback to non-existant object\")\n        return;\n    }\n    if (property in object) {\n        const callback_orig = object[property]\n        object[property] = function () {\n            const r = callback_orig.apply(this, arguments);\n            callback.apply(this, arguments);\n            return r\n        };\n    } else {\n        object[property] = callback;\n    }\n}\n\nasync function uploadFile(file) {\n    //TODO: Add uploaded file to cache with Cache.put()?\n    try {\n        // Wrap file in formdata so it includes filename\n        const body = new FormData();\n        const i = file.webkitRelativePath.lastIndexOf('/');\n        const subfolder = file.webkitRelativePath.slice(0,i+1)\n        const new_file = new File([file], file.name, {\n            type: file.type,\n            lastModified: file.lastModified,\n        });\n        body.append(\"image\", new_file);\n        if (i > 0) {\n            body.append(\"subfolder\", subfolder);\n        }\n        const resp = await api.fetchApi(\"/upload/image\", {\n            method: \"POST\",\n            body,\n        });\n\n        if (resp.status === 200) {\n            return resp.status\n        } else {\n            alert(resp.status + \" - \" + resp.statusText);\n        }\n    } catch (error) {\n        alert(error);\n    }\n}\nfunction fitHeight(node) {\n    node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1] + 20])\n    node?.graph?.setDirtyCanvas(true);\n}\nfunction addVideoPreview(nodeType) {\n    chainCallback(nodeType.prototype, \"onNodeCreated\", function() {\n        var element = document.createElement(\"div\");\n        const previewNode = this;\n        previewNode.fps = 24\n        previewNode.size[1] += 45;\n        var previewWidget = this.addDOMWidget(\"videopreview\", \"preview\", element, {\n            serialize: false,\n            hideOnZoom: false,\n            getValue() {\n                return element.value;\n            },\n            setValue(v) {\n                element.value = v;\n            },\n        });\n        previewWidget.computeSize = function(width) {\n            if (this.aspectRatio && !this.parentEl.hidden) {\n                let height = (previewNode.size[0]-20)/ this.aspectRatio + 10;\n                if (!(height > 0)) {\n                    height = 0;\n                }\n                this.computedHeight = height + 50;\n                return [width, height];\n            }\n            return [width, -4];//no loaded src, widget should not display\n        }\n        //element.style['pointer-events'] = \"none\"\n        previewWidget.value = {hidden: false, paused: false, params: {}}\n        previewWidget.parentEl = document.createElement(\"div\");\n        previewWidget.parentEl.className = \"deforumVideoSavePreview\";\n        previewWidget.parentEl.style['width'] = \"100%\"\n        element.appendChild(previewWidget.parentEl);\n        previewWidget.imgEl = document.createElement(\"img\");\n        previewWidget.imgEl.style['width'] = \"100%\"\n        previewWidget.imgEl.hidden = true;\n        // Create an audio element for audio playback\n        previewWidget.audioEl = document.createElement(\"audio\");\n        previewWidget.audioEl.controls = true; // Optional: Show controls\n        previewWidget.audioEl.loop = true; // Enable audio looping by default\n        previewWidget.audioEl.style[\"width\"] = \"100%\"; // Make audio widget as wide as the node\n\n        element.appendChild(previewWidget.audioEl); // Append audio element to the DOM\n\n\n\n        previewWidget.parentEl.appendChild(previewWidget.imgEl)\n        let frameIndex = 0;\n        let cachedFrames = this.getCachedFrames(); // Assuming this method exists and retrieves an array of frame data\n        previewWidget.imgEl.onload = () => {\n            previewWidget.aspectRatio = previewWidget.imgEl.naturalWidth / previewWidget.imgEl.naturalHeight;\n            fitHeight(this);\n        };\n\n        // Use the audio element's timeline to control the frame index\n        previewWidget.audioEl.addEventListener('timeupdate', function() {\n            // Calculate the current frame based on the audio current time and fps\n            const currentTime = previewWidget.audioEl.currentTime;\n            frameIndex = Math.floor(currentTime * this.fps) % this.getCachedFrames().length;\n            if (this.playing === false) {\n                updateFrame(frameIndex);\n            }\n        }.bind(this));\n\n\n        function updateFrame(index) {\n            const cachedFrames = previewNode.getCachedFrames(); // Get cached frames\n            if (cachedFrames && cachedFrames.length > 0) {\n                previewWidget.imgEl.hidden = false;\n                previewWidget.imgEl.src = cachedFrames[index];\n            }\n        }\n\n\n        this.playing = false;\n        this.playbackInterval = 41.666666666666664;\n        this.startPlayback = function(playbackInterval) {\n            if (this.playing === false) {\n                this.playing = true;\n                const widget = this; // Capture 'this' to use inside setInterval function\n                this.imageSequenceInterval = setInterval(() => {\n                    const cachedFrames = this.getCachedFrames();\n                    //const displayFrames = cachedFrames.length > 0 ? cachedFrames : frames;\n                    if (cachedFrames && cachedFrames.length > 0) {\n                        previewWidget.imgEl.hidden = false;\n                        previewWidget.imgEl.src = cachedFrames[frameIndex];\n                        frameIndex = (frameIndex + 1) % cachedFrames.length;\n                    }\n                }, this.playbackInterval); // Update frame every 80ms\n            }\n        };\n        // Function to stop playback\n        this.stopPlayback = function() {\n            const prevIndex = previewWidget.audioEl.currentTime\n            if (this.imageSequenceInterval) {\n                clearInterval(this.imageSequenceInterval);\n                this.imageSequenceInterval = null; // Clear the interval ID\n                this.playing = false; // Mark as not playing\n            }\n\n        };\n        this.setPlaybackInterval = function(newInterval) {\n            // Check if the new interval is different from the current playback interval\n            if (this.playbackInterval !== newInterval) {\n                this.playbackInterval = newInterval; // Update the playback interval\n            }\n            console.log(\"New interval\", newInterval)\n\n            const wasPlaying = this.playing; // Check if audio was playing\n            const currentTime = previewWidget.audioEl.currentTime; // Store current playback time\n            if (wasPlaying) {\n                // Wait for the audio to be loaded\n                this.playing = false; // Mark as not playing\n                clearInterval(this.imageSequenceInterval);\n                this.imageSequenceInterval = null; // Clear the interval ID\n                previewWidget.audioEl.oncanplaythrough = function() {\n                    previewWidget.audioEl.currentTime = currentTime; // Seek to the previous playback time\n                    previewWidget.audioEl.play(); // Resume playback\n                    previewWidget.audioEl.oncanplaythrough = null; // Remove the event listener to avoid memory leaks\n                };\n            }\n        };\n        previewWidget.audioEl.addEventListener('play', () => this.startPlayback(previewWidget.playbackInterval));\n        previewWidget.audioEl.addEventListener('pause', () => this.stopPlayback());\n\n        this.updateAudio = function (audioBase64) {\n            //this.cacheAudio(audioBase64);\n//            console.log(this.getCachedAudio().length)\n\n            // Check if audio was playing\n            const wasPlaying = this.playing;\n            const currentTime = previewWidget.audioEl.currentTime;\n            if (previewWidget.audioEl.src !== audioBase64) {\n                previewWidget.audioEl.src = audioBase64;\n                // If the audio was playing, continue playback\n                if (!previewWidget.audioEl.paused) {\n                    previewWidget.audioEl.load(); // Load the new audio source\n//                    previewWidget.audioEl.currentTime = currentTime;\n                    previewWidget.audioEl.play(); // Resume playback\n                    previewWidget.audioEl.currentTime = currentTime;\n\n                }\n            }\n        };\n    });\n}\n\nfunction addUploadWidget(nodeType, nodeData, widgetName, type=\"video\") {\n    chainCallback(nodeType.prototype, \"onNodeCreated\", function() {\n        const pathWidget = this.widgets.find((w) => w.name === widgetName);\n        const fileInput = document.createElement(\"input\");\n        if (type == \"folder\") {\n            Object.assign(fileInput, {\n                type: \"file\",\n                style: \"display: none\",\n                webkitdirectory: true,\n                onchange: async () => {\n                    const directory = fileInput.files[0].webkitRelativePath;\n                    const i = directory.lastIndexOf('/');\n                    if (i <= 0) {\n                        throw \"No directory found\";\n                    }\n                    const path = directory.slice(0,directory.lastIndexOf('/'))\n                    if (pathWidget.options.values.includes(path)) {\n                        alert(\"A folder of the same name already exists\");\n                        return;\n                    }\n                    let successes = 0;\n                    for(const file of fileInput.files) {\n                        if (await uploadFile(file) == 200) {\n                            successes++;\n                        } else {\n                            //Upload failed, but some prior uploads may have succeeded\n                            //Stop future uploads to prevent cascading failures\n                            //and only add to list if an upload has succeeded\n                            if (successes > 0) {\n                                break\n                            } else {\n                                return;\n                            }\n                        }\n                    }\n                    pathWidget.options.values.push(path);\n                    pathWidget.value = path;\n                    if (pathWidget.callback) {\n                        pathWidget.callback(path)\n                    }\n                },\n            });\n        } else if (type == \"video\") {\n            Object.assign(fileInput, {\n                type: \"file\",\n                accept: \"video/webm,video/mp4,video/mkv,image/gif\",\n                style: \"display: none\",\n                onchange: async () => {\n                    if (fileInput.files.length) {\n                        if (await uploadFile(fileInput.files[0]) != 200) {\n                            //upload failed and file can not be added to options\n                            return;\n                        }\n                        const filename = fileInput.files[0].name;\n                        pathWidget.options.values.push(filename);\n                        pathWidget.value = filename;\n                        if (pathWidget.callback) {\n                            pathWidget.callback(filename)\n                        }\n                    }\n                },\n            });\n        } else {\n            throw \"Unknown upload type\"\n        }\n        document.body.append(fileInput);\n        let uploadWidget = this.addWidget(\"button\", \"choose \" + type + \" to upload\", \"image\", () => {\n            //clear the active click event\n            app.canvas.node_widget = null\n\n            fileInput.click();\n        });\n        uploadWidget.options.serialize = false;\n    });\n}\n\nfunction extendNodePrototypeWithFrameCaching(nodeType) {\n    nodeType.prototype.frameCache = []; // Initialize an empty cache for frames\n    nodeType.prototype.audioCache = ''; // Initialize an empty string for audio cache\n\n    // Method to add frames to the cache\n    nodeType.prototype.cacheFrames = function(frames) {\n        this.frameCache = this.frameCache.concat(frames);\n    };\n\n    // Method to cache audio data\n    nodeType.prototype.cacheAudio = function(audioBase64) {\n        this.audioCache = this.audioCache + audioBase64; // Cache the base64 audio data\n    };\n\n    // Method to clear both frame and audio caches\n    nodeType.prototype.clearCache = function() {\n        this.frameCache = [];\n        this.audioCache = '';\n    };\n\n    // Method to get cached frames (unchanged)\n    nodeType.prototype.getCachedFrames = function() {\n        return this.frameCache;\n    };\n\n    // Method to get cached audio\n    nodeType.prototype.getCachedAudio = function() {\n        return this.audioCache;\n    };\n}\n\n\napp.registerExtension({\n\tname: \"deforum.deforumIterator\",\n\tinit() {\n\t\tconst STRING = ComfyWidgets.STRING;\n\t\tComfyWidgets.STRING = function (node, inputName, inputData) {\n\t\t\tconst r = STRING.apply(this, arguments);\n\t\t\tr.widget.dynamicPrompts = inputData?.[1].dynamicPrompts;\n\t\t\treturn r;\n\t\t};\n\t},\n\tbeforeRegisterNodeDef(nodeType, nodeData) {\n\t\tif (nodeType.comfyClass === \"DeforumIteratorNode\") {\n            const onIteratorExecuted = nodeType.prototype.onExecuted\n            nodeType.prototype.onExecuted = function (message) {\n                const r = onIteratorExecuted ? onIteratorExecuted.apply(this, message) : undefined\n                for (const w of this.widgets || []) {\n                    if (w.name === \"reset_counter\") {\n                        const counterWidget = w;\n                        counterWidget.value = false;\n                    } else if (w.name === \"reset_latent\") {\n                        const resetWidget = w;\n                        resetWidget.value = false;\n                    }\n                }\n                const v = app.nodeOutputs?.[this.id + \"\"];\n                if (!this.flags.collapsed && v) {\n\n                    const counter = v[\"counter\"]\n                    const max_frames = v[\"max_frames\"]\n                    const enableAutorun = v[\"enable_autoqueue\"][0]\n\n\n\n                    if (counter[0] >= max_frames[0]) {\n                        if (document.getElementById('autoQueueCheckbox').checked === true) {\n                            document.getElementById('autoQueueCheckbox').click();\n                        }\n                    }\n\n                    if (enableAutorun === true) {\n                        if (document.getElementById('autoQueueCheckbox').checked === false) {\n                            document.getElementById('autoQueueCheckbox').click();\n                            document.getElementById('extraOptions').style.display = 'block';\n                        }\n                    }\n\n\n\n                }\n\n\n            return r\n            }\n\n            const onDrawForeground = nodeType.prototype.onDrawForeground;\n\t\t\tnodeType.prototype.onDrawForeground = function (ctx) {\n\t\t\t\tconst r = onDrawForeground?.apply?.(this, arguments);\n\t\t\t\tconst v = app.nodeOutputs?.[this.id + \"\"];\n\t\t\t\tif (!this.flags.collapsed && v) {\n\n\t\t\t\t\tconst text = v[\"counter\"] + \"\";\n\t\t\t\t\tctx.save();\n\t\t\t\t\tctx.font = \"bold 48px sans-serif\";\n\t\t\t\t\tctx.fillStyle = \"dodgerblue\";\n\t\t\t\t\tconst sz = ctx.measureText(text);\n\t\t\t\t\tctx.fillText(text, (this.size[0]) / 2 - sz.width - 5, LiteGraph.NODE_SLOT_HEIGHT * 3);\n\t\t\t\t\tctx.restore();\n\t\t\t\t}\n\n\t\t\t\treturn r;\n\t\t\t};\n\t\t}  else if (nodeType.comfyClass === \"DeforumBigBoneResetNode\") {\n\n\n            const onResetExecuted = nodeType.prototype.onExecuted\n            nodeType.prototype.onExecuted = function (message) {\n                const r = onResetExecuted ? onResetExecuted.apply(this, message) : undefined\n                for (const w of this.widgets || []) {\n\n                    if (w.name === \"reset_deforum\") {\n                        const counterWidget = w;\n                        counterWidget.value = false;\n                    }\n                }\n\n\n\n            return r\n            }\n\n\n\t\t} else if (nodeType.comfyClass === \"DeforumLoadVideo\") {\n                addUploadWidget(nodeType, nodeData, \"video\");\n\n\t\t} else if (nodeType.comfyClass === \"DeforumVideoSaveNode\") {\n            extendNodePrototypeWithFrameCaching(nodeType);\n            addVideoPreview(nodeType);\n            const onVideoSaveExecuted = nodeType.prototype.onExecuted\n\n            let restoreWidget\n            nodeType.prototype.onExecuted = function (message) {\n\n            const r = onVideoSaveExecuted ? onVideoSaveExecuted.apply(this, message) : undefined\n                let swapSkipSave = false;\n\n            for (const w of this.widgets || []) {\n\n\n                    if (w.name === \"dump_now\") {\n                        const dumpWidget = w;\n                        if (dumpWidget.value === true) {\n                            swapSkipSave = true\n                        }\n                        dumpWidget.value = false;\n                        this.shouldResetAnimation = true;\n                    } else if (w.name === \"skip_save\") {\n                        const saveWidget = w;\n                        if (swapSkipSave === true) {\n                            saveWidget.value = false;\n                        }\n                    } else if (w.name === \"clear_cache\") {\n                        const cacheClearWidget = w;\n                        if (cacheClearWidget.value === true) {\n                            cacheClearWidget.value = false;\n                        }\n                    }else if (w.name === \"restore\") {\n                        restoreWidget = w\n                    }\n                }\n                const output = app.nodeOutputs?.[this.id + \"\"];\n                const should_reset = output[\"should_dump\"]\n                const fps = output[\"fps\"]\n                const millisecondsPerFrame = 1000 / fps[0];\n                this.fps = fps\n\n\n                if (output && \"frames\" in output) { // Safely check if 'frames' is a key in 'output'\n\n\n\n                    if (this.playing === false) {\n                        //this.playing = true;\n\n                        this.setPlaybackInterval(millisecondsPerFrame);\n\n                        this.cacheFrames(output[\"frames\"]);\n                        if (\"audio\" in output) {\n                            this.updateAudio(output[\"audio\"]);\n\n                        }\n                        //this.startPlayback(millisecondsPerFrame);\n                    } else {\n                        this.cacheFrames(output[\"frames\"]);\n\n                        if (\"audio\" in output) {\n                            this.updateAudio(output[\"audio\"]);\n                        }\n\n                    }\n                    if (should_reset[0] === true) {\n                        this.stopPlayback();\n                        this.clearCache();\n                        this.cacheFrames(output[\"frames\"]);\n                        restoreWidget.value = false;\n                    } else {\n                        if (this.getCachedFrames().length < output[\"counter\"]) {\n                            restoreWidget.value = true;\n                            this.clearCache();\n                        } else {\n                            restoreWidget.value = false;\n                        }\n\n\n\n                    }\n                }\n\n\n            return r\n            }\n            const onVideoSaveForeground = nodeType.prototype.onDrawForeground;\n            nodeType.prototype.onDrawForeground = function (ctx) {\n                const r = onVideoSaveForeground?.apply?.(this, arguments);\n                const v = app.nodeOutputs?.[this.id + \"\"];\n                if (!this.flags.collapsed && v) {\n\n                    const text = v[\"counter\"] + \" cached frame(s)\";\n                    ctx.save();\n\n                    // Set font for measuring text width and for drawing\n                    ctx.font = \"bold 14px sans-serif\";\n\n                    // Measure text to center it on the rectangle\n                    const sz = ctx.measureText(text);\n                    const textWidth = sz.width;\n                    const textHeight = 14; // Approximation based on font size\n\n                    // Rectangle dimensions and position\n                    const rectWidth = textWidth + 20; // Padding around text\n                    const rectHeight = textHeight + 10; // Padding around text\n                    const rectX = (this.size[0] - rectWidth) / 2;\n                    const rectY = LiteGraph.NODE_TITLE_HEIGHT - rectHeight / 2 - 15;\n\n                    // Draw rectangle\n                    ctx.fillStyle = \"rgba(0, 0, 0, 0.8)\"; // Semi-transparent dark rectangle\n                    ctx.fillRect(rectX, rectY, rectWidth, rectHeight);\n\n                    // Draw text centered in the rectangle\n                    ctx.fillStyle = \"white\"; // White text color\n                    const textX = (this.size[0] - textWidth) / 2;\n                    const textY = LiteGraph.NODE_TITLE_HEIGHT + textHeight / 2 - 15; // Adjust based on the font size\n\n                    ctx.fillText(text, textX, textY);\n                    ctx.restore();\n\n\n                }\n\n                return r;\n            };\n\n\t\t};\n\t},\n});\nclass FloatingConsole {\n    constructor() {\n        this.element = document.createElement('div');\n        this.element.id = 'floating-console';\n        this.titleBar = this.createTitleBar();\n        this.contentContainer = this.createContentContainer();\n\n        this.element.appendChild(this.titleBar);\n        this.element.appendChild(this.contentContainer);\n\n        document.body.appendChild(this.element);\n\n        this.dragging = false;\n        this.prevX = 0;\n        this.prevY = 0;\n\n        this.setupStyles();\n        this.addEventListeners();\n        this.addMenuButton();\n    }\n\n    setupStyles() {\n        Object.assign(this.element.style, {\n            position: 'fixed',\n            bottom: '10px',\n            right: '10px',\n            width: '300px',\n            maxHeight: '600px',\n            backgroundColor: 'rgba(0, 0, 0, 0.8)',\n            color: 'white',\n            borderRadius: '5px',\n            zIndex: '1000',\n            display: 'none', // Consider starting visible for debugging\n            boxSizing: 'border-box',\n            overflow: 'hidden',\n            resize: 'both',\n        });\n\n        // Ensure the content container allows for scrolling overflow content\n        Object.assign(this.contentContainer.style, {\n            overflowY: 'auto',\n            maxHeight: '565px', // Adjust based on titleBar height to prevent overflow\n        });\n        this.adjustContentContainerSize();\n    }\n    adjustContentContainerSize() {\n        // Calculate available height for content container\n        const titleBarHeight = this.titleBar.offsetHeight;\n        const consoleHeight = this.element.offsetHeight;\n        const availableHeight = consoleHeight - titleBarHeight;\n\n        // Update content container's maxHeight to fill available space\n        this.contentContainer.style.maxHeight = `${availableHeight}px`;\n    }\n    createTitleBar() {\n        const bar = document.createElement('div');\n        bar.textContent = 'Console';\n        Object.assign(bar.style, {\n            padding: '5px',\n            cursor: 'move',\n            backgroundColor: '#333',\n            borderTopLeftRadius: '5px',\n            borderTopRightRadius: '5px',\n            userSelect: 'none',\n        });\n        return bar;\n    }\n\n    createContentContainer() {\n        const container = document.createElement('div');\n        return container;\n    }\n\n    addEventListeners() {\n        this.titleBar.addEventListener('mousedown', (e) => {\n            // Mark as dragging\n            this.dragging = true;\n\n            // Record the initial mouse position\n            this.prevX = e.clientX;\n            this.prevY = e.clientY;\n\n            if (!this.element.style.left || !this.element.style.top) {\n                // Calculate initial left and top based on current position\n                const rect = this.element.getBoundingClientRect();\n                this.element.style.right = ''; // Clear 'right' since we're switching to 'left/top' positioning\n                this.element.style.bottom = ''; // Clear 'bottom' as well\n\n                // Set initial left and top based on the element's current position\n                this.element.style.left = `${rect.left}px`;\n                this.element.style.top = `${rect.top}px`;\n            }\n        });\n\n        document.addEventListener('mousemove', (e) => {\n            if (!this.dragging) return;\n            const dx = e.clientX - this.prevX;\n            const dy = e.clientY - this.prevY;\n\n            const { style } = this.element;\n            style.left = `${parseInt(style.left || 0, 10) + dx}px`;\n            style.top = `${parseInt(style.top || 0, 10) + dy}px`;\n\n            this.prevX = e.clientX;\n            this.prevY = e.clientY;\n        });\n\n        document.addEventListener('mouseup', () => {\n            this.dragging = false;\n            this.adjustContentContainerSize();\n        });\n    }\n\n    addMenuButton() {\n        const menu = document.querySelector(\".comfy-menu\");\n        // Create and append the toggle button for the floating console\n        const consoleToggleButton = document.createElement(\"button\");\n        consoleToggleButton.textContent = \"Toggle Console\";\n        consoleToggleButton.onclick = () => {\n            // Check if the console is currently visible and toggle accordingly\n            if (floatingConsole.isVisible()) {\n                floatingConsole.hide();\n                consoleToggleButton.textContent = \"Show Console\"; // Update button text\n            } else {\n                floatingConsole.show();\n                consoleToggleButton.textContent = \"Hide Console\"; // Update button text\n            }\n        }\n        menu.append(consoleToggleButton);\n    }\n\n    show() {\n        this.element.style.display = 'block';\n    }\n\n    hide() {\n        this.element.style.display = 'none';\n    }\n\n    isVisible() {\n        return this.element.style.display !== 'none';\n    }\n\n    log(message) {\n        const msgElement = document.createElement('div');\n        msgElement.textContent = message;\n        this.contentContainer.appendChild(msgElement);\n        this.contentContainer.scrollTop = this.contentContainer.scrollHeight; // Auto-scroll to bottom\n    }\n\n    clear() {\n        this.contentContainer.innerHTML = '';\n    }\n}\n\n// Instantiate the floating console\nconst floatingConsole = new FloatingConsole();\n\n// Extend the app plugin to handle console_output events\napp.registerExtension({\n    name: \"consoleOutput\",\n    init() {\n        api.addEventListener('console_output', (event) => {\n            floatingConsole.log(event.detail.message);\n        });\n    }\n});"
  }
]