BG5 commited on
Commit
21596b8
·
1 Parent(s): 8db1376

Upload 114 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .dockerignore +22 -0
  2. .gitignore +40 -0
  3. Dockerfile +34 -0
  4. LICENSE +201 -0
  5. docker/Dockerfile +35 -0
  6. docker/build-image.sh +34 -0
  7. docker/build-manifest.sh +21 -0
  8. docs/api.md +124 -0
  9. docs/config.md +41 -0
  10. docs/discord-params.md +16 -0
  11. docs/docker-start.md +21 -0
  12. docs/img_1.png +0 -0
  13. docs/img_10.png +0 -0
  14. docs/img_2.png +0 -0
  15. docs/img_3.png +0 -0
  16. docs/img_4.png +0 -0
  17. docs/img_5.png +0 -0
  18. docs/img_6.png +0 -0
  19. docs/img_7.png +0 -0
  20. docs/img_8.png +0 -0
  21. docs/img_9.png +0 -0
  22. docs/manager-qrcode.png +0 -0
  23. docs/params_session_id.png +0 -0
  24. docs/params_user.png +0 -0
  25. docs/railway-start.md +36 -0
  26. docs/railway_img_1.png +0 -0
  27. docs/railway_img_10.png +0 -0
  28. docs/railway_img_11.png +0 -0
  29. docs/railway_img_12.png +0 -0
  30. docs/railway_img_2.png +0 -0
  31. docs/railway_img_3.png +0 -0
  32. docs/railway_img_4.png +0 -0
  33. docs/railway_img_5.png +0 -0
  34. docs/railway_img_6.png +0 -0
  35. docs/railway_img_7.png +0 -0
  36. docs/railway_img_8.png +0 -0
  37. docs/railway_img_9.png +0 -0
  38. docs/receipt-code.png +0 -0
  39. docs/zeabur-start.md +33 -0
  40. pom.xml +125 -0
  41. src/main/java/com/github/novicezk/midjourney/Constants.java +19 -0
  42. src/main/java/com/github/novicezk/midjourney/ProxyApplication.java +19 -0
  43. src/main/java/com/github/novicezk/midjourney/ProxyProperties.java +201 -0
  44. src/main/java/com/github/novicezk/midjourney/ReturnCode.java +42 -0
  45. src/main/java/com/github/novicezk/midjourney/controller/SubmitController.java +227 -0
  46. src/main/java/com/github/novicezk/midjourney/controller/TaskController.java +65 -0
  47. src/main/java/com/github/novicezk/midjourney/dto/BaseSubmitDTO.java +16 -0
  48. src/main/java/com/github/novicezk/midjourney/dto/SubmitBlendDTO.java +21 -0
  49. src/main/java/com/github/novicezk/midjourney/dto/SubmitChangeDTO.java +25 -0
  50. src/main/java/com/github/novicezk/midjourney/dto/SubmitDescribeDTO.java +15 -0
