From eb58bf0a4d9b662027aa0463c9a3902de41d0a5b Mon Sep 17 00:00:00 2001 From: JeLuF Date: Tue, 25 Jul 2023 20:30:23 +0200 Subject: [PATCH 01/61] sqlalchemy --- ui/easydiffusion/app.py | 1 + ui/easydiffusion/server.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/easydiffusion/app.py b/ui/easydiffusion/app.py index e3de614d..923c0f35 100644 --- a/ui/easydiffusion/app.py +++ b/ui/easydiffusion/app.py @@ -36,6 +36,7 @@ SD_UI_DIR = os.getenv("SD_UI_PATH", None) CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, "..", "scripts")) MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "models")) +BUCKET_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "bucket")) USER_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, "..", "plugins")) CORE_PLUGINS_DIR = os.path.abspath(os.path.join(SD_UI_DIR, "plugins")) diff --git a/ui/easydiffusion/server.py b/ui/easydiffusion/server.py index df788b0c..4705d112 100644 --- a/ui/easydiffusion/server.py +++ b/ui/easydiffusion/server.py @@ -8,7 +8,7 @@ import os import traceback from typing import List, Union -from easydiffusion import app, model_manager, task_manager +from easydiffusion import app, model_manager, task_manager, bucket_manager from easydiffusion.types import GenerateImageRequest, MergeRequest, TaskData from easydiffusion.utils import log from fastapi import FastAPI, HTTPException From 4b27b45e4ca5c0ffabe292c9cbffc18bbb393672 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Tue, 25 Jul 2023 20:30:35 +0200 Subject: [PATCH 02/61] sqlalchemy --- ui/easydiffusion/bucket_manager.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 ui/easydiffusion/bucket_manager.py diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py new file mode 100644 index 00000000..e24b0cc0 --- /dev/null +++ b/ui/easydiffusion/bucket_manager.py @@ -0,0 +1,14 @@ +import os +from easydiffusion import app + +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +SQLALCHEMY_DATABASE_URL = "sqlite:///"+os.path.join(app.BUCKET_DIR, "bucket.db") +print("## SQLALCHEMY_DATABASE_URL = ", SQLALCHEMY_DATABASE_URL) + +engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() From 646aeac85839dbffdec2b5922a0770408ef734ab Mon Sep 17 00:00:00 2001 From: JeLuF Date: Wed, 26 Jul 2023 01:16:27 +0200 Subject: [PATCH 03/61] sqlalchemy bucket v1 --- scripts/check_modules.py | 1 + ui/easydiffusion/bucket_crud.py | 36 +++++++++++++++++ ui/easydiffusion/bucket_database.py | 15 +++++++ ui/easydiffusion/bucket_manager.py | 63 ++++++++++++++++++++++++----- ui/easydiffusion/bucket_models.py | 25 ++++++++++++ ui/easydiffusion/bucket_schemas.py | 37 +++++++++++++++++ ui/easydiffusion/server.py | 2 +- ui/main.py | 3 +- 8 files changed, 170 insertions(+), 12 deletions(-) create mode 100644 ui/easydiffusion/bucket_crud.py create mode 100644 ui/easydiffusion/bucket_database.py create mode 100644 ui/easydiffusion/bucket_models.py create mode 100644 ui/easydiffusion/bucket_schemas.py diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 03e50db0..0f50e842 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -25,6 +25,7 @@ modules_to_check = { "fastapi": "0.85.1", "pycloudflared": "0.2.0", "ruamel.yaml": "0.17.21", + "sqlalchemy": "2.0.19", # "xformers": "0.0.16", } modules_to_log = ["torch", "torchvision", "sdkit", "stable-diffusion-sdkit"] diff --git a/ui/easydiffusion/bucket_crud.py b/ui/easydiffusion/bucket_crud.py new file mode 100644 index 00000000..ece14fcc --- /dev/null +++ b/ui/easydiffusion/bucket_crud.py @@ -0,0 +1,36 @@ +from sqlalchemy.orm import Session + +from easydiffusion import bucket_models, bucket_schemas + + +def get_bucket(db: Session, bucket_id: int): + return db.query(bucket_models.Bucket).filter(bucket_models.Bucket.id == bucket_id).first() + + +def get_bucket_by_path(db: Session, path: str): + return db.query(bucket_models.Bucket).filter(bucket_models.Bucket.path == path).first() + + +def get_buckets(db: Session, skip: int = 0, limit: int = 100): + return db.query(bucket_models.Bucket).offset(skip).limit(limit).all() + + +def create_bucket(db: Session, bucket: bucket_schemas.BucketCreate): + db_bucket = bucket_models.Bucket(path=bucket.path) + db.add(db_bucket) + db.commit() + db.refresh(db_bucket) + return db_bucket + + +def get_bucketfiles(db: Session, skip: int = 0, limit: int = 100): + return db.query(bucket_models.BucketFile).offset(skip).limit(limit).all() + + +def create_bucketfile(db: Session, bucketfile: bucket_schemas.BucketFileCreate, bucket_id: int): + db_bucketfile = bucket_models.BucketFile(**bucketfile.dict(), bucket_id=bucket_id) + db.add(db_bucketfile) + db.commit() + db.refresh(db_bucketfile) + return db_bucketfile + diff --git a/ui/easydiffusion/bucket_database.py b/ui/easydiffusion/bucket_database.py new file mode 100644 index 00000000..e3c92845 --- /dev/null +++ b/ui/easydiffusion/bucket_database.py @@ -0,0 +1,15 @@ +import os +from easydiffusion import app + +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +os.makedirs(app.BUCKET_DIR, exist_ok=True) +SQLALCHEMY_DATABASE_URL = "sqlite:///"+os.path.join(app.BUCKET_DIR, "bucket.db") +print("## SQLALCHEMY_DATABASE_URL = ", SQLALCHEMY_DATABASE_URL) + +engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +BucketBase = declarative_base() diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index e24b0cc0..0985573f 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -1,14 +1,57 @@ -import os -from easydiffusion import app +from typing import List -from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker +from fastapi import Depends, FastAPI, HTTPException +from sqlalchemy.orm import Session -SQLALCHEMY_DATABASE_URL = "sqlite:///"+os.path.join(app.BUCKET_DIR, "bucket.db") -print("## SQLALCHEMY_DATABASE_URL = ", SQLALCHEMY_DATABASE_URL) +from easydiffusion import bucket_crud, bucket_models, bucket_schemas +from easydiffusion.bucket_database import SessionLocal, engine -engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -Base = declarative_base() +def init(): + from easydiffusion.server import server_api + + bucket_models.BucketBase.metadata.create_all(bind=engine) + + + # Dependency + def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + + @server_api.post("/buckets/", response_model=bucket_schemas.Bucket) + def create_bucket(bucket: bucket_schemas.BucketCreate, db: Session = Depends(get_db)): + db_bucket = bucket_crud.get_bucket_by_path(db, path=bucket.path) + if db_bucket: + raise HTTPException(status_code=400, detail="Bucket already exists") + return bucket_crud.create_bucket(db=db, bucket=bucket) + + @server_api.get("/buckets/", response_model=List[bucket_schemas.Bucket]) + def read_bucket(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + buckets = bucket_crud.get_buckets(db, skip=skip, limit=limit) + return buckets + + + @server_api.get("/buckets/{bucket_id}", response_model=bucket_schemas.Bucket) + def read_bucket(bucket_id: int, db: Session = Depends(get_db)): + db_bucket = bucket_crud.get_bucket(db, bucket_id=bucket_id) + if db_bucket is None: + raise HTTPException(status_code=404, detail="Bucket not found") + return db_bucket + + + @server_api.post("/buckets/{bucket_id}/items/", response_model=bucket_schemas.BucketFile) + def create_bucketfile_in_bucket( + bucket_id: int, bucketfile: bucket_schemas.BucketFileCreate, db: Session = Depends(get_db) + ): + return bucket_crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) + + + @server_api.get("/bucketfiles/", response_model=List[bucket_schemas.BucketFile]) + def read_bucketfiles(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + bucketfiles = bucket_crud.get_bucketfiles(db, skip=skip, limit=limit) + return bucketfiles + diff --git a/ui/easydiffusion/bucket_models.py b/ui/easydiffusion/bucket_models.py new file mode 100644 index 00000000..3ede1a36 --- /dev/null +++ b/ui/easydiffusion/bucket_models.py @@ -0,0 +1,25 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, BLOB +from sqlalchemy.orm import relationship + +from easydiffusion.bucket_database import BucketBase + + +class Bucket(BucketBase): + __tablename__ = "bucket" + + id = Column(Integer, primary_key=True, index=True) + path = Column(String, unique=True, index=True) + + bucketfiles = relationship("BucketFile", back_populates="bucket") + + +class BucketFile(BucketBase): + __tablename__ = "bucketfile" + + id = Column(Integer, primary_key=True, index=True) + filename = Column(String, index=True) + data = Column(BLOB, index=False) + bucket_id = Column(Integer, ForeignKey("bucket.id")) + + bucket = relationship("Bucket", back_populates="bucketfiles") + diff --git a/ui/easydiffusion/bucket_schemas.py b/ui/easydiffusion/bucket_schemas.py new file mode 100644 index 00000000..9f7e2377 --- /dev/null +++ b/ui/easydiffusion/bucket_schemas.py @@ -0,0 +1,37 @@ +from typing import List, Union + +from pydantic import BaseModel + + +class BucketFileBase(BaseModel): + filename: str + data: bytes + + +class BucketFileCreate(BucketFileBase): + pass + + +class BucketFile(BucketFileBase): + id: int + bucket_id: int + + class Config: + orm_mode = True + + +class BucketBase(BaseModel): + path: str + + +class BucketCreate(BucketBase): + pass + + +class Bucket(BucketBase): + id: int + bucketfiles: List[BucketFile] = [] + + class Config: + orm_mode = True + diff --git a/ui/easydiffusion/server.py b/ui/easydiffusion/server.py index 4705d112..df788b0c 100644 --- a/ui/easydiffusion/server.py +++ b/ui/easydiffusion/server.py @@ -8,7 +8,7 @@ import os import traceback from typing import List, Union -from easydiffusion import app, model_manager, task_manager, bucket_manager +from easydiffusion import app, model_manager, task_manager from easydiffusion.types import GenerateImageRequest, MergeRequest, TaskData from easydiffusion.utils import log from fastapi import FastAPI, HTTPException diff --git a/ui/main.py b/ui/main.py index f5998622..a7568ba6 100644 --- a/ui/main.py +++ b/ui/main.py @@ -1,4 +1,4 @@ -from easydiffusion import model_manager, app, server +from easydiffusion import model_manager, app, server, bucket_manager from easydiffusion.server import server_api # required for uvicorn server.init() @@ -7,6 +7,7 @@ server.init() model_manager.init() app.init() app.init_render_threads() +bucket_manager.init() # start the browser ui app.open_browser() From 97035a54edccf28c5a8fb026c93e5ea4b3da7cf3 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 27 Jul 2023 21:56:36 +0200 Subject: [PATCH 04/61] Bucket API --- scripts/check_modules.py | 1 + ui/easydiffusion/bucket_crud.py | 5 +- ui/easydiffusion/bucket_manager.py | 76 +++++++++++++++++++++++++++++- ui/easydiffusion/bucket_models.py | 6 +-- ui/easydiffusion/bucket_schemas.py | 1 - 5 files changed, 81 insertions(+), 8 deletions(-) diff --git a/scripts/check_modules.py b/scripts/check_modules.py index 0f50e842..c2713c64 100644 --- a/scripts/check_modules.py +++ b/scripts/check_modules.py @@ -26,6 +26,7 @@ modules_to_check = { "pycloudflared": "0.2.0", "ruamel.yaml": "0.17.21", "sqlalchemy": "2.0.19", + "python-multipart": "0.0.6", # "xformers": "0.0.16", } modules_to_log = ["torch", "torchvision", "sdkit", "stable-diffusion-sdkit"] diff --git a/ui/easydiffusion/bucket_crud.py b/ui/easydiffusion/bucket_crud.py index ece14fcc..5dada176 100644 --- a/ui/easydiffusion/bucket_crud.py +++ b/ui/easydiffusion/bucket_crud.py @@ -29,8 +29,9 @@ def get_bucketfiles(db: Session, skip: int = 0, limit: int = 100): def create_bucketfile(db: Session, bucketfile: bucket_schemas.BucketFileCreate, bucket_id: int): db_bucketfile = bucket_models.BucketFile(**bucketfile.dict(), bucket_id=bucket_id) - db.add(db_bucketfile) + db.merge(db_bucketfile) db.commit() - db.refresh(db_bucketfile) + from pprint import pprint + db_bucketfile = db.query(bucket_models.BucketFile).filter(bucket_models.BucketFile.bucket_id==bucket_id, bucket_models.BucketFile.filename==bucketfile.filename).first() return db_bucketfile diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 0985573f..58d0e30c 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -1,11 +1,31 @@ from typing import List -from fastapi import Depends, FastAPI, HTTPException +from fastapi import Depends, FastAPI, HTTPException, Response, File from sqlalchemy.orm import Session from easydiffusion import bucket_crud, bucket_models, bucket_schemas from easydiffusion.bucket_database import SessionLocal, engine +from requests.compat import urlparse + +import base64, json + +MIME_TYPES = { + "jpg": "image/jpeg", + "jpeg": "image/jpeg", + "gif": "image/gif", + "png": "image/png", + "webp": "image/webp", + "js": "text/javascript", + "htm": "text/html", + "html": "text/html", + "css": "text/css", + "json": "application/json", + "mjs": "application/json", + "yaml": "application/yaml", + "svg": "image/svg+xml", + "txt": "text/plain", +} def init(): from easydiffusion.server import server_api @@ -21,6 +41,42 @@ def init(): finally: db.close() + @server_api.get("/bucket/{obj_path:path}") + def bucket_get_object(obj_path: str, db: Session = Depends(get_db)): + filename = get_filename_from_url(obj_path) + path = get_path_from_url(obj_path) + + if filename==None: + bucket = bucket_crud.get_bucket_by_path(db, path=path) + if bucket == None: + raise HTTPException(status_code=404, detail="Bucket not found") + bucketfiles = db.query(bucket_models.BucketFile).with_entities(bucket_models.BucketFile.filename).filter(bucket_models.BucketFile.bucket_id == bucket.id).all() + bucketfiles = [ x.filename for x in bucketfiles ] + return bucketfiles + + else: + bucket_id = bucket_crud.get_bucket_by_path(db, path).id + bucketfile = db.query(bucket_models.BucketFile).filter(bucket_models.BucketFile.bucket_id == bucket_id, bucket_models.BucketFile.filename == filename).first() + + suffix = get_suffix_from_filename(filename) + + return Response(content=bucketfile.data, media_type=MIME_TYPES.get(suffix, "application/octet-stream")) + + @server_api.post("/bucket/{obj_path:path}") + def bucket_post_object(obj_path: str, file: bytes = File(), db: Session = Depends(get_db)): + filename = get_filename_from_url(obj_path) + path = get_path_from_url(obj_path) + bucket = bucket_crud.get_bucket_by_path(db, path) + + if bucket == None: + bucket_id = bucket_crud.create_bucket(db=db, bucket=bucket_schemas.BucketCreate(path=path)) + else: + bucket_id = bucket.id + + bucketfile = bucket_schemas.BucketFileCreate(filename=filename, data=file) + result = bucket_crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) + result.data = base64.encodestring(result.data) + return result @server_api.post("/buckets/", response_model=bucket_schemas.Bucket) def create_bucket(bucket: bucket_schemas.BucketCreate, db: Session = Depends(get_db)): @@ -47,7 +103,10 @@ def init(): def create_bucketfile_in_bucket( bucket_id: int, bucketfile: bucket_schemas.BucketFileCreate, db: Session = Depends(get_db) ): - return bucket_crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) + bucketfile.data = base64.decodestring(bucketfile.data) + result = bucket_crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) + result.data = base64.encodestring(result.data) + return result @server_api.get("/bucketfiles/", response_model=List[bucket_schemas.BucketFile]) @@ -55,3 +114,16 @@ def init(): bucketfiles = bucket_crud.get_bucketfiles(db, skip=skip, limit=limit) return bucketfiles + +def get_filename_from_url(url): + path = urlparse(url).path + name = path[path.rfind('/')+1:] + return name or None + +def get_path_from_url(url): + path = urlparse(url).path + path = path[0:path.rfind('/')] + return path or None + +def get_suffix_from_filename(filename): + return filename[filename.rfind('.')+1:] diff --git a/ui/easydiffusion/bucket_models.py b/ui/easydiffusion/bucket_models.py index 3ede1a36..933f8140 100644 --- a/ui/easydiffusion/bucket_models.py +++ b/ui/easydiffusion/bucket_models.py @@ -16,10 +16,10 @@ class Bucket(BucketBase): class BucketFile(BucketBase): __tablename__ = "bucketfile" - id = Column(Integer, primary_key=True, index=True) - filename = Column(String, index=True) + filename = Column(String, index=True, primary_key=True) + bucket_id = Column(Integer, ForeignKey("bucket.id"), primary_key=True) + data = Column(BLOB, index=False) - bucket_id = Column(Integer, ForeignKey("bucket.id")) bucket = relationship("Bucket", back_populates="bucketfiles") diff --git a/ui/easydiffusion/bucket_schemas.py b/ui/easydiffusion/bucket_schemas.py index 9f7e2377..68bc04e2 100644 --- a/ui/easydiffusion/bucket_schemas.py +++ b/ui/easydiffusion/bucket_schemas.py @@ -13,7 +13,6 @@ class BucketFileCreate(BucketFileBase): class BucketFile(BucketFileBase): - id: int bucket_id: int class Config: From 5f736368c8ce2d685a2ed94201adb9f0e54ad752 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Thu, 27 Jul 2023 22:44:30 +0200 Subject: [PATCH 05/61] Move easydb files to its own folders --- ui/easydiffusion/bucket_crud.py | 37 ------------- ui/easydiffusion/bucket_manager.py | 54 +++++-------------- ui/easydiffusion/easydb/crud.py | 25 +++++++++ .../database.py} | 0 .../{bucket_models.py => easydb/models.py} | 2 +- .../{bucket_schemas.py => easydb/schemas.py} | 0 6 files changed, 40 insertions(+), 78 deletions(-) delete mode 100644 ui/easydiffusion/bucket_crud.py create mode 100644 ui/easydiffusion/easydb/crud.py rename ui/easydiffusion/{bucket_database.py => easydb/database.py} (100%) rename ui/easydiffusion/{bucket_models.py => easydb/models.py} (92%) rename ui/easydiffusion/{bucket_schemas.py => easydb/schemas.py} (100%) diff --git a/ui/easydiffusion/bucket_crud.py b/ui/easydiffusion/bucket_crud.py deleted file mode 100644 index 5dada176..00000000 --- a/ui/easydiffusion/bucket_crud.py +++ /dev/null @@ -1,37 +0,0 @@ -from sqlalchemy.orm import Session - -from easydiffusion import bucket_models, bucket_schemas - - -def get_bucket(db: Session, bucket_id: int): - return db.query(bucket_models.Bucket).filter(bucket_models.Bucket.id == bucket_id).first() - - -def get_bucket_by_path(db: Session, path: str): - return db.query(bucket_models.Bucket).filter(bucket_models.Bucket.path == path).first() - - -def get_buckets(db: Session, skip: int = 0, limit: int = 100): - return db.query(bucket_models.Bucket).offset(skip).limit(limit).all() - - -def create_bucket(db: Session, bucket: bucket_schemas.BucketCreate): - db_bucket = bucket_models.Bucket(path=bucket.path) - db.add(db_bucket) - db.commit() - db.refresh(db_bucket) - return db_bucket - - -def get_bucketfiles(db: Session, skip: int = 0, limit: int = 100): - return db.query(bucket_models.BucketFile).offset(skip).limit(limit).all() - - -def create_bucketfile(db: Session, bucketfile: bucket_schemas.BucketFileCreate, bucket_id: int): - db_bucketfile = bucket_models.BucketFile(**bucketfile.dict(), bucket_id=bucket_id) - db.merge(db_bucketfile) - db.commit() - from pprint import pprint - db_bucketfile = db.query(bucket_models.BucketFile).filter(bucket_models.BucketFile.bucket_id==bucket_id, bucket_models.BucketFile.filename==bucketfile.filename).first() - return db_bucketfile - diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 58d0e30c..f587bc7f 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -3,8 +3,8 @@ from typing import List from fastapi import Depends, FastAPI, HTTPException, Response, File from sqlalchemy.orm import Session -from easydiffusion import bucket_crud, bucket_models, bucket_schemas -from easydiffusion.bucket_database import SessionLocal, engine +from easydiffusion.easydb import crud, models, schemas +from easydiffusion.easydb.database import SessionLocal, engine from requests.compat import urlparse @@ -30,7 +30,7 @@ MIME_TYPES = { def init(): from easydiffusion.server import server_api - bucket_models.BucketBase.metadata.create_all(bind=engine) + models.BucketBase.metadata.create_all(bind=engine) # Dependency @@ -47,16 +47,16 @@ def init(): path = get_path_from_url(obj_path) if filename==None: - bucket = bucket_crud.get_bucket_by_path(db, path=path) + bucket = crud.get_bucket_by_path(db, path=path) if bucket == None: raise HTTPException(status_code=404, detail="Bucket not found") - bucketfiles = db.query(bucket_models.BucketFile).with_entities(bucket_models.BucketFile.filename).filter(bucket_models.BucketFile.bucket_id == bucket.id).all() + bucketfiles = db.query(models.BucketFile).with_entities(models.BucketFile.filename).filter(models.BucketFile.bucket_id == bucket.id).all() bucketfiles = [ x.filename for x in bucketfiles ] return bucketfiles else: - bucket_id = bucket_crud.get_bucket_by_path(db, path).id - bucketfile = db.query(bucket_models.BucketFile).filter(bucket_models.BucketFile.bucket_id == bucket_id, bucket_models.BucketFile.filename == filename).first() + bucket_id = crud.get_bucket_by_path(db, path).id + bucketfile = db.query(models.BucketFile).filter(models.BucketFile.bucket_id == bucket_id, models.BucketFile.filename == filename).first() suffix = get_suffix_from_filename(filename) @@ -66,55 +66,29 @@ def init(): def bucket_post_object(obj_path: str, file: bytes = File(), db: Session = Depends(get_db)): filename = get_filename_from_url(obj_path) path = get_path_from_url(obj_path) - bucket = bucket_crud.get_bucket_by_path(db, path) + bucket = crud.get_bucket_by_path(db, path) if bucket == None: - bucket_id = bucket_crud.create_bucket(db=db, bucket=bucket_schemas.BucketCreate(path=path)) + bucket_id = crud.create_bucket(db=db, bucket=schemas.BucketCreate(path=path)) else: bucket_id = bucket.id - bucketfile = bucket_schemas.BucketFileCreate(filename=filename, data=file) - result = bucket_crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) + bucketfile = schemas.BucketFileCreate(filename=filename, data=file) + result = crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) result.data = base64.encodestring(result.data) return result - @server_api.post("/buckets/", response_model=bucket_schemas.Bucket) - def create_bucket(bucket: bucket_schemas.BucketCreate, db: Session = Depends(get_db)): - db_bucket = bucket_crud.get_bucket_by_path(db, path=bucket.path) - if db_bucket: - raise HTTPException(status_code=400, detail="Bucket already exists") - return bucket_crud.create_bucket(db=db, bucket=bucket) - @server_api.get("/buckets/", response_model=List[bucket_schemas.Bucket]) - def read_bucket(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - buckets = bucket_crud.get_buckets(db, skip=skip, limit=limit) - return buckets - - - @server_api.get("/buckets/{bucket_id}", response_model=bucket_schemas.Bucket) - def read_bucket(bucket_id: int, db: Session = Depends(get_db)): - db_bucket = bucket_crud.get_bucket(db, bucket_id=bucket_id) - if db_bucket is None: - raise HTTPException(status_code=404, detail="Bucket not found") - return db_bucket - - - @server_api.post("/buckets/{bucket_id}/items/", response_model=bucket_schemas.BucketFile) + @server_api.post("/buckets/{bucket_id}/items/", response_model=schemas.BucketFile) def create_bucketfile_in_bucket( - bucket_id: int, bucketfile: bucket_schemas.BucketFileCreate, db: Session = Depends(get_db) + bucket_id: int, bucketfile: schemas.BucketFileCreate, db: Session = Depends(get_db) ): bucketfile.data = base64.decodestring(bucketfile.data) - result = bucket_crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) + result = crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id) result.data = base64.encodestring(result.data) return result - @server_api.get("/bucketfiles/", response_model=List[bucket_schemas.BucketFile]) - def read_bucketfiles(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - bucketfiles = bucket_crud.get_bucketfiles(db, skip=skip, limit=limit) - return bucketfiles - - def get_filename_from_url(url): path = urlparse(url).path name = path[path.rfind('/')+1:] diff --git a/ui/easydiffusion/easydb/crud.py b/ui/easydiffusion/easydb/crud.py new file mode 100644 index 00000000..7550a52a --- /dev/null +++ b/ui/easydiffusion/easydb/crud.py @@ -0,0 +1,25 @@ +from sqlalchemy.orm import Session + +from easydiffusion.easydb import models, schemas + + +def get_bucket_by_path(db: Session, path: str): + return db.query(models.Bucket).filter(models.Bucket.path == path).first() + + +def create_bucket(db: Session, bucket: schemas.BucketCreate): + db_bucket = models.Bucket(path=bucket.path) + db.add(db_bucket) + db.commit() + db.refresh(db_bucket) + return db_bucket + + +def create_bucketfile(db: Session, bucketfile: schemas.BucketFileCreate, bucket_id: int): + db_bucketfile = models.BucketFile(**bucketfile.dict(), bucket_id=bucket_id) + db.merge(db_bucketfile) + db.commit() + from pprint import pprint + db_bucketfile = db.query(models.BucketFile).filter(models.BucketFile.bucket_id==bucket_id, models.BucketFile.filename==bucketfile.filename).first() + return db_bucketfile + diff --git a/ui/easydiffusion/bucket_database.py b/ui/easydiffusion/easydb/database.py similarity index 100% rename from ui/easydiffusion/bucket_database.py rename to ui/easydiffusion/easydb/database.py diff --git a/ui/easydiffusion/bucket_models.py b/ui/easydiffusion/easydb/models.py similarity index 92% rename from ui/easydiffusion/bucket_models.py rename to ui/easydiffusion/easydb/models.py index 933f8140..04834951 100644 --- a/ui/easydiffusion/bucket_models.py +++ b/ui/easydiffusion/easydb/models.py @@ -1,7 +1,7 @@ from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, BLOB from sqlalchemy.orm import relationship -from easydiffusion.bucket_database import BucketBase +from easydiffusion.easydb.database import BucketBase class Bucket(BucketBase): diff --git a/ui/easydiffusion/bucket_schemas.py b/ui/easydiffusion/easydb/schemas.py similarity index 100% rename from ui/easydiffusion/bucket_schemas.py rename to ui/easydiffusion/easydb/schemas.py From ab37dbf98f0b176751ba66a866e23ae5a45594ef Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 30 Jul 2023 12:59:40 +0200 Subject: [PATCH 06/61] show images --- ui/media/images/noimg.png | Bin 0 -> 1338 bytes ui/media/js/main.js | 53 ++++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 ui/media/images/noimg.png diff --git a/ui/media/images/noimg.png b/ui/media/images/noimg.png new file mode 100644 index 0000000000000000000000000000000000000000..33d7332fcbf24582eed6cdb2248bd4ee09c5ec80 GIT binary patch literal 1338 zcma)+c~}y57{`BT2AZI=W2I>xROVdz7`k#MN?zzB&$M(3J}MrC+M^Dd2W#Ht+491L z6^5G2N0$1Z>WcB3it9q2~GhpD+ zbZpUmt$`%zbws?^8aJvh7!KOzUQW)huZVb_7TRYOh;3h2iYbl0ikvz6;i7}a!|odK zRS-SMKVsVeFjcFmCl~-O>Q1e#{~xPm?ogvDa=ogyYdg!3PX;I!Rs63E;l2oJ)Wi|{1DRNF+EDDbUurnehGfal z7Ub!ilY_^o(g$++gTd&r8O+m^7X!+*oz6KSAr^u_funIQaweT)jNl>lW)y77n2U4Q z#A3Qn|CswKhPGOQYH4X%&v1Szv9Jq!ipW2;iLE!G7RM+hEusZQYoxuC%*MusNF*}v zFipIy_rQV5BF{$L6D_badV~qSNNP!?X^GS>V1UGupf|Yp^xzDhbV58?J5HpQ0I~b+ z{k)S|T#V)Fj@6muy+C^5ahpq7FFHE*DT%cMNQ((Kx;z>BMTHI!d{>&Is867=xR400 zvIK&_2Bt1cmOeZ@3~a^T4NL9T=qxn0O!ykh4&VNWoY-+rZGrL!QW@Je7HE~9&*5;M z7c}|*az?4-S0x{Rv!*z2{RRJGLqL--VMOIx86%RW{&;-AfDuuFWKX|UZvHBa9rCKVY2*m59(2_=FHhb=;Ch#AdQMPh zB*a67c-w042DP`i+F?@C6!A?d+HGIc{A8_UN%PTOA7o#Df4?e>xj7LUW?o(&ZwRF4 zHnd!A{kUIeF?$teG_6OWm8xn}r!5N)WZV5Nr263_vSo7=)j|4syBdRA^DAVqoKW~O zPaw-kkR5o4#TDEl<^SNHL|lucqrdMKN3@0$5dV(7)23O1^@z0hdktqS}7wc$JkSUSf{Pd|P?ScMV%9PP`ZvOYWeX z3R$M{y^Rw2UNv6G{lz^-@v;Msx^+0FB?;i~^Sy7(eV3kYT^T|?3yooHbA{z%!1~Ca znGb3{j@KljdM1U2y|&u^<0H1Cd-3??=A9#}gV6?{7uu?GB&U5BOgCKG!!E8K>_fWw z->tgp?~!2N-fa2%loo7#^EF+mDGEtS)PBh6Ha-rOcOHH%zjRGEIW5h-+3A9(Z1zL! zd}*#P!bScUL$El%r(T#4Y;_&G-Ex->j~ATjtnK(St|@eE_M*#BWbkMlYIb%u_73rV z$NGyG8OlyBmy1?_Y0}#Ae8HV~&a|qCHie$}3`HC2?(U9iYZRK5)qbSEL-|5AAXWUB zvA5!ApUTtC!T|-lbB^i&ygp}{s~G}-lbSC72Pgpi8-_fV4p)oNFTLMT=06?8y5rpL IVeps#2IeDOrvLx| literal 0 HcmV?d00001 diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 0b936066..4b2b5dfd 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -508,6 +508,7 @@ function showImages(reqBody, res, outputContainer, livePreview) { { text: "Upscale", on_click: onUpscaleClick, filter: (req, img) => !req.use_upscale }, { text: "Fix Faces", on_click: onFixFacesClick, filter: (req, img) => !req.use_face_correction }, ], + { text: "Use as Thumbnail", on_click: onUseAsThumbnailClick }, ] // include the plugins @@ -632,6 +633,20 @@ function onMakeSimilarClick(req, img) { createTask(newTaskRequest) } +function onUseAsThumbnailClick(req, img) { + console.log(req) + console.log(img) + let embedding = prompt("Embedding name") + fetch(img.src) + .then(response => response.blob()) + .then(async function(blob) { + const formData = new FormData() + formData.append("file", blob) + const response = await fetch(`bucket/embeddings/${embedding}.jpg`, { method: 'POST', body: formData }); + console.log(response) + }) +} + function enqueueImageVariationTask(req, img, reqDiff) { const imageSeed = img.getAttribute("data-seed") @@ -2157,19 +2172,27 @@ document.getElementById("toggle-cloudflare-tunnel").addEventListener("click", as /* Embeddings */ +let icl = [] function updateEmbeddingsList(filter = "") { - function html(model, prefix = "", filter = "") { + function html(model, iconlist = [], prefix = "", filter = "") { filter = filter.toLowerCase() let toplevel = "" let folders = "" + console.log(iconlist) + let embIcon = Object.assign({}, ...iconlist.map( x=> ({[x.toLowerCase().split('.').slice(0,-1).join('.')]:x}))) model?.forEach((m) => { if (typeof m == "string") { - if (m.toLowerCase().search(filter) != -1) { - toplevel += ` ` + let token=m.toLowerCase() + if (token.search(filter) != -1) { + let img = '/media/images/noimg.png' + if (token in embIcon) { + img = `/bucket/embeddings/${embIcon[token]}` + } + toplevel += ` ` } } else { - let subdir = html(m[1], prefix + m[0] + "/", filter) + let subdir = html(m[1], iconlist, prefix + m[0] + "/", filter) if (subdir != "") { folders += `

${prefix}${m[0]}

` + subdir + '
' } @@ -2179,7 +2202,7 @@ function updateEmbeddingsList(filter = "") { } function onButtonClick(e) { - let text = e.target.dataset["embedding"] + let text = e.target.closest("button").dataset["embedding"] const insertIntoNegative = e.shiftKey || positiveEmbeddingText.classList.contains("displayNone") if (embeddingsModeField.value == "insert") { @@ -2214,14 +2237,18 @@ function updateEmbeddingsList(filter = "") { } // END of remove block - embeddingsList.innerHTML = warning + html(modelsOptions.embeddings, "", filter) - embeddingsList.querySelectorAll("button").forEach((b) => { - b.addEventListener("click", onButtonClick) - }) - createCollapsibles(embeddingsList) - if (filter != "") { - embeddingsExpandAll() - } + fetch("/bucket/embeddings/") + .then(response => response.json()) + .then(iconlist => { + embeddingsList.innerHTML = warning + html(modelsOptions.embeddings, iconlist, "", filter) + embeddingsList.querySelectorAll("button").forEach((b) => { + b.addEventListener("click", onButtonClick) + }) + createCollapsibles(embeddingsList) + if (filter != "") { + embeddingsExpandAll() + } + }) } function showEmbeddingDialog() { From 92009cff2400255a7b5e0d47553c8fbafe621b3c Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Sun, 30 Jul 2023 22:45:52 +0200 Subject: [PATCH 07/61] Yanked files from https://github.com/JeLuF/stable-diffusion-ui/tree/bucketlite/ui/easydiffusion/easydb to develop --- ui/easydiffusion/easydb/crud.py | 25 ++++++++++++++++++++ ui/easydiffusion/easydb/database.py | 15 ++++++++++++ ui/easydiffusion/easydb/models.py | 25 ++++++++++++++++++++ ui/easydiffusion/easydb/schemas.py | 36 +++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 ui/easydiffusion/easydb/crud.py create mode 100644 ui/easydiffusion/easydb/database.py create mode 100644 ui/easydiffusion/easydb/models.py create mode 100644 ui/easydiffusion/easydb/schemas.py diff --git a/ui/easydiffusion/easydb/crud.py b/ui/easydiffusion/easydb/crud.py new file mode 100644 index 00000000..7550a52a --- /dev/null +++ b/ui/easydiffusion/easydb/crud.py @@ -0,0 +1,25 @@ +from sqlalchemy.orm import Session + +from easydiffusion.easydb import models, schemas + + +def get_bucket_by_path(db: Session, path: str): + return db.query(models.Bucket).filter(models.Bucket.path == path).first() + + +def create_bucket(db: Session, bucket: schemas.BucketCreate): + db_bucket = models.Bucket(path=bucket.path) + db.add(db_bucket) + db.commit() + db.refresh(db_bucket) + return db_bucket + + +def create_bucketfile(db: Session, bucketfile: schemas.BucketFileCreate, bucket_id: int): + db_bucketfile = models.BucketFile(**bucketfile.dict(), bucket_id=bucket_id) + db.merge(db_bucketfile) + db.commit() + from pprint import pprint + db_bucketfile = db.query(models.BucketFile).filter(models.BucketFile.bucket_id==bucket_id, models.BucketFile.filename==bucketfile.filename).first() + return db_bucketfile + diff --git a/ui/easydiffusion/easydb/database.py b/ui/easydiffusion/easydb/database.py new file mode 100644 index 00000000..e3c92845 --- /dev/null +++ b/ui/easydiffusion/easydb/database.py @@ -0,0 +1,15 @@ +import os +from easydiffusion import app + +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +os.makedirs(app.BUCKET_DIR, exist_ok=True) +SQLALCHEMY_DATABASE_URL = "sqlite:///"+os.path.join(app.BUCKET_DIR, "bucket.db") +print("## SQLALCHEMY_DATABASE_URL = ", SQLALCHEMY_DATABASE_URL) + +engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +BucketBase = declarative_base() diff --git a/ui/easydiffusion/easydb/models.py b/ui/easydiffusion/easydb/models.py new file mode 100644 index 00000000..04834951 --- /dev/null +++ b/ui/easydiffusion/easydb/models.py @@ -0,0 +1,25 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, BLOB +from sqlalchemy.orm import relationship + +from easydiffusion.easydb.database import BucketBase + + +class Bucket(BucketBase): + __tablename__ = "bucket" + + id = Column(Integer, primary_key=True, index=True) + path = Column(String, unique=True, index=True) + + bucketfiles = relationship("BucketFile", back_populates="bucket") + + +class BucketFile(BucketBase): + __tablename__ = "bucketfile" + + filename = Column(String, index=True, primary_key=True) + bucket_id = Column(Integer, ForeignKey("bucket.id"), primary_key=True) + + data = Column(BLOB, index=False) + + bucket = relationship("Bucket", back_populates="bucketfiles") + diff --git a/ui/easydiffusion/easydb/schemas.py b/ui/easydiffusion/easydb/schemas.py new file mode 100644 index 00000000..68bc04e2 --- /dev/null +++ b/ui/easydiffusion/easydb/schemas.py @@ -0,0 +1,36 @@ +from typing import List, Union + +from pydantic import BaseModel + + +class BucketFileBase(BaseModel): + filename: str + data: bytes + + +class BucketFileCreate(BucketFileBase): + pass + + +class BucketFile(BucketFileBase): + bucket_id: int + + class Config: + orm_mode = True + + +class BucketBase(BaseModel): + path: str + + +class BucketCreate(BucketBase): + pass + + +class Bucket(BucketBase): + id: int + bucketfiles: List[BucketFile] = [] + + class Config: + orm_mode = True + From a70a795662c8aa9a9f7de4439fa3dfcfc48fcb4a Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 31 Jul 2023 23:35:49 +0200 Subject: [PATCH 08/61] add croppr --- 3rd-PARTY-LICENSES | 28 ++++++++++++++++++++++++++++ ui/index.html | 31 +++++++++++++++++++++++++++++++ ui/media/css/croppr.css | 1 + ui/media/css/main.css | 26 ++++++++++++++++++++++++++ ui/media/js/croppr.js | 1 + ui/media/js/main.js | 35 ++++++++++++++++++++++++----------- 6 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 ui/media/css/croppr.css create mode 100755 ui/media/js/croppr.js diff --git a/3rd-PARTY-LICENSES b/3rd-PARTY-LICENSES index bd29393a..78bfe3bb 100644 --- a/3rd-PARTY-LICENSES +++ b/3rd-PARTY-LICENSES @@ -712,3 +712,31 @@ FileSaver.js is licensed under the MIT license: SOFTWARE. [1]: http://eligrey.com + +croppr.js +========= +https://github.com/jamesssooi/Croppr.js + +croppr.js is licensed under the MIT license: + + MIT License + + Copyright (c) 2017 James Ooi + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/ui/index.html b/ui/index.html index 0fcb807d..de4273aa 100644 --- a/ui/index.html +++ b/ui/index.html @@ -18,12 +18,14 @@ + +
@@ -630,6 +632,35 @@
+ +
+
+

Use as thumbnail

+ Use a pictures as thumbnail for embeddings, LORAs, etc. +
+
+ +
+
+
+
+
+ +
+
+

Use the thumbnail for …

+ +
+
+ + +
+
+
+
+ @@ -455,6 +458,40 @@ + +
diff --git a/ui/media/js/auto-save.js b/ui/media/js/auto-save.js index 5ada13d5..bcfd4bc6 100644 --- a/ui/media/js/auto-save.js +++ b/ui/media/js/auto-save.js @@ -56,6 +56,7 @@ const SETTINGS_IDS_LIST = [ "tree_toggle", "json_toggle", "extract_lora_from_prompt", + "embedding-card-size-selector", ] const IGNORE_BY_DEFAULT = ["prompt"] diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 30ed6012..b440e794 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -141,6 +141,7 @@ let embeddingsDialogCloseBtn = embeddingsDialog.querySelector("#embeddings-dialo let embeddingsSearchBox = document.querySelector("#embeddings-search-box") let embeddingsList = document.querySelector("#embeddings-list") let embeddingsModeField = document.querySelector("#embeddings-mode") +let embeddingsCardSizeSelector = document.querySelector("#embedding-card-size-selector") let positiveEmbeddingText = document.querySelector("#positive-embedding-text") let negativeEmbeddingText = document.querySelector("#negative-embedding-text") @@ -684,6 +685,7 @@ function onMakeSimilarClick(req, img) { createTask(newTaskRequest) } +// gets a flat list of all models of a certain type, ignoring directories function getAllModelNames(type) { function f(tree) { if (tree == undefined) { @@ -808,7 +810,6 @@ useAsThumbSaveBtn.addEventListener("click", (e) => { }) }) - function enqueueImageVariationTask(req, img, reqDiff) { const imageSeed = img.getAttribute("data-seed") @@ -2645,43 +2646,50 @@ let icl = [] function updateEmbeddingsList(filter = "") { function html(model, iconlist = [], prefix = "", filter = "") { filter = filter.toLowerCase() - let toplevel = "" - let folders = "" - console.log(iconlist) + let toplevel = document.createElement("div") + let folders = document.createElement("div") let embIcon = Object.assign({}, ...iconlist.map( x=> ({[x.toLowerCase().split('.').slice(0,-1).join('.')]:x}))) - console.log(embIcon) let profileName = profileNameField.value model?.forEach((m) => { if (typeof m == "string") { let token=m.toLowerCase() if (token.search(filter) != -1) { - let img = '/media/images/noimg.png' - if (token in embIcon) { - img = `/bucket/${profileName}/embeddings/${embIcon[token]}` - } + let button if (iconlist.length==0) { - img="" + button = document.createElement("button") + button.innerText="m" } else { - img=`
` + let img = '/media/images/noimg.png' + if (token in embIcon) { + img = `/bucket/${profileName}/embeddings/${embIcon[token]}` + } + button = createModifierCard(m, [img,img], true) } - toplevel += ` ` + button.dataset["embedding"] = m + button.addEventListener("click", onButtonClick) + toplevel.appendChild(button) } } else { let subdir = html(m[1], iconlist, prefix + m[0] + "/", filter) - if (subdir != "") { - folders += - `

${prefix}${m[0]}

` + - subdir + - "
" + if (typeof(subdir) == "object") { + let div1 = document.createElement("div") + let div2 = document.createElement("div") + div1.classList.add("collapsible-content") + div1.classList.add("embedding-category") + div1.appendChild(subdir) + div2.replaceChildren(htmlToElement(`

${prefix}${m[0]}

`), div1) + folders.appendChild(div2) } } }) - return toplevel + folders + let result = document.createElement("div") + result.replaceChildren(toplevel, htmlToElement('
'), folders) + return result } function onButtonClick(e) { - let text = e.target.closest("button").dataset["embedding"] + let text = e.target.closest("[data-embedding]").dataset["embedding"] const insertIntoNegative = e.shiftKey || positiveEmbeddingText.classList.contains("displayNone") if (embeddingsModeField.value == "insert") { @@ -2717,7 +2725,7 @@ function updateEmbeddingsList(filter = "") { ` // Remove after fixing https://github.com/huggingface/diffusers/issues/3922 - let warning = "" + let warning = "
" if (vramUsageLevelField.value == "low") { warning = `
@@ -2731,14 +2739,12 @@ function updateEmbeddingsList(filter = "") { .then(response => response.status==200 ? response.json(): []) .then(async function(iconlist) { - embeddingsList.innerHTML = warning + html(modelsOptions.embeddings, iconlist, "", filter) - embeddingsList.querySelectorAll("button").forEach((b) => { - b.addEventListener("click", onButtonClick) - }) + embeddingsList.replaceChildren(htmlToElement(warning), html(modelsOptions.embeddings, iconlist, "", filter)) createCollapsibles(embeddingsList) if (filter != "") { embeddingsExpandAll() } + resizeModifierCards(embeddingsCardSizeSelector.value) }) } @@ -2747,23 +2753,33 @@ function showEmbeddingDialog() { embeddingsSearchBox.value = "" embeddingsDialog.showModal() } + embeddingsButton.addEventListener("click", () => { positiveEmbeddingText.classList.remove("displayNone") negativeEmbeddingText.classList.add("displayNone") showEmbeddingDialog() }) + negativeEmbeddingsButton.addEventListener("click", () => { positiveEmbeddingText.classList.add("displayNone") negativeEmbeddingText.classList.remove("displayNone") showEmbeddingDialog() }) + embeddingsDialogCloseBtn.addEventListener("click", (e) => { embeddingsDialog.close() }) + embeddingsSearchBox.addEventListener("input", (e) => { updateEmbeddingsList(embeddingsSearchBox.value) }) +embeddingsCardSizeSelector.addEventListener("change", (e) => { + resizeModifierCards(embeddingsCardSizeSelector.value) +}) + + + modalDialogCloseOnBackdropClick(embeddingsDialog) makeDialogDraggable(embeddingsDialog) diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 673ba3dd..8fef9450 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -1097,6 +1097,14 @@ async function deleteKeys(keyToDelete) { } } +/** + * @param {String} Data URL of the image + * @param {Integer} Top left X-coordinate of the crop area + * @param {Integer} Top left Y-coordinate of the crop area + * @param {Integer} Width of the crop area + * @param {Integer} Height of the crop area + * @return {String} + */ function cropImageDataUrl(dataUrl, x, y, width, height) { return new Promise((resolve, reject) => { const image = new Image() @@ -1120,6 +1128,16 @@ function cropImageDataUrl(dataUrl, x, y, width, height) { }) } +/** + * @param {String} HTML representing a single element + * @return {Element} + */ +function htmlToElement(html) { + var template = document.createElement('template'); + html = html.trim(); // Never return a text node of whitespace as the result + template.innerHTML = html; + return template.content.firstChild; +} function modalDialogCloseOnBackdropClick(dialog) { dialog.addEventListener('mousedown', function (event) { From 3cc951cdaaa616d294f54fd208e704ac23542ed3 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Sun, 6 Aug 2023 21:52:01 +0200 Subject: [PATCH 20/61] Resolved issue mentioned by @JeLuF concerning paths --- ui/easydiffusion/bucket_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 5c30dc77..4400fd17 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -8,6 +8,7 @@ from easydiffusion.easydb import crud, models, schemas from easydiffusion.easydb.database import SessionLocal, engine from requests.compat import urlparse +from os.path import abspath import base64, json @@ -92,7 +93,7 @@ def init(): @server_api.get("/image/{image_path:path}") def get_image(image_path: str, db: Session = Depends(get_db)): from easydiffusion.easydb.mappings import Image - image_path = image_path.replace("/", "\\") + image_path = str(abspath(image_path)) amount = len(db.query(Image).filter(Image.path == image_path).all()) if amount > 0: image = db.query(Image).filter(Image.path == image_path).first() From 73a6e6c7371304d129553f31cc8c10f3b4d6a39d Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Sun, 6 Aug 2023 22:06:16 +0200 Subject: [PATCH 21/61] Moved JS&CSS into appropriate files and changed observer to refresh button --- ui/index.html | 32 +------------------------------- ui/media/css/main.css | 17 +++++++++++++++++ ui/media/js/main.js | 12 ++++++++++++ 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/ui/index.html b/ui/index.html index 9f5547e2..25c78fd4 100644 --- a/ui/index.html +++ b/ui/index.html @@ -458,38 +458,8 @@
- diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 5e1cee43..826ff168 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1817,3 +1817,20 @@ div#enlarge-buttons { .imgContainer .spinnerStatus { font-size: 10pt; } + +/* Gallery CSS */ +#imagecontainer { + display: flex; + justify-content: space-around; + flex-flow: row wrap; + align-items: center; +} + +#imagecontainer>img { + width: 30vw; + min-width: 256px; + max-width: 1024px; + height: auto; + margin-block: 1vh; + border: 4px white solid; +} \ No newline at end of file diff --git a/ui/media/js/main.js b/ui/media/js/main.js index d8c1b614..c0ed9112 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -2787,3 +2787,15 @@ let recentResolutionsValues = [] heightField.value = temp }) })() + +/* Gallery JS */ + +function refreshGallery() { + let container = document.getElementById("imagecontainer") + container.remove() + fetch('/all_images') + .then(response => response.text()) + .then(text => new DOMParser().parseFromString(text, 'text/html')) + .then(html_like => html_like.getElementsByTagName('div')[0]) + .then(div => document.getElementById("tab-content-gallery").appendChild(div)) +} \ No newline at end of file From 042773030880e158df9e9dd51be628720b8ddddb Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Sun, 6 Aug 2023 22:13:27 +0200 Subject: [PATCH 22/61] Aesthetic changes --- ui/index.html | 2 +- ui/media/css/main.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 25c78fd4..15385bda 100644 --- a/ui/index.html +++ b/ui/index.html @@ -50,7 +50,7 @@ Help & Community - Gallery + Gallery diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 826ff168..f04da78f 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1833,4 +1833,8 @@ div#enlarge-buttons { height: auto; margin-block: 1vh; border: 4px white solid; +} + +#tab-content-gallery>button { + margin: 8px; } \ No newline at end of file From 9b40d6c733b327d854bc84570472c0ab886148f1 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Sun, 6 Aug 2023 23:02:18 +0200 Subject: [PATCH 23/61] Remove debugging code --- ui/easydiffusion/easydb/crud.py | 1 - ui/easydiffusion/easydb/database.py | 1 - ui/media/js/main.js | 4 ---- 3 files changed, 6 deletions(-) diff --git a/ui/easydiffusion/easydb/crud.py b/ui/easydiffusion/easydb/crud.py index 7550a52a..65bea255 100644 --- a/ui/easydiffusion/easydb/crud.py +++ b/ui/easydiffusion/easydb/crud.py @@ -19,7 +19,6 @@ def create_bucketfile(db: Session, bucketfile: schemas.BucketFileCreate, bucket_ db_bucketfile = models.BucketFile(**bucketfile.dict(), bucket_id=bucket_id) db.merge(db_bucketfile) db.commit() - from pprint import pprint db_bucketfile = db.query(models.BucketFile).filter(models.BucketFile.bucket_id==bucket_id, models.BucketFile.filename==bucketfile.filename).first() return db_bucketfile diff --git a/ui/easydiffusion/easydb/database.py b/ui/easydiffusion/easydb/database.py index e3c92845..6cb43ecb 100644 --- a/ui/easydiffusion/easydb/database.py +++ b/ui/easydiffusion/easydb/database.py @@ -7,7 +7,6 @@ from sqlalchemy.orm import sessionmaker os.makedirs(app.BUCKET_DIR, exist_ok=True) SQLALCHEMY_DATABASE_URL = "sqlite:///"+os.path.join(app.BUCKET_DIR, "bucket.db") -print("## SQLALCHEMY_DATABASE_URL = ", SQLALCHEMY_DATABASE_URL) engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 4b2fe0d8..dfc3c04a 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -763,10 +763,8 @@ function onUseAsThumbnailClick(req, img) { })) useAsThumbSelect.replaceChildren(optgroup) - useAsThumbDialog.showModal() onUseAsThumbnailClick.scale = scale - } modalDialogCloseOnBackdropClick(useAsThumbDialog) @@ -795,9 +793,7 @@ useAsThumbSaveBtn.addEventListener("click", (e) => { formData.append("file", blob) let options = useAsThumbSelect.selectedOptions let promises = [] - console.log(options) for (let embedding of options) { - console.log(`bucket/${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`) promises.push(fetch(`bucket/${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`, { method: 'POST', body: formData })) } return Promise.all(promises) From 00c55444a95cd525621bffc0267a9918f339d9f9 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Mon, 7 Aug 2023 00:02:02 +0200 Subject: [PATCH 24/61] remove unused variable --- ui/media/js/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index dfc3c04a..c3a42b8a 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -2638,7 +2638,6 @@ document.getElementById("toggle-tensorrt-install").addEventListener("click", fun /* Embeddings */ -let icl = [] function updateEmbeddingsList(filter = "") { function html(model, iconlist = [], prefix = "", filter = "") { filter = filter.toLowerCase() From e828a72b084cae7d721be9388ee0f6f83abc77ba Mon Sep 17 00:00:00 2001 From: JeLuF Date: Tue, 8 Aug 2023 18:48:08 +0200 Subject: [PATCH 25/61] simplify DB query --- ui/easydiffusion/bucket_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 4400fd17..023ec192 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -94,11 +94,10 @@ def init(): def get_image(image_path: str, db: Session = Depends(get_db)): from easydiffusion.easydb.mappings import Image image_path = str(abspath(image_path)) - amount = len(db.query(Image).filter(Image.path == image_path).all()) - if amount > 0: + try: image = db.query(Image).filter(Image.path == image_path).first() return FileResponse(image.path) - else: + except Exception as e: raise HTTPException(status_code=404, detail="Image not found") @server_api.get("/all_images") From e3026967ab84f8954c41c7ec8df76bc529ecd232 Mon Sep 17 00:00:00 2001 From: JeLuF Date: Wed, 9 Aug 2023 16:56:10 +0200 Subject: [PATCH 26/61] Return json --- ui/easydiffusion/bucket_manager.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 4400fd17..8cd946e1 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -105,12 +105,13 @@ def init(): def get_all_images(db: Session = Depends(get_db)): from easydiffusion.easydb.mappings import Image images = db.query(Image).all() - sum_string = "
" - for img in images: - options = f"Path: {img.path}\nPrompt: {img.prompt}\nNegative Prompt: {img.negative_prompt}\nSeed: {img.seed}\nModel: {img.use_stable_diffusion_model}\nSize: {img.height}x{img.width}\nSampler: {img.sampler_name}\nSteps: {img.num_inference_steps}\nGuidance Scale: {img.guidance_scale}\nLoRA: {img.lora}\nUpscaling: {img.use_upscale}\nFace Correction: {img.use_face_correction}\n" - sum_string += f"" - sum_string += "
" - return Response(content=sum_string, media_type="text/html") + # sum_string = "
" + # for img in images: + # options = f"Path: {img.path}\nPrompt: {img.prompt}\nNegative Prompt: {img.negative_prompt}\nSeed: {img.seed}\nModel: {img.use_stable_diffusion_model}\nSize: {img.height}x{img.width}\nSampler: {img.sampler_name}\nSteps: {img.num_inference_steps}\nGuidance Scale: {img.guidance_scale}\nLoRA: {img.lora}\nUpscaling: {img.use_upscale}\nFace Correction: {img.use_face_correction}\n" + # sum_string += f"" + # sum_string += "
" + # return Response(content=sum_string, media_type="text/html") + return images def get_filename_from_url(url): From 87d3e4430ee31557e70c1108e7d7a67b35221dec Mon Sep 17 00:00:00 2001 From: JeLuF Date: Wed, 9 Aug 2023 20:02:52 +0200 Subject: [PATCH 27/61] Masonry gallery, add timestamp --- ui/easydiffusion/bucket_manager.py | 14 +++------ ui/easydiffusion/easydb/mappings.py | 10 ++++--- ui/easydiffusion/utils/save_utils.py | 4 +-- ui/index.html | 4 ++- ui/media/css/main.css | 43 ++++++++++++++++++++-------- ui/media/js/main.js | 24 ++++++++++++---- 6 files changed, 64 insertions(+), 35 deletions(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index cf09c581..0d72ed06 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -91,24 +91,18 @@ def init(): @server_api.get("/image/{image_path:path}") def get_image(image_path: str, db: Session = Depends(get_db)): - from easydiffusion.easydb.mappings import Image + from easydiffusion.easydb.mappings import GalleryImage image_path = str(abspath(image_path)) try: - image = db.query(Image).filter(Image.path == image_path).first() + image = db.query(GalleryImage).filter(GalleryImage.path == image_path).first() return FileResponse(image.path) except Exception as e: raise HTTPException(status_code=404, detail="Image not found") @server_api.get("/all_images") def get_all_images(db: Session = Depends(get_db)): - from easydiffusion.easydb.mappings import Image - images = db.query(Image).all() - # sum_string = "
" - # for img in images: - # options = f"Path: {img.path}\nPrompt: {img.prompt}\nNegative Prompt: {img.negative_prompt}\nSeed: {img.seed}\nModel: {img.use_stable_diffusion_model}\nSize: {img.height}x{img.width}\nSampler: {img.sampler_name}\nSteps: {img.num_inference_steps}\nGuidance Scale: {img.guidance_scale}\nLoRA: {img.lora}\nUpscaling: {img.use_upscale}\nFace Correction: {img.use_face_correction}\n" - # sum_string += f"" - # sum_string += "
" - # return Response(content=sum_string, media_type="text/html") + from easydiffusion.easydb.mappings import GalleryImage + images = db.query(GalleryImage).all() return images diff --git a/ui/easydiffusion/easydb/mappings.py b/ui/easydiffusion/easydb/mappings.py index ad68ecab..31549709 100644 --- a/ui/easydiffusion/easydb/mappings.py +++ b/ui/easydiffusion/easydb/mappings.py @@ -1,9 +1,10 @@ -from sqlalchemy import Column, Integer, String, Float, Boolean +from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.sql import func Base = declarative_base() -class Image(Base): +class GalleryImage(Base): __tablename__ = 'images' path = Column(String, primary_key=True) @@ -23,10 +24,11 @@ class Image(Base): use_upscale = Column(String) prompt = Column(String) negative_prompt = Column(String) + time_created = Column(DateTime(timezone=True), server_default=func.now()) def __repr__(self): - return "" % ( + return "" % ( self.path, self.seed, self.use_stable_diffusion_model, self.clip_skip, self.use_vae_model, self.sampler_name, self.width, self.height, self.num_inference_steps, self.guidance_scale, self.lora, self.use_hypernetwork_model, self.tiling, self.use_face_correction, self.use_upscale, self.prompt, self.negative_prompt) from easydiffusion.easydb.database import engine -Image.metadata.create_all(engine) \ No newline at end of file +GalleryImage.metadata.create_all(engine) diff --git a/ui/easydiffusion/utils/save_utils.py b/ui/easydiffusion/utils/save_utils.py index a21e32b8..7f668280 100644 --- a/ui/easydiffusion/utils/save_utils.py +++ b/ui/easydiffusion/utils/save_utils.py @@ -157,11 +157,11 @@ def save_images_to_disk( else: return metadata_entries[i]["use_lora_model"] + ":" + str(metadata_entries[i]["lora_alpha"]) - from easydiffusion.easydb.mappings import Image + from easydiffusion.easydb.mappings import GalleryImage from easydiffusion.easydb.database import SessionLocal session = SessionLocal() - session.add(Image( + session.add(GalleryImage( path = path_i, seed = metadata_entries[i]["seed"], use_stable_diffusion_model = metadata_entries[i]["use_stable_diffusion_model"], diff --git a/ui/index.html b/ui/index.html index 65be9480..d5803fcc 100644 --- a/ui/index.html +++ b/ui/index.html @@ -518,7 +518,9 @@ diff --git a/ui/media/css/main.css b/ui/media/css/main.css index dfd12c32..e0d1d02d 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1889,21 +1889,40 @@ div#enlarge-buttons { } /* Gallery CSS */ -#imagecontainer { - display: flex; - justify-content: space-around; - flex-flow: row wrap; - align-items: center; +.gallery { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + font-family: sans-serif; } -#imagecontainer>img { - width: 30vw; - min-width: 256px; - max-width: 1024px; - height: auto; - margin-block: 1vh; - border: 4px white solid; +.gallery-container { + columns: 5 ; + column-gap: 1.5rem; + width: 95%; + margin: 0 0 ; } +.gallery-container div { + margin: 0 1.5rem 1.5rem 0; + display: inline-block; + width: 100%; + padding: 5px; + + transition: all .75s ease-in-out; +} + +.gallery-container div img { + width: 100%; + border-radius: 5px; + transition: all .25s ease-in-out; + box-shadow: 5px 5px 5px rgba(0,0,0,0.4); +} + +.gallery-container div img:hover { + box-shadow: 1px 1px 15px rgba(32,0,128,0.8); +} + #tab-content-gallery>button { margin: 8px; diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 6799a648..a4476252 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -3085,13 +3085,25 @@ let recentResolutionsValues = [] })() /* Gallery JS */ +function galleryImage(item) { + let div = document.createElement("div") + let img = document.createElement("img") + + img.src = "/image/" + item.path + img.dataset["request"] = JSON.stringify(item) + div.appendChild(img) + return div +} function refreshGallery() { let container = document.getElementById("imagecontainer") - container.remove() + container.innerHTML="" fetch('/all_images') - .then(response => response.text()) - .then(text => new DOMParser().parseFromString(text, 'text/html')) - .then(html_like => html_like.getElementsByTagName('div')[0]) - .then(div => document.getElementById("tab-content-gallery").appendChild(div)) -} \ No newline at end of file + .then(response => response.json()) + .then(json => { + console.log(json) + json.forEach( item => { + container.appendChild(galleryImage(item)) + }) + }) +} From fbc816d004f0e23b8c7cccea53e926e67fceae8a Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Wed, 9 Aug 2023 21:10:38 +0200 Subject: [PATCH 28/61] Changed link back --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8acafd76..c7848a7c 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Does not require technical knowledge, does not require pre-installed software. 1 Click the download button for your operating system:

- - - + + +

**Hardware requirements:** From 64fc777a2be8ee456c9119d34e1eae75b3bbb175 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Wed, 9 Aug 2023 22:21:36 +0200 Subject: [PATCH 29/61] Added nsfw flag, that way there won't be issues later on if it was to be implemented --- ui/easydiffusion/easydb/mappings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/easydiffusion/easydb/mappings.py b/ui/easydiffusion/easydb/mappings.py index 31549709..035f2ee8 100644 --- a/ui/easydiffusion/easydb/mappings.py +++ b/ui/easydiffusion/easydb/mappings.py @@ -25,6 +25,7 @@ class GalleryImage(Base): prompt = Column(String) negative_prompt = Column(String) time_created = Column(DateTime(timezone=True), server_default=func.now()) + nsfw = Column(String, server_default='unknown') def __repr__(self): return "" % ( From 2bf7116f011868ab8675aa6fa94e215f1b813d77 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Wed, 9 Aug 2023 22:50:03 +0200 Subject: [PATCH 30/61] Added Gallery search for prompt & model --- ui/easydiffusion/bucket_manager.py | 11 +++++++---- ui/index.html | 6 +++++- ui/media/css/main.css | 13 ++++++++++++- ui/media/js/main.js | 13 ++++++++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 0d72ed06..43b5f147 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -100,11 +100,14 @@ def init(): raise HTTPException(status_code=404, detail="Image not found") @server_api.get("/all_images") - def get_all_images(db: Session = Depends(get_db)): + def get_all_images(prompt: str = "", model: str = "", db: Session = Depends(get_db)): from easydiffusion.easydb.mappings import GalleryImage - images = db.query(GalleryImage).all() - return images - + images = db.query(GalleryImage) + if prompt != "": + images = images.filter(GalleryImage.path.like("%"+prompt+"%")) + if model != "": + images = images.filter(GalleryImage.use_stable_diffusion_model.like("%"+model+"%")) + return images.all() def get_filename_from_url(url): path = urlparse(url).path diff --git a/ui/index.html b/ui/index.html index d5803fcc..9aeb7ab2 100644 --- a/ui/index.html +++ b/ui/index.html @@ -517,7 +517,11 @@ diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 5e03cbb9..0c314284 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -142,6 +142,8 @@ let embeddingsSearchBox = document.querySelector("#embeddings-search-box") let embeddingsList = document.querySelector("#embeddings-list") let embeddingsModeField = document.querySelector("#embeddings-mode") let embeddingsCardSizeSelector = document.querySelector("#embedding-card-size-selector") +let galleryImginfoDialog = document.querySelector("#gallery-imginfo") +let galleryImginfoDialogContent = document.querySelector("#gallery-imginfo-content") let positiveEmbeddingText = document.querySelector("#positive-embedding-text") let negativeEmbeddingText = document.querySelector("#negative-embedding-text") @@ -3085,6 +3087,30 @@ let recentResolutionsValues = [] })() /* Gallery JS */ + +const IMAGE_INFO = { + "Prompt": "prompt", + "Negative Prompt": "negative_prompt", + "Seed": "seed", + "Time": "time_created", + "Model": "use_stable_diffusion_model", + "VAE Model": "use_vae_model", + "Hypernetwork": "use_hypernetwork_model", + "LORA": "lora", + "Path": "path", + "Width": "width", + "Height": "height", + "Steps": "num_inference_steps", + "Sampler": "sampler_name", + "Guidance Scale": "guidance_scale", + "Tiling": "tiling", + "Upscaler": "use_upscale", + "Face Correction": "use_face_correction", + "Clip Skip": "clip_skip", +} + +const IGNORE_TOKENS = ["None", "none", "Null", "null", "false", "False"] + function galleryImage(item) { let div = document.createElement("div") div.classList.add("gallery-image") @@ -3096,9 +3122,34 @@ function galleryImage(item) { let hover = document.createElement("div") hover.classList.add("gallery-image-hover") + let infoBtn = document.createElement("button") + infoBtn.classList.add("tertiaryButton") + infoBtn.innerHTML = '' + infoBtn.addEventListener("click", function() { + let table = document.createElement("table") + console.log(item) + + for (const [label, key] of Object.entries(IMAGE_INFO)) { + console.log(label, key, item[key]) + console.log(IGNORE_TOKENS.findIndex( k => (k == item[key]))) + if (IGNORE_TOKENS.findIndex( k => (k == item[key])) == -1) { + let data = item[key] + if (key == "path") { + data = "…/"+data.split(/[\/\\]/).pop() + } + table.appendChild(htmlToElement(`${label}:${data}`)) + } + } + galleryImginfoDialogContent.replaceChildren(table) + galleryImginfoDialog.showModal() + }) + hover.appendChild(infoBtn) + + let imageExpandBtn=document.createElement("button") imageExpandBtn.classList.add("tertiaryButton") imageExpandBtn.innerHTML = '' + imageExpandBtn.style["margin-left"] = "0.2em" imageExpandBtn.addEventListener("click", function() { function previousImage(img) { const allImages = Array.from(document.getElementById("imagecontainer").querySelectorAll(".gallery-image img")) @@ -3126,6 +3177,16 @@ function galleryImage(item) { }) hover.appendChild(imageExpandBtn) + + let openInNewTabBtn = document.createElement("button") + openInNewTabBtn.classList.add("tertiaryButton") + openInNewTabBtn.innerHTML = '' + openInNewTabBtn.style["margin-left"] = "0.2em" + openInNewTabBtn.addEventListener("click", (e) => { + window.open(img.src) + }) + hover.appendChild(openInNewTabBtn) + hover.appendChild(document.createElement("br")) let useAsInputBtn = document.createElement("button") @@ -3139,6 +3200,13 @@ function galleryImage(item) { return div } +modalDialogCloseOnBackdropClick(galleryImginfoDialog) +makeDialogDraggable(galleryImginfoDialog) + +galleryImginfoDialog.querySelector("#gallery-imginfo-close-button").addEventListener("click", () => { + galleryImginfoDialog.close() +}) + function refreshGallery() { let container = document.getElementById("imagecontainer") container.innerHTML="" From 20792837130f693566971e0eae6d93e4cca03872 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:32:20 +0200 Subject: [PATCH 36/61] Added cross page use these settings --- ui/easydiffusion/bucket_manager.py | 2 +- ui/easydiffusion/easydb/mappings.py | 25 +++++++++++++++++++++++++ ui/media/js/main.js | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 34b8bd2f..925ced27 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -116,7 +116,7 @@ def init(): try: image: GalleryImage = db.query(GalleryImage).filter(GalleryImage.path == image_path).first() head = "" - body = "
" + image.htmlForm() + "" + body = f"
" + image.htmlForm() + "" return Response(content="" + head + body + "", media_type="text/html") except Exception as e: print(e) diff --git a/ui/easydiffusion/easydb/mappings.py b/ui/easydiffusion/easydb/mappings.py index a01b50de..214a4c68 100644 --- a/ui/easydiffusion/easydb/mappings.py +++ b/ui/easydiffusion/easydb/mappings.py @@ -50,6 +50,31 @@ class GalleryImage(Base): "

Upscale: " + str(self.use_upscale) + "

" + \ "

Time Created: " + str(self.time_created) + "

" + \ "

NSFW: " + str(self.nsfw) + "

" + + def settingsJSON(self) -> str: + # some are still missing: prompt strength, lora + json = { + "numOutputsTotal": 1, + "seed": self.seed, + "reqBody": { + "prompt": self.prompt, + "negative_prompt": self.negative_prompt, + "width": self.width, + "height": self.height, + "seed": self.seed, + "num_inference_steps": self.num_inference_steps, + "guidance_scale": self.guidance_scale, + "use_face_correction": self.use_face_correction, + "use_upscale": self.use_upscale, + "sampler_name": self.sampler_name, + "use_stable_diffusion_model": self.use_stable_diffusion_model, + "clip_skip": self.clip_skip, + "tiling": self.tiling, + "use_vae_model": self.use_vae_model, + "use_hypernetwork_model": self.use_hypernetwork_model + }} + from json import dumps + return dumps(json) from easydiffusion.easydb.database import engine diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 11011a20..d54913fa 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -3094,7 +3094,7 @@ function galleryImage(item) { w.addEventListener("DOMContentLoaded", () => { w.document.getElementsByTagName("body")[0].classList.add(themeField.value) w.document.getElementById("use_these_settings").addEventListener("click", () => { - alert("use these settings") + restoreTaskToUI(JSON.parse(w.document.getElementById("use_these_settings").getAttribute("json"))) }) w.document.getElementById("use_as_input").addEventListener("click", () => { alert("use as input") From 216ecce506994c76c100d4e27fe57e5121c6e8ec Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:40:49 +0200 Subject: [PATCH 37/61] The use as Input button confuses me, that's why it is now disabled --- ui/easydiffusion/bucket_manager.py | 2 +- ui/media/css/single-gallery.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index 925ced27..f14c7bc0 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -116,7 +116,7 @@ def init(): try: image: GalleryImage = db.query(GalleryImage).filter(GalleryImage.path == image_path).first() head = "" - body = f"
" + image.htmlForm() + "" + body = f"
" + image.htmlForm() + "" return Response(content="" + head + body + "", media_type="text/html") except Exception as e: print(e) diff --git a/ui/media/css/single-gallery.css b/ui/media/css/single-gallery.css index 24f22e47..2427bf0b 100644 --- a/ui/media/css/single-gallery.css +++ b/ui/media/css/single-gallery.css @@ -35,4 +35,8 @@ button { div { margin: 16px; +} + +:disabled { + color: gray; } \ No newline at end of file From f3367a67732a57c9a91fc24f411fe2ac0afb4d3d Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Sat, 12 Aug 2023 16:23:36 +0200 Subject: [PATCH 38/61] Implemented JeLuF's suggestion --- ui/media/js/main.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 5beb094d..0628b015 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -3097,12 +3097,12 @@ function galleryImage(item) { function refreshGallery() { let container = document.getElementById("imagecontainer") - let promptsearchfield = document.getElementById("gallery-prompt-search").value - let promptsearch = promptsearchfield.length > 0 ? "prompt=" + promptsearchfield + "&" : "" - let modelsearchfield = document.getElementById("gallery-model-search").value - let modelsearch = modelsearchfield.length > 0 ? "model=" + modelsearchfield + "&" : "" + params = new URLSearchParams({ + prompt: promptsearchfield, + model: modelsearchfield + }) container.innerHTML="" - fetch('/all_images?' + promptsearch + modelsearch) + fetch('/all_images?' + params) .then(response => response.json()) .then(json => { console.log(json) From ff75adab7f3f5e36fd3775a58eadaef2421a1c55 Mon Sep 17 00:00:00 2001 From: ManInDark <61268856+ManInDark@users.noreply.github.com> Date: Sat, 12 Aug 2023 16:23:36 +0200 Subject: [PATCH 39/61] Implemented JeLuF's suggestion & added pagination basics --- ui/easydiffusion/bucket_manager.py | 3 ++- ui/index.html | 1 + ui/media/js/main.js | 11 ++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py index f14c7bc0..e8d65a48 100644 --- a/ui/easydiffusion/bucket_manager.py +++ b/ui/easydiffusion/bucket_manager.py @@ -100,13 +100,14 @@ def init(): raise HTTPException(status_code=404, detail="Image not found") @server_api.get("/all_images") - def get_all_images(prompt: str = "", model: str = "", db: Session = Depends(get_db)): + def get_all_images(prompt: str = "", model: str = "", page = 0, db: Session = Depends(get_db)): from easydiffusion.easydb.mappings import GalleryImage images = db.query(GalleryImage) if prompt != "": images = images.filter(GalleryImage.path.like("%"+prompt+"%")) if model != "": images = images.filter(GalleryImage.use_stable_diffusion_model.like("%"+model+"%")) + images = images.offset(page*50).limit(50) return images.all() @server_api.get("/single_image") diff --git a/ui/index.html b/ui/index.html index 9aeb7ab2..5676ab5c 100644 --- a/ui/index.html +++ b/ui/index.html @@ -520,6 +520,7 @@