├── README.ja.md ├── README.md ├── misc ├── sss00.png ├── sss01-1.png ├── sss01-2.png ├── sss02-1.png ├── sss02-2.png ├── sss03-1.png ├── sss03-2.png ├── sss04-1.png ├── sss04-2.png ├── sss05-1.png ├── sss05-2.png ├── sss06-1.png ├── sss06-2.png ├── sss07-1.png ├── sss07-2.png ├── sss07-3.png ├── sss07-4.png ├── sss09.png ├── sss10.png ├── sss11.png ├── sss12.png └── sss_top.png └── scripts ├── eagle-pnginfo.py ├── eagleapi ├── api_application.py ├── api_folder.py ├── api_item.py └── api_util.py ├── parser.py └── tag_generator.py /README.ja.md: -------------------------------------------------------------------------------- 1 | # Eagle-pnginfo 2 | 3 | ![](misc/sss_top.png) 4 | 5 | [English README](README.md) 6 | 7 | - [AUTOMATIC1111's Stable Diffusion Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) 用の Extension です 8 | - WebUIで生成した画像を、生成情報(プロンプト・Generation Info)を含めて、お手元の PC で動いている [Eagle](https://jp.eagle.cool/) (画像管理ソフト) へ転送・登録します 9 | 10 | ## インストール方法 11 | 12 | - `Extensions` タブを開く 13 | 14 | - `Install from URL` にこのレポジトリの URL を入力 15 | 16 | - `Install` を実行 17 | 18 | - 別途、PCへ [Eagle]([https://jp.eagle.cool/](https://jp.eagle.cool/) をインストールしておく 19 | 20 | ## 使い方 / How to use 21 | 22 | - "設定" タブで、この Extension を有効にする 23 | 24 | - 別途、"Eagle" アプリを立ち上げておく 25 | 26 | - "AUTO1111" の Web UI を開き、いつも通り画像を生成する 27 | 28 | - 生成された画像は、自動的に Eagle アプリに登録されます 29 | 30 | ## 設定項目について 31 | 32 | | In "Setting" tab | ![](misc/sss09.png) | 33 | | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 34 | | Send all image to Eagle | この Extension を有効にします | 35 | | Save Generation info as Annotation | PNGinfo に表示されるような、3行からなる生成情報を、Eagle の メモ欄に登録します | 36 | | Save positive prompt to Eagle as tags | プロンプトを Eagle の tag として登録します | 37 | | Save negative prompt to Eagle as | ネガティブプロンプトを Eagle の tag として登録します
None: 登録しません
tag: 登録します
n:tag 登録します。登録時、タグ名の頭に "n:" をつけ、通常のプロンプトの tag と判別できるようにします | 38 | | Use prompt parser when save prompt to eagle as tags | プロンプトを Eagle の tag として登録する場合に、 prompt_parser を利用して強調を除外します | 39 | | Additinal tag pattern | Generation info の3行目に表示されている項目について、タグ化する項目を選択できます
![](misc/sss10.png)
使用可能な設定は以下の通りです。
```Steps,Sampler,CFG scale,Seed,Face restoration,Size,Model hash,Model,Hypernet,Hypernet strength,Variation seed,Variation seed strength,Seed resize from,Denoising strength,Conditional mask weight,Eta,Clip skip,ENSD``` | 40 | | Outside Eagle server connection (url:port) | URL:Portを用いて、公開されている Eagle サーバへ画像を送信する設定です | 41 | | FolderID or FolderName on Eagle (option) | Eagle 上での画像保存先フォルダを、FolderID または Folder名で設定できます | 42 | | Allow to crete folder on Eagle, if specified foldername dont exists. | (option) 指定された名前のフォルダが存在しない場合に、新しくその名前のフォルダを作成することを許可する設定です | 43 | 44 | ## 設定サンプル 45 | 46 | | Settings | Result | Comment | 47 | | --------------------- | --------------------- | --------------------------------------------------------------------------------------------------- | 48 | | ![](misc/sss00.png) | No output to Eagle | | 49 | | ![](misc/sss01-1.png) | ![](misc/sss01-2.png) | 画像を Eagle へ送ります | 50 | | ![](misc/sss02-1.png) | ![](misc/sss02-2.png) | 画像を Eagle へ送ります
Generation info 付き | 51 | | ![](misc/sss03-1.png) | ![](misc/sss03-2.png) | 画像を Eagle へ送ります
Generation info, tag, positive prompt 付き | 52 | | ![](misc/sss04-1.png) | ![](misc/sss04-2.png) | 画像を Eagle へ送ります
Generation info, tag, negative prompt 付き | 53 | | ![](misc/sss05-1.png) | ![](misc/sss05-2.png) | 画像を Eagle へ送ります
Generation info, tag, negative prompt 付き。< i.e.) n:bad anatomy | 54 | | ![](misc/sss06-1.png) | ![](misc/sss06-2.png) | 画像を Eagle へ送ります
Generation info, tag, positive prompt, negative prompt 付き。< i.e.) n:bad anatomy | 55 | 56 | ### 設定サンプル: フォルダへの保存(IDで指定) 57 | 58 | | Settings | Result | Comment | 59 | | --------------------- | --------------------- | ------------------------------------------------------------------------- | 60 | | ![](misc/sss07-4.png) | | folderID を入れておくと、対象のフォルダに画像が格納されます | 61 | | ![](misc/sss07-1.png) | ![](misc/sss07-3.png) | "Eagle forlderID" を取得するには、Eagle UI で対象のフォルダを右クリックし、"リンクをコピー" を選択 | 62 | | ![](misc/sss07-2.png) | | folderID の悪い例
コピーしたものをそのまま入力すると左画像のように長いパス名が入っていますが、必要になるのは右端の文字列のみです | 63 | 64 | ### 設定サンプル: フォルダへの保存(フォルダ名で指定) 65 | 66 | ![](misc/sss11.png) 67 | 68 | - 画像を保存したいフォルダ名を入力してください 69 | - 上記のサンプルでは `eagle_inbox` を指定しています 70 | 71 | ### 設定サンプル: ネットワーク越しで Eagle Server へ保存 72 | 73 | ![](misc/sss12.png) 74 | 75 | - Eagle サーバへアクセス出来るようにする必要があります 76 | - サーバアドレスを以下の形式で入力してください ```http://:``` 77 | - 使用しない場合は、空白にしてください 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Eagle-pnginfo 2 | 3 | ![](misc/sss_top.png) 4 | 5 | [日本語 README はこちら](README.ja.md) 6 | 7 | - This is Extension for [AUTOMATIC1111's Stable Diffusion Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) 8 | - Send your creation image to [Eagle](https://jp.eagle.cool/) (image management software) with Generation info, tags. 9 | 10 | ## How to Install 11 | 12 | - Go to `Extensions` tab on your web UI 13 | 14 | - `Install from URL` with this repo URL 15 | 16 | - Install 17 | 18 | - Install [Eagle]([https://jp.eagle.cool/](https://jp.eagle.cool/)) 19 | 20 | ## How to use 21 | 22 | - Enable this extension in "Setting" 23 | 24 | - Start "Eagle" application 25 | 26 | - Open "AUTO1111" and create image as usual. 27 | 28 | - images sent to "Eagle" automatically 29 | 30 | ## About Setting params 31 | 32 | | In "Setting" tab | ![](misc/sss09.png) | 33 | | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 34 | | "Send all image to Eagle" | Enable this extension | 35 | | "Save Generation info as Annotation" | Save PNGinfo style text to "memo" on Eagle | 36 | | "Save positive prompt to Eagle as tags" | Save each prompt word as tag on Eagle | 37 | | "Save negative prompt to Eagle as" | Save each negative prompt word as tag on Eagle
None  : disabled
tag   : normal tag. i.e.) "bad anatomy"
n:tag : tag with "n:". i.e.)"n:bad annatomy" | 38 | | "Use prompt parser when save prompt to eagle as tags" | Use prompt parser when save prompts | 39 | | Additinal tag pattern | Add tags about Generation info params.
![](misc/sss10.png)
Usable word is listed,
```Steps,Sampler,CFG scale,Seed,Face restoration,Size,Model hash,Model,Hypernet,Hypernet strength,Variation seed,Variation seed strength,Seed resize from,Denoising strength,Conditional mask weight,Eta,Clip skip,ENSD``` | 40 | | Outside Eagle server connection (url:port) | (Default: http://localhost:41595)
 URL:port as Eagle server address.
```http://:``` | 41 | | FolderID or FolderName on Eagle (option) | (option) Specify folder by ID on Eagle to input images | 42 | | Allow to crete folder on Eagle, if specified foldername dont exists. | (option) Allow create new folder with specified name, when folder with this name dont exists. | 43 | 44 | ## Setting sample 45 | 46 | | Settings | Result | Comment | 47 | | --------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------ | 48 | | ![](misc/sss00.png) | No output to Eagle | | 49 | | ![](misc/sss01-1.png) | ![](misc/sss01-2.png) | Image sent to Eagle, only with filename. | 50 | | ![](misc/sss02-1.png) | ![](misc/sss02-2.png) | Image sent to Eagle with Generation info. | 51 | | ![](misc/sss03-1.png) | ![](misc/sss03-2.png) | Image sent to Eagle, with Generation info, tags from positive prompt | 52 | | ![](misc/sss04-1.png) | ![](misc/sss04-2.png) | Image sent to Eagle, with Generation info, tags from negative prompt | 53 | | ![](misc/sss05-1.png) | ![](misc/sss05-2.png) | Image sent to Eagle, with Generation info, tags from negative prompt decorated with "n:".
 i.e.) n:bad anatomy | 54 | | ![](misc/sss06-1.png) | ![](misc/sss06-2.png) | Image sent to Eagle, with Generation info, tags from positive prompt and negative prompt decorated with "n:". | 55 | 56 | ### Setting sample: Save to folder (by ID) 57 | 58 | | Settings | Result | Comment | 59 | | --------------------- | --------------------- | -------------------------------------------------------------------------------------- | 60 | | ![](misc/sss07-4.png) | | Input folderID | 61 | | ![](misc/sss07-1.png) | ![](misc/sss07-3.png) | .You can get "Eagle forlderID" on Eagle UI. Right click folder and select "copy link". | 62 | | ![](misc/sss07-2.png) | | Bad sample of folderID.
