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>
283 lines
12 KiB
JavaScript
283 lines
12 KiB
JavaScript
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')})
|
|
])
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}) |