Add custom nodes, Civitai loras (LFS), and vast.ai setup script
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Includes 30 custom nodes committed directly, 7 Civitai-exclusive loras stored via Git LFS, and a setup script that installs all dependencies and downloads HuggingFace-hosted models on vast.ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
import { app } from "../../../../scripts/app.js";
|
||||
import { api } from "../../../../scripts/api.js";
|
||||
import { $el, ComfyDialog } from "../../../../scripts/ui.js";
|
||||
import { $t } from '../common/i18n.js'
|
||||
import { toast } from "../common/toast.js";
|
||||
import {sleep, accSub} from "../common/utils.js";
|
||||
|
||||
let api_keys = []
|
||||
let api_current = 0
|
||||
let user_info = {}
|
||||
|
||||
const api_cost = {
|
||||
'sd3': 6.5,
|
||||
'sd3-turbo': 4,
|
||||
}
|
||||
|
||||
class AccountDialog extends ComfyDialog {
|
||||
constructor() {
|
||||
super();
|
||||
this.lists = []
|
||||
this.dialog_div = null
|
||||
this.user_div = null
|
||||
}
|
||||
|
||||
addItem(index, user_div){
|
||||
return $el('div.easyuse-account-dialog-item',[
|
||||
$el('input',{type:'text',placeholder:'Enter name',oninput: e=>{
|
||||
const dataIndex = Array.prototype.indexOf.call(this.dialog_div.querySelectorAll('.easyuse-account-dialog-item'), e.target.parentNode)
|
||||
api_keys[dataIndex]['name'] = e.target.value
|
||||
},value:api_keys[index]['name']}),
|
||||
$el('input.key',{type:'text',oninput: e=>{
|
||||
const dataIndex = Array.prototype.indexOf.call(this.dialog_div.querySelectorAll('.easyuse-account-dialog-item'), e.target.parentNode)
|
||||
api_keys[dataIndex]['key'] = e.target.value
|
||||
},placeholder:'Enter APIKEY', value:api_keys[index]['key']}),
|
||||
$el('button.choose',{textContent:$t('Choose'),onclick:async(e)=>{
|
||||
const dataIndex = Array.prototype.indexOf.call(this.dialog_div.querySelectorAll('.easyuse-account-dialog-item'), e.target.parentNode)
|
||||
let name = api_keys[dataIndex]['name']
|
||||
let key = api_keys[dataIndex]['key']
|
||||
if(!name){
|
||||
toast.error($t('Please enter the account name'))
|
||||
return
|
||||
}
|
||||
else if(!key){
|
||||
toast.error($t('Please enter the APIKEY'))
|
||||
return
|
||||
}
|
||||
let missing = true
|
||||
for(let i=0;i<api_keys.length;i++){
|
||||
if(!api_keys[i].key) {
|
||||
missing = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if(!missing){
|
||||
toast.error($t('APIKEY is not Empty'))
|
||||
return
|
||||
}
|
||||
// 保存记录
|
||||
api_current = dataIndex
|
||||
const body = new FormData();
|
||||
body.append('api_keys', JSON.stringify(api_keys));
|
||||
body.append('current',api_current)
|
||||
const res = await api.fetchApi('/easyuse/stability/set_api_keys', {
|
||||
method: 'POST',
|
||||
body
|
||||
})
|
||||
if (res.status == 200) {
|
||||
const data = await res.json()
|
||||
if(data?.account && data?.balance){
|
||||
const avatar = data.account?.profile_picture || null
|
||||
const email = data.account?.email || null
|
||||
const credits = data.balance?.credits || 0
|
||||
user_div.replaceChildren(
|
||||
$el('div.easyuse-account-user-info', {
|
||||
onclick:_=>{
|
||||
new AccountDialog().show(user_div);
|
||||
}
|
||||
},[
|
||||
$el('div.user',[
|
||||
$el('div.avatar', avatar ? [$el('img',{src:avatar})] : '😀'),
|
||||
$el('div.info', [
|
||||
$el('h5.name', email),
|
||||
$el('h6.remark','Credits: '+ credits)
|
||||
])
|
||||
]),
|
||||
$el('div.edit', {textContent:$t('Edit')})
|
||||
])
|
||||
)
|
||||
toast.success($t('Save Succeed'))
|
||||
}
|
||||
else toast.success($t('Save Succeed'))
|
||||
this.close()
|
||||
} else {
|
||||
toast.error($t('Save Failed'))
|
||||
}
|
||||
}}),
|
||||
$el('button.delete',{textContent:$t('Delete'),onclick:e=>{
|
||||
const dataIndex = Array.prototype.indexOf.call(this.dialog_div.querySelectorAll('.easyuse-account-dialog-item'), e.target.parentNode)
|
||||
if(api_keys.length<=1){
|
||||
toast.error($t('At least one account is required'))
|
||||
return
|
||||
}
|
||||
api_keys.splice(dataIndex,1)
|
||||
this.dialog_div.removeChild(e.target.parentNode)
|
||||
}}),
|
||||
])
|
||||
}
|
||||
|
||||
show(userdiv) {
|
||||
api_keys.forEach((item,index)=>{
|
||||
this.lists.push(this.addItem(index,userdiv))
|
||||
})
|
||||
this.dialog_div = $el("div.easyuse-account-dialog", this.lists)
|
||||
super.show(
|
||||
$el('div.easyuse-account-dialog-main',[
|
||||
$el('div',[
|
||||
$el('a',{href:'https://platform.stability.ai/account/keys',target:'_blank',textContent:$t('Getting Your APIKEY')}),
|
||||
]),
|
||||
this.dialog_div,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
createButtons() {
|
||||
const btns = super.createButtons();
|
||||
btns.unshift($el('button',{
|
||||
type:'button',
|
||||
textContent:$t('Save Account Info'),
|
||||
onclick:_=>{
|
||||
let missing = true
|
||||
for(let i=0;i<api_keys.length;i++){
|
||||
if(!api_keys[i].key) {
|
||||
missing = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if(!missing){
|
||||
toast.error($t('APIKEY is not Empty'))
|
||||
}
|
||||
else {
|
||||
const body = new FormData();
|
||||
body.append('api_keys', JSON.stringify(api_keys));
|
||||
api.fetchApi('/easyuse/stability/set_api_keys', {
|
||||
method: 'POST',
|
||||
body
|
||||
}).then(res => {
|
||||
if (res.status == 200) {
|
||||
toast.success($t('Save Succeed'))
|
||||
|
||||
} else {
|
||||
toast.error($t('Save Failed'))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}))
|
||||
btns.unshift($el('button',{
|
||||
type:'button',
|
||||
textContent:$t('Add Account'),
|
||||
onclick:_=>{
|
||||
const name = 'Account '+(api_keys.length).toString()
|
||||
api_keys.push({name,key:''})
|
||||
const item = this.addItem(api_keys.length - 1)
|
||||
this.lists.push(item)
|
||||
this.dialog_div.appendChild(item)
|
||||
}
|
||||
}))
|
||||
return btns
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: 'comfy.easyUse.account',
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if(nodeData.name == 'easy stableDiffusion3API'){
|
||||
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = async function() {
|
||||
onNodeCreated ? onNodeCreated?.apply(this, arguments) : undefined;
|
||||
const seed_widget = this.widgets.find(w => ['seed_num','seed'].includes(w.name))
|
||||
const seed_control = this.widgets.find(w=> ['control_before_generate','control_after_generate'].includes(w.name))
|
||||
let model_widget = this.widgets.find(w => w.name == 'model')
|
||||
model_widget.callback = value =>{
|
||||
cost_widget.value = '-'+api_cost[value]
|
||||
}
|
||||
const cost_widget = this.addWidget('text', 'cost_credit', '0', _=>{
|
||||
},{
|
||||
serialize:false,
|
||||
})
|
||||
cost_widget.disabled = true
|
||||
setTimeout(_=>{
|
||||
if(seed_control.name == 'control_before_generate' && seed_widget.value === 0){
|
||||
seed_widget.value = Math.floor(Math.random() * 4294967294)
|
||||
}
|
||||
cost_widget.value = '-'+api_cost[model_widget.value]
|
||||
},100)
|
||||
let user_div = $el('div.easyuse-account-user', [$t('Loading UserInfo...')])
|
||||
let account = this.addDOMWidget('account',"btn",$el('div.easyuse-account',user_div));
|
||||
// 更新balance信息
|
||||
api.addEventListener('stable-diffusion-api-generate-succeed', async ({detail}) => {
|
||||
let remarkDiv = user_div.querySelectorAll('.remark')
|
||||
if(remarkDiv && remarkDiv[0]){
|
||||
const credits = detail?.model ? api_cost[detail.model] : 0
|
||||
if(credits) {
|
||||
let balance = accSub(parseFloat(remarkDiv[0].innerText.replace(/Credits: /g,'')),credits)
|
||||
if(balance>0){
|
||||
remarkDiv[0].innerText = 'Credits: '+ balance.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
await sleep(10000)
|
||||
const res = await api.fetchApi('/easyuse/stability/balance')
|
||||
if(res.status == 200){
|
||||
const data = await res.json()
|
||||
if(data?.balance){
|
||||
const credits = data.balance?.credits || 0
|
||||
if(remarkDiv && remarkDiv[0]){
|
||||
remarkDiv[0].innerText = 'Credits: ' + credits
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 获取api_keys
|
||||
const res = await api.fetchApi('/easyuse/stability/api_keys')
|
||||
if (res.status == 200){
|
||||
let data = await res.json()
|
||||
api_keys = data.keys
|
||||
api_current = data.current
|
||||
if (api_keys.length > 0 && api_current!==undefined){
|
||||
const api_key = api_keys[api_current]['key']
|
||||
const api_name = api_keys[api_current]['name']
|
||||
if(!api_key){
|
||||
user_div.replaceChildren(
|
||||
$el('div.easyuse-account-user-info', {
|
||||
onclick:_=>{
|
||||
new AccountDialog().show(user_div);
|
||||
}
|
||||
},[
|
||||
$el('div.user',[
|
||||
$el('div.avatar', '😀'),
|
||||
$el('div.info', [
|
||||
$el('h5.name', api_name),
|
||||
$el('h6.remark',$t('Click to set the APIKEY first'))
|
||||
])
|
||||
]),
|
||||
$el('div.edit', {textContent:$t('Edit')})
|
||||
])
|
||||
)
|
||||
}else{
|
||||
// 获取账号信息
|
||||
const res = await api.fetchApi('/easyuse/stability/user_info')
|
||||
if(res.status == 200){
|
||||
const data = await res.json()
|
||||
if(data?.account && data?.balance){
|
||||
const avatar = data.account?.profile_picture || null
|
||||
const email = data.account?.email || null
|
||||
const credits = data.balance?.credits || 0
|
||||
user_div.replaceChildren(
|
||||
$el('div.easyuse-account-user-info', {
|
||||
onclick:_=>{
|
||||
new AccountDialog().show(user_div);
|
||||
}
|
||||
},[
|
||||
$el('div.user',[
|
||||
$el('div.avatar', avatar ? [$el('img',{src:avatar})] : '😀'),
|
||||
$el('div.info', [
|
||||
$el('h5.name', email),
|
||||
$el('h6.remark','Credits: '+ credits)
|
||||
])
|
||||
]),
|
||||
$el('div.edit', {textContent:$t('Edit')})
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user