Only right-end value required. | 63 | 64 | ### Setting sample: Save to folder (by Name) 65 | 66 | ![](misc/sss11.png) 67 | 68 | - Input folder Name on Eagle 69 | 70 | - Example is `eagle_inbox` 71 | 72 | - Image will saved in this folder 73 | 74 | ### Setting sample: Save to (not local) Eagle server 75 | 76 | ![](misc/sss12.png) 77 | 78 | - Run your Eagle server public, and note server IP and Port 79 | - Input address of your server as ```http://:``` 80 | -------------------------------------------------------------------------------- /misc/sss00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss00.png -------------------------------------------------------------------------------- /misc/sss01-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss01-1.png -------------------------------------------------------------------------------- /misc/sss01-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss01-2.png -------------------------------------------------------------------------------- /misc/sss02-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss02-1.png -------------------------------------------------------------------------------- /misc/sss02-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss02-2.png -------------------------------------------------------------------------------- /misc/sss03-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss03-1.png -------------------------------------------------------------------------------- /misc/sss03-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss03-2.png -------------------------------------------------------------------------------- /misc/sss04-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss04-1.png -------------------------------------------------------------------------------- /misc/sss04-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss04-2.png -------------------------------------------------------------------------------- /misc/sss05-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss05-1.png -------------------------------------------------------------------------------- /misc/sss05-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss05-2.png -------------------------------------------------------------------------------- /misc/sss06-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss06-1.png -------------------------------------------------------------------------------- /misc/sss06-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss06-2.png -------------------------------------------------------------------------------- /misc/sss07-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss07-1.png -------------------------------------------------------------------------------- /misc/sss07-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss07-2.png -------------------------------------------------------------------------------- /misc/sss07-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss07-3.png -------------------------------------------------------------------------------- /misc/sss07-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss07-4.png -------------------------------------------------------------------------------- /misc/sss09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss09.png -------------------------------------------------------------------------------- /misc/sss10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss10.png -------------------------------------------------------------------------------- /misc/sss11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss11.png -------------------------------------------------------------------------------- /misc/sss12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss12.png -------------------------------------------------------------------------------- /misc/sss_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbc-mc/sdweb-eagle-pnginfo/15e67fd152c176e87eee14a52e720959a53fd5f6/misc/sss_top.png -------------------------------------------------------------------------------- /scripts/eagle-pnginfo.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import gradio as gr 4 | 5 | from modules import paths, script_callbacks, shared 6 | 7 | from scripts.eagleapi import api_application 8 | from scripts.eagleapi import api_item 9 | from scripts.eagleapi import api_util 10 | from scripts.parser import Parser 11 | from scripts.tag_generator import TagGenerator 12 | 13 | DEBUG = False 14 | def dprint(str): 15 | if DEBUG: 16 | print(str) 17 | 18 | path_root = paths.script_path 19 | 20 | def on_ui_settings(): 21 | # flg: Enable/Disable 22 | shared.opts.add_option("enable_eagle_integration", shared.OptionInfo(False, "Send all image to Eagle", section=("eagle_pnginfo", "Eagle Pnginfo"))) 23 | # flg: save generation info to annotation 24 | shared.opts.add_option("save_generationinfo_to_eagle_as_annotation", shared.OptionInfo(False, "Save Generation info as Annotation", section=("eagle_pnginfo", "Eagle Pnginfo"))) 25 | # flg: save positive prompt to tags 26 | shared.opts.add_option("save_positive_prompt_to_eagle_as_tags", shared.OptionInfo(False, "Save positive prompt to Eagle as tags", section=("eagle_pnginfo", "Eagle Pnginfo"))) 27 | shared.opts.add_option("save_negative_prompt_to_eagle_as", shared.OptionInfo("n:tag", "Save negative prompt as", gr.Radio, {"choices": ["None", "tag", "n:tag"]}, section=("eagle_pnginfo", "Eagle Pnginfo"))) 28 | shared.opts.add_option("use_prompt_parser_when_save_prompt_to_eagle_as_tags", shared.OptionInfo(False, "Use prompt parser when save prompt to eagle as tags", section=("eagle_pnginfo", "Eagle Pnginfo"))) 29 | # txt: Additinal tags 30 | shared.opts.add_option("additional_tags_to_eagle", shared.OptionInfo("", "Additinal tag pattern", section=("eagle_pnginfo", "Eagle Pnginfo"))) 31 | # txt: server_url 32 | shared.opts.add_option("outside_server_url_port", shared.OptionInfo("", "Outside Eagle server connection (url:port)", section=("eagle_pnginfo", "Eagle Pnginfo"))) 33 | # specify Eagle folderID 34 | shared.opts.add_option("save_to_eagle_folderid", shared.OptionInfo("", "(option) FolderID or FolderName on Eagle", component_args=shared.hide_dirs, section=("eagle_pnginfo", "Eagle Pnginfo"))) 35 | # specify Eagle folderID 36 | shared.opts.add_option("allow_to_create_folder_on_eagle", shared.OptionInfo(False, "(option) Allow to create folder on Eagle, if specified foldername dont exists.", section=("eagle_pnginfo", "Eagle Pnginfo"))) 37 | 38 | # image saved callback 39 | def on_image_saved(params:script_callbacks.ImageSaveParams): 40 | if not shared.opts.enable_eagle_integration: 41 | dprint(f"DEBUG:on_image_saved: DISABLED") 42 | else: 43 | dprint(f"DEBUG:on_image_saved: ENABELD. enable_eagle_pnginfo is true.") 44 | # collect info 45 | fullfn = os.path.join(path_root, params.filename) 46 | info = params.pnginfo.get('parameters', None) 47 | filename = os.path.splitext(os.path.basename(fullfn))[0] 48 | # 49 | pos_prompt = params.p.prompt 50 | neg_prompt = params.p.negative_prompt 51 | # 52 | annotation = None 53 | tags = [] 54 | if shared.opts.save_generationinfo_to_eagle_as_annotation: 55 | annotation = info 56 | if shared.opts.save_positive_prompt_to_eagle_as_tags: 57 | if len(pos_prompt.split(",")) > 0: 58 | tags += Parser.prompt_to_tags(pos_prompt) 59 | if shared.opts.save_negative_prompt_to_eagle_as == "tag": 60 | if len(neg_prompt.split(",")) > 0: 61 | tags += Parser.prompt_to_tags(neg_prompt) 62 | elif shared.opts.save_negative_prompt_to_eagle_as == "n:tag": 63 | if len(neg_prompt.split(",")) > 0: 64 | tags += [ f"n:{x}" for x in Parser.prompt_to_tags(neg_prompt) ] 65 | if shared.opts.additional_tags_to_eagle != "": 66 | gen = TagGenerator(p=params.p, image=params.image) 67 | _tags = gen.generate_from_p(shared.opts.additional_tags_to_eagle) 68 | if _tags and len(_tags) > 0: 69 | tags += _tags 70 | 71 | def _get_folderId(folder_name_or_id, allow_create_new_folder, server_url="http://localhost", port=41595): 72 | _ret = api_util.find_or_create_folder(folder_name_or_id, allow_create_new_folder, server_url, port) 73 | return _ret 74 | 75 | # send to Eagle 76 | if shared.opts.outside_server_url_port != "" and api_application.is_valid_url_port(shared.opts.outside_server_url_port): 77 | # send by URL 78 | dprint("DEBUG: try to send URL") 79 | item = api_item.EAGLE_ITEM_URL( 80 | url=fullfn, 81 | name=filename, 82 | tags=tags, 83 | annotation=annotation 84 | ) 85 | server_url, port = api_util.get_url_port(shared.opts.outside_server_url_port) 86 | folderId = _get_folderId(shared.opts.save_to_eagle_folderid, shared.opts.allow_to_create_folder_on_eagle, server_url=server_url, port=port) 87 | _ret = api_item.add_from_URL_base64( 88 | item, 89 | folderId=folderId, 90 | server_url=server_url, 91 | port=port 92 | ) 93 | else: 94 | # send to local 95 | dprint("DEBUG: try to send local") 96 | item = api_item.EAGLE_ITEM_PATH( 97 | filefullpath=fullfn, 98 | filename=filename, 99 | annotation=annotation, 100 | tags=tags 101 | ) 102 | folderId = _get_folderId(shared.opts.save_to_eagle_folderid, shared.opts.allow_to_create_folder_on_eagle) 103 | _ret = api_item.add_from_path( 104 | item=item, 105 | folderId=folderId 106 | ) 107 | dprint(f"DEBUG: {_ret}") 108 | dprint(f" content :{_ret.content}") 109 | dprint(f" status_code:{_ret.status_code}") 110 | 111 | # on_image_saved 112 | script_callbacks.on_image_saved(on_image_saved) 113 | 114 | # 115 | script_callbacks.on_ui_settings(on_ui_settings) 116 | -------------------------------------------------------------------------------- /scripts/eagleapi/api_application.py: -------------------------------------------------------------------------------- 1 | # seealso: https://api.eagle.cool/application/info 2 | # 3 | import requests 4 | 5 | from . import api_util 6 | 7 | def info(server_url="http://localhost", port=41595, timeout_connect=3, timeout_read=10): 8 | """EAGLE API:/api/application/info 9 | 10 | Returns: 11 | Response: return of requests.post 12 | """ 13 | 14 | API_URL = f"{server_url}:{port}/api/application/info" 15 | 16 | try: 17 | r_get = requests.get(API_URL, timeout=(timeout_connect, timeout_read)) 18 | except requests.exceptions.Timeout as e: 19 | print("Error: api_application.info") 20 | print(e) 21 | return 22 | 23 | return r_get 24 | 25 | # 26 | # Support function 27 | # 28 | def is_alive(server_url="http://localhost", port=41595, timeout_connect=3, timeout_read=10): 29 | if not port or type(port) != int or port == "": 30 | port=41595 31 | try: 32 | r_get = info(server_url, port, timeout_connect, timeout_read) 33 | except Exception as e: 34 | print("Error: api_application.is_alive") 35 | print(e) 36 | return False 37 | try: 38 | r_get.raise_for_status() 39 | return True 40 | except: 41 | return False 42 | 43 | def is_valid_url_port(server_url_port="", timeout_connect=3, timeout_read=3): 44 | if not server_url_port or server_url_port == "": 45 | return False 46 | server_url, port = api_util.get_url_port(server_url_port) 47 | if not server_url or not port: 48 | return False 49 | if not is_alive(server_url=server_url, port=port, timeout_connect=timeout_connect, timeout_read=timeout_read): 50 | return False 51 | return True 52 | -------------------------------------------------------------------------------- /scripts/eagleapi/api_folder.py: -------------------------------------------------------------------------------- 1 | # see also https://api.eagle.cool/folder/list 2 | # 3 | import requests 4 | import sys 5 | 6 | from . import api_util 7 | 8 | def create(newfoldername, server_url="http://localhost", port=41595, allow_duplicate_name=True, timeout_connect=3, timeout_read=10): 9 | """EAGLE API:/api/folder/list 10 | 11 | Method: POST 12 | 13 | Returns: 14 | list(response dict): return list of response.json() 15 | """ 16 | API_URL = f"{server_url}:{port}/api/folder/create" 17 | 18 | def _init_data(newfoldername): 19 | _data = {} 20 | if newfoldername and newfoldername != "": 21 | _data.update({"folderName": newfoldername}) 22 | return _data 23 | data = _init_data(newfoldername) 24 | 25 | # check duplicate if needed 26 | if not allow_duplicate_name: 27 | r_post = list() 28 | _ret = api_util.findFolderByName(r_post, newfoldername) 29 | if _ret != None or len(_ret) > 0: 30 | print(f"ERROR: create folder with same name is forbidden by option. [eagleapi.folder.create] foldername=\"{newfoldername}\"", file=sys.stderr) 31 | return 32 | 33 | r_post = requests.post(API_URL, json=data, timeout=(timeout_connect, timeout_read)) 34 | return r_post 35 | 36 | 37 | def rename(folderId, newName, server_url="http://localhost", port=41595, timeout_connect=3, timeout_read=10): 38 | """EAGLE API:/api/folder/rename 39 | 40 | Method: POST 41 | 42 | Returns: 43 | list(response dict): return list of response.json() 44 | """ 45 | data = { 46 | "folderId": folderId, 47 | "newName": newName 48 | } 49 | API_URL = f"{server_url}:{port}/api/folder/rename" 50 | r_post = requests.post(API_URL, json=data, timeout=(timeout_connect, timeout_read)) 51 | return r_post 52 | 53 | 54 | def list(server_url="http://localhost", port=41595, timeout_connect=3, timeout_read=10): 55 | """EAGLE API:/api/folder/list 56 | 57 | Method: GET 58 | 59 | Returns: 60 | Response: return of requests.post 61 | """ 62 | 63 | API_URL = f"{server_url}:{port}/api/folder/list" 64 | 65 | r_get = requests.get(API_URL, timeout=(timeout_connect, timeout_read)) 66 | 67 | return r_get 68 | -------------------------------------------------------------------------------- /scripts/eagleapi/api_item.py: -------------------------------------------------------------------------------- 1 | # seealso: https://api.eagle.cool/item/add-from-path 2 | # seealso: https://api.eagle.cool/item/add-from-paths 3 | # 4 | import requests 5 | import os 6 | 7 | import base64 8 | 9 | DEBUG = False 10 | def dprint(str): 11 | if DEBUG: 12 | print(str) 13 | 14 | class EAGLE_ITEM_PATH: 15 | def __init__(self, filefullpath, filename="", website="", tags:list=[], annotation=""): 16 | """Data container for addFromPath, addFromPaths 17 | 18 | Args: 19 | filefullpath : Required, full path of the local files. 20 | filename : (option), name of image to be added. 21 | website : (option), address of the source of the image. 22 | tags (list) : (option), tags for the image. 23 | annotation : (option), annotation for the image. 24 | """ 25 | self.filefullpath = filefullpath 26 | self.filename = filename 27 | self.website = website 28 | self.tags = tags 29 | self.annotation = annotation 30 | 31 | def output_data(self): 32 | """ 33 | output data in json format for POST 34 | """ 35 | _data = { 36 | "path": self.filefullpath, 37 | "name": os.path.splitext(os.path.basename(self.filefullpath))[0] if (self.filename == None or self.filename == "") else self.filename 38 | } 39 | if self.website and self.website != "": 40 | _data.update({"website": self.website}) 41 | if self.tags and len(self.tags) > 0: 42 | _data.update({"tags": self.tags}) 43 | if self.annotation and self.annotation != "": 44 | _data.update({"annotation": self.annotation}) 45 | return _data 46 | 47 | 48 | class EAGLE_ITEM_URL: 49 | def __init__(self, url, name, website="", tags=[], annotation="", modificationTime="", folderId="", headers={}): 50 | """Data container for addFromURL, addFromURLs 51 | url : Required, the URL of the image to be added. Supports http, https, base64 52 | name : Required, The name of the image to be added. 53 | website : The Address of the source of the image 54 | tags: Tags for the image. 55 | annotation: The annotation for the image. 56 | modificationTime: The creation date of the image. The parameter can be used to alter the image's sorting order in Eagle. 57 | headers: Optional, customize the HTTP headers properties, this could be used to circumvent the security of certain websites. 58 | 59 | folderId: If this parameter is defined, the image will be added to the corresponding folder. 60 | """ 61 | self.url = url 62 | self.name = name 63 | self.website = website 64 | self.tags = tags 65 | self.annotation = annotation 66 | self.modificationTime = modificationTime 67 | self.folderId = folderId 68 | self.headers = headers 69 | 70 | def convert_file_to_base64url(self, filepath=None): 71 | if (not filepath or filepath == ""): 72 | if self.url and self.url != "": 73 | filepath = self.url 74 | else: 75 | print("Error convert_file_to_base64url: invalid filepath") 76 | return filepath 77 | else: 78 | self.url = filepath 79 | if not os.path.exists(filepath): 80 | print("Error convert_file_to_base64url: file not found.") 81 | return filepath 82 | try: 83 | with open(filepath, "rb") as file: 84 | enc_file = base64.urlsafe_b64encode(file.read()) 85 | self.url = f"data:image/png;base64, {enc_file.decode('utf-8')}" 86 | except Exception as e: 87 | print("Error convert_file_to_base64url: eocode failed") 88 | print(e) 89 | return filepath 90 | 91 | return self.url 92 | 93 | def output_data(self): 94 | """ 95 | output data in json format for POST 96 | """ 97 | _data = { 98 | "url": self.url, 99 | "name": self.name 100 | } 101 | # add optional data 102 | if self.website and self.website != "": 103 | _data.update({"website": self.website}) 104 | if self.tags and len(self.tags) > 0: 105 | _data.update({"tags": self.tags}) 106 | if self.annotation and self.annotation != "": 107 | _data.update({"annotation": self.annotation}) 108 | if self.modificationTime and self.modificationTime != "": 109 | _data.update({"modificationTime": self.modificationTime}) 110 | if self.folderId and self.folderId != "": 111 | _data.update({"folderId": self.folderId}) 112 | if self.headers and len(self.headers) > 0: 113 | _data.update({"headers": self.headers}) 114 | return _data 115 | 116 | 117 | def add_from_URL(item:EAGLE_ITEM_URL, folderId=None, server_url="http://localhost", port=41595): 118 | API_URL = f"{server_url}:{port}/api/item/addFromURL" 119 | _data = item.output_data() 120 | if folderId and folderId != "": 121 | _data.update({"folderId": folderId}) 122 | r_post = requests.post(API_URL, json=_data) 123 | return r_post 124 | 125 | 126 | def add_from_URL_base64(item:EAGLE_ITEM_URL, folderId=None, server_url="http://localhost", port=41595): 127 | API_URL = f"{server_url}:{port}/api/item/addFromURL" 128 | item.url = item.convert_file_to_base64url() 129 | _data = item.output_data() 130 | if folderId and folderId != "": 131 | _data.update({"folderId": folderId}) 132 | r_post = requests.post(API_URL, json=_data) 133 | return r_post 134 | 135 | 136 | def add_from_path(item:EAGLE_ITEM_PATH, folderId=None, server_url="http://localhost", port=41595): 137 | API_URL = f"{server_url}:{port}/api/item/addFromPath" 138 | _data = item.output_data() 139 | if folderId and folderId != "": 140 | _data.update({"folderId": folderId}) 141 | r_post = requests.post(API_URL, json=_data) 142 | return r_post 143 | 144 | 145 | def add_from_paths(files, folderId=None, server_url="http://localhost", port=41595, step=None): 146 | """EAGLE API:/api/item/addFromPaths 147 | 148 | Method: POST 149 | 150 | Args: 151 | path: Required, the path of the local files. 152 | name: Required, the name of images to be added. 153 | website: The Address of the source of the images. 154 | annotation: The annotation for the images. 155 | tags: Tags for the images. 156 | folderId: If this parameter is defined, the image will be added to the corresponding folder. 157 | step: interval image num of doing POST. Defaults is None (disabled) 158 | 159 | Returns: 160 | Response: return of requests.posts 161 | """ 162 | API_URL = f"{server_url}:{port}/api/item/addFromPaths" 163 | 164 | if step: 165 | step = int(step) 166 | 167 | def _init_data(): 168 | _data = {"items": []} 169 | if folderId and folderId != "": 170 | _data.update({"folderId": folderId}) 171 | return _data 172 | 173 | r_posts = [] 174 | data = _init_data() 175 | for _index, _item in enumerate(files): 176 | _item:EAGLE_ITEM_PATH = _item 177 | _data = _item.output_data() 178 | if _data: 179 | data["items"].append(_data) 180 | if step and step > 0: 181 | if ((_index + 1) - ((_index + 1) // step) * step) == 0: 182 | _ret = requests.post(API_URL, json=data) 183 | try: 184 | r_posts.append(_ret.json()) 185 | except: 186 | r_posts.append(_ret) 187 | data = _init_data() 188 | if (len(data["items"]) > 0) or (not step or step <= 0): 189 | _ret = requests.post(API_URL, json=data) 190 | try: 191 | r_posts.append(_ret.json()) 192 | except: 193 | r_posts.append(_ret) 194 | 195 | return [ x for x in r_posts if x != "" ] 196 | -------------------------------------------------------------------------------- /scripts/eagleapi/api_util.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import ipaddress 3 | from urllib.parse import urlparse 4 | 5 | from . import api_folder 6 | 7 | def get_url_port(server_url_port=""): 8 | if not server_url_port or server_url_port == "": 9 | return None, None 10 | o = urlparse(server_url_port) 11 | _url = f"http://{o.hostname}" 12 | if o.hostname != "localhost": 13 | _ip = ipaddress.ip_address(o.hostname) 14 | if _ip.version == 6: 15 | _url = f"http://[{o.hostname}]" 16 | port = o.port 17 | return _url, port 18 | 19 | # util for /api/folder/list 20 | def findFolderByID(r_posts, target_id): 21 | return findFolderByName(r_posts, target_id, findByID=True) 22 | 23 | def findFolderByName(r_posts, target_name, findByID=False): 24 | _ret = [] 25 | if not target_name or target_name == "" or not r_posts: 26 | return None 27 | _all_folder = getAllFolder(r_posts) 28 | for _data in _all_folder: 29 | if (findByID and _data.get("id", "") == target_name) or (_data.get("name", "") == target_name): 30 | _ret = _data 31 | break 32 | return _ret 33 | 34 | def getAllFolder(r_posts): 35 | """ get dict of {"folderId": _data, ...""" 36 | def dig_folder(data, dig_count, dig_limit=10): 37 | dig_count+=1 38 | if(dig_count>dig_limit): 39 | return [] 40 | _ret = [data] 41 | if "children" in data and len(data["children"]) > 0: 42 | for _child in data["children"]: 43 | _ret += dig_folder(_child, dig_count) 44 | return _ret 45 | _ret = [] 46 | if not r_posts: 47 | return None 48 | _posts = r_posts.json() 49 | if not _posts or "status" not in _posts or _posts["status"] != "success": 50 | return None 51 | if "data" in _posts and len(_posts["data"]) > 0: 52 | for _data in _posts["data"]: 53 | _ret += dig_folder(_data, 0) 54 | return _ret 55 | 56 | # 57 | # Support functions 58 | # 59 | 60 | def print_response(_res:requests.Response): 61 | print(f"status code : {_res.status_code}") 62 | print(f"headers : {_res.headers}") 63 | print(f"text : {_res.text}") 64 | print(f"encoding : {_res.encoding}") 65 | print(f"cookies : {_res.cookies}") 66 | 67 | print(f"content : {_res.content}") 68 | print(f"content decode: {_res.content.decode(encoding=_res.apparent_encoding)}") 69 | 70 | def get_json_from_response(_res:requests.Response): 71 | try: 72 | _result = _res.json() 73 | return _result 74 | except: 75 | return _res 76 | 77 | def find_or_create_folder(folder_name_or_id, allow_create_new_folder=False, server_url="http://localhost", port=41595, timeout_connect=3, timeout_read=10): 78 | """ 79 | Find or Create folder on Eagle, by folderId or FolderName 80 | 81 | Args: 82 | folder_name_or_id (str): 83 | allow_create_new_folder (bool, optional): if True, create new folder on Eagle. Defaults to False. 84 | server_url (str, optional): Defaults to "http://localhost". 85 | port (int, optional): Defaults to 41595. 86 | timeout_connect (int, optional): Defaults to 3. 87 | timeout_read (int, optional): Defaults to 10. 88 | Return: 89 | folderId or "" 90 | 91 | """ 92 | _eagle_folderid = "" 93 | if folder_name_or_id and folder_name_or_id !="": 94 | _ret_folder_list = api_folder.list(server_url=server_url, port=port, timeout_connect=timeout_connect, timeout_read=timeout_read) 95 | 96 | # serach by name 97 | _ret = findFolderByName(_ret_folder_list, folder_name_or_id) 98 | if _ret and len(_ret) > 0: 99 | _eagle_folderid = _ret.get("id", "") 100 | # serach by ID 101 | if _eagle_folderid == "": 102 | _ret = findFolderByID(_ret_folder_list, folder_name_or_id) 103 | if _ret and len(_ret) > 0: 104 | _eagle_folderid = _ret.get("id", "") 105 | if _eagle_folderid == "": 106 | if allow_create_new_folder: # allow new 107 | _r_get = api_folder.create(folder_name_or_id, server_url=server_url, port=port, timeout_connect=timeout_connect, timeout_read=timeout_read) 108 | try: 109 | _eagle_folderid = _r_get.json().get("data").get("id") 110 | except: 111 | _eagle_folderid = "" 112 | return _eagle_folderid 113 | -------------------------------------------------------------------------------- /scripts/parser.py: -------------------------------------------------------------------------------- 1 | from modules import prompt_parser, shared 2 | 3 | class Parser: 4 | def prompt_to_tags(prompt): 5 | use_prompt_parser = shared.opts.use_prompt_parser_when_save_prompt_to_eagle_as_tags 6 | 7 | p = prompt 8 | if use_prompt_parser: 9 | p = ','.join(map(lambda x: x[0].strip(), prompt_parser.parse_prompt_attention(p))) 10 | 11 | return [ x.strip() for x in p.split(",") if x.strip() != "" ] 12 | -------------------------------------------------------------------------------- /scripts/tag_generator.py: -------------------------------------------------------------------------------- 1 | from modules import shared 2 | 3 | 4 | class TagGenerator(): 5 | # @seealso modules.images FilenameGenerator replacements 6 | # @seealso modules.processing create_infotext generation_params 7 | replacements ={ 8 | "Steps": lambda self: self.p.steps, 9 | "Sampler": lambda self: self.p.sampler_name, 10 | "CFG scale": lambda self: self.p.cfg_scale, 11 | "Seed": lambda self: self.p.seed if self.p.seed is not None else '', 12 | "Face restoration": lambda self: (shared.opts.face_restoration_model if self.p.restore_faces else None), 13 | "Size": lambda self: f"{self.p.width}x{self.p.height}", 14 | "Model hash": lambda self: getattr(self.p, 'sd_model_hash', None if not shared.opts.add_model_hash_to_info or not shared.sd_model.sd_model_hash else shared.sd_model.sd_model_hash), 15 | "Model": lambda self: (None if not shared.opts.add_model_name_to_info or not shared.sd_model.sd_checkpoint_info.model_name else shared.sd_model.sd_checkpoint_info.model_name.replace(',', '').replace(':', '')), 16 | "Hypernet": lambda self: (None if shared.loaded_hypernetwork is None else shared.loaded_hypernetwork.name), 17 | "Hypernet strength": lambda self: (None if shared.loaded_hypernetwork is None or shared.opts.sd_hypernetwork_strength >= 1 else shared.opts.sd_hypernetwork_strength), 18 | "Variation seed": lambda self: (None if self.p.subseed_strength == 0 else self.p.seed), 19 | "Variation seed strength": lambda self: (None if self.p.subseed_strength == 0 else self.p.subseed_strength), 20 | "Seed resize from": lambda self: (None if self.p.seed_resize_from_w == 0 or self.p.seed_resize_from_h == 0 else f"{self.p.seed_resize_from_w}x{self.p.seed_resize_from_h}"), 21 | "Denoising strength": lambda self: getattr(self.p, 'denoising_strength', None), 22 | "Conditional mask weight": lambda self: getattr(self.p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if self.p.is_using_inpainting_conditioning else None, 23 | "Eta": lambda self: (None if self.p.sampler is None or self.p.sampler.eta == self.p.sampler.default_eta else self.p.sampler.eta), 24 | "Clip skip": lambda self: None if getattr(self.p, 'clip_skip', shared.opts.CLIP_stop_at_last_layers) <= 1 else getattr(self.p, 'clip_skip', shared.opts.CLIP_stop_at_last_layers), 25 | "ENSD": lambda self: None if shared.opts.eta_noise_seed_delta == 0 else shared.opts.eta_noise_seed_delta 26 | } 27 | 28 | def __init__(self, p=None, image=None): 29 | self.p = p 30 | self.image = image 31 | 32 | def generate_from_geninfo(self, tags_to_eagle, geninfo): 33 | geninfo = geninfo.split("\n") 34 | if len(geninfo) == 3: 35 | geninfo = geninfo[2] 36 | else: 37 | return [] 38 | # generate lists from geninfo. i.e) "Steps: 30, CFG scale: 7.5" -> ["Steps: 30", "CFG scale: 7.5"] 39 | geninfo_params = [ x.strip() for x in geninfo.split(",") if x.strip() != "" ] 40 | geninfo_dict = {} # {"Steps": "30", "CFG scale": "7.5"} 41 | for item in geninfo_params: 42 | geninfo_dict.update({item.split(":")[0]: item.split(":")[1].strip()}) 43 | tag_list = [ x.strip() for x in tags_to_eagle.split(",") if x.strip() != "" ] 44 | _tags = [ f"{x}: {geninfo_dict.get(x)}" for x in geninfo_dict.keys() if x in tag_list ] 45 | return _tags 46 | 47 | def generate_from_p(self, tags_to_eagle): 48 | tag_list = [ x.strip() for x in tags_to_eagle.split(",") if x.strip() != "" ] 49 | 50 | tags = [] 51 | for _tag in tag_list: 52 | if not _tag or _tag == "": 53 | continue 54 | func = self.replacements.get(_tag) 55 | if func: 56 | try: 57 | _tag_data = func(self) 58 | except Exception as e: 59 | print(e) 60 | _tag_data = "" 61 | if _tag_data: 62 | tags += [f"{_tag}: {_tag_data}"] 63 | tags = [ x for x in tags if x.strip() != "" ] 64 | return tags 65 | --------------------------------------------------------------------------------