Upload 116 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- Dockerfile +34 -0
- LICENSE +201 -0
- README3333.md +79 -0
- docker/Dockerfile +35 -0
- docker/build-image.sh +34 -0
- docker/build-manifest.sh +21 -0
- docs/api.md +139 -0
- docs/config.md +68 -0
- docs/discord-params.md +11 -0
- docs/docker-start.md +21 -0
- docs/img_10.png +0 -0
- docs/img_8.png +0 -0
- docs/img_9.png +0 -0
- docs/manager-qrcode.png +0 -0
- docs/params_user.png +0 -0
- docs/railway-start.md +36 -0
- docs/railway_img_1.png +0 -0
- docs/railway_img_10.png +0 -0
- docs/railway_img_11.png +0 -0
- docs/railway_img_12.png +0 -0
- docs/railway_img_2.png +0 -0
- docs/railway_img_3.png +0 -0
- docs/railway_img_4.png +0 -0
- docs/railway_img_5.png +0 -0
- docs/railway_img_6.png +0 -0
- docs/railway_img_7.png +0 -0
- docs/railway_img_8.png +0 -0
- docs/railway_img_9.png +0 -0
- docs/receipt-code.png +0 -0
- docs/zeabur-start.md +33 -0
- pom.xml +120 -0
- src/main/java/com/github/novicezk/midjourney/Constants.java +20 -0
- src/main/java/com/github/novicezk/midjourney/ProxyApplication.java +19 -0
- src/main/java/com/github/novicezk/midjourney/ProxyProperties.java +207 -0
- src/main/java/com/github/novicezk/midjourney/ReturnCode.java +42 -0
- src/main/java/com/github/novicezk/midjourney/controller/AccountController.java +36 -0
- src/main/java/com/github/novicezk/midjourney/controller/SubmitController.java +228 -0
- src/main/java/com/github/novicezk/midjourney/controller/TaskController.java +64 -0
- src/main/java/com/github/novicezk/midjourney/domain/DiscordAccount.java +38 -0
- src/main/java/com/github/novicezk/midjourney/domain/DomainObject.java +72 -0
- src/main/java/com/github/novicezk/midjourney/dto/BaseSubmitDTO.java +16 -0
- src/main/java/com/github/novicezk/midjourney/dto/SubmitBlendDTO.java +21 -0
- src/main/java/com/github/novicezk/midjourney/dto/SubmitChangeDTO.java +25 -0
- src/main/java/com/github/novicezk/midjourney/dto/SubmitDescribeDTO.java +15 -0
- src/main/java/com/github/novicezk/midjourney/dto/SubmitImagineDTO.java +26 -0
- src/main/java/com/github/novicezk/midjourney/dto/SubmitSimpleChangeDTO.java +17 -0
- src/main/java/com/github/novicezk/midjourney/dto/TaskConditionDTO.java +14 -0
- src/main/java/com/github/novicezk/midjourney/enums/BlendDimensions.java +21 -0
- src/main/java/com/github/novicezk/midjourney/enums/MessageType.java +26 -0
- src/main/java/com/github/novicezk/midjourney/enums/TaskAction.java +30 -0
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.
|
README3333.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# midjourney-proxy
|
| 2 |
+
|
| 3 |
+
代理 MidJourney 的discord频道,实现api形式调用AI绘图
|
| 4 |
+
|
| 5 |
+
[](https://www.github.com/novicezk/midjourney-proxy)
|
| 6 |
+
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
| 7 |
+
|
| 8 |
+
## 主要功能
|
| 9 |
+
- [x] 支持 Imagine 指令和相关动作
|
| 10 |
+
- [x] Imagine 时支持添加图片base64,作为垫图
|
| 11 |
+
- [x] 支持 Blend(图片混合)、Describe(图生文) 指令
|
| 12 |
+
- [x] 支持任务实时进度
|
| 13 |
+
- [x] 支持中英文翻译,需配置百度翻译或gpt
|
| 14 |
+
- [x] prompt 敏感词判断,支持覆盖调整
|
| 15 |
+
- [x] user-token 连接 wss,可以获取错误信息和完整功能
|
| 16 |
+
- [x] 支持 discord域名(server、cdn、wss)反代,配置 mj.ng-discord
|
| 17 |
+
- [x] 支持多账号配置,每个账号可设置对应的任务队列
|
| 18 |
+
|
| 19 |
+
**🚀 更多功能请查看 [midjourney-proxy-plus](https://github.com/litter-coder/midjourney-proxy-plus)**
|
| 20 |
+
> - [x] 支持开源版的所有功能
|
| 21 |
+
> - [x] 支持 Shorten(prompt分析) 指令
|
| 22 |
+
> - [x] 支持焦点移动: Pan ⬅️ ➡️ ⬆️ ⬇️
|
| 23 |
+
> - [x] 支持图片变焦: Zoom 🔍
|
| 24 |
+
> - [x] 支持局部重绘: Vary (Region) 🖌
|
| 25 |
+
> - [x] 支持几乎所有的关联按钮动作和🎛️ Remix模式
|
| 26 |
+
> - [x] 支持获取图片的seed值
|
| 27 |
+
> - [x] 中英文翻译额外支持deepl
|
| 28 |
+
> - [x] 账号池持久化,动态维护
|
| 29 |
+
> - [x] 支持获取账号/info、/settings信息
|
| 30 |
+
> - [x] 内嵌管理后台页面
|
| 31 |
+
|
| 32 |
+
## 使用前提
|
| 33 |
+
1. 注册并订阅 MidJourney,创建自己的频道,参考 https://docs.midjourney.com/docs/quick-start
|
| 34 |
+
2. 获取用户Token、服务器ID、频道ID:[获取方式](./docs/discord-params.md)
|
| 35 |
+
|
| 36 |
+
## 快速启动
|
| 37 |
+
1. `Railway`: 基于Railway平台,不需要自己的服务器: [部署方式](./docs/railway-start.md);若Railway不能使用,可使用Zeabur启动
|
| 38 |
+
2. `Zeabur`: 基于Zeabur平台,不需要自己的服务器: [部署方式](./docs/zeabur-start.md)
|
| 39 |
+
3. `Docker`: 在服务器或本地使用Docker启动: [部署方式](./docs/docker-start.md)
|
| 40 |
+
|
| 41 |
+
## 本地开发
|
| 42 |
+
- 依赖java17和maven
|
| 43 |
+
- 更改配置项: 修改src/main/application.yml
|
| 44 |
+
- 项目运行: 启动ProxyApplication的main函数
|
| 45 |
+
- 更改代码后,构建镜像: Dockerfile取消VOLUME的注释,执行 `docker build . -t midjourney-proxy`
|
| 46 |
+
|
| 47 |
+
## 配置项
|
| 48 |
+
- mj.accounts: 参考 [账号池配置](./docs/config.md#%E8%B4%A6%E5%8F%B7%E6%B1%A0%E9%85%8D%E7%BD%AE%E5%8F%82%E8%80%83)
|
| 49 |
+
- mj.task-store.type: 任务存储方式,默认in_memory(内存\重启后丢失),可选redis
|
| 50 |
+
- mj.task-store.timeout: 任务存储过期时间,过期后删除,默认30天
|
| 51 |
+
- mj.api-secret: 接口密钥,为空不启用鉴权;调用接口时需要加请求头 mj-api-secret
|
| 52 |
+
- mj.translate-way: 中文prompt翻译成英文的方式,可选null(默认)、baidu、gpt、deepl
|
| 53 |
+
- 更多配置查看 [配置项](./docs/config.md)
|
| 54 |
+
|
| 55 |
+
## 相关文档
|
| 56 |
+
1. [API接口说明](./docs/api.md)
|
| 57 |
+
2. [版本更新记录](https://github.com/novicezk/midjourney-proxy/wiki/%E6%9B%B4%E6%96%B0%E8%AE%B0%E5%BD%95)
|
| 58 |
+
|
| 59 |
+
## 注意事项
|
| 60 |
+
1. 作图频繁等行为,可能会触发midjourney账号警告,请谨慎使用
|
| 61 |
+
2. 常见问题及解决办法见 [Wiki / FAQ](https://github.com/novicezk/midjourney-proxy/wiki/FAQ)
|
| 62 |
+
3. 在 [Issues](https://github.com/novicezk/midjourney-proxy/issues) 中提出其他问题或建议
|
| 63 |
+
4. 感兴趣的朋友也欢迎加入交流群讨论一下,扫码进群名额已满,加管理员微信邀请进群
|
| 64 |
+
|
| 65 |
+
<img src="https://raw.githubusercontent.com/novicezk/midjourney-proxy/main/docs/manager-qrcode.png" width="220" alt="微信二维码"/>
|
| 66 |
+
|
| 67 |
+
## 应用项目
|
| 68 |
+
依赖此项目且开源的,欢迎联系作者,加到此处展示
|
| 69 |
+
- [wechat-midjourney](https://github.com/novicezk/wechat-midjourney) : 代理微信客户端,接入MidJourney,仅示例应用场景,不再更新
|
| 70 |
+
- [stable-diffusion-mobileui](https://github.com/yuanyuekeji/stable-diffusion-mobileui) : SDUI,基于本接口和SD,可一键打包生成H5和小程序
|
| 71 |
+
- [ChatGPT-Midjourney](https://github.com/Licoy/ChatGPT-Midjourney) : 一键拥有你自己的 ChatGPT+Midjourney 网页服务
|
| 72 |
+
- [MidJourney-Web](https://github.com/ConnectAI-E/MidJourney-Web) : 🍎 Supercharged Experience For MidJourney On Web UI
|
| 73 |
+
|
| 74 |
+
## 其它
|
| 75 |
+
如果觉得这个项目对你有所帮助,请帮忙点个star;也可以请作者喝杯茶~
|
| 76 |
+
|
| 77 |
+
<img src="https://raw.githubusercontent.com/novicezk/midjourney-proxy/main/docs/receipt-code.png" width="220" alt="二维码"/>
|
| 78 |
+
|
| 79 |
+
[](https://star-history.com/#novicezk/midjourney-proxy&Date)
|
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,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
"properties": {
|
| 33 |
+
"discordInstanceId": "1118138338562560102"
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
```
|
| 37 |
+
- code=21: 任务已存在,U时可能发生
|
| 38 |
+
```json
|
| 39 |
+
{
|
| 40 |
+
"code": 21,
|
| 41 |
+
"description": "任务已存在",
|
| 42 |
+
"result": "0741798445574458",
|
| 43 |
+
"properties": {
|
| 44 |
+
"status": "SUCCESS",
|
| 45 |
+
"imageUrl": "https://xxxx"
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
```
|
| 49 |
+
- code=22: 提交成功,进入队列等待
|
| 50 |
+
```json
|
| 51 |
+
{
|
| 52 |
+
"code": 22,
|
| 53 |
+
"description": "排队中,前面还有1个任务",
|
| 54 |
+
"result": "0741798445574458",
|
| 55 |
+
"properties": {
|
| 56 |
+
"numberOfQueues": 1,
|
| 57 |
+
"discordInstanceId": "1118138338562560102"
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
```
|
| 61 |
+
- code=23: 队列已满,请稍后尝试
|
| 62 |
+
```json
|
| 63 |
+
{
|
| 64 |
+
"code": 23,
|
| 65 |
+
"description": "队列已满,请稍后尝试",
|
| 66 |
+
"result": "14001929738841620",
|
| 67 |
+
"properties": {
|
| 68 |
+
"discordInstanceId": "1118138338562560102"
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
```
|
| 72 |
+
- code=24: prompt包含敏感词
|
| 73 |
+
```json
|
| 74 |
+
{
|
| 75 |
+
"code": 24,
|
| 76 |
+
"description": "可能包含敏感词",
|
| 77 |
+
"properties": {
|
| 78 |
+
"promptEn": "nude body",
|
| 79 |
+
"bannedWord": "nude"
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
```
|
| 83 |
+
- other: 提交错误,description为错误描述
|
| 84 |
+
|
| 85 |
+
## 3. `/mj/submit/simple-change` 绘图变化-simple
|
| 86 |
+
接口作用同 `/mj/submit/change`(绘图变化),传参方式不同,该接口接收content,格式为`ID 操作`,例如:1320098173412546 U2
|
| 87 |
+
|
| 88 |
+
- 放大 U1~U4
|
| 89 |
+
- 变换 V1~V4
|
| 90 |
+
- 重新执行 R
|
| 91 |
+
|
| 92 |
+
## 4. `/mj/submit/describe` 图生文
|
| 93 |
+
```json
|
| 94 |
+
{
|
| 95 |
+
// 图片的base64字符串
|
| 96 |
+
"base64": "data:image/png;base64,xxx"
|
| 97 |
+
}
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
后续任务完成后,properties中finalPrompt即为图片生成的prompt
|
| 101 |
+
```json
|
| 102 |
+
{
|
| 103 |
+
"id":"14001929738841620",
|
| 104 |
+
"action":"DESCRIBE",
|
| 105 |
+
"status": "SUCCESS",
|
| 106 |
+
"description":"/describe 14001929738841620.png",
|
| 107 |
+
"imageUrl":"https://cdn.discordapp.com/attachments/xxx/xxx/14001929738841620.png",
|
| 108 |
+
"properties": {
|
| 109 |
+
"finalPrompt": "1️⃣ Cat --ar 5:4\n\n2️⃣ Cat2 --ar 5:4\n\n3️⃣ Cat3 --ar 5:4\n\n4️⃣ Cat4 --ar 5:4"
|
| 110 |
+
}
|
| 111 |
+
// ...
|
| 112 |
+
}
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
## 5. 任务变更回调
|
| 116 |
+
任务状态变化或进度改变时,会调用业务系统的接口
|
| 117 |
+
- 接口地址为配置的 mj.notify-hook,任务提交时支持传`notifyHook`以改变此任务的回调地址
|
| 118 |
+
- 两者都为空时,不触发回调
|
| 119 |
+
|
| 120 |
+
POST application/json
|
| 121 |
+
```json
|
| 122 |
+
{
|
| 123 |
+
"id": "14001929738841620",
|
| 124 |
+
"action": "IMAGINE",
|
| 125 |
+
"status": "SUCCESS",
|
| 126 |
+
"prompt": "猫猫",
|
| 127 |
+
"promptEn": "Cat",
|
| 128 |
+
"description": "/imagine 猫猫",
|
| 129 |
+
"submitTime": 1689231405854,
|
| 130 |
+
"startTime": 1689231442755,
|
| 131 |
+
"finishTime": 1689231544312,
|
| 132 |
+
"progress": "100%",
|
| 133 |
+
"imageUrl": "https://cdn.discordapp.com/attachments/xxx/xxx/xxxx.png",
|
| 134 |
+
"failReason": null,
|
| 135 |
+
"properties": {
|
| 136 |
+
"finalPrompt": "Cat"
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
```
|
docs/config.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 配置项
|
| 2 |
+
|
| 3 |
+
| 变量名 | 非空 | 描述 |
|
| 4 |
+
|:------------------------------|:--:|:----------------------------------------------|
|
| 5 |
+
| mj.accounts | 是 | [账号池配置](./config.md#%E8%B4%A6%E5%8F%B7%E6%B1%A0%E9%85%8D%E7%BD%AE%E5%8F%82%E8%80%83),配置后不需要额外设置mj.discord |
|
| 6 |
+
| mj.discord.guild-id | 是 | discord服务器ID |
|
| 7 |
+
| mj.discord.channel-id | 是 | discord频道ID |
|
| 8 |
+
| mj.discord.user-token | 是 | discord用户Token |
|
| 9 |
+
| mj.discord.user-agent | 否 | 调用discord接口、连接wss时的user-agent,建议从浏览器network复制 |
|
| 10 |
+
| mj.discord.core-size | 否 | 并发数,默认为3 |
|
| 11 |
+
| mj.discord.queue-size | 否 | 等待队列,默认长度10 |
|
| 12 |
+
| mj.discord.timeout-minutes | 否 | 任务超时时间,默认为5分钟 |
|
| 13 |
+
| mj.api-secret | 否 | 接口密钥,为空不启用鉴权;调用接口时需要加请求头 mj-api-secret |
|
| 14 |
+
| mj.notify-hook | 否 | 全局的任务状态变更回调地址 |
|
| 15 |
+
| mj.notify-notify-pool-size | 否 | 通知回调线程池大小,默认10 |
|
| 16 |
+
| mj.task-store.type | 否 | 任务存储方式,默认in_memory(内存\重启后丢失),可选redis |
|
| 17 |
+
| mj.task-store.timeout | 否 | 任务过期时间,过期后删除,默认30天 |
|
| 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 |
+
### 账号池配置参考
|
| 35 |
+
```yaml
|
| 36 |
+
mj:
|
| 37 |
+
accounts:
|
| 38 |
+
- guild-id: xxx
|
| 39 |
+
channel-id: xxx
|
| 40 |
+
user-token: xxxx
|
| 41 |
+
user-agent: xxxx
|
| 42 |
+
- guild-id: xxx
|
| 43 |
+
channel-id: xxx
|
| 44 |
+
user-token: xxxx
|
| 45 |
+
user-agent: xxxx
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
账号字段说明
|
| 49 |
+
|
| 50 |
+
| 名称 | 非空 | 描述 |
|
| 51 |
+
|:------------------| :----: |:--------------------------------------------------------------------|
|
| 52 |
+
| guild-id | 是 | discord服务器ID |
|
| 53 |
+
| channel-id | 是 | discord频道ID |
|
| 54 |
+
| user-token | 是 | discord用户Token |
|
| 55 |
+
| user-agent | 否 | 调用discord接口、连接wss时的user-agent,建议从浏览器network复制 |
|
| 56 |
+
| enable | 否 | 是否可用,默认true |
|
| 57 |
+
| core-size | 否 | 并发数,默认3 |
|
| 58 |
+
| queue-size | 否 | 等待队列长度,默认10 |
|
| 59 |
+
| timeout-minutes | 否 | 任务超时时间(分钟),默认5 |
|
| 60 |
+
|
| 61 |
+
### spring.redis配置参考
|
| 62 |
+
```yaml
|
| 63 |
+
spring:
|
| 64 |
+
redis:
|
| 65 |
+
host: 10.107.xxx.xxx
|
| 66 |
+
port: 6379
|
| 67 |
+
password: xxx
|
| 68 |
+
```
|
docs/discord-params.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 获取discord配置参数
|
| 2 |
+
|
| 3 |
+
### 1. 获取用户Token
|
| 4 |
+
进入频道,打开network,刷新页面,找到 `messages` 的请求,这里的 authorization 即用户Token,后续设置到 `mj.discord.user-token`
|
| 5 |
+
|
| 6 |
+

|
| 7 |
+
|
| 8 |
+
### 2. 获取服务器ID、频道ID
|
| 9 |
+
|
| 10 |
+
频道的url里取出 服务器ID、频道ID,后续设置到配置项
|
| 11 |
+

|
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.5
|
| 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.5
|
| 21 |
+
```
|
docs/img_10.png
ADDED
|
docs/img_8.png
ADDED
|
docs/img_9.png
ADDED
|
docs/manager-qrcode.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 |
+

|
| 13 |
+

|
| 14 |
+

|
| 15 |
+
|
| 16 |
+
### 4. 选择该fork仓库,新建项目,设置环境变量
|
| 17 |
+

|
| 18 |
+

|
| 19 |
+

|
| 20 |
+

|
| 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 |
+

|
| 26 |
+
|
| 27 |
+
若部署启动失败请查看日志,检查配置项
|
| 28 |
+

|
| 29 |
+

|
| 30 |
+
|
| 31 |
+
### 6. 开始使用
|
| 32 |
+
等待部署成功后,生成随机域名
|
| 33 |
+

|
| 34 |
+

|
| 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,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.5</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 |
+
|
| 99 |
+
<dependency>
|
| 100 |
+
<groupId>org.springframework.boot</groupId>
|
| 101 |
+
<artifactId>spring-boot-configuration-processor</artifactId>
|
| 102 |
+
<optional>true</optional>
|
| 103 |
+
</dependency>
|
| 104 |
+
<dependency>
|
| 105 |
+
<groupId>org.projectlombok</groupId>
|
| 106 |
+
<artifactId>lombok</artifactId>
|
| 107 |
+
<optional>true</optional>
|
| 108 |
+
</dependency>
|
| 109 |
+
</dependencies>
|
| 110 |
+
|
| 111 |
+
<build>
|
| 112 |
+
<plugins>
|
| 113 |
+
<plugin>
|
| 114 |
+
<groupId>org.springframework.boot</groupId>
|
| 115 |
+
<artifactId>spring-boot-maven-plugin</artifactId>
|
| 116 |
+
</plugin>
|
| 117 |
+
</plugins>
|
| 118 |
+
</build>
|
| 119 |
+
|
| 120 |
+
</project>
|
src/main/java/com/github/novicezk/midjourney/Constants.java
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
public static final String TASK_PROPERTY_PROGRESS_MESSAGE_ID = "progressMessageId";
|
| 13 |
+
public static final String TASK_PROPERTY_FLAGS = "flags";
|
| 14 |
+
public static final String TASK_PROPERTY_NONCE = "nonce";
|
| 15 |
+
public static final String TASK_PROPERTY_DISCORD_INSTANCE_ID = "discordInstanceId";
|
| 16 |
+
// 任务扩展属性 end
|
| 17 |
+
|
| 18 |
+
public static final String API_SECRET_HEADER_NAME = "mj-api-secret";
|
| 19 |
+
public static final String DEFAULT_DISCORD_USER_AGENT = "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";
|
| 20 |
+
}
|
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,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
import java.util.ArrayList;
|
| 10 |
+
import java.util.List;
|
| 11 |
+
|
| 12 |
+
@Data
|
| 13 |
+
@Component
|
| 14 |
+
@ConfigurationProperties(prefix = "mj")
|
| 15 |
+
public class ProxyProperties {
|
| 16 |
+
/**
|
| 17 |
+
* task存储配置.
|
| 18 |
+
*/
|
| 19 |
+
private final TaskStore taskStore = new TaskStore();
|
| 20 |
+
/**
|
| 21 |
+
* discord账号选择规则.
|
| 22 |
+
*/
|
| 23 |
+
private String accountChooseRule = "BestWaitIdleRule";
|
| 24 |
+
/**
|
| 25 |
+
* discord单账号配置.
|
| 26 |
+
*/
|
| 27 |
+
private final DiscordAccountConfig discord = new DiscordAccountConfig();
|
| 28 |
+
/**
|
| 29 |
+
* discord账号池配置.
|
| 30 |
+
*/
|
| 31 |
+
private final List<DiscordAccountConfig> accounts = new ArrayList<>();
|
| 32 |
+
/**
|
| 33 |
+
* 代理配置.
|
| 34 |
+
*/
|
| 35 |
+
private final ProxyConfig proxy = new ProxyConfig();
|
| 36 |
+
/**
|
| 37 |
+
* 反代配置.
|
| 38 |
+
*/
|
| 39 |
+
private final NgDiscordConfig ngDiscord = new NgDiscordConfig();
|
| 40 |
+
/**
|
| 41 |
+
* 百度翻译配置.
|
| 42 |
+
*/
|
| 43 |
+
private final BaiduTranslateConfig baiduTranslate = new BaiduTranslateConfig();
|
| 44 |
+
/**
|
| 45 |
+
* openai配置.
|
| 46 |
+
*/
|
| 47 |
+
private final OpenaiConfig openai = new OpenaiConfig();
|
| 48 |
+
/**
|
| 49 |
+
* 中文prompt翻译方式.
|
| 50 |
+
*/
|
| 51 |
+
private TranslateWay translateWay = TranslateWay.NULL;
|
| 52 |
+
/**
|
| 53 |
+
* 接口密钥,为空不启用鉴权;调用接口时需要加请求头 mj-api-secret.
|
| 54 |
+
*/
|
| 55 |
+
private String apiSecret;
|
| 56 |
+
/**
|
| 57 |
+
* 任务状态变更回调地址.
|
| 58 |
+
*/
|
| 59 |
+
private String notifyHook;
|
| 60 |
+
/**
|
| 61 |
+
* 通知回调线程池大小.
|
| 62 |
+
*/
|
| 63 |
+
private int notifyPoolSize = 10;
|
| 64 |
+
|
| 65 |
+
@Data
|
| 66 |
+
public static class DiscordAccountConfig {
|
| 67 |
+
/**
|
| 68 |
+
* 服务器ID.
|
| 69 |
+
*/
|
| 70 |
+
private String guildId;
|
| 71 |
+
/**
|
| 72 |
+
* 频道ID.
|
| 73 |
+
*/
|
| 74 |
+
private String channelId;
|
| 75 |
+
/**
|
| 76 |
+
* 用户Token.
|
| 77 |
+
*/
|
| 78 |
+
private String userToken;
|
| 79 |
+
/**
|
| 80 |
+
* 用户UserAgent.
|
| 81 |
+
*/
|
| 82 |
+
private String userAgent = Constants.DEFAULT_DISCORD_USER_AGENT;
|
| 83 |
+
/**
|
| 84 |
+
* 是否可用.
|
| 85 |
+
*/
|
| 86 |
+
private boolean enable = true;
|
| 87 |
+
/**
|
| 88 |
+
* 并发数.
|
| 89 |
+
*/
|
| 90 |
+
private int coreSize = 3;
|
| 91 |
+
/**
|
| 92 |
+
* 等待队列长度.
|
| 93 |
+
*/
|
| 94 |
+
private int queueSize = 10;
|
| 95 |
+
/**
|
| 96 |
+
* 任务超时时间(分钟).
|
| 97 |
+
*/
|
| 98 |
+
private int timeoutMinutes = 5;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
@Data
|
| 102 |
+
public static class BaiduTranslateConfig {
|
| 103 |
+
/**
|
| 104 |
+
* 百度翻译的APP_ID.
|
| 105 |
+
*/
|
| 106 |
+
private String appid;
|
| 107 |
+
/**
|
| 108 |
+
* 百度翻译的密钥.
|
| 109 |
+
*/
|
| 110 |
+
private String appSecret;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
@Data
|
| 114 |
+
public static class OpenaiConfig {
|
| 115 |
+
/**
|
| 116 |
+
* 自定义gpt的api-url.
|
| 117 |
+
*/
|
| 118 |
+
private String gptApiUrl;
|
| 119 |
+
/**
|
| 120 |
+
* gpt的api-key.
|
| 121 |
+
*/
|
| 122 |
+
private String gptApiKey;
|
| 123 |
+
/**
|
| 124 |
+
* 超时时间.
|
| 125 |
+
*/
|
| 126 |
+
private Duration timeout = Duration.ofSeconds(30);
|
| 127 |
+
/**
|
| 128 |
+
* 使用的模型.
|
| 129 |
+
*/
|
| 130 |
+
private String model = "gpt-3.5-turbo";
|
| 131 |
+
/**
|
| 132 |
+
* 返回结果的最大分词数.
|
| 133 |
+
*/
|
| 134 |
+
private int maxTokens = 2048;
|
| 135 |
+
/**
|
| 136 |
+
* 相似度,取值 0-2.
|
| 137 |
+
*/
|
| 138 |
+
private double temperature = 0;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
@Data
|
| 142 |
+
public static class TaskStore {
|
| 143 |
+
/**
|
| 144 |
+
* 任务过期时间,默认30天.
|
| 145 |
+
*/
|
| 146 |
+
private Duration timeout = Duration.ofDays(30);
|
| 147 |
+
/**
|
| 148 |
+
* 任务存储方式: redis(默认)、in_memory.
|
| 149 |
+
*/
|
| 150 |
+
private Type type = Type.IN_MEMORY;
|
| 151 |
+
|
| 152 |
+
public enum Type {
|
| 153 |
+
/**
|
| 154 |
+
* redis.
|
| 155 |
+
*/
|
| 156 |
+
REDIS,
|
| 157 |
+
/**
|
| 158 |
+
* in_memory.
|
| 159 |
+
*/
|
| 160 |
+
IN_MEMORY
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
@Data
|
| 165 |
+
public static class ProxyConfig {
|
| 166 |
+
/**
|
| 167 |
+
* 代理host.
|
| 168 |
+
*/
|
| 169 |
+
private String host;
|
| 170 |
+
/**
|
| 171 |
+
* 代理端口.
|
| 172 |
+
*/
|
| 173 |
+
private Integer port;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
@Data
|
| 177 |
+
public static class NgDiscordConfig {
|
| 178 |
+
/**
|
| 179 |
+
* https://discord.com 反代.
|
| 180 |
+
*/
|
| 181 |
+
private String server;
|
| 182 |
+
/**
|
| 183 |
+
* https://cdn.discordapp.com 反代.
|
| 184 |
+
*/
|
| 185 |
+
private String cdn;
|
| 186 |
+
/**
|
| 187 |
+
* wss://gateway.discord.gg 反代.
|
| 188 |
+
*/
|
| 189 |
+
private String wss;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
@Data
|
| 193 |
+
public static class TaskQueueConfig {
|
| 194 |
+
/**
|
| 195 |
+
* 并发数.
|
| 196 |
+
*/
|
| 197 |
+
private int coreSize = 3;
|
| 198 |
+
/**
|
| 199 |
+
* 等待队列长度.
|
| 200 |
+
*/
|
| 201 |
+
private int queueSize = 10;
|
| 202 |
+
/**
|
| 203 |
+
* 任务超时时间(分钟).
|
| 204 |
+
*/
|
| 205 |
+
private int timeoutMinutes = 5;
|
| 206 |
+
}
|
| 207 |
+
}
|
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/AccountController.java
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.github.novicezk.midjourney.controller;
|
| 2 |
+
|
| 3 |
+
import com.github.novicezk.midjourney.domain.DiscordAccount;
|
| 4 |
+
import com.github.novicezk.midjourney.loadbalancer.DiscordInstance;
|
| 5 |
+
import com.github.novicezk.midjourney.loadbalancer.DiscordLoadBalancer;
|
| 6 |
+
import io.swagger.annotations.Api;
|
| 7 |
+
import io.swagger.annotations.ApiOperation;
|
| 8 |
+
import io.swagger.annotations.ApiParam;
|
| 9 |
+
import lombok.RequiredArgsConstructor;
|
| 10 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 11 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 12 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 13 |
+
import org.springframework.web.bind.annotation.RestController;
|
| 14 |
+
|
| 15 |
+
import java.util.List;
|
| 16 |
+
|
| 17 |
+
@Api(tags = "账号查询")
|
| 18 |
+
@RestController
|
| 19 |
+
@RequestMapping("/account")
|
| 20 |
+
@RequiredArgsConstructor
|
| 21 |
+
public class AccountController {
|
| 22 |
+
private final DiscordLoadBalancer loadBalancer;
|
| 23 |
+
|
| 24 |
+
@ApiOperation(value = "指定ID获取账号")
|
| 25 |
+
@GetMapping("/{id}/fetch")
|
| 26 |
+
public DiscordAccount fetch(@ApiParam(value = "账号ID") @PathVariable String id) {
|
| 27 |
+
DiscordInstance instance = this.loadBalancer.getDiscordInstance(id);
|
| 28 |
+
return instance == null ? null : instance.account();
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
@ApiOperation(value = "查询所有账号")
|
| 32 |
+
@GetMapping("/list")
|
| 33 |
+
public List<DiscordAccount> list() {
|
| 34 |
+
return this.loadBalancer.getAllInstances().stream().map(DiscordInstance::account).toList();
|
| 35 |
+
}
|
| 36 |
+
}
|
src/main/java/com/github/novicezk/midjourney/controller/SubmitController.java
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.setProperty(Constants.TASK_PROPERTY_DISCORD_INSTANCE_ID, targetTask.getProperty(Constants.TASK_PROPERTY_DISCORD_INSTANCE_ID));
|
| 145 |
+
task.setDescription(description);
|
| 146 |
+
int messageFlags = targetTask.getPropertyGeneric(Constants.TASK_PROPERTY_FLAGS);
|
| 147 |
+
String messageId = targetTask.getPropertyGeneric(Constants.TASK_PROPERTY_MESSAGE_ID);
|
| 148 |
+
String messageHash = targetTask.getPropertyGeneric(Constants.TASK_PROPERTY_MESSAGE_HASH);
|
| 149 |
+
if (TaskAction.UPSCALE.equals(changeDTO.getAction())) {
|
| 150 |
+
return this.taskService.submitUpscale(task, messageId, messageHash, changeDTO.getIndex(), messageFlags);
|
| 151 |
+
} else if (TaskAction.VARIATION.equals(changeDTO.getAction())) {
|
| 152 |
+
return this.taskService.submitVariation(task, messageId, messageHash, changeDTO.getIndex(), messageFlags);
|
| 153 |
+
} else {
|
| 154 |
+
return this.taskService.submitReroll(task, messageId, messageHash, messageFlags);
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
@ApiOperation(value = "提交Describe任务")
|
| 159 |
+
@PostMapping("/describe")
|
| 160 |
+
public SubmitResultVO describe(@RequestBody SubmitDescribeDTO describeDTO) {
|
| 161 |
+
if (CharSequenceUtil.isBlank(describeDTO.getBase64())) {
|
| 162 |
+
return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64不能为空");
|
| 163 |
+
}
|
| 164 |
+
IDataUrlSerializer serializer = new DataUrlSerializer();
|
| 165 |
+
DataUrl dataUrl;
|
| 166 |
+
try {
|
| 167 |
+
dataUrl = serializer.unserialize(describeDTO.getBase64());
|
| 168 |
+
} catch (MalformedURLException e) {
|
| 169 |
+
return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64格式错误");
|
| 170 |
+
}
|
| 171 |
+
Task task = newTask(describeDTO);
|
| 172 |
+
task.setAction(TaskAction.DESCRIBE);
|
| 173 |
+
String taskFileName = task.getId() + "." + MimeTypeUtils.guessFileSuffix(dataUrl.getMimeType());
|
| 174 |
+
task.setDescription("/describe " + taskFileName);
|
| 175 |
+
return this.taskService.submitDescribe(task, dataUrl);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
@ApiOperation(value = "提交Blend任务")
|
| 179 |
+
@PostMapping("/blend")
|
| 180 |
+
public SubmitResultVO blend(@RequestBody SubmitBlendDTO blendDTO) {
|
| 181 |
+
List<String> base64Array = blendDTO.getBase64Array();
|
| 182 |
+
if (base64Array == null || base64Array.size() < 2 || base64Array.size() > 5) {
|
| 183 |
+
return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64List参数错误");
|
| 184 |
+
}
|
| 185 |
+
if (blendDTO.getDimensions() == null) {
|
| 186 |
+
return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "dimensions参数错误");
|
| 187 |
+
}
|
| 188 |
+
IDataUrlSerializer serializer = new DataUrlSerializer();
|
| 189 |
+
List<DataUrl> dataUrlList = new ArrayList<>();
|
| 190 |
+
try {
|
| 191 |
+
for (String base64 : base64Array) {
|
| 192 |
+
DataUrl dataUrl = serializer.unserialize(base64);
|
| 193 |
+
dataUrlList.add(dataUrl);
|
| 194 |
+
}
|
| 195 |
+
} catch (MalformedURLException e) {
|
| 196 |
+
return SubmitResultVO.fail(ReturnCode.VALIDATION_ERROR, "base64格式错误");
|
| 197 |
+
}
|
| 198 |
+
Task task = newTask(blendDTO);
|
| 199 |
+
task.setAction(TaskAction.BLEND);
|
| 200 |
+
task.setDescription("/blend " + task.getId() + " " + dataUrlList.size());
|
| 201 |
+
return this.taskService.submitBlend(task, dataUrlList, blendDTO.getDimensions());
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
private Task newTask(BaseSubmitDTO base) {
|
| 205 |
+
Task task = new Task();
|
| 206 |
+
task.setId(System.currentTimeMillis() + "" + RandomUtil.randomNumbers(3));
|
| 207 |
+
task.setSubmitTime(System.currentTimeMillis());
|
| 208 |
+
task.setState(base.getState());
|
| 209 |
+
String notifyHook = CharSequenceUtil.isBlank(base.getNotifyHook()) ? this.properties.getNotifyHook() : base.getNotifyHook();
|
| 210 |
+
task.setProperty(Constants.TASK_PROPERTY_NOTIFY_HOOK, notifyHook);
|
| 211 |
+
task.setProperty(Constants.TASK_PROPERTY_NONCE, SnowFlake.INSTANCE.nextId());
|
| 212 |
+
return task;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
private String translatePrompt(String prompt) {
|
| 216 |
+
String promptEn;
|
| 217 |
+
int paramStart = prompt.indexOf(" --");
|
| 218 |
+
if (paramStart > 0) {
|
| 219 |
+
promptEn = this.translateService.translateToEnglish(prompt.substring(0, paramStart)).trim() + prompt.substring(paramStart);
|
| 220 |
+
} else {
|
| 221 |
+
promptEn = this.translateService.translateToEnglish(prompt).trim();
|
| 222 |
+
}
|
| 223 |
+
if (CharSequenceUtil.isBlank(promptEn)) {
|
| 224 |
+
promptEn = prompt;
|
| 225 |
+
}
|
| 226 |
+
return promptEn;
|
| 227 |
+
}
|
| 228 |
+
}
|
src/main/java/com/github/novicezk/midjourney/controller/TaskController.java
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.loadbalancer.DiscordLoadBalancer;
|
| 6 |
+
import com.github.novicezk.midjourney.service.TaskStoreService;
|
| 7 |
+
import com.github.novicezk.midjourney.support.Task;
|
| 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 |
+
|
| 24 |
+
@Api(tags = "任务查询")
|
| 25 |
+
@RestController
|
| 26 |
+
@RequestMapping("/task")
|
| 27 |
+
@RequiredArgsConstructor
|
| 28 |
+
public class TaskController {
|
| 29 |
+
private final TaskStoreService taskStoreService;
|
| 30 |
+
private final DiscordLoadBalancer discordLoadBalancer;
|
| 31 |
+
|
| 32 |
+
@ApiOperation(value = "指定ID获取任务")
|
| 33 |
+
@GetMapping("/{id}/fetch")
|
| 34 |
+
public Task fetch(@ApiParam(value = "任务ID") @PathVariable String id) {
|
| 35 |
+
return this.taskStoreService.get(id);
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
@ApiOperation(value = "查询任务队列")
|
| 39 |
+
@GetMapping("/queue")
|
| 40 |
+
public List<Task> queue() {
|
| 41 |
+
return this.discordLoadBalancer.getQueueTaskIds().stream()
|
| 42 |
+
.map(this.taskStoreService::get).filter(Objects::nonNull)
|
| 43 |
+
.sorted(Comparator.comparing(Task::getSubmitTime))
|
| 44 |
+
.toList();
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
@ApiOperation(value = "查询所有任务")
|
| 48 |
+
@GetMapping("/list")
|
| 49 |
+
public List<Task> list() {
|
| 50 |
+
return this.taskStoreService.list().stream()
|
| 51 |
+
.sorted((t1, t2) -> CompareUtil.compare(t2.getSubmitTime(), t1.getSubmitTime()))
|
| 52 |
+
.toList();
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
@ApiOperation(value = "根据ID列表查询任务")
|
| 56 |
+
@PostMapping("/list-by-condition")
|
| 57 |
+
public List<Task> listByIds(@RequestBody TaskConditionDTO conditionDTO) {
|
| 58 |
+
if (conditionDTO.getIds() == null) {
|
| 59 |
+
return Collections.emptyList();
|
| 60 |
+
}
|
| 61 |
+
return conditionDTO.getIds().stream().map(this.taskStoreService::get).filter(Objects::nonNull).toList();
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
}
|
src/main/java/com/github/novicezk/midjourney/domain/DiscordAccount.java
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.github.novicezk.midjourney.domain;
|
| 2 |
+
|
| 3 |
+
import com.fasterxml.jackson.annotation.JsonIgnore;
|
| 4 |
+
import com.github.novicezk.midjourney.Constants;
|
| 5 |
+
import io.swagger.annotations.ApiModel;
|
| 6 |
+
import io.swagger.annotations.ApiModelProperty;
|
| 7 |
+
import lombok.Data;
|
| 8 |
+
import lombok.EqualsAndHashCode;
|
| 9 |
+
|
| 10 |
+
@Data
|
| 11 |
+
@EqualsAndHashCode(callSuper = true)
|
| 12 |
+
@ApiModel("Discord账号")
|
| 13 |
+
public class DiscordAccount extends DomainObject {
|
| 14 |
+
|
| 15 |
+
@ApiModelProperty("服务器ID")
|
| 16 |
+
private String guildId;
|
| 17 |
+
@ApiModelProperty("频道ID")
|
| 18 |
+
private String channelId;
|
| 19 |
+
@ApiModelProperty("用户Token")
|
| 20 |
+
private String userToken;
|
| 21 |
+
@ApiModelProperty("用户UserAgent")
|
| 22 |
+
private String userAgent = Constants.DEFAULT_DISCORD_USER_AGENT;
|
| 23 |
+
|
| 24 |
+
@ApiModelProperty("是否可用")
|
| 25 |
+
private boolean enable = true;
|
| 26 |
+
|
| 27 |
+
@ApiModelProperty("并发数")
|
| 28 |
+
private int coreSize = 3;
|
| 29 |
+
@ApiModelProperty("等待队列长度")
|
| 30 |
+
private int queueSize = 10;
|
| 31 |
+
@ApiModelProperty("任务超时时间(分钟)")
|
| 32 |
+
private int timeoutMinutes = 5;
|
| 33 |
+
|
| 34 |
+
@JsonIgnore
|
| 35 |
+
public String getDisplay() {
|
| 36 |
+
return this.channelId;
|
| 37 |
+
}
|
| 38 |
+
}
|
src/main/java/com/github/novicezk/midjourney/domain/DomainObject.java
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.github.novicezk.midjourney.domain;
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
import com.fasterxml.jackson.annotation.JsonIgnore;
|
| 5 |
+
import io.swagger.annotations.ApiModelProperty;
|
| 6 |
+
import lombok.Getter;
|
| 7 |
+
import lombok.Setter;
|
| 8 |
+
|
| 9 |
+
import java.io.Serializable;
|
| 10 |
+
import java.util.HashMap;
|
| 11 |
+
import java.util.Map;
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
public class DomainObject implements Serializable {
|
| 15 |
+
@Getter
|
| 16 |
+
@Setter
|
| 17 |
+
@ApiModelProperty("ID")
|
| 18 |
+
protected String id;
|
| 19 |
+
|
| 20 |
+
@Setter
|
| 21 |
+
protected Map<String, Object> properties; // 扩展属性,仅支持基本类型
|
| 22 |
+
|
| 23 |
+
@JsonIgnore
|
| 24 |
+
private final transient Object lock = new Object();
|
| 25 |
+
|
| 26 |
+
public void sleep() throws InterruptedException {
|
| 27 |
+
synchronized (this.lock) {
|
| 28 |
+
this.lock.wait();
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
public void awake() {
|
| 33 |
+
synchronized (this.lock) {
|
| 34 |
+
this.lock.notifyAll();
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
public DomainObject setProperty(String name, Object value) {
|
| 39 |
+
getProperties().put(name, value);
|
| 40 |
+
return this;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
public DomainObject removeProperty(String name) {
|
| 44 |
+
getProperties().remove(name);
|
| 45 |
+
return this;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
public Object getProperty(String name) {
|
| 49 |
+
return getProperties().get(name);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
@SuppressWarnings("unchecked")
|
| 53 |
+
public <T> T getPropertyGeneric(String name) {
|
| 54 |
+
return (T) getProperty(name);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
public <T> T getProperty(String name, Class<T> clz) {
|
| 58 |
+
return getProperty(name, clz, null);
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
public <T> T getProperty(String name, Class<T> clz, T defaultValue) {
|
| 62 |
+
Object value = getProperty(name);
|
| 63 |
+
return value == null ? defaultValue : clz.cast(value);
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
public Map<String, Object> getProperties() {
|
| 67 |
+
if (this.properties == null) {
|
| 68 |
+
this.properties = new HashMap<>();
|
| 69 |
+
}
|
| 70 |
+
return this.properties;
|
| 71 |
+
}
|
| 72 |
+
}
|
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 |
+
}
|
src/main/java/com/github/novicezk/midjourney/dto/SubmitImagineDTO.java
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
import java.util.List;
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@Data
|
| 12 |
+
@ApiModel("Imagine提交参数")
|
| 13 |
+
@EqualsAndHashCode(callSuper = true)
|
| 14 |
+
public class SubmitImagineDTO extends BaseSubmitDTO {
|
| 15 |
+
|
| 16 |
+
@ApiModelProperty(value = "提示词", required = true, example = "Cat")
|
| 17 |
+
private String prompt;
|
| 18 |
+
|
| 19 |
+
@ApiModelProperty(value = "垫图base64数组")
|
| 20 |
+
private List<String> base64Array;
|
| 21 |
+
|
| 22 |
+
@ApiModelProperty(hidden = true)
|
| 23 |
+
@Deprecated(since = "3.0", forRemoval = true)
|
| 24 |
+
private String base64;
|
| 25 |
+
|
| 26 |
+
}
|
src/main/java/com/github/novicezk/midjourney/dto/SubmitSimpleChangeDTO.java
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
|
| 9 |
+
@Data
|
| 10 |
+
@ApiModel("变化任务提交参数-simple")
|
| 11 |
+
@EqualsAndHashCode(callSuper = true)
|
| 12 |
+
public class SubmitSimpleChangeDTO extends BaseSubmitDTO {
|
| 13 |
+
|
| 14 |
+
@ApiModelProperty(value = "变化描述: ID $action$index", required = true, example = "1320098173412546 U2")
|
| 15 |
+
private String content;
|
| 16 |
+
|
| 17 |
+
}
|
src/main/java/com/github/novicezk/midjourney/dto/TaskConditionDTO.java
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.github.novicezk.midjourney.dto;
|
| 2 |
+
|
| 3 |
+
import io.swagger.annotations.ApiModel;
|
| 4 |
+
import lombok.Data;
|
| 5 |
+
|
| 6 |
+
import java.util.List;
|
| 7 |
+
|
| 8 |
+
@Data
|
| 9 |
+
@ApiModel("任务查询参数")
|
| 10 |
+
public class TaskConditionDTO {
|
| 11 |
+
|
| 12 |
+
private List<String> ids;
|
| 13 |
+
|
| 14 |
+
}
|
src/main/java/com/github/novicezk/midjourney/enums/BlendDimensions.java
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.github.novicezk.midjourney.enums;
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
public enum BlendDimensions {
|
| 5 |
+
|
| 6 |
+
PORTRAIT("2:3"),
|
| 7 |
+
|
| 8 |
+
SQUARE("1:1"),
|
| 9 |
+
|
| 10 |
+
LANDSCAPE("3:2");
|
| 11 |
+
|
| 12 |
+
private final String value;
|
| 13 |
+
|
| 14 |
+
BlendDimensions(String value) {
|
| 15 |
+
this.value = value;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
public String getValue() {
|
| 19 |
+
return this.value;
|
| 20 |
+
}
|
| 21 |
+
}
|
src/main/java/com/github/novicezk/midjourney/enums/MessageType.java
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.github.novicezk.midjourney.enums;
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
public enum MessageType {
|
| 5 |
+
/**
|
| 6 |
+
* 创建.
|
| 7 |
+
*/
|
| 8 |
+
CREATE,
|
| 9 |
+
/**
|
| 10 |
+
* 修改.
|
| 11 |
+
*/
|
| 12 |
+
UPDATE,
|
| 13 |
+
/**
|
| 14 |
+
* 删除.
|
| 15 |
+
*/
|
| 16 |
+
DELETE;
|
| 17 |
+
|
| 18 |
+
public static MessageType of(String type) {
|
| 19 |
+
return switch (type) {
|
| 20 |
+
case "MESSAGE_CREATE" -> CREATE;
|
| 21 |
+
case "MESSAGE_UPDATE" -> UPDATE;
|
| 22 |
+
case "MESSAGE_DELETE" -> DELETE;
|
| 23 |
+
default -> null;
|
| 24 |
+
};
|
| 25 |
+
}
|
| 26 |
+
}
|
src/main/java/com/github/novicezk/midjourney/enums/TaskAction.java
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.github.novicezk.midjourney.enums;
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
public enum TaskAction {
|
| 5 |
+
/**
|
| 6 |
+
* 生成图片.
|
| 7 |
+
*/
|
| 8 |
+
IMAGINE,
|
| 9 |
+
/**
|
| 10 |
+
* 选中放大.
|
| 11 |
+
*/
|
| 12 |
+
UPSCALE,
|
| 13 |
+
/**
|
| 14 |
+
* 选中其中的一张图,生成四张相似的.
|
| 15 |
+
*/
|
| 16 |
+
VARIATION,
|
| 17 |
+
/**
|
| 18 |
+
* 重新执行.
|
| 19 |
+
*/
|
| 20 |
+
REROLL,
|
| 21 |
+
/**
|
| 22 |
+
* 图转prompt.
|
| 23 |
+
*/
|
| 24 |
+
DESCRIBE,
|
| 25 |
+
/**
|
| 26 |
+
* 多图混合.
|
| 27 |
+
*/
|
| 28 |
+
BLEND
|
| 29 |
+
|
| 30 |
+
}
|