.dockerignore ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ target/
2
+
3
+ ### IntelliJ IDEA ###
4
+ .idea
5
+ *.iws
6
+ *.iml
7
+ *.ipr
8
+
9
+ ### VS Code ###
10
+ .vscode/
11
+
12
+ ### Macos ###
13
+ .DS_Store
14
+
15
+ ### application config #
16
+ config/application.yml
17
+
18
+ .git
19
+ .gitignore
20
+ docker
21
+ docs
22
+ README.md
.gitignore ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ target/
2
+ !.mvn/wrapper/maven-wrapper.jar
3
+ !**/src/main/**
4
+ !**/src/test/**
5
+ bin/
6
+
7
+ ### STS ###
8
+ .apt_generated
9
+ .classpath
10
+ .factorypath
11
+ .project
12
+ .settings
13
+ .springBeans
14
+ .sts4-cache
15
+
16
+ ### IntelliJ IDEA ###
17
+ .idea
18
+ *.iws
19
+ *.iml
20
+ *.ipr
21
+
22
+ ### NetBeans ###
23
+ /nbproject/private/
24
+ /nbbuild/
25
+ /dist/
26
+ /nbdist/
27
+ /.nb-gradle/
28
+ build/
29
+
30
+ ### VS Code ###
31
+ .vscode/
32
+
33
+ ### Macos ###
34
+ .DS_Store
35
+
36
+ ### application config #
37
+ config/application.yml
38
+
39
+ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
40
+ hs_err_pid*
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM maven:3.8.5-openjdk-17
2
+
3
+ ARG user=spring
4
+ ARG group=spring
5
+
6
+ ENV SPRING_HOME=/home/spring
7
+
8
+ RUN groupadd -g 1000 ${group} \
9
+ && useradd -d "$SPRING_HOME" -u 1000 -g 1000 -m -s /bin/bash ${user} \
10
+ && mkdir -p $SPRING_HOME/config \
11
+ && mkdir -p $SPRING_HOME/logs \
12
+ && chown -R ${user}:${group} $SPRING_HOME/config $SPRING_HOME/logs
13
+
14
+ # Railway 不支持使用 VOLUME, 本地需要构建时,取消下一行的注释
15
+ # VOLUME ["$SPRING_HOME/config", "$SPRING_HOME/logs"]
16
+
17
+ USER ${user}
18
+ WORKDIR $SPRING_HOME
19
+
20
+ COPY . .
21
+
22
+ RUN mvn clean package \
23
+ && mv target/midjourney-proxy-*.jar ./app.jar \
24
+ && rm -rf target
25
+
26
+ EXPOSE 8080 9876
27
+
28
+ ENV JAVA_OPTS -XX:MaxRAMPercentage=85 -Djava.awt.headless=true -XX:+HeapDumpOnOutOfMemoryError \
29
+ -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -Xlog:gc:file=/home/spring/logs/gc.log \
30
+ -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9876 -Dcom.sun.management.jmxremote.ssl=false \
31
+ -Dcom.sun.management.jmxremote.authenticate=false -Dlogging.file.path=/home/spring/logs \
32
+ -Dserver.port=8080 -Duser.timezone=Asia/Shanghai
33
+
34
+ ENTRYPOINT ["bash","-c","java $JAVA_OPTS -jar app.jar"]
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
docker/Dockerfile ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM openjdk:17.0
2
+
3
+ ARG user=spring
4
+ ARG group=spring
5
+
6
+ ENV SPRING_HOME=/home/spring
7
+ ENV APP_HOME=$SPRING_HOME/app
8
+
9
+ ENV JAVA_OPTS -XX:MaxRAMPercentage=85 -Djava.awt.headless=true -XX:+HeapDumpOnOutOfMemoryError \
10
+ -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -Xlog:gc:file=/home/spring/logs/gc.log \
11
+ -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9876 -Dcom.sun.management.jmxremote.ssl=false \
12
+ -Dcom.sun.management.jmxremote.authenticate=false -Dlogging.file.path=/home/spring/logs \
13
+ -Dserver.port=8080 -Duser.timezone=Asia/Shanghai
14
+
15
+ RUN groupadd -g 1000 ${group} \
16
+ && useradd -d "$SPRING_HOME" -u 1000 -g 1000 -m -s /bin/bash ${user} \
17
+ && mkdir -p $SPRING_HOME/config \
18
+ && mkdir -p $SPRING_HOME/logs \
19
+ && mkdir -p $APP_HOME \
20
+ && chown -R ${user}:${group} $SPRING_HOME/config $SPRING_HOME/logs $APP_HOME
21
+
22
+ VOLUME ["$SPRING_HOME/config", "$SPRING_HOME/logs"]
23
+
24
+ USER ${user}
25
+
26
+ WORKDIR $SPRING_HOME
27
+
28
+ EXPOSE 8080 9876
29
+
30
+ ENTRYPOINT ["bash","-c","java $JAVA_OPTS -cp ./app org.springframework.boot.loader.JarLauncher"]
31
+
32
+ COPY --chown=${user}:${group} dependencies $APP_HOME/
33
+ COPY --chown=${user}:${group} spring-boot-loader $APP_HOME/
34
+ COPY --chown=${user}:${group} snapshot-dependencies $APP_HOME/
35
+ COPY --chown=${user}:${group} application $APP_HOME/
docker/build-image.sh ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e -u -o pipefail
3
+
4
+ if [ $# -lt 1 ]; then
5
+ echo 'version is required'
6
+ exit 1
7
+ fi
8
+
9
+ VERSION=$1
10
+ ARCH=amd64
11
+
12
+ if [ $# -ge 2 ]; then
13
+ ARCH=$2
14
+ fi
15
+
16
+ JAR_FILE_COUNT=$(find "../target/" -maxdepth 1 -name '*.jar' | wc -l)
17
+ if [ $JAR_FILE_COUNT == 0 ]; then
18
+ echo "jar file not found, please execute: mvn clean package"
19
+ exit 1
20
+ fi
21
+
22
+ JAR_FILE_NAME=$(ls ../target/*.jar|grep -v source)
23
+ echo ${JAR_FILE_NAME}
24
+
25
+ cp ${JAR_FILE_NAME} ./app.jar
26
+
27
+ java -Djarmode=layertools -jar app.jar extract
28
+
29
+ docker build . -t midjourney-proxy:${VERSION}
30
+
31
+ rm -rf application dependencies snapshot-dependencies spring-boot-loader app.jar
32
+
33
+ docker tag midjourney-proxy:${VERSION} novicezk/midjourney-proxy-${ARCH}:${VERSION}
34
+ docker push novicezk/midjourney-proxy-${ARCH}:${VERSION}
docker/build-manifest.sh ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e -u -o pipefail
3
+
4
+ if [ $# -lt 1 ]; then
5
+ echo 'version is required'
6
+ exit 1
7
+ fi
8
+
9
+ VERSION=$1
10
+
11
+ echo "create manifest..."
12
+ docker manifest create novicezk/midjourney-proxy:${VERSION} novicezk/midjourney-proxy-amd64:${VERSION} novicezk/midjourney-proxy-arm64v8:${VERSION}
13
+
14
+ echo "annotate amd64..."
15
+ docker manifest annotate novicezk/midjourney-proxy:${VERSION} novicezk/midjourney-proxy-amd64:${VERSION} --os linux --arch amd64
16
+
17
+ echo "annotate arm64v8..."
18
+ docker manifest annotate novicezk/midjourney-proxy:${VERSION} novicezk/midjourney-proxy-arm64v8:${VERSION} --os linux --arch arm64 --variant v8
19
+
20
+ echo "push manifest..."
21
+ docker manifest push novicezk/midjourney-proxy:${VERSION}
docs/api.md ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # API接口说明
2
+
3
+ `http://ip:port/mj` 已有api文档,此处仅作补充
4
+
5
+ ## 1. 数据结构
6
+
7
+ ### 任务
8
+ | 字段 | 类型 | 示例 | 描述 |
9
+ |:-----:|:----:|:----|:----|
10
+ | id | string | 1689231405853400 | 任务ID |
11
+ | action | string | IMAGINE | 任务类型: IMAGINE(绘图)、UPSCALE(选中放大)、VARIATION(选中变换)、REROLL(重新执行)、DESCRIBE(图生文)、BLEAND(图片混合) |
12
+ | status | string | SUCCESS | 任务状态: NOT_START(未启动)、SUBMITTED(已提交处理)、IN_PROGRESS(执行中)、FAILURE(失败)、SUCCESS(成功) |
13
+ | prompt | string | 猫猫 | 提示词 |
14
+ | promptEn | string | Cat | 英文提示词 |
15
+ | description | string | /imagine 猫猫 | 任务描述 |
16
+ | submitTime | number | 1689231405854 | 提交时间 |
17
+ | startTime | number | 1689231442755 | 开始执行时间 |
18
+ | finishTime | number | 1689231544312 | 结束时间 |
19
+ | progress | string | 100% | 任务进度 |
20
+ | imageUrl | string | https://cdn.discordapp.com/attachments/xxx/xxx/xxxx.png | 生成图片的url, 成功或执行中时有值,可能为png或webp |
21
+ | failReason | string | [Invalid parameter] Invalid value | 失败原因, 失败时有值 |
22
+ | properties | object | {"finalPrompt": "Cat"} | 任务的扩展属性,系统内部使用 |
23
+
24
+
25
+ ## 2. 任务提交返回
26
+ - code=1: 提交成功,result为任务ID
27
+ ```json
28
+ {
29
+ "code": 1,
30
+ "description": "成功",
31
+ "result": "8498455807619990"
32
+ }
33
+ ```
34
+ - code=21: 任务已存在,U时可能发生
35
+ ```json
36
+ {
37
+ "code": 21,
38
+ "description": "任务已存在",
39
+ "result": "0741798445574458",
40
+ "properties": {
41
+ "status": "SUCCESS",
42
+ "imageUrl": "https://xxxx"
43
+ }
44
+ }
45
+ ```
46
+ - code=22: 提交成功,进入队列等待
47
+ ```json
48
+ {
49
+ "code": 22,
50
+ "description": "排队中,前面还有1个任务",
51
+ "result": "0741798445574458",
52
+ "properties": {
53
+ "numberOfQueues": 1
54
+ }
55
+ }
56
+ ```
57
+ - code=24: prompt包含敏感词
58
+ ```json
59
+ {
60
+ "code": 24,
61
+ "description": "可能包含敏感词",
62
+ "properties": {
63
+ "promptEn": "nude body",
64
+ "bannedWord": "nude"
65
+ }
66
+ }
67
+ ```
68
+ - other: 提交错误,description为错误描述
69
+
70
+ ## 3. `/mj/submit/simple-change` 绘图变化-simple
71
+ 接口作用同 `/mj/submit/change`(绘图变化),传参方式不同,该接口接收content,格式为`ID 操作`,例如:1320098173412546 U2
72
+
73
+ - 放大 U1~U4
74
+ - 变换 V1~V4
75
+ - 重新执行 R
76
+
77
+ ## 4. `/mj/submit/describe` 图生文
78
+ ```json
79
+ {
80
+ // 图片的base64字符串
81
+ "base64": "data:image/png;base64,xxx"
82
+ }
83
+ ```
84
+
85
+ 后续任务完成后,properties中finalPrompt即为图片生成的prompt
86
+ ```json
87
+ {
88
+ "id":"14001929738841620",
89
+ "action":"DESCRIBE",
90
+ "status": "SUCCESS",
91
+ "description":"/describe 14001929738841620.png",
92
+ "imageUrl":"https://cdn.discordapp.com/attachments/xxx/xxx/14001929738841620.png",
93
+ "properties": {
94
+ "finalPrompt": "1️⃣ Cat --ar 5:4\n\n2️⃣ Cat2 --ar 5:4\n\n3️⃣ Cat3 --ar 5:4\n\n4️⃣ Cat4 --ar 5:4"
95
+ }
96
+ // ...
97
+ }
98
+ ```
99
+
100
+ ## 5. 任务变更回调
101
+ 任务状态变化或进度改变时,会调用业务系统的接口
102
+ - 接口地址为配置的 mj.notify-hook,任务提交时支持传`notifyHook`以改变此任务的回调地址
103
+ - 两者都为空时,不触发回调
104
+
105
+ POST application/json
106
+ ```json
107
+ {
108
+ "id": "14001929738841620",
109
+ "action": "IMAGINE",
110
+ "status": "SUCCESS",
111
+ "prompt": "猫猫",
112
+ "promptEn": "Cat",
113
+ "description": "/imagine 猫猫",
114
+ "submitTime": 1689231405854,
115
+ "startTime": 1689231442755,
116
+ "finishTime": 1689231544312,
117
+ "progress": "100%",
118
+ "imageUrl": "https://cdn.discordapp.com/attachments/xxx/xxx/xxxx.png",
119
+ "failReason": null,
120
+ "properties": {
121
+ "finalPrompt": "Cat"
122
+ }
123
+ }
124
+ ```
docs/config.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 配置项
2
+
3
+ | 变量名 | 非空 | 描述 |
4
+ | :-----| :----: | :---- |
5
+ | mj.discord.guild-id | 是 | discord服务器ID |
6
+ | mj.discord.channel-id | 是 | discord频道ID |
7
+ | mj.discord.user-token | 是 | discord用户Token |
8
+ | mj.discord.session-id | 否 | discord用户SessionId,建议从interactions请求中复制替换掉 |
9
+ | mj.discord.user-agent | 否 | 调用discord接口、连接wss时的user-agent,建议从浏览器network复制 |
10
+ | mj.api-secret | 否 | 接口密钥,为空不启用鉴权;调用接口时需要加请求头 mj-api-secret |
11
+ | mj.notify-hook | 否 | 全局的任务状态变更回调地址 |
12
+ | mj.notify-notify-pool-size | 否 | 通知回调线程池大小,默认10 |
13
+ | mj.task-store.type | 否 | 任务存储方式,默认in_memory(内存\重启后丢失),可选redis |
14
+ | mj.task-store.timeout | 否 | 任务过期时间,过期后删除,默认30天 |
15
+ | mj.queue.core-size | 否 | 并发数,默认为3 |
16
+ | mj.queue.queue-size | 否 | 等待队列,默认长度10 |
17
+ | mj.queue.timeout-minutes | 否 | 任务超时时间,默认为5分钟 |
18
+ | mj.proxy.host | 否 | 代理host,全局代理不生效时设置 |
19
+ | mj.proxy.port | 否 | 代理port,全局代理不生效时设置 |
20
+ | mj.ng-discord.server | 否 | https://discord.com 反代地址 |
21
+ | mj.ng-discord.cdn | 否 | https://cdn.discordapp.com 反代地址 |
22
+ | mj.ng-discord.wss | 否 | wss://gateway.discord.gg 反代地址 |
23
+ | mj.translate-way | 否 | 中文prompt翻译成英文的方式,可选null(默认)、baidu、gpt |
24
+ | mj.baidu-translate.appid | 否 | 百度翻译的appid |
25
+ | mj.baidu-translate.app-secret | 否 | 百度翻译的app-secret |
26
+ | mj.openai.gpt-api-url | 否 | 自定义gpt的接口地址,默认不需要配置 |
27
+ | mj.openai.gpt-api-key | 否 | gpt的api-key |
28
+ | mj.openai.timeout | 否 | openai调用的超时时间,默认30秒 |
29
+ | mj.openai.model | 否 | openai的模型,默认gpt-3.5-turbo |
30
+ | mj.openai.max-tokens | 否 | 返回结果的最大分词数,默认2048 |
31
+ | mj.openai.temperature | 否 | 相似度(0-2.0),默认0 |
32
+ | spring.redis | 否 | 任务存储方式设置为redis,需配置redis相关属性 |
33
+
34
+ ### spring.redis配置参考
35
+ ```yaml
36
+ spring:
37
+ redis:
38
+ host: 10.107.xxx.xxx
39
+ port: 6379
40
+ password: xxx
41
+ ```
docs/discord-params.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## 获取discord配置参数
2
+
3
+ ### 1. 获取用户Token
4
+ 进入频道,打开network,刷新页面,找到 `messages` 的请求,这里的 authorization 即用户Token,后续设置到 `mj.discord.user-token`
5
+
6
+ ![User Token](img_8.png)
7
+
8
+ ### 2. 获取用户sessionId
9
+ 进入频道,打开network,发送/imagine作图指令,找到 `interactions` 的请求,这里的 session_id 即用户sessionId,后续设置到 `mj.discord.session-id`
10
+
11
+ ![User Session](params_session_id.png)
12
+
13
+ ### 3. 获取服务器ID、频道ID
14
+
15
+ 频道的url里取出 服务器ID、频道ID,后续设置到配置项
16
+ ![Guild Channel ID](img_9.png)
docs/docker-start.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Docker 部署教程
2
+
3
+ 1. /xxx/xxx/config目录下创建 application.yml(mj配置项)、banned-words.txt(可选,覆盖默认的敏感词文件);参考src/main/resources下的文件
4
+ 2. 启动容器,映射config目录
5
+ ```shell
6
+ docker run -d --name midjourney-proxy \
7
+ -p 8080:8080 \
8
+ -v /xxx/xxx/config:/home/spring/config \
9
+ novicezk/midjourney-proxy:2.4
10
+ ```
11
+ 3. 访问 `http://ip:port/mj` 查看API文档
12
+
13
+ 附: 不映射config目录方式,直接在启动命令中设置参数
14
+ ```shell
15
+ docker run -d --name midjourney-proxy \
16
+ -p 8080:8080 \
17
+ -e mj.discord.guild-id=xxx \
18
+ -e mj.discord.channel-id=xxx \
19
+ -e mj.discord.user-token=xxx \
20
+ novicezk/midjourney-proxy:2.4
21
+ ```
docs/img_1.png ADDED
docs/img_10.png ADDED
docs/img_2.png ADDED
docs/img_3.png ADDED
docs/img_4.png ADDED
docs/img_5.png ADDED
docs/img_6.png ADDED
docs/img_7.png ADDED
docs/img_8.png ADDED
docs/img_9.png ADDED
docs/manager-qrcode.png ADDED
docs/params_session_id.png ADDED
docs/params_user.png ADDED
docs/railway-start.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Railway 部署教程
2
+
3
+ Railway是一个提供弹性部署方案的平台,服务器在海外,方便MidJourney的调用。
4
+
5
+ **Railway 提供 5 美元,500 个小时/月的免费额度**
6
+
7
+ ### 1. Fork本仓库
8
+ ### 2. Railway使用github账号登录
9
+ 进入 [railway官网](https://railway.app) 选择 `Login` -> `Github`,登录github账号
10
+
11
+ ### 3. [New Project](https://railway.app/new) 添加对fork仓库的授权
12
+ ![railway_img_1](./railway_img_1.png)
13
+ ![railway_img_2](./railway_img_2.png)
14
+ ![railway_img_3](./railway_img_3.png)
15
+
16
+ ### 4. 选择该fork仓库,新建项目,设置环境变量
17
+ ![railway_img_4](./railway_img_4.png)
18
+ ![railway_img_5](./railway_img_5.png)
19
+ ![railway_img_6](./railway_img_6.png)
20
+ ![railway_img_7](./railway_img_7.png)
21
+ 此处配置项参考 [Wiki / 配置项](https://github.com/novicezk/midjourney-proxy/wiki/%E9%85%8D%E7%BD%AE%E9%A1%B9) ,建议配置api密钥启用鉴权,接口调用时需添加请求头 `mj-api-secret`
22
+
23
+ ### 5. 启动服务
24
+ 进入刚才的Project,它应该已经在自动部署了,后续更新配置之后会自动重新部署
25
+ ![railway_img_8](./railway_img_8.png)
26
+
27
+ 若部署启动失败请查看日志,检查配置项
28
+ ![railway_img_9](./railway_img_9.png)
29
+ ![railway_img_10](./railway_img_10.png)
30
+
31
+ ### 6. 开始使用
32
+ 等待部署成功后,生成随机域名
33
+ ![railway_img_11](./railway_img_11.png)
34
+ ![railway_img_12](./railway_img_12.png)
35
+
36
+ 访问 `https://midjourney-proxy-***.app/mj`
docs/railway_img_1.png ADDED
docs/railway_img_10.png ADDED
docs/railway_img_11.png ADDED
docs/railway_img_12.png ADDED
docs/railway_img_2.png ADDED
docs/railway_img_3.png ADDED
docs/railway_img_4.png ADDED
docs/railway_img_5.png ADDED
docs/railway_img_6.png ADDED
docs/railway_img_7.png ADDED
docs/railway_img_8.png ADDED
docs/railway_img_9.png ADDED
docs/receipt-code.png ADDED
docs/zeabur-start.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Zeabur 部署教程
2
+
3
+ ### Zeabur 优势
4
+ 1. 新注册的 `Github` 账号可能无法使用 `Railway`,但是能用 `Zeabur`
5
+ 2. 通过 `Railway` 部署的项目会自动生成一个域名,然而因为某些原因,形如 `*.up.railway.app` 的域名在国内无法访问
6
+ 3. `Zeabur` 服务器运行在国外,但是其生成的域名 `*.zeabur.app` 没有被污染,国内可直接访问
7
+
8
+ ### 开始部署
9
+
10
+ 1. 打开网址 https://zeabur.com/zh-CN
11
+ 2. 点击现在开始
12
+ 3. 点击 `Sign in with GitHub`
13
+ 4. 登陆你的 `Github` 账号
14
+ 5. 点击 `Authorize zeabur` 授权
15
+ 6. 点击 `创建项目` 并输入一个项目名称,点击 `创建`
16
+ 7. 点击 `+` 添加服务,选择 `Git-Deploy service from source code in GitHub repository.`
17
+ 8. 点击 `Configure GitHub` 根据需要选择 `All repositories` 或者 `Only select repositories`
18
+ 9. 点击 `install`,之后自动跳转,最好再刷新一下页面
19
+ 10. 点击 你 fork 的 `midjourney-proxy` 项目
20
+ 11. 点击环境变量,点击编辑原始环境变量,添加你需要的环境变量
21
+ 12. 关于环境变量,与 `Railway` 稍有不同,需要把 `.` 和 `-` 全部换成 `_`,例如如下格式
22
+ ```properties
23
+ PORT=8080
24
+ mj_discord_guild_id=xxx
25
+ mj_discord_channel_id=xxx
26
+ mj_discord_user_token=xxx
27
+ mj_api_secret=***
28
+ ```
29
+ 此处配置项参考 [Wiki / 配置项](https://github.com/novicezk/midjourney-proxy/wiki/%E9%85%8D%E7%BD%AE%E9%A1%B9) ,建议配置api密钥启用鉴权,接口调用时需添加请求头 `mj-api-secret`
30
+ 13. 然后取消 `Building`,点击 `Redeploy` (此做法是为了让环境变量生效)
31
+ 14. 部署 `midjourney-proxy` 大概需要 `2` 分钟,此时你可以做的是:配置域名
32
+ 15. 点击下方的域名,点击生成域名,输入前缀,例如 `midjourney-proxy-demo`,点击保存;或者添加自定义域名,之后加上 `CNAME` 解析
33
+ 16. 等待部署成功,访问 `https://midjourney-proxy-demo.zeabur.app/mj`
pom.xml ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+ <modelVersion>4.0.0</modelVersion>
6
+
7
+ <parent>
8
+ <groupId>org.springframework.boot</groupId>
9
+ <artifactId>spring-boot-starter-parent</artifactId>
10
+ <version>2.6.14</version>
11
+ </parent>
12
+
13
+ <groupId>com.github.novicezk</groupId>
14
+ <artifactId>midjourney-proxy</artifactId>
15
+ <version>2.4</version>
16
+
17
+ <properties>
18
+ <hutool.version>5.8.18</hutool.version>
19
+ <org-json.version>20220924</org-json.version>
20
+ <jda.version>5.0.0-beta.9</jda.version>
21
+ <chatgpt-java.version>1.0.14-beta1</chatgpt-java.version>
22
+ <dataurl.version>2.0.0</dataurl.version>
23
+ <knife4j.verison>4.1.0</knife4j.verison>
24
+ <user-agent-utils.verison>1.21</user-agent-utils.verison>
25
+ <httpclient.verison>4.5.14</httpclient.verison>
26
+ <java.version>17</java.version>
27
+ <maven.compiler.source>${java.version}</maven.compiler.source>
28
+ <maven.compiler.target>${java.version}</maven.compiler.target>
29
+ </properties>
30
+
31
+ <dependencies>
32
+ <dependency>
33
+ <groupId>org.springframework.boot</groupId>
34
+ <artifactId>spring-boot-starter-web</artifactId>
35
+ </dependency>
36
+ <dependency>
37
+ <groupId>org.springframework.boot</groupId>
38
+ <artifactId>spring-boot-starter-data-redis</artifactId>
39
+ </dependency>
40
+
41
+ <dependency>
42
+ <groupId>cn.hutool</groupId>
43
+ <artifactId>hutool-core</artifactId>
44
+ <version>${hutool.version}</version>
45
+ </dependency>
46
+ <dependency>
47
+ <groupId>cn.hutool</groupId>
48
+ <artifactId>hutool-cache</artifactId>
49
+ <version>${hutool.version}</version>
50
+ </dependency>
51
+ <dependency>
52
+ <groupId>cn.hutool</groupId>
53
+ <artifactId>hutool-crypto</artifactId>
54
+ <version>${hutool.version}</version>
55
+ </dependency>
56
+ <dependency>
57
+ <groupId>org.json</groupId>
58
+ <artifactId>json</artifactId>
59
+ <version>${org-json.version}</version>
60
+ </dependency>
61
+ <dependency>
62
+ <groupId>net.dv8tion</groupId>
63
+ <artifactId>JDA</artifactId>
64
+ <version>${jda.version}</version>
65
+ <exclusions>
66
+ <exclusion>
67
+ <groupId>club.minnced</groupId>
68
+ <artifactId>opus-java</artifactId>
69
+ </exclusion>
70
+ </exclusions>
71
+ </dependency>
72
+ <dependency>
73
+ <groupId>com.unfbx</groupId>
74
+ <artifactId>chatgpt-java</artifactId>
75
+ <version>${chatgpt-java.version}</version>
76
+ <exclusions>
77
+ <exclusion>
78
+ <artifactId>slf4j-simple</artifactId>
79
+ <groupId>org.slf4j</groupId>
80
+ </exclusion>
81
+ </exclusions>
82
+ </dependency>
83
+ <dependency>
84
+ <groupId>eu.maxschuster</groupId>
85
+ <artifactId>dataurl</artifactId>
86
+ <version>${dataurl.version}</version>
87
+ </dependency>
88
+ <dependency>
89
+ <groupId>com.github.xiaoymin</groupId>
90
+ <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
91
+ <version>${knife4j.verison}</version>
92
+ </dependency>
93
+ <dependency>
94
+ <groupId>eu.bitwalker</groupId>
95
+ <artifactId>UserAgentUtils</artifactId>
96
+ <version>${user-agent-utils.verison}</version>
97
+ </dependency>
98
+ <dependency>
99
+ <groupId>org.apache.httpcomponents</groupId>
100
+ <artifactId>httpclient</artifactId>
101
+ <version>${httpclient.verison}</version>
102
+ </dependency>
103
+
104
+ <dependency>
105
+ <groupId>org.springframework.boot</groupId>
106
+ <artifactId>spring-boot-configuration-processor</artifactId>
107
+ <optional>true</optional>
108
+ </dependency>
109
+ <dependency>
110
+ <groupId>org.projectlombok</groupId>
111
+ <artifactId>lombok</artifactId>
112
+ <optional>true</optional>
113
+ </dependency>
114
+ </dependencies>
115
+
116
+ <build>
117
+ <plugins>
118
+ <plugin>
119
+ <groupId>org.springframework.boot</groupId>
120
+ <artifactId>spring-boot-maven-plugin</artifactId>
121
+ </plugin>
122
+ </plugins>
123
+ </build>
124
+
125
+ </project>
src/main/java/com/github/novicezk/midjourney/Constants.java ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney;
2
+
3
+ import lombok.experimental.UtilityClass;
4
+
5
+ @UtilityClass
6
+ public final class Constants {
7
+ // 任务扩展属性 start
8
+ public static final String TASK_PROPERTY_NOTIFY_HOOK = "notifyHook";
9
+ public static final String TASK_PROPERTY_FINAL_PROMPT = "finalPrompt";
10
+ public static final String TASK_PROPERTY_MESSAGE_ID = "messageId";
11
+ public static final String TASK_PROPERTY_MESSAGE_HASH = "messageHash";
12
+
13
+ public static final String TASK_PROPERTY_PROGRESS_MESSAGE_ID = "progressMessageId";
14
+ public static final String TASK_PROPERTY_FLAGS = "flags";
15
+ public static final String TASK_PROPERTY_NONCE = "nonce";
16
+ // 任务扩展属性 end
17
+
18
+ public static final String API_SECRET_HEADER_NAME = "mj-api-secret";
19
+ }
src/main/java/com/github/novicezk/midjourney/ProxyApplication.java ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney;
2
+
3
+ import org.springframework.boot.SpringApplication;
4
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
5
+ import org.springframework.context.annotation.Import;
6
+ import org.springframework.scheduling.annotation.EnableScheduling;
7
+ import spring.config.BeanConfig;
8
+ import spring.config.WebMvcConfig;
9
+
10
+ @EnableScheduling
11
+ @SpringBootApplication
12
+ @Import({BeanConfig.class, WebMvcConfig.class})
13
+ public class ProxyApplication {
14
+
15
+ public static void main(String[] args) {
16
+ SpringApplication.run(ProxyApplication.class, args);
17
+ }
18
+
19
+ }
src/main/java/com/github/novicezk/midjourney/ProxyProperties.java ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney;
2
+
3
+ import com.github.novicezk.midjourney.enums.TranslateWay;
4
+ import lombok.Data;
5
+ import org.springframework.boot.context.properties.ConfigurationProperties;
6
+ import org.springframework.stereotype.Component;
7
+
8
+ import java.time.Duration;
9
+
10
+ @Data
11
+ @Component
12
+ @ConfigurationProperties(prefix = "mj")
13
+ public class ProxyProperties {
14
+ /**
15
+ * task存储配置.
16
+ */
17
+ private final TaskStore taskStore = new TaskStore();
18
+ /**
19
+ * discord配置.
20
+ */
21
+ private final DiscordConfig discord = new DiscordConfig();
22
+ /**
23
+ * 代理配置.
24
+ */
25
+ private final ProxyConfig proxy = new ProxyConfig();
26
+ /**
27
+ * 反代配置.
28
+ */
29
+ private final NgDiscordConfig ngDiscord = new NgDiscordConfig();
30
+ /**
31
+ * 任务队列配置.
32
+ */
33
+ private final TaskQueueConfig queue = new TaskQueueConfig();
34
+ /**
35
+ * 百度翻译配置.
36
+ */
37
+ private final BaiduTranslateConfig baiduTranslate = new BaiduTranslateConfig();
38
+ /**
39
+ * openai配置.
40
+ */
41
+ private final OpenaiConfig openai = new OpenaiConfig();
42
+ /**
43
+ * 中文prompt翻译方式.
44
+ */
45
+ private TranslateWay translateWay = TranslateWay.NULL;
46
+ /**
47
+ * 接口密钥,为空不启用鉴权;调用接口时需要加请求头 mj-api-secret.
48
+ */
49
+ private String apiSecret;
50
+ /**
51
+ * 任务状态变更回调地址.
52
+ */
53
+ private String notifyHook;
54
+ /**
55
+ * 通知回调线程池大小.
56
+ */
57
+ private int notifyPoolSize = 10;
58
+ /**
59
+ * 接口是否返回任务扩展属性.
60
+ */
61
+ private boolean includeTaskExtended = false;
62
+
63
+ @Data
64
+ public static class DiscordConfig {
65
+ /**
66
+ * 你的服务器id.
67
+ */
68
+ private String guildId;
69
+ /**
70
+ * 你的频道id.
71
+ */
72
+ private String channelId;
73
+ /**
74
+ * 你的登录token.
75
+ */
76
+ private String userToken;
77
+ /**
78
+ * 你的频道id.
79
+ */
80
+ private String sessionId = "9c4055428e13bcbf2248a6b36084c5f3";
81
+ /**
82
+ * 调用discord接口、连接wss时的user-agent.
83
+ */
84
+ private String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36";
85
+ /**
86
+ * 是否使用user_token连接wss,默认启用.
87
+ */
88
+ private boolean userWss = true;
89
+ /**
90
+ * 你的机器人token.
91
+ */
92
+ private String botToken;
93
+ }
94
+
95
+ @Data
96
+ public static class BaiduTranslateConfig {
97
+ /**
98
+ * 百度翻译的APP_ID.
99
+ */
100
+ private String appid;
101
+ /**
102
+ * 百度翻译的密钥.
103
+ */
104
+ private String appSecret;
105
+ }
106
+
107
+ @Data
108
+ public static class OpenaiConfig {
109
+ /**
110
+ * 自定义gpt的api-url.
111
+ */
112
+ private String gptApiUrl;
113
+ /**
114
+ * gpt的api-key.
115
+ */
116
+ private String gptApiKey;
117
+ /**
118
+ * 超时时间.
119
+ */
120
+ private Duration timeout = Duration.ofSeconds(30);
121
+ /**
122
+ * 使用的模型.
123
+ */
124
+ private String model = "gpt-3.5-turbo";
125
+ /**
126
+ * 返回结果的最大分词数.
127
+ */
128
+ private int maxTokens = 2048;
129
+ /**
130
+ * 相似度,取值 0-2.
131
+ */
132
+ private double temperature = 0;
133
+ }
134
+
135
+ @Data
136
+ public static class TaskStore {
137
+ /**
138
+ * 任务过期时间,默认30天.
139
+ */
140
+ private Duration timeout = Duration.ofDays(30);
141
+ /**
142
+ * 任务存储方式: redis(默认)、in_memory.
143
+ */
144
+ private Type type = Type.IN_MEMORY;
145
+
146
+ public enum Type {
147
+ /**
148
+ * redis.
149
+ */
150
+ REDIS,
151
+ /**
152
+ * in_memory.
153
+ */
154
+ IN_MEMORY
155
+ }
156
+ }
157
+
158
+ @Data
159
+ public static class ProxyConfig {
160
+ /**
161
+ * 代理host.
162
+ */
163
+ private String host;
164
+ /**
165
+ * 代理端口.
166
+ */
167
+ private Integer port;
168
+ }
169
+
170
+ @Data
171
+ public static class NgDiscordConfig {
172
+ /**
173
+ * https://discord.com 反代.
174
+ */
175
+ private String server;
176
+ /**
177
+ * https://cdn.discordapp.com 反代.
178
+ */
179
+ private String cdn;
180
+ /**
181
+ * wss://gateway.discord.gg 反代.
182
+ */
183
+ private String wss;
184
+ }
185
+
186
+ @Data
187
+ public static class TaskQueueConfig {
188
+ /**
189
+ * 并发数.
190
+ */
191
+ private int coreSize = 3;
192
+ /**
193
+ * 等待队列长度.
194
+ */
195
+ private int queueSize = 10;
196
+ /**
197
+ * 任务超时时间(分钟).
198
+ */
199
+ private int timeoutMinutes = 5;
200
+ }
201
+ }
src/main/java/com/github/novicezk/midjourney/ReturnCode.java ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney;
2
+
3
+ import lombok.experimental.UtilityClass;
4
+
5
+ @UtilityClass
6
+ public final class ReturnCode {
7
+ /**
8
+ * 成功.
9
+ */
10
+ public static final int SUCCESS = 1;
11
+ /**
12
+ * 数据未找到.
13
+ */
14
+ public static final int NOT_FOUND = 3;
15
+ /**
16
+ * 校验错误.
17
+ */
18
+ public static final int VALIDATION_ERROR = 4;
19
+ /**
20
+ * 系统异常.
21
+ */
22
+ public static final int FAILURE = 9;
23
+
24
+ /**
25
+ * 已存在.
26
+ */
27
+ public static final int EXISTED = 21;
28
+ /**
29
+ * 排队中.
30
+ */
31
+ public static final int IN_QUEUE = 22;
32
+ /**
33
+ * 队列已满.
34
+ */
35
+ public static final int QUEUE_REJECTED = 23;
36
+ /**
37
+ * prompt包含敏感词.
38
+ */
39
+ public static final int BANNED_PROMPT = 24;
40
+
41
+
42
+ }
src/main/java/com/github/novicezk/midjourney/controller/SubmitController.java ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney.controller;
2
+
3
+ import cn.hutool.core.text.CharSequenceUtil;
4
+ import cn.hutool.core.util.RandomUtil;
5
+ import com.github.novicezk.midjourney.Constants;
6
+ import com.github.novicezk.midjourney.ProxyProperties;
7
+ import com.github.novicezk.midjourney.ReturnCode;
8
+ import com.github.novicezk.midjourney.dto.BaseSubmitDTO;
9
+ import com.github.novicezk.midjourney.dto.SubmitBlendDTO;
10
+ import com.github.novicezk.midjourney.dto.SubmitChangeDTO;
11
+ import com.github.novicezk.midjourney.dto.SubmitDescribeDTO;
12
+ import com.github.novicezk.midjourney.dto.SubmitImagineDTO;
13
+ import com.github.novicezk.midjourney.dto.SubmitSimpleChangeDTO;
14
+ import com.github.novicezk.midjourney.enums.TaskAction;
15
+ import com.github.novicezk.midjourney.enums.TaskStatus;
16
+ import com.github.novicezk.midjourney.exception.BannedPromptException;
17
+ import com.github.novicezk.midjourney.result.SubmitResultVO;
18
+ import com.github.novicezk.midjourney.service.TaskService;
19
+ import com.github.novicezk.midjourney.service.TaskStoreService;
20
+ import com.github.novicezk.midjourney.service.TranslateService;
21
+ import com.github.novicezk.midjourney.support.Task;
22
+ import com.github.novicezk.midjourney.support.TaskCondition;
23
+ import com.github.novicezk.midjourney.util.BannedPromptUtils;
24
+ import com.github.novicezk.midjourney.util.ConvertUtils;
25
+ import com.github.novicezk.midjourney.util.MimeTypeUtils;
26
+ import com.github.novicezk.midjourney.util.SnowFlake;
27
+ import com.github.novicezk.midjourney.util.TaskChangeParams;
28
+ import eu.maxschuster.dataurl.DataUrl;
29
+ import eu.maxschuster.dataurl.DataUrlSerializer;
30
+ import eu.maxschuster.dataurl.IDataUrlSerializer;
31
+ import io.swagger.annotations.Api;
32
+ import io.swagger.annotations.ApiOperation;
33
+ import lombok.RequiredArgsConstructor;
34
+ import org.springframework.web.bind.annotation.PostMapping;
35
+ import org.springframework.web.bind.annotation.RequestBody;
36
+ import org.springframework.web.bind.annotation.RequestMapping;
37
+ import org.springframework.web.bind.annotation.RestController;
38
+
39
+ import java.net.MalformedURLException;
40
+ import java.util.ArrayList;
41
+ import java.util.List;
42
+ import java.util.Optional;
43
+ import java.util.Set;
44
+
45
+ @Api(tags = "任务提交")
46
+ @RestController
47
+ @RequestMapping("/submit")
48
+ @RequiredArgsConstructor
49
+ public class SubmitController {
50
+ private final TranslateService translateService;
51
+ private final TaskStoreService taskStoreService;
52
+ private final ProxyProperties properties;
53
+ private final TaskService taskService;
54
+
55
+ @ApiOperation(value = "提交Imagine任务")
56
+ @PostMapping("/imagine")
57
+ public SubmitResultVO imagine(@RequestBody SubmitImagineDTO imagineDTO) {
58
+ String prompt = imagineDTO.getPrompt();
59
+ if (CharSequenceUtil.isBlank(prompt)) {
60
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "prompt不能为空");
61
+ }
62
+ prompt = prompt.trim();
63
+ Task task = newTask(imagineDTO);
64
+ task.setAction(TaskAction.IMAGINE);
65
+ task.setPrompt(prompt);
66
+ String promptEn = translatePrompt(prompt);
67
+ try {
68
+ BannedPromptUtils.checkBanned(promptEn);
69
+ } catch (BannedPromptException e) {
70
+ return SubmitResultVO.fail(ReturnCode.BANNED_PROMPT, "可能包含敏感词")
71
+ .setProperty("promptEn", promptEn).setProperty("bannedWord", e.getMessage());
72
+ }
73
+ List<String> base64Array = Optional.ofNullable(imagineDTO.getBase64Array()).orElse(new ArrayList<>());
74
+ if (CharSequenceUtil.isNotBlank(imagineDTO.getBase64())) {
75
+ base64Array.add(imagineDTO.getBase64());
76
+ }
77
+ List<DataUrl> dataUrls;
78
+ try {
79
+ dataUrls = ConvertUtils.convertBase64Array(base64Array);
80
+ } catch (MalformedURLException e) {
81
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64格式错误");
82
+ }
83
+ task.setPromptEn(promptEn);
84
+ task.setDescription("/imagine " + prompt);
85
+ return this.taskService.submitImagine(task, dataUrls);
86
+ }
87
+
88
+ @ApiOperation(value = "绘图变化-simple")
89
+ @PostMapping("/simple-change")
90
+ public SubmitResultVO simpleChange(@RequestBody SubmitSimpleChangeDTO simpleChangeDTO) {
91
+ TaskChangeParams changeParams = ConvertUtils.convertChangeParams(simpleChangeDTO.getContent());
92
+ if (changeParams == null) {
93
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "content参数错误");
94
+ }
95
+ SubmitChangeDTO changeDTO = new SubmitChangeDTO();
96
+ changeDTO.setAction(changeParams.getAction());
97
+ changeDTO.setTaskId(changeParams.getId());
98
+ changeDTO.setIndex(changeParams.getIndex());
99
+ changeDTO.setState(simpleChangeDTO.getState());
100
+ changeDTO.setNotifyHook(simpleChangeDTO.getNotifyHook());
101
+ return change(changeDTO);
102
+ }
103
+
104
+ @ApiOperation(value = "绘图变化")
105
+ @PostMapping("/change")
106
+ public SubmitResultVO change(@RequestBody SubmitChangeDTO changeDTO) {
107
+ if (CharSequenceUtil.isBlank(changeDTO.getTaskId())) {
108
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "taskId不能为空");
109
+ }
110
+ if (!Set.of(TaskAction.UPSCALE, TaskAction.VARIATION, TaskAction.REROLL).contains(changeDTO.getAction())) {
111
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "action参数错误");
112
+ }
113
+ String description = "/up " + changeDTO.getTaskId();
114
+ if (TaskAction.REROLL.equals(changeDTO.getAction())) {
115
+ description += " R";
116
+ } else {
117
+ description += " " + changeDTO.getAction().name().charAt(0) + changeDTO.getIndex();
118
+ }
119
+ if (TaskAction.UPSCALE.equals(changeDTO.getAction())) {
120
+ TaskCondition condition = new TaskCondition().setDescription(description);
121
+ Task existTask = this.taskStoreService.findOne(condition);
122
+ if (existTask != null) {
123
+ return SubmitResultVO.of(ReturnCode.EXISTED, "任务已存在", existTask.getId())
124
+ .setProperty("status", existTask.getStatus())
125
+ .setProperty("imageUrl", existTask.getImageUrl());
126
+ }
127
+ }
128
+ Task targetTask = this.taskStoreService.get(changeDTO.getTaskId());
129
+ if (targetTask == null) {
130
+ return SubmitResultVO.fail(ReturnCode.NOT_FOUND, "关联任务不存在或已失效");
131
+ }
132
+ if (!TaskStatus.SUCCESS.equals(targetTask.getStatus())) {
133
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "关联任务状态错误");
134
+ }
135
+ if (!Set.of(TaskAction.IMAGINE, TaskAction.VARIATION, TaskAction.REROLL, TaskAction.BLEND).contains(targetTask.getAction())) {
136
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "关联任务不允许执行变化");
137
+ }
138
+ Task task = newTask(changeDTO);
139
+ task.setAction(changeDTO.getAction());
140
+ task.setPrompt(targetTask.getPrompt());
141
+ task.setPromptEn(targetTask.getPromptEn());
142
+ task.setProperty(Constants.TASK_PROPERTY_FINAL_PROMPT, targetTask.getProperty(Constants.TASK_PROPERTY_FINAL_PROMPT));
143
+ task.setProperty(Constants.TASK_PROPERTY_PROGRESS_MESSAGE_ID, targetTask.getProperty(Constants.TASK_PROPERTY_MESSAGE_ID));
144
+ task.setDescription(description);
145
+ int messageFlags = targetTask.getPropertyGeneric(Constants.TASK_PROPERTY_FLAGS);
146
+ String messageId = targetTask.getPropertyGeneric(Constants.TASK_PROPERTY_MESSAGE_ID);
147
+ String messageHash = targetTask.getPropertyGeneric(Constants.TASK_PROPERTY_MESSAGE_HASH);
148
+ if (TaskAction.UPSCALE.equals(changeDTO.getAction())) {
149
+ return this.taskService.submitUpscale(task, messageId, messageHash, changeDTO.getIndex(), messageFlags);
150
+ } else if (TaskAction.VARIATION.equals(changeDTO.getAction())) {
151
+ return this.taskService.submitVariation(task, messageId, messageHash, changeDTO.getIndex(), messageFlags);
152
+ } else {
153
+ return this.taskService.submitReroll(task, messageId, messageHash, messageFlags);
154
+ }
155
+ }
156
+
157
+ @ApiOperation(value = "提交Describe任务")
158
+ @PostMapping("/describe")
159
+ public SubmitResultVO describe(@RequestBody SubmitDescribeDTO describeDTO) {
160
+ if (CharSequenceUtil.isBlank(describeDTO.getBase64())) {
161
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64不能为空");
162
+ }
163
+ IDataUrlSerializer serializer = new DataUrlSerializer();
164
+ DataUrl dataUrl;
165
+ try {
166
+ dataUrl = serializer.unserialize(describeDTO.getBase64());
167
+ } catch (MalformedURLException e) {
168
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64格式错误");
169
+ }
170
+ Task task = newTask(describeDTO);
171
+ task.setAction(TaskAction.DESCRIBE);
172
+ String taskFileName = task.getId() + "." + MimeTypeUtils.guessFileSuffix(dataUrl.getMimeType());
173
+ task.setDescription("/describe " + taskFileName);
174
+ return this.taskService.submitDescribe(task, dataUrl);
175
+ }
176
+
177
+ @ApiOperation(value = "提交Blend任务")
178
+ @PostMapping("/blend")
179
+ public SubmitResultVO blend(@RequestBody SubmitBlendDTO blendDTO) {
180
+ List<String> base64Array = blendDTO.getBase64Array();
181
+ if (base64Array == null || base64Array.size() < 2 || base64Array.size() > 5) {
182
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64List参数错误");
183
+ }
184
+ if (blendDTO.getDimensions() == null) {
185
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "dimensions参数错误");
186
+ }
187
+ IDataUrlSerializer serializer = new DataUrlSerializer();
188
+ List<DataUrl> dataUrlList = new ArrayList<>();
189
+ try {
190
+ for (String base64 : base64Array) {
191
+ DataUrl dataUrl = serializer.unserialize(base64);
192
+ dataUrlList.add(dataUrl);
193
+ }
194
+ } catch (MalformedURLException e) {
195
+ return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64格式错误");
196
+ }
197
+ Task task = newTask(blendDTO);
198
+ task.setAction(TaskAction.BLEND);
199
+ task.setDescription("/blend " + task.getId() + " " + dataUrlList.size());
200
+ return this.taskService.submitBlend(task, dataUrlList, blendDTO.getDimensions());
201
+ }
202
+
203
+ private Task newTask(BaseSubmitDTO base) {
204
+ Task task = new Task();
205
+ task.setId(System.currentTimeMillis() + "" + RandomUtil.randomNumbers(3));
206
+ task.setSubmitTime(System.currentTimeMillis());
207
+ task.setState(base.getState());
208
+ String notifyHook = CharSequenceUtil.isBlank(base.getNotifyHook()) ? this.properties.getNotifyHook() : base.getNotifyHook();
209
+ task.setProperty(Constants.TASK_PROPERTY_NOTIFY_HOOK, notifyHook);
210
+ task.setProperty(Constants.TASK_PROPERTY_NONCE, SnowFlake.INSTANCE.nextId());
211
+ return task;
212
+ }
213
+
214
+ private String translatePrompt(String prompt) {
215
+ String promptEn;
216
+ int paramStart = prompt.indexOf(" --");
217
+ if (paramStart > 0) {
218
+ promptEn = this.translateService.translateToEnglish(prompt.substring(0, paramStart)).trim() + prompt.substring(paramStart);
219
+ } else {
220
+ promptEn = this.translateService.translateToEnglish(prompt).trim();
221
+ }
222
+ if (CharSequenceUtil.isBlank(promptEn)) {
223
+ promptEn = prompt;
224
+ }
225
+ return promptEn;
226
+ }
227
+ }
src/main/java/com/github/novicezk/midjourney/controller/TaskController.java ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney.controller;
2
+
3
+ import cn.hutool.core.comparator.CompareUtil;
4
+ import com.github.novicezk.midjourney.dto.TaskConditionDTO;
5
+ import com.github.novicezk.midjourney.service.TaskStoreService;
6
+ import com.github.novicezk.midjourney.support.Task;
7
+ import com.github.novicezk.midjourney.support.TaskQueueHelper;
8
+ import io.swagger.annotations.Api;
9
+ import io.swagger.annotations.ApiOperation;
10
+ import io.swagger.annotations.ApiParam;
11
+ import lombok.RequiredArgsConstructor;
12
+ import org.springframework.web.bind.annotation.GetMapping;
13
+ import org.springframework.web.bind.annotation.PathVariable;
14
+ import org.springframework.web.bind.annotation.PostMapping;
15
+ import org.springframework.web.bind.annotation.RequestBody;
16
+ import org.springframework.web.bind.annotation.RequestMapping;
17
+ import org.springframework.web.bind.annotation.RestController;
18
+
19
+ import java.util.Collections;
20
+ import java.util.Comparator;
21
+ import java.util.List;
22
+ import java.util.Objects;
23
+ import java.util.Set;
24
+
25
+ @Api(tags = "任务查询")
26
+ @RestController
27
+ @RequestMapping("/task")
28
+ @RequiredArgsConstructor
29
+ public class TaskController {
30
+ private final TaskStoreService taskStoreService;
31
+ private final TaskQueueHelper taskQueueHelper;
32
+
33
+ @ApiOperation(value = "查询所有任务")
34
+ @GetMapping("/list")
35
+ public List<Task> list() {
36
+ return this.taskStoreService.list().stream()
37
+ .sorted((t1, t2) -> CompareUtil.compare(t2.getSubmitTime(), t1.getSubmitTime()))
38
+ .toList();
39
+ }
40
+
41
+ @ApiOperation(value = "指定ID获取任务")
42
+ @GetMapping("/{id}/fetch")
43
+ public Task fetch(@ApiParam(value = "任务ID") @PathVariable String id) {
44
+ return this.taskStoreService.get(id);
45
+ }
46
+
47
+ @ApiOperation(value = "查询任务队列")
48
+ @GetMapping("/queue")
49
+ public List<Task> queue() {
50
+ Set<String> queueTaskIds = this.taskQueueHelper.getQueueTaskIds();
51
+ return queueTaskIds.stream().map(this.taskStoreService::get).filter(Objects::nonNull)
52
+ .sorted(Comparator.comparing(Task::getSubmitTime))
53
+ .toList();
54
+ }
55
+
56
+ @ApiOperation(value = "根据条件查询任务")
57
+ @PostMapping("/list-by-condition")
58
+ public List<Task> listByCondition(@RequestBody TaskConditionDTO conditionDTO) {
59
+ if (conditionDTO.getIds() == null) {
60
+ return Collections.emptyList();
61
+ }
62
+ return conditionDTO.getIds().stream().map(this.taskStoreService::get).filter(Objects::nonNull).toList();
63
+ }
64
+
65
+ }
src/main/java/com/github/novicezk/midjourney/dto/BaseSubmitDTO.java ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney.dto;
2
+
3
+ import io.swagger.annotations.ApiModelProperty;
4
+ import lombok.Getter;
5
+ import lombok.Setter;
6
+
7
+ @Getter
8
+ @Setter
9
+ public abstract class BaseSubmitDTO {
10
+
11
+ @ApiModelProperty("自定义参数")
12
+ protected String state;
13
+
14
+ @ApiModelProperty("回调地址, 为空时使用全局notifyHook")
15
+ protected String notifyHook;
16
+ }
src/main/java/com/github/novicezk/midjourney/dto/SubmitBlendDTO.java ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney.dto;
2
+
3
+ import com.github.novicezk.midjourney.enums.BlendDimensions;
4
+ import io.swagger.annotations.ApiModel;
5
+ import io.swagger.annotations.ApiModelProperty;
6
+ import lombok.Data;
7
+ import lombok.EqualsAndHashCode;
8
+
9
+ import java.util.List;
10
+
11
+ @Data
12
+ @ApiModel("Blend提交参数")
13
+ @EqualsAndHashCode(callSuper = true)
14
+ public class SubmitBlendDTO extends BaseSubmitDTO {
15
+
16
+ @ApiModelProperty(value = "图片base64数组", required = true, example = "[\"data:image/png;base64,xxx1\", \"data:image/png;base64,xxx2\"]")
17
+ private List<String> base64Array;
18
+
19
+ @ApiModelProperty(value = "比例: PORTRAIT(2:3); SQUARE(1:1); LANDSCAPE(3:2)", example = "SQUARE")
20
+ private BlendDimensions dimensions = BlendDimensions.SQUARE;
21
+ }
src/main/java/com/github/novicezk/midjourney/dto/SubmitChangeDTO.java ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney.dto;
2
+
3
+ import com.github.novicezk.midjourney.enums.TaskAction;
4
+ import io.swagger.annotations.ApiModel;
5
+ import io.swagger.annotations.ApiModelProperty;
6
+ import lombok.Data;
7
+ import lombok.EqualsAndHashCode;
8
+
9
+
10
+ @Data
11
+ @ApiModel("变化任务提交参数")
12
+ @EqualsAndHashCode(callSuper = true)
13
+ public class SubmitChangeDTO extends BaseSubmitDTO {
14
+
15
+ @ApiModelProperty(value = "任务ID", required = true, example = "\"1320098173412546\"")
16
+ private String taskId;
17
+
18
+ @ApiModelProperty(value = "UPSCALE(放大); VARIATION(变换); REROLL(重新生成)", required = true,
19
+ allowableValues = "UPSCALE, VARIATION, REROLL", example = "UPSCALE")
20
+ private TaskAction action;
21
+
22
+ @ApiModelProperty(value = "序号(1~4), action为UPSCALE,VARIATION时必传", allowableValues = "range[1, 4]", example = "1")
23
+ private Integer index;
24
+
25
+ }
src/main/java/com/github/novicezk/midjourney/dto/SubmitDescribeDTO.java ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.github.novicezk.midjourney.dto;
2
+
3
+ import io.swagger.annotations.ApiModel;
4
+ import io.swagger.annotations.ApiModelProperty;
5
+ import lombok.Data;
6
+ import lombok.EqualsAndHashCode;
7
+
8
+ @Data
9
+ @ApiModel("Describe提交参数")
10
+ @EqualsAndHashCode(callSuper = true)
11
+ public class SubmitDescribeDTO extends BaseSubmitDTO {
12
+
13
+ @ApiModelProperty(value = "图片base64", required = true, example = "data:image/png;base64,xxx")
14
+ private String base64;
15
+ }