You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PhotoRun.vue 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <template>
  2. <div class="c-photo-run" :style="previewStyle" data-private>
  3. <div v-if="run.preferences.body.randomize || run.preferences.body.progressive.enabled" class="run__preferences">
  4. <div class="preference">
  5. <span>Boobs</span>
  6. <span>{{ run.preferences.body.boobs.size | fixedValue }}</span>
  7. </div>
  8. <div class="preference">
  9. <span>Areola</span>
  10. <span>{{ run.preferences.body.areola.size | fixedValue }}</span>
  11. </div>
  12. <div class="preference">
  13. <span>Nipple</span>
  14. <span>{{ run.preferences.body.nipple.size | fixedValue }}</span>
  15. </div>
  16. <div class="preference">
  17. <span>Vagina</span>
  18. <span>{{ run.preferences.body.vagina.size | fixedValue }}</span>
  19. </div>
  20. <div class="preference">
  21. <span>Pubic hair</span>
  22. <span>{{ run.preferences.body.pubicHair.size | fixedValue }}</span>
  23. </div>
  24. </div>
  25. <div class="run__content">
  26. <div v-if="run.running" class="content__item">
  27. <p class="text-white">
  28. <span><font-awesome-icon icon="running" /></span>
  29. <span>{{ run.timer.duration }}s</span>
  30. </p>
  31. </div>
  32. <div v-else-if="run.failed" class="content__item">
  33. <p class="text-danger-500">
  34. <span><font-awesome-icon icon="exclamation-circle" /></span>
  35. <span>Error!</span>
  36. </p>
  37. </div>
  38. <div v-else-if="run.finished" class="content__item">
  39. <p class="text-white">
  40. <span><font-awesome-icon icon="heart" /></span>
  41. <span>{{ run.timer.duration }}s</span>
  42. </p>
  43. </div>
  44. <div v-else class="content__item">
  45. <p class="text-white">
  46. <span><font-awesome-icon icon="clock" /></span>
  47. </p>
  48. </div>
  49. <div v-show="run.finished && !run.failed" class="content__item">
  50. <button v-tooltip="'Save photo'" class="button button--success button--sm" @click.prevent="save">
  51. <font-awesome-icon icon="download" />
  52. </button>
  53. </div>
  54. <div v-show="run.finished" class="content__item">
  55. <button v-tooltip="'Rerun'" class="button button--danger button--sm" @click.prevent="rerun">
  56. <font-awesome-icon icon="undo" />
  57. </button>
  58. </div>
  59. <div v-show="run.running" class="content__item">
  60. <button v-tooltip="'Stop'" class="button button--danger button--sm" @click.prevent="cancel">
  61. <font-awesome-icon icon="stop" />
  62. </button>
  63. </div>
  64. <div v-show="hasMaskfin" class="content__item">
  65. <button v-tooltip="'View maskfin'" class="button button--info button--sm" @click.prevent="$refs.maskfinDialog.showModal()">
  66. <font-awesome-icon icon="mask" />
  67. </button>
  68. </div>
  69. <div class="content__item">
  70. <button v-tooltip="'View terminal'" class="button button--sm" @click.prevent="$refs.terminalDialog.showModal()">
  71. <font-awesome-icon icon="terminal" />
  72. </button>
  73. </div>
  74. </div>
  75. <!-- Maskfin Dialog -->
  76. <dialog ref="maskfinDialog">
  77. <div class="dialog__content dialog__maskfin">
  78. <div class="maskfin__preview">
  79. <img :src="run.maskfinFile.path">
  80. </div>
  81. <div class="maskfin__description">
  82. <p>This is the Maskfin, a mask that represents in layers the areas that the algorithm will replace with the fake nude.</p>
  83. <p>Click on the "Add to queue" button to add it as an additional photo, edit the layers and continue with the nudification. You can also save it to your computer, edit it with an external program and continue the nudification manually.</p>
  84. <p>For more information please consult the <a :href="manualURL" target="_blank">guide</a>.</p>
  85. </div>
  86. <div class="dialog__buttons">
  87. <button class="button" @click.prevent="addMaskToQueue">
  88. Add to queue
  89. </button>
  90. <button class="button button--success" @click.prevent="saveMask">
  91. Save
  92. </button>
  93. <button class="button button--danger" @click.prevent="$refs.maskfinDialog.close()">
  94. Close
  95. </button>
  96. </div>
  97. </div>
  98. </dialog>
  99. <!-- Terminal Dialog -->
  100. <dialog ref="terminalDialog">
  101. <div class="dialog__content">
  102. <div class="terminal">
  103. <li v-for="(item, index) in run.cli.lines" :key="index" :class="item.css">
  104. > {{ item.text }}
  105. </li>
  106. </div>
  107. <div class="dialog__buttons">
  108. <button class="button button--danger" @click.prevent="$refs.terminalDialog.close()">
  109. Close
  110. </button>
  111. </div>
  112. </div>
  113. </dialog>
  114. </div>
  115. </template>
  116. <script>
  117. import { isNil } from 'lodash'
  118. import { nucleus } from '~/modules/services'
  119. import { Nudify } from '~/modules/nudify'
  120. const { showSaveDialogSync } = $provider.api.dialog
  121. export default {
  122. filters: {
  123. size(value) {
  124. return Number.parseFloat(value).toFixed(2)
  125. },
  126. fixedValue(value) {
  127. return Number(value).toFixed(2)
  128. },
  129. },
  130. props: {
  131. run: {
  132. type: Object,
  133. required: true,
  134. },
  135. },
  136. computed: {
  137. previewStyle() {
  138. if (!this.run.outputFile.exists) {
  139. return {}
  140. }
  141. return { backgroundImage: `url(${this.run.outputFile.path})` }
  142. },
  143. hasMaskfin() {
  144. return this.run.maskfinFile.exists
  145. },
  146. manualURL() {
  147. return nucleus.urls?.docs?.manual || 'https://time.dreamnet.tech/docs/guide/upload'
  148. },
  149. },
  150. methods: {
  151. save() {
  152. const savePath = showSaveDialogSync({
  153. defaultPath: this.run.outputName,
  154. filters: [
  155. { name: 'PNG', extensions: ['png'] },
  156. { name: 'JPG', extensions: ['jpg'] },
  157. { name: 'GIF', extensions: ['gif'] },
  158. ],
  159. })
  160. if (isNil(savePath)) {
  161. return
  162. }
  163. this.run.outputFile.copy(savePath)
  164. },
  165. rerun() {
  166. this.run.photo.rerun(this.run)
  167. },
  168. cancel() {
  169. this.run.photo.cancelRun(this.run)
  170. },
  171. addMaskToQueue() {
  172. Nudify.add(this.run.maskfinFile, { isMaskfin: true })
  173. },
  174. saveMask() {
  175. const savePath = showSaveDialogSync({
  176. defaultPath: `maskfin-${this.run.outputName}`,
  177. filters: [
  178. { name: 'PNG', extensions: ['png'] },
  179. { name: 'JPG', extensions: ['jpg'] },
  180. { name: 'GIF', extensions: ['gif'] },
  181. ],
  182. })
  183. if (isNil(savePath)) {
  184. return
  185. }
  186. this.run.maskfinFile.copy(savePath)
  187. },
  188. },
  189. }
  190. </script>
  191. <style lang="scss" scoped>
  192. .c-photo-run {
  193. @apply bg-cover bg-center border border-dark-500;
  194. @apply relative;
  195. background-image: url('~@/assets/images/background.png');
  196. height: 512px;
  197. &:hover {
  198. .run__content,
  199. .run__preferences {
  200. @apply opacity-100;
  201. }
  202. }
  203. }
  204. .run__content,
  205. .run__preferences {
  206. @apply absolute opacity-0 bg-dark-500-80 w-full;
  207. @apply flex;
  208. backdrop-filter: blur(6px);
  209. transition: all .1s linear;
  210. }
  211. .run__preferences {
  212. @apply flex top-0;
  213. height: 80px;
  214. .preference {
  215. @apply flex flex-col flex-1 items-center justify-center;
  216. span {
  217. &:first-child {
  218. @apply text-xs;
  219. }
  220. &:last-child {
  221. @apply text-sm text-white font-bold;
  222. }
  223. }
  224. }
  225. }
  226. .run__content {
  227. @apply bottom-0;
  228. height: 100px;
  229. .content__item {
  230. @apply flex-1 flex justify-center items-center;
  231. &:not(:first-child) {
  232. @apply mr-2;
  233. }
  234. .button {
  235. @apply w-full;
  236. }
  237. p {
  238. @apply font-bold text-center;
  239. span {
  240. @apply block;
  241. }
  242. }
  243. }
  244. }
  245. .dialog__maskfin {
  246. a {
  247. @apply text-primary-500 underline;
  248. }
  249. .maskfin__preview {
  250. @apply mb-4;
  251. }
  252. .maskfin__description {
  253. @apply text-sm mb-4;
  254. p {
  255. @apply mb-2;
  256. }
  257. }
  258. }
  259. .section__preferences {
  260. p {
  261. @apply text-sm;
  262. .preference__name {
  263. @apply inline-block text-generic-300;
  264. width: 150px;
  265. }
  266. .preference__value {
  267. @apply inline-block font-bold text-generic-100;
  268. }
  269. }
  270. }
  271. .terminal {
  272. @apply p-2 mb-2 bg-black overflow-auto rounded;
  273. height: 400px;
  274. li {
  275. @apply font-mono text-xs text-generic-100 mb-2 block;
  276. &.text-danger {
  277. @apply text-danger-500;
  278. }
  279. }
  280. }
  281. </style>