diff --git a/README.md b/README.md index 0892532..580f076 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,13 @@ If you would like to make a donation to support development, please use [GitHub ## Supported AIs - [OpenAI](https://openai.com) +- [Midjourney](https://midjourney.com)\* - [Stability AI](https://stability.ai) - [Dream](https://dream.ai) - [DeepAI](https://deepai.org) +\* _Midjourney currently uses an unofficial third party [package](https://github.com/erictik/midjourney-client). Use this integration at your own risk._ + ## Voice Commands Activate the microphone to interact with Phrame using the following voice commands. @@ -241,6 +244,44 @@ openai: - cinematic ``` +### `midjourney` + +_Midjourney currently uses an unofficial third party [package](https://github.com/erictik/midjourney-client). Use this integration at your own risk._ + +To configure Midjourney, you will need the following: + +- [Discord Server](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server) ID and Channel ID + - Obtain by going to your Discord channel in a browser which should follow this pattern - `https://discord.com/channels/SERVER_ID/CHANNEL_ID` +- Invite [Midjourney bot](https://docs.midjourney.com/docs/invite-the-bot) to your server +- While not necessary, it is also recommended to use a [Hugging Face token](https://huggingface.co/docs/hub/security-tokens) for security prompts + +All other default settings found bellow will also be applied. You can overwrite the settings by updating your `config.yml` file. + +```yaml +# midjourney settings (default: shown below) + +midjourney: + # discord server id + server_id: + # discord channel id + channel_id: + # discord token (https://linuxhint.com/get-discord-token) + token: + # hugging face token (https://huggingface.co/docs/hub/security-tokens) + hugging_face_token: + + image: + # enable or disable image generation + enable: true + # options added to a prompt that change how an image generates (https://docs.midjourney.com/docs/parameter-list) + parameters: --chaos 80 --no text + # upscale options (false, random, 1,2,3,4) + upscale: random + # used with summary to guide the image model towards a particular style + style: + - cinematic +``` + ### `stabilityai` To configure Stability AI, obtain an [API key](https://platform.stability.ai) and add it to your config like the following. All other default settings found bellow will also be applied. You can overwrite the settings by updating your `config.yml` file. diff --git a/api/package-lock.json b/api/package-lock.json index 213ead4..40f9474 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -20,6 +20,7 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "luxon": "^3.3.0", + "midjourney": "^3.1.88", "multer": "^1.4.5-lts.1", "openai": "^3.2.1", "read-last-lines": "^1.8.0", @@ -58,6 +59,14 @@ "kuler": "^2.0.0" } }, + "node_modules/@huggingface/inference": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-2.5.1.tgz", + "integrity": "sha512-5Jx+cwRYrGtvN/27VJbzEcjFlFR7ixd+jNC/hbjcRDSZ3QteEUVKwMCgXplwjWkQbvoeWik+reR2m5fAWUvvpw==", + "engines": { + "node": ">=18" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", @@ -887,6 +896,11 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1305,6 +1319,14 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1401,6 +1423,34 @@ "node": ">= 0.6" } }, + "node_modules/midjourney": { + "version": "3.1.88", + "resolved": "https://registry.npmjs.org/midjourney/-/midjourney-3.1.88.tgz", + "integrity": "sha512-T05YKCOjUuBVIkMZRQ3w4Pm+wV38cOEgjt6cxDm+Isef+GSFBAniGYElfY23Jobakmr7ArcKSd2UAKu/zuKLIw==", + "dependencies": { + "@huggingface/inference": "^2.5.0", + "isomorphic-ws": "^5.0.0", + "mime": "^3.0.0", + "p-queue": "^6.6.2", + "snowyflake": "^2.0.0", + "tslib": "^2.5.0", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/midjourney/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -1575,9 +1625,9 @@ "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" }, "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -1678,6 +1728,40 @@ "follow-redirects": "^1.14.8" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2074,6 +2158,14 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/snowyflake": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/snowyflake/-/snowyflake-2.0.0.tgz", + "integrity": "sha512-BxeqV0KJxJASu6EBJGUkX194Zhh37AEa0ow/JRK39icWbLTG9Wl/7LAL6a/ZMSjNm4O9pZk6QoLcWP7f/YKmtA==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/socket.io": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", @@ -2310,6 +2402,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2475,6 +2572,26 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -2513,6 +2630,11 @@ "kuler": "^2.0.0" } }, + "@huggingface/inference": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-2.5.1.tgz", + "integrity": "sha512-5Jx+cwRYrGtvN/27VJbzEcjFlFR7ixd+jNC/hbjcRDSZ3QteEUVKwMCgXplwjWkQbvoeWik+reR2m5fAWUvvpw==" + }, "@mapbox/node-pre-gyp": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", @@ -3172,6 +3294,11 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -3483,6 +3610,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "requires": {} + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3557,6 +3690,27 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, + "midjourney": { + "version": "3.1.88", + "resolved": "https://registry.npmjs.org/midjourney/-/midjourney-3.1.88.tgz", + "integrity": "sha512-T05YKCOjUuBVIkMZRQ3w4Pm+wV38cOEgjt6cxDm+Isef+GSFBAniGYElfY23Jobakmr7ArcKSd2UAKu/zuKLIw==", + "requires": { + "@huggingface/inference": "^2.5.0", + "isomorphic-ws": "^5.0.0", + "mime": "^3.0.0", + "p-queue": "^6.6.2", + "snowyflake": "^2.0.0", + "tslib": "^2.5.0", + "ws": "^8.13.0" + }, + "dependencies": { + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==" + } + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -3688,9 +3842,9 @@ "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" }, "node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "requires": { "whatwg-url": "^5.0.0" } @@ -3767,6 +3921,28 @@ } } }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" + }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -4066,6 +4242,11 @@ "is-arrayish": "^0.3.1" } }, + "snowyflake": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/snowyflake/-/snowyflake-2.0.0.tgz", + "integrity": "sha512-BxeqV0KJxJASu6EBJGUkX194Zhh37AEa0ow/JRK39icWbLTG9Wl/7LAL6a/ZMSjNm4O9pZk6QoLcWP7f/YKmtA==" + }, "socket.io": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", @@ -4246,6 +4427,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4378,6 +4564,12 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "requires": {} + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/api/package.json b/api/package.json index ff0bbff..f9a7537 100644 --- a/api/package.json +++ b/api/package.json @@ -21,6 +21,7 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "luxon": "^3.3.0", + "midjourney": "^3.1.88", "multer": "^1.4.5-lts.1", "openai": "^3.2.1", "read-last-lines": "^1.8.0", diff --git a/api/src/ai/midjourney.ts b/api/src/ai/midjourney.ts new file mode 100644 index 0000000..459d726 --- /dev/null +++ b/api/src/ai/midjourney.ts @@ -0,0 +1,129 @@ +import { Midjourney, MJMessage, MJInfo } from 'midjourney'; + +import AI, { LogError } from './ai'; +import config from '../config'; + +const { + MIDJOURNEY: { TOKEN, SERVER_ID, CHANNEL_ID, HUGGING_FACE_TOKEN, IMAGE }, +} = config(); + +const getRandom = (min: number, max: number): number => { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +}; + +const connection: { + status: boolean; + timeout: NodeJS.Timeout | null; +} = { status: false, timeout: null }; + +export default class extends AI { + client: Midjourney; + waitTimeMinutes: number; + + constructor(meta?: any) { + super('midjourney', meta); + this.waitTimeMinutes = 5; + this.styles = IMAGE.STYLE; + this.client = new Midjourney({ + ServerId: SERVER_ID, + ChannelId: CHANNEL_ID, + SalaiToken: TOKEN, + HuggingFaceToken: HUGGING_FACE_TOKEN, + Debug: false, + Ws: true, + }); + } + + async upscale(image: MJMessage, index: 1 | 2 | 3 | 4) { + if (!image.id || !image.hash) return; + await this.sleep(this.jitter()); + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error('upscale timeout')), this.waitTimeMinutes * 60000) + ); + const promise = this.client.Upscale({ + index, + msgId: image.id, + hash: image.hash, + flags: image.flags, + loading: (uri: string, progress: string) => { + this.log.info(`upscale progress ${progress}`); + }, + }); + const upscale = (await Promise.race([promise, timeout])) as MJMessage; + if (upscale?.uri) { + await this.downloadImage([{ url: upscale.uri }]); + } else throw new Error('no image returned'); + } + + async connect() { + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error('client timeout')), 10000) + ); + const promise = this.client.Connect(); + await Promise.race([promise, timeout]); + } + + async info() { + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error('info timeout')), 10000) + ); + const promise = this.client.Info(); + return Promise.race([promise, timeout]) as typeof promise; + } + + async imagine() { + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error('imagine timeout')), this.waitTimeMinutes * 60000) + ); + const promise = this.client.Imagine( + `${this.meta.summary.summary}, ${this.style} ${IMAGE.PARAMETERS}`, + (uri: string, progress: string) => { + this.log.info(`imagine: ${progress}`); + } + ); + const image = (await Promise.race([promise, timeout])) as MJMessage; + if (!image) throw new Error('no image returned'); + return image; + } + + async generateImage() { + await this.connect(); + this.waitTimeMinutes = (await this.info())?.jobMode.toLowerCase() === 'relaxed' ? 10 : 5; + const image = await this.imagine(); + if (image?.uri) await this.downloadImage([{ url: image.uri }]); + if (!IMAGE.UPSCALE || !image) return; + if (IMAGE.UPSCALE === 'random') { + const rand = getRandom(1, 4); + await this.createAttemptHandler(`upscale ${rand}/4`, this.upscale.bind(this))(image, rand); + } else { + for (let i = 1; i < 1 + IMAGE.UPSCALE; i++) { + await this.createAttemptHandler(`upscale ${i}/4`, this.upscale.bind(this))(image, i); + } + } + this.client.Close(); + } + + logError({ type, error }: LogError) { + this.log.error(`${type}: ${error?.response?.data?.error?.message || error}`); + } + + async test() { + try { + if (connection.status) return true; + await this.connect(); + this.log.info(await this.info()); + this.client.Close(); + connection.status = true; + if (connection.timeout) clearTimeout(connection.timeout); + connection.timeout = setTimeout(() => { + connection.status = false; + }, 1000 * 60 * 5); + return true; + } catch (error) { + this.log.error(error); + return error; + } + } +} diff --git a/api/src/config/default.ts b/api/src/config/default.ts index b4ed779..09a1bf2 100644 --- a/api/src/config/default.ts +++ b/api/src/config/default.ts @@ -63,4 +63,12 @@ export default { style: ['buliojourney v2'], }, }, + midjourney: { + image: { + enable: true, + parameters: '--chaos 80 --no text', + upscale: 'random', + style: ['cinematic'], + }, + }, }; diff --git a/api/src/schemas/index.ts b/api/src/schemas/index.ts index c198d8a..0653ab5 100644 --- a/api/src/schemas/index.ts +++ b/api/src/schemas/index.ts @@ -256,6 +256,17 @@ const schema = z.object({ }), }) .nullish(), + midjourney: z + .object({ + server_id: z.string(), + channel_id: z.string(), + token: z.string(), + hugging_face_token: z.string().nullish(), + image: z.object({ + enable: z.boolean(), + parameters: z.string(), + upscale: z.union([z.literal(false), z.literal('random'), z.number().min(1).max(4)]), + }), }) .nullish(), system: z.object({