deforum-art / sd-forge-deforum

Deforum extension for stable-diffusion-webui-forge
https://deforum.github.io
Other
34 stars 1 forks source link

[Feature Request]: #18

Open sdman88 opened 6 months ago

sdman88 commented 6 months ago

Is there an existing issue for this?

What would your feature do ?

If possible could the "generate forever/cancel generate forever" options found in txt2img of webui when right-clicking the generate button be added to the deforum generate button to allow for automatic continuous generations?

Proposed workflow

  1. Right-click the generate button to have pop-up box show the generate forever/cancel generate forever options
  2. Selecting "generate forever" will continuously generate animations automatically one after the other until cancelled without need to manually hit generate each time
  3. Selecting "cancel generate forever" would stop the automatic continuous generation and go back to manual mode

Additional information

Screenshot 2024-04-15 152244

Are you going to help adding it?

Did what I could, see my modified webui javascript>contextMenus.js code below to add generate forever/cancel generate forever functionality to deforum

sdman88 commented 6 months ago

Actually think I managed to figure this out via trial and error and some digging into the code of webui. Here is the code I added to my javascript>contextMenus.js file of webui for anyone else looking to add this functionality to deforum. Basically had to modify the contextMenus.js file in notepad and all I did was duplicate the existing txt2img_generate/txt2img_interrupt code and modified it to use deforum tags.

To Use: Copy all of the code that appears below after the colon and paste over all the existing code in contextMenus.js file using notepad. The file appears in javascript folder of webui. Make sure to replace all of the original existing code found in the contextMenus.js file since the code below contains all the original code + the added deforum code. (note: you might want to make backup of original contextMenus.js file and save it in different location not in the webui folder in case something goes wrong or breaks, but my limited testing seemed to work fine):

var contextMenuInit = function() { let eventListenerApplied = false; let menuSpecs = new Map();

const uid = function() {
    return Date.now().toString(36) + Math.random().toString(36).substring(2);
};

function showContextMenu(event, element, menuEntries) {
    let posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
    let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;

    let oldMenu = gradioApp().querySelector('#context-menu');
    if (oldMenu) {
        oldMenu.remove();
    }

    let baseStyle = window.getComputedStyle(uiCurrentTab);

    const contextMenu = document.createElement('nav');
    contextMenu.id = "context-menu";
    contextMenu.style.background = baseStyle.background;
    contextMenu.style.color = baseStyle.color;
    contextMenu.style.fontFamily = baseStyle.fontFamily;
    contextMenu.style.top = posy + 'px';
    contextMenu.style.left = posx + 'px';

    const contextMenuList = document.createElement('ul');
    contextMenuList.className = 'context-menu-items';
    contextMenu.append(contextMenuList);

    menuEntries.forEach(function(entry) {
        let contextMenuEntry = document.createElement('a');
        contextMenuEntry.innerHTML = entry['name'];
        contextMenuEntry.addEventListener("click", function() {
            entry['func']();
        });
        contextMenuList.append(contextMenuEntry);

    });

    gradioApp().appendChild(contextMenu);

    let menuWidth = contextMenu.offsetWidth + 4;
    let menuHeight = contextMenu.offsetHeight + 4;

    let windowWidth = window.innerWidth;
    let windowHeight = window.innerHeight;

    if ((windowWidth - posx) < menuWidth) {
        contextMenu.style.left = windowWidth - menuWidth + "px";
    }

    if ((windowHeight - posy) < menuHeight) {
        contextMenu.style.top = windowHeight - menuHeight + "px";
    }

}

function appendContextMenuOption(targetElementSelector, entryName, entryFunction) {

    var currentItems = menuSpecs.get(targetElementSelector);

    if (!currentItems) {
        currentItems = [];
        menuSpecs.set(targetElementSelector, currentItems);
    }
    let newItem = {
        id: targetElementSelector + '_' + uid(),
        name: entryName,
        func: entryFunction,
        isNew: true
    };

    currentItems.push(newItem);
    return newItem['id'];
}

function removeContextMenuOption(uid) {
    menuSpecs.forEach(function(v) {
        let index = -1;
        v.forEach(function(e, ei) {
            if (e['id'] == uid) {
                index = ei;
            }
        });
        if (index >= 0) {
            v.splice(index, 1);
        }
    });
}

function addContextMenuEventListener() {
    if (eventListenerApplied) {
        return;
    }
    gradioApp().addEventListener("click", function(e) {
        if (!e.isTrusted) {
            return;
        }

        let oldMenu = gradioApp().querySelector('#context-menu');
        if (oldMenu) {
            oldMenu.remove();
        }
    });
    gradioApp().addEventListener("contextmenu", function(e) {
        let oldMenu = gradioApp().querySelector('#context-menu');
        if (oldMenu) {
            oldMenu.remove();
        }
        menuSpecs.forEach(function(v, k) {
            if (e.composedPath()[0].matches(k)) {
                showContextMenu(e, e.composedPath()[0], v);
                e.preventDefault();
            }
        });
    });
    eventListenerApplied = true;

}

return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener];

};

var initResponse = contextMenuInit(); var appendContextMenuOption = initResponse[0]; var removeContextMenuOption = initResponse[1]; var addContextMenuEventListener = initResponse[2];

(function() { //Start example Context Menu Items let generateOnRepeat = function(genbuttonid, interruptbuttonid) { let genbutton = gradioApp().querySelector(genbuttonid); let interruptbutton = gradioApp().querySelector(interruptbuttonid); if (!interruptbutton.offsetParent) { genbutton.click(); } clearInterval(window.generateOnRepeatInterval); window.generateOnRepeatInterval = setInterval(function() { if (!interruptbutton.offsetParent) { genbutton.click(); } }, 500); };

let generateOnRepeat_deforum = function() { generateOnRepeat('#deforum_generate', '#deforum_interrupt'); };

let generateOnRepeat_txt2img = function() {
    generateOnRepeat('#txt2img_generate', '#txt2img_interrupt');
};

let generateOnRepeat_img2img = function() {
    generateOnRepeat('#img2img_generate', '#img2img_interrupt');
};

appendContextMenuOption('#txt2img_generate', 'Generate forever', generateOnRepeat_txt2img);
appendContextMenuOption('#txt2img_interrupt', 'Generate forever', generateOnRepeat_txt2img);
appendContextMenuOption('#img2img_generate', 'Generate forever', generateOnRepeat_img2img);
appendContextMenuOption('#img2img_interrupt', 'Generate forever', generateOnRepeat_img2img);

appendContextMenuOption('#deforum_generate', 'Generate forever', generateOnRepeat_deforum); appendContextMenuOption('#deforum_interrupt', 'Generate forever', generateOnRepeat_deforum);

let cancelGenerateForever = function() {
    clearInterval(window.generateOnRepeatInterval);
};

appendContextMenuOption('#txt2img_interrupt', 'Cancel generate forever', cancelGenerateForever);
appendContextMenuOption('#txt2img_generate', 'Cancel generate forever', cancelGenerateForever);
appendContextMenuOption('#img2img_interrupt', 'Cancel generate forever', cancelGenerateForever);
appendContextMenuOption('#img2img_generate', 'Cancel generate forever', cancelGenerateForever);

appendContextMenuOption('#deforum_interrupt', 'Cancel generate forever', cancelGenerateForever); appendContextMenuOption('#deforum_generate', 'Cancel generate forever', cancelGenerateForever);

})(); //End example Context Menu Items

onAfterUiUpdate(addContextMenuEventListener);