@@ -17,7 +17,12 @@ module.exports = { | |||
], | |||
globals: { | |||
$provider: false, | |||
AppError: false | |||
AppError: false, | |||
LogError: false, | |||
Warning: false, | |||
Exception: false, | |||
consola: false, | |||
Consola: false, | |||
}, | |||
parserOptions: { | |||
parser: "babel-eslint", | |||
@@ -47,7 +52,7 @@ module.exports = { | |||
"promise/no-callback-in-promise": "off", | |||
"promise/catch-or-return": "off", | |||
"linebreak-style": "warn", | |||
"new-parens": ['error', 'never'], | |||
"new-parens": "off", | |||
"lodash/import-scope": [ | |||
"off", | |||
"member" | |||
@@ -88,6 +93,7 @@ module.exports = { | |||
"error", | |||
"never" | |||
], | |||
"prefer-spread": "off", | |||
"quote-props": [ | |||
"error", | |||
"as-needed" |
@@ -10,7 +10,7 @@ | |||
*/ | |||
.box { | |||
@apply bg-dark-500 rounded-sm shadow mb-6; | |||
@apply bg-dark-600 shadow mb-6 border-t border-l border-r border-dark-100; | |||
.box__photo { | |||
@apply relative; |
@@ -1,7 +1,7 @@ | |||
.button { | |||
@apply inline-flex items-center justify-center; | |||
@apply border border-primary-500-30; | |||
@apply px-4 rounded-sm; | |||
@apply px-4 rounded; | |||
@apply text-primary-400 font-semibold uppercase; | |||
@apply outline-none #{!important}; | |||
height: 40px; |
@@ -11,18 +11,22 @@ | |||
} | |||
.input { | |||
@apply border border-dark-300 bg-dark-600; | |||
@apply border border-dark-300 bg-dark-700; | |||
@apply rounded py-2 px-4 w-full text-generic-300 shadow-inner; | |||
outline: none !important; | |||
transition: all .2s ease-in-out; | |||
&:hover, &:focus { | |||
@apply text-generic-100; | |||
@apply text-generic-100 shadow-inner-md; | |||
} | |||
&::placeholder { | |||
@apply text-generic-700; | |||
} | |||
&[disabled] { | |||
@apply opacity-75 cursor-not-allowed; | |||
} | |||
} | |||
select.input { |
@@ -1 +1 @@ | |||
@import url('https://fonts.googleapis.com/css?family=Catamaran:300,400,500,600,700&display=swap'); | |||
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,400i,500,700|Roboto+Slab:300,400,500,600,700'); |
@@ -6,10 +6,9 @@ | |||
} | |||
html { | |||
@apply bg-black text-generic-500; | |||
font-family: theme('fontFamily.sans'); | |||
@apply bg-black text-generic-500 font-sans; | |||
background-image: url('https://www.toptal.com/designers/subtlepatterns/patterns/papyrus-dark.png'); /* Background pattern from Toptal Subtle Patterns */ | |||
font-size: 16px; | |||
word-spacing: 1px; | |||
-ms-text-size-adjust: 100%; | |||
-webkit-text-size-adjust: 100%; | |||
-moz-osx-font-smoothing: grayscale; | |||
@@ -38,12 +37,28 @@ dialog { | |||
@apply p-4 bg-dark-500 rounded; | |||
@apply flex flex-col justify-center; | |||
} | |||
.dialog__buttons { | |||
@apply flex mt-4; | |||
.button { | |||
@apply flex-1; | |||
&:not(:last-child) { | |||
@apply mr-2; | |||
} | |||
} | |||
} | |||
} | |||
dialog::backdrop { | |||
background: rgba(25, 25, 26, 0.75); | |||
} | |||
.title { | |||
@apply font-serif; | |||
} | |||
.swal-content { | |||
font-size: 16px; | |||
@@ -71,7 +86,7 @@ dialog::backdrop { | |||
*::-webkit-scrollbar-thumb | |||
{ | |||
@apply bg-dark-100 rounded-sm; | |||
@apply bg-dark-100; | |||
transition: all .1s ease-in-out; | |||
&:hover { | |||
@@ -89,4 +104,12 @@ dialog::backdrop { | |||
code { | |||
@apply text-sm text-center; | |||
} | |||
} | |||
.vue-slider { | |||
@apply w-full #{!important}; | |||
} | |||
.vue-slider-process { | |||
@apply bg-primary-500 #{!important}; | |||
} |
@@ -1,6 +1,3 @@ | |||
@tailwind base; | |||
// @import './base/reset'; | |||
@tailwind components; | |||
//@import './components/all'; | |||
@tailwind utilities; | |||
//@import './utilities/all'; |
@@ -9,7 +9,7 @@ | |||
About | |||
</nuxt-link> | |||
<nuxt-link v-if="canNudify" class="navbar__item" to="/dreamnet"> | |||
<nuxt-link class="navbar__item" to="/dreamnet"> | |||
DreamNet | |||
</nuxt-link> | |||
@@ -55,7 +55,8 @@ export default { | |||
methods: { | |||
createError() { | |||
throw new Error('User Interface test error.') | |||
// throw new Error('User Interface test error.') | |||
throw new Warning('Warning Test') | |||
}, | |||
}, | |||
} |
@@ -277,7 +277,7 @@ export default { | |||
} | |||
</script> | |||
<style lang="scss"> | |||
<style lang="scss" scoped> | |||
.c-photo-run { | |||
@apply bg-cover bg-center border border-dark-500; | |||
@apply relative; | |||
@@ -313,9 +313,10 @@ export default { | |||
} | |||
.run__content { | |||
@apply opacity-0 bg-dark-500-90 w-full; | |||
@apply opacity-0 bg-dark-500-80 w-full; | |||
@apply absolute bottom-0; | |||
@apply flex; | |||
backdrop-filter: blur(5px); | |||
transition: all .1s linear; | |||
height: 100px; | |||
@@ -358,18 +359,6 @@ export default { | |||
} | |||
} | |||
.dialog__buttons { | |||
@apply flex; | |||
.button { | |||
@apply flex-1; | |||
&:not(:last-child) { | |||
@apply mr-2; | |||
} | |||
} | |||
} | |||
.section__preferences { | |||
p { | |||
@apply text-sm; |
@@ -30,7 +30,7 @@ | |||
@drop="openDrop"> | |||
<p class="dropzone-hint"> | |||
<font-awesome-icon icon="camera" /> | |||
Drop the photo(s)/folder here! | |||
Drop the photos here! | |||
</p> | |||
</div> | |||
@@ -76,7 +76,7 @@ | |||
<div class="box__content"> | |||
<button class="button" @click.prevent="openFolder"> | |||
<span>import folder</span> | |||
<span>Open folder</span> | |||
</button> | |||
</div> | |||
</div> | |||
@@ -97,7 +97,7 @@ | |||
<input v-model="webAddress" type="url" class="input mb-2" placeholder="https://"> | |||
<button class="button" @click="openUrl"> | |||
Go! | |||
Submit | |||
</button> | |||
</div> | |||
</div> | |||
@@ -118,7 +118,7 @@ | |||
<input v-model="instagramPhoto" type="url" class="input mb-2" placeholder="https://www.instagram.com/p/dU4fHDw-Ho/"> | |||
<button class="button" @click="openInstagramPhoto"> | |||
Go! | |||
Submit | |||
</button> | |||
</div> | |||
</div> | |||
@@ -132,9 +132,12 @@ import { | |||
isNil, isEmpty, startsWith, | |||
map, isArray, | |||
} from 'lodash' | |||
import Swal from 'sweetalert2' | |||
import { nucleus } from '~/modules/services' | |||
import { Nudify } from '~/modules/nudify' | |||
const consola = Consola.create('upload') | |||
const { instagram } = $provider | |||
const { dialog } = $provider.api | |||
@@ -152,9 +155,6 @@ export default { | |||
isDragging: false, | |||
}), | |||
created() { | |||
}, | |||
methods: { | |||
/** | |||
@@ -168,6 +168,9 @@ export default { | |||
Nudify.addFile(file.path) | |||
}, | |||
/** | |||
* | |||
*/ | |||
async addFiles(files) { | |||
if (!isArray(files)) { | |||
return | |||
@@ -188,7 +191,7 @@ export default { | |||
const paths = map(files, 'path') | |||
nucleus.track('UPLOAD_FILE') | |||
consola.track('FILE') | |||
this.addFiles(paths) | |||
@@ -211,13 +214,13 @@ export default { | |||
*/ | |||
openUrl() { | |||
if (isEmpty(this.webAddress) || (!startsWith(this.webAddress, 'http://') && !startsWith(this.webAddress, 'https://'))) { | |||
throw new AppError('Please enter a valid web address.', { title: 'Upload failed.', level: 'warning' }) | |||
throw new Warning('Upload failed.', 'Please enter a valid web address.') | |||
} | |||
nucleus.track('UPLOAD_URL') | |||
Nudify.addUrl(this.webAddress) | |||
consola.track('URL') | |||
this.webAddress = '' | |||
}, | |||
@@ -226,7 +229,7 @@ export default { | |||
*/ | |||
async openInstagramPhoto() { | |||
if (isEmpty(this.instagramPhoto)) { | |||
throw new AppError('Please enter a valid Instagram photo.', { title: 'Upload failed.', level: 'warning' }) | |||
throw new Warning('Upload failed.', 'Please enter a valid Instagram photo.') | |||
} | |||
let post | |||
@@ -234,15 +237,17 @@ export default { | |||
try { | |||
post = await instagram.getPost(this.instagramPhoto) | |||
} catch (error) { | |||
throw new AppError('Unable to download the photo, please verify that the address is correct and that you are connected to the Internet.', { title: 'Upload failed.', error, level: 'warning' }) | |||
throw new Warning('Upload failed.', 'Unable to download the photo, please verify that the address is correct and that you are connected to the Internet.', error) | |||
} | |||
if (post.isVideo) { | |||
throw new AppError('The videos are not supported yet.', { title: 'Upload failed.', level: 'warning' }) | |||
throw new Warning('Upload failed.', 'Videos are not supported yet.') | |||
} | |||
Nudify.addUrl(post.downloadUrl) | |||
consola.track('INSTAGRAM') | |||
this.instagramPhoto = '' | |||
}, | |||
@@ -284,12 +289,12 @@ export default { | |||
const url = event.dataTransfer.getData('url') | |||
if (url.length > 0) { | |||
nucleus.track('UPLOAD_DROP_URL') | |||
Nudify.addUrl(url) | |||
consola.track('DROP_URL') | |||
} else if (files.length > 0) { | |||
const paths = map(files, 'path') | |||
this.addFiles(paths) | |||
nucleus.track('UPLOAD_DROP_FILE') | |||
consola.track('DROP_FILE') | |||
} | |||
}, | |||
}, | |||
@@ -332,7 +337,7 @@ export default { | |||
.uploader__dropzone { | |||
@apply flex items-center justify-center; | |||
@apply bg-dark-500 mb-6; | |||
@apply rounded border-2 border-dashed border-dark-100; | |||
@apply border-2 border-dashed border-dark-100; | |||
height: 200px; | |||
transition: all 0.1s linear; | |||
@@ -0,0 +1,91 @@ | |||
<template> | |||
<section class="box box--items"> | |||
<div class="box__content"> | |||
<box-item :description="`Value: ${currentValue.size}`" :label="`${label} size`"> | |||
<VueSlider v-model="currentValue.size" :min="0.3" :max="2" :interval="0.05" /> | |||
</box-item> | |||
<box-item | |||
v-show="!body.randomize && body.progressive.enabled" | |||
label="Progressive." | |||
description="Increase the value progressively in each run"> | |||
<select v-model="currentValue.progressive" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<div v-show="body.randomize"> | |||
<box-item | |||
label="Randomize." | |||
description="Randomize the value in each run."> | |||
<select v-model="currentValue.randomize.enabled" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<box-item | |||
label="Range." | |||
:description="`Min: ${currentValue.randomize.min} - Max: ${currentValue.randomize.max}`"> | |||
<VueSlider | |||
v-model="randomizeRange" | |||
:min-range="minRange" | |||
:max-range="maxRange" | |||
:min="minRange" | |||
:max="maxRange" | |||
:interval="0.05" /> | |||
</box-item> | |||
</div> | |||
</div> | |||
</section> | |||
</template> | |||
<script> | |||
import { VModel } from '~/mixins' | |||
export default { | |||
mixins: [VModel], | |||
props: { | |||
label: { | |||
type: String, | |||
required: true, | |||
}, | |||
minRange: { | |||
type: Number, | |||
default: 0.3, | |||
}, | |||
maxRange: { | |||
type: Number, | |||
default: 2, | |||
}, | |||
}, | |||
computed: { | |||
randomizeRange: { | |||
get() { | |||
return [this.currentValue.randomize.min, this.currentValue.randomize.max] | |||
}, | |||
set(value) { | |||
const [min, max] = value | |||
this.currentValue.randomize.min = min | |||
this.currentValue.randomize.max = max | |||
}, | |||
}, | |||
body() { | |||
return this.$parent.currentValue?.body | |||
}, | |||
}, | |||
} | |||
</script> |
@@ -3,22 +3,22 @@ | |||
<section v-show="currentValue.advanced.transformMode !== 'import-maskfin'" class="box box--items"> | |||
<div class="box__header"> | |||
<h2 class="title"> | |||
Runs | |||
Per run. | |||
</h2> | |||
<h3 class="subtitle"> | |||
Customize what will happen in each processing. | |||
Customize what will happen in each transformation. | |||
</h3> | |||
</div> | |||
<div class="box__content"> | |||
<box-item | |||
label="Number of runs" | |||
description="How many times will the photo be processed?"> | |||
label="Runs." | |||
description="Number of times the photo will be transformed."> | |||
<input v-model="currentValue.body.executions" type="number" min="1" class="input"> | |||
</box-item> | |||
<box-item | |||
label="Randomize" | |||
label="Randomize." | |||
description="Random body preferences will be set at each run."> | |||
<select v-model="currentValue.body.randomize" class="input"> | |||
<option :value="true"> | |||
@@ -32,7 +32,7 @@ | |||
<box-item | |||
v-show="!currentValue.body.randomize" | |||
label="Progressive" | |||
label="Progressive." | |||
:description="`Body preferences will increase their value ${currentValue.body.progressive.rate} at each run.`"> | |||
<select v-model="currentValue.body.progressive.enabled" class="input"> | |||
<option :value="true"> | |||
@@ -46,264 +46,33 @@ | |||
<box-item | |||
v-show="!currentValue.body.randomize" | |||
label="Progressive rate" | |||
:description="`Current value: ${currentValue.body.progressive.rate}`"> | |||
<div class="slider-container"> | |||
<input | |||
v-model="currentValue.body.progressive.rate" | |||
type="range" | |||
class="slider" | |||
min="0.1" | |||
max="0.9" | |||
step="0.1"> | |||
<span class="min">0.1</span> | |||
<span class="max">0.9</span> | |||
</div> | |||
label="Progressive Rate." | |||
:description="`Value: ${currentValue.body.progressive.rate}`"> | |||
<VueSlider v-model="currentValue.body.progressive.rate" :min="0.1" :max="0.9" :interval="0.05" /> | |||
</box-item> | |||
</div> | |||
</section> | |||
<section v-show="currentValue.advanced.transformMode !== 'import-maskfin'" class="box box--items"> | |||
<div class="box__header"> | |||
<h2 class="title"> | |||
Body | |||
</h2> | |||
<h3 class="subtitle"> | |||
Customize the body of your dream. | |||
</h3> | |||
</div> | |||
<div class="box__content"> | |||
<!-- Boobs --> | |||
<box-item :description="`Current value: ${currentValue.body.boobs.size}`" label="Boobs size"> | |||
<div class="slider-container"> | |||
<input | |||
v-model="currentValue.body.boobs.size" | |||
type="range" | |||
class="slider" | |||
in="0.3" | |||
max="2" | |||
step="0.1"> | |||
<span class="min">0.3</span> | |||
<span class="max">2.0</span> | |||
</div> | |||
</box-item> | |||
<box-item | |||
v-show="currentValue.body.randomize" | |||
label="Randomize" | |||
description="Randomize the value in each run." | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.boobs.randomize" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<box-item | |||
v-show="!currentValue.body.randomize && currentValue.body.progressive.enabled" | |||
label="Progressive" | |||
description="Increase the value progressively in each run" | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.boobs.progressive" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<!-- Areola --> | |||
<box-item :description="`Current value: ${currentValue.body.areola.size}`" label="Areola size"> | |||
<div class="slider-container"> | |||
<input | |||
v-model="currentValue.body.areola.size" | |||
type="range" | |||
class="slider" | |||
in="0.3" | |||
max="2" | |||
step="0.1"> | |||
<span class="min">0.3</span> | |||
<span class="max">2.0</span> | |||
</div> | |||
</box-item> | |||
<box-item | |||
v-show="currentValue.body.randomize" | |||
label="Randomize" | |||
description="Randomize the value in each run." | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.areola.randomize" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<box-item | |||
v-show="!currentValue.body.randomize && currentValue.body.progressive.enabled" | |||
label="Progressive" | |||
description="Increase the value progressively in each run" | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.areola.progressive" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<!-- Nipple --> | |||
<box-item :description="`Current value: ${currentValue.body.nipple.size}`" label="Nipple Size"> | |||
<div class="slider-container"> | |||
<input | |||
v-model="currentValue.body.nipple.size" | |||
type="range" | |||
class="slider" | |||
in="0.3" | |||
max="2" | |||
step="0.1"> | |||
<span class="min">0.3</span> | |||
<span class="max">2.0</span> | |||
</div> | |||
</box-item> | |||
<box-item | |||
v-show="currentValue.body.randomize" | |||
label="Randomize" | |||
description="Randomize the value in each run." | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.nipple.randomize" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<!-- Boobs --> | |||
<Preference v-model="currentValue.body.boobs" label="Boobs" /> | |||
<box-item | |||
v-show="!currentValue.body.randomize && currentValue.body.progressive.enabled" | |||
label="Progressive" | |||
description="Increase the value progressively in each run" | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.nipple.progressive" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<!-- Vagina --> | |||
<box-item :description="`Current value: ${currentValue.body.vagina.size}`" label="Vagina Size"> | |||
<div class="slider-container"> | |||
<input | |||
v-model="currentValue.body.vagina.size" | |||
type="range" | |||
class="slider" | |||
in="0.3" | |||
max="1.5" | |||
step="0.1"></input> | |||
<span class="min">0.3</span> | |||
<span class="max">1.5</span> | |||
</div> | |||
</box-item> | |||
<box-item | |||
v-show="currentValue.body.randomize" | |||
label="Randomize" | |||
description="Randomize the value in each run." | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.vagina.randomize" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<box-item | |||
v-show="!currentValue.body.randomize && currentValue.body.progressive.enabled" | |||
label="Progressive" | |||
description="Increase the value progressively in each run" | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.vagina.progressive" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<!-- Areola --> | |||
<Preference v-model="currentValue.body.areola" label="Areola" /> | |||
<box-item :description="`Current value: ${currentValue.body.pubicHair.size}`" label="Pubic Hair"> | |||
<div class="slider-container"> | |||
<input | |||
v-model="currentValue.body.pubicHair.size" | |||
type="range" | |||
class="slider" | |||
in="0" | |||
max="2" | |||
step="0.1"></input> | |||
<span class="min">Disabled</span> | |||
<span class="max">2.0</span> | |||
</div> | |||
</box-item> | |||
<!-- Nipple --> | |||
<Preference v-model="currentValue.body.nipple" label="Nipple" /> | |||
<box-item | |||
v-show="currentValue.body.randomize" | |||
label="Randomize" | |||
description="Randomize the value in each run." | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.pubicHair.randomize" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
<!-- Vagina --> | |||
<Preference v-model="currentValue.body.vagina" label="Vagina" :max-range="1.5" /> | |||
<box-item | |||
v-show="!currentValue.body.randomize && currentValue.body.progressive.enabled" | |||
label="Progressive" | |||
description="Increase the value progressively in each run" | |||
class="box__item--sub"> | |||
<select v-model="currentValue.body.pubicHair.progressive" class="input"> | |||
<option :value="true"> | |||
Enabled | |||
</option> | |||
<option :value="false"> | |||
Disabled | |||
</option> | |||
</select> | |||
</box-item> | |||
</div> | |||
</section> | |||
<!-- Pubic Hair --> | |||
<Preference v-model="currentValue.body.pubicHair" label="Pubic Hair" :min-range="0" /> | |||
<!-- Advanced --> | |||
<section class="box box--items"> | |||
<div class="box__header"> | |||
<h2 class="title"> | |||
Advanced | |||
Advanced. | |||
</h2> | |||
<h3 class="subtitle"> | |||
Additional processing settings. | |||
@@ -312,7 +81,7 @@ | |||
<div class="box__content"> | |||
<box-item | |||
label="Scale method" | |||
label="Scale method." | |||
description="Method to scale the photo to 512x512"> | |||
<select v-model="currentValue.advanced.scaleMode" class="input"> | |||
<option value="none"> | |||
@@ -337,7 +106,7 @@ | |||
</box-item> | |||
<box-item | |||
label="Transform method" | |||
label="Transform method." | |||
description="Transformation method, only recommended for advanced users."> | |||
<select v-model="currentValue.advanced.transformMode" class="input"> | |||
<option value="normal"> | |||
@@ -353,7 +122,7 @@ | |||
</box-item> | |||
<box-item | |||
label="Color transfer" | |||
label="Color transfer." | |||
description="Use a experimental color transfer algorithm to try to recover the original colors of the photo."> | |||
<select v-model="currentValue.advanced.useColorTransfer" class="input"> | |||
<option :value="true"> |
@@ -11,5 +11,7 @@ | |||
import Vue from 'vue' | |||
import SettingsPreferences from './SettingsPreferences' | |||
import Preference from './Preference' | |||
Vue.component('settings-preferences', SettingsPreferences) | |||
Vue.component('Preference', Preference) |
@@ -17,69 +17,19 @@ | |||
v-else-if="!updater.update.active" | |||
:label="`${projectTitle} ${updater.latest.tag_name} available.`" | |||
icon="fire-alt" | |||
class="update-item"> | |||
<button v-tooltip="'Download and install the update automatically.'" type="button" class="button is-sm" @click.prevent="updater.start()"> | |||
Update | |||
</button> | |||
<a v-tooltip="'Download the update manually.'" :href="downloadURL" target="_blank" class="button is-sm"> | |||
Manual | |||
</a> | |||
</box-item> | |||
<!-- update... --> | |||
<!-- eslint-disable-next-line vue/valid-template-root ---> | |||
<box-item | |||
v-else | |||
:label="updater.update.status" | |||
icon="globe"> | |||
<template slot="description"> | |||
<p v-if="updater.update.status === 'Downloading...'" class="item__description"> | |||
<strong>{{ updater.update.progress | progress }}</strong> - {{ updater.update.written | size }}/{{ updater.update.total | size }} MB. | |||
</p> | |||
<p v-else class="item__description"> | |||
Wait a few minutes, please do not close the program. | |||
</p> | |||
</template> | |||
<button type="button" class="button is-danger is-sm" @click.prevent="updater.cancel()"> | |||
Cancel | |||
</button> | |||
</box-item> | |||
class="update-item" | |||
:is-link="true" | |||
@click="next" /> | |||
</template> | |||
<script> | |||
import { isString, toNumber } from 'lodash' | |||
/* eslint import/namespace: ['error', { allowComputed: true }] */ | |||
import * as updateProviders from '~/modules/updater' | |||
import * as providers from '~/modules/updater' | |||
const { shell } = $provider.api | |||
const { getPath } = $provider.paths | |||
export default { | |||
filters: { | |||
progress(value) { | |||
if (isString(value)) { | |||
// eslint-disable-next-line no-param-reassign | |||
value = toNumber(value) | |||
} | |||
const progress = (value * 100).toFixed(2) | |||
return `${progress}%` | |||
}, | |||
size(value) { | |||
if (isString(value)) { | |||
// eslint-disable-next-line no-param-reassign | |||
value = toNumber(value) | |||
} | |||
return value.toFixed(2) | |||
}, | |||
}, | |||
props: { | |||
project: { | |||
type: String, | |||
@@ -90,31 +40,31 @@ export default { | |||
type: String, | |||
default: 'Project', | |||
}, | |||
href: { | |||
type: String, | |||
required: true, | |||
}, | |||
}, | |||
data: () => ({ | |||
currentVersion: 'v0.0.0', | |||
updater: {}, | |||
}), | |||
computed: { | |||
downloadURL() { | |||
return this.updater.downloadUrls[0] | |||
currentVersion() { | |||
return this.updater?.currentVersion || 'v0.0.0' | |||
}, | |||
}, | |||
created() { | |||
this.currentVersion = this.updater.currentVersion | |||
this.updater = updateProviders[this.project] | |||
}, | |||
beforeDestroy() { | |||
this.updater.cancel() | |||
this.updater = providers[this.project] | |||
}, | |||
methods: { | |||
openDownload() { | |||
shell.openItem(getPath('downloads')) | |||
next() { | |||
console.log(this.href) | |||
this.$router.push(this.href) | |||
}, | |||
}, | |||
} |
@@ -36,22 +36,27 @@ export default { | |||
type: [String, Array], | |||
default: undefined, | |||
}, | |||
label: { | |||
type: String, | |||
required: true, | |||
}, | |||
description: { | |||
type: String, | |||
default: undefined, | |||
}, | |||
version: { | |||
type: String, | |||
default: undefined, | |||
}, | |||
href: { | |||
type: String, | |||
default: undefined, | |||
}, | |||
isLink: { | |||
type: Boolean, | |||
default: false, | |||
@@ -69,7 +74,7 @@ export default { | |||
cssClass() { | |||
return { | |||
'is-link': !isNil(this.href) || this.isLink, | |||
'box__item--link': !isNil(this.href) || this.isLink, | |||
} | |||
}, | |||
}, | |||
@@ -125,16 +130,12 @@ export default { | |||
@apply pl-8; | |||
} | |||
&:hover { | |||
@apply bg-dark-400 text-white; | |||
} | |||
&.is-link { | |||
&.box__item--link { | |||
@apply cursor-pointer; | |||
transition: all .1s ease-in-out; | |||
&:hover { | |||
@apply bg-dark-600; | |||
@apply bg-dark-600 text-white; | |||
} | |||
} | |||
@@ -163,7 +164,7 @@ export default { | |||
} | |||
.item__action { | |||
@apply w-1/3 ml-4 flex flex-col justify-center; | |||
@apply w-1/3 ml-4 flex items-center justify-center; | |||
} | |||
} | |||
} |
@@ -0,0 +1,157 @@ | |||
<template> | |||
<div class="project-update"> | |||
<!-- Update available --> | |||
<div v-if="!updater.update.active" class="update__status"> | |||
{{ updater.latest.tag_name }} available: | |||
</div> | |||
<!-- Downloading --> | |||
<div v-else-if="isDownloading" class="update__status"> | |||
Downloading ~ <strong>{{ updater.update.progress | progress }}</strong> ~ {{ updater.update.written | size }}/{{ updater.update.total | size }} MB. | |||
</div> | |||
<!-- Installing --> | |||
<div v-else-if="isInstalling" class="update__status"> | |||
Installing... | |||
</div> | |||
<!-- Download Progress --> | |||
<div v-show="isDownloading" class="update__progressbar"> | |||
<progress min="0" max="100" :value="updater.update.progress" /> | |||
</div> | |||
<!-- Actions --> | |||
<div class="update__actions"> | |||
<button v-show="!updater.update.active" class="button button--success" @click.prevent="updater.start()"> | |||
Download | |||
</button> | |||
<button v-show="updater.update.active" class="button button--danger" @click.prevent="updater.cancel()"> | |||
Cancel | |||
</button> | |||
<button class="button button--info" @click.prevent="$refs.mirrorsDialog.show()"> | |||
Mirrors | |||
</button> | |||
</div> | |||
<!-- Mirrors Dialog --> | |||
<dialog ref="mirrorsDialog"> | |||
<div class="dialog__content"> | |||
<ul class="mirrors"> | |||
<li v-for="(item, index) in updater.downloadUrls" :key="index"> | |||
<a :href="item" target="_blank">{{ item | domain }}</a> | |||
</li> | |||
</ul> | |||
<div class="dialog__buttons"> | |||
<button class="button button--danger" @click.prevent="$refs.mirrorsDialog.close()"> | |||
Close | |||
</button> | |||
</div> | |||
</div> | |||
</dialog> | |||
</div> | |||
</template> | |||
<script> | |||
import { isString, toNumber } from 'lodash' | |||
import * as providers from '~/modules/updater' | |||
export default { | |||
filters: { | |||
progress(value) { | |||
if (isString(value)) { | |||
value = toNumber(value) | |||
} | |||
return `${value}%` | |||
}, | |||
size(value) { | |||
if (isString(value)) { | |||
value = toNumber(value) | |||
} | |||
return value.toFixed(2) | |||
}, | |||
domain(value) { | |||
return (new URL(value)).hostname | |||
}, | |||
}, | |||
props: { | |||
project: { | |||
type: String, | |||
required: true, | |||
}, | |||
}, | |||
data: () => ({ | |||
updater: null, | |||
}), | |||
computed: { | |||
currentVersion() { | |||
return this.updater?.currentVersion || 'v0.0.0' | |||
}, | |||
isDownloading() { | |||
return this.updater?.update?.status === 'downloading' | |||
}, | |||
isInstalling() { | |||
return this.updater?.update?.status === 'installing' | |||
}, | |||
}, | |||
created() { | |||
// eslint-disable-next-line import/namespace | |||
this.updater = providers[this.project] | |||
}, | |||
beforeDestroy() { | |||
this.updater.cancel() | |||
}, | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.project-update { | |||
@apply flex flex-col items-center justify-center; | |||
} | |||
.update__status { | |||
@apply mb-6 text-2xl text-white; | |||
} | |||
.update__progressbar { | |||
@apply mb-6; | |||
width: 80%; | |||
progress { | |||
@apply w-full border-0 bg-dark-600; | |||
height: 18px; | |||
border-radius: 9px; | |||
&::-webkit-progress-bar { | |||
@apply bg-dark-800; | |||
border-radius: 9px; | |||
} | |||
&::-webkit-progress-value { | |||
@apply bg-primary-500; | |||
border-radius: 9px; | |||
} | |||
} | |||
} | |||
.mirrors { | |||
@apply list-disc ml-6; | |||
a:hover { | |||
@apply underline; | |||
} | |||
} | |||
</style> |
@@ -13,9 +13,11 @@ import Update from './AppUpdate' | |||
import BoxItem from './BoxItem' | |||
import AppPhoto from './AppPhoto' | |||
import AppNews from './AppNews' | |||
import ProjectUpdate from './ProjectUpdate' | |||
Vue.component('app-title', Title) | |||
Vue.component('app-update', Update) | |||
Vue.component('app-photo', AppPhoto) | |||
Vue.component('app-news', AppNews) | |||
Vue.component('box-item', BoxItem) | |||
Vue.component('ProjectUpdate', ProjectUpdate) |
@@ -18,6 +18,12 @@ import { getPath } from './modules/tools/paths' | |||
import { settings, ngrok } from './modules' | |||
import config from '~/nuxt.config' | |||
// logger setup | |||
Logger.setOptions({ | |||
filename: getPath('userData', 'dreamtime.main.log'), | |||
logLevel: process.env.LOG || 'debug', | |||
}) | |||
const logger = Logger.create('electron') | |||
// NuxtJS root directory | |||
@@ -38,12 +44,6 @@ class DreamApp { | |||
* | |||
*/ | |||
static async boot() { | |||
// logger setup | |||
Logger.setOptions({ | |||
filename: getPath('userData', 'dreamtime.main.log'), | |||
logLevel: process.env.LOG || 'debug', | |||
}) | |||
logger.info('Booting...') | |||
logger.debug(`Enviroment: ${process.env.name}`) | |||
@@ -76,6 +76,7 @@ class DreamApp { | |||
// this may increase performance on some systems. | |||
if (settings.app?.disableHardwareAcceleration) { | |||
logger.debug('Hardware Acceleration disabled.') | |||
app.disableHardwareAcceleration() | |||
} | |||
} | |||
@@ -104,12 +105,13 @@ class DreamApp { | |||
// application exit. | |||
app.on('will-quit', async (event) => { | |||
logger.debug('Exiting...') | |||
logger.debug('Received exit event.') | |||
event.preventDefault() | |||
await this.shutdown() | |||
logger.debug('Bye!') | |||
app.exit() | |||
}) | |||
@@ -134,7 +136,7 @@ class DreamApp { | |||
event.preventDefault() | |||
logger.warn('Blocked attempt to load an external page.', { | |||
logger.warn('Illegal page load blocked!', { | |||
event, | |||
url, | |||
}) | |||
@@ -181,6 +183,8 @@ class DreamApp { | |||
*/ | |||
// eslint-disable-next-line no-empty-function | |||
static async shutdown() { | |||
logger.debug('Shutting down services...') | |||
if (process.env.name === 'development') { | |||
await ngrok.disconnect() | |||
} | |||
@@ -233,7 +237,7 @@ class DreamApp { | |||
* Wait until the NuxtJS server is ready. | |||
*/ | |||
static pollUi() { | |||
logger.debug(`Requesting status from the server: ${this.uiUrl}`) | |||
logger.debug(`Requesting server (${this.uiUrl})...`) | |||
const http = require('http') | |||
@@ -243,12 +247,12 @@ class DreamApp { | |||
logger.debug('Server ready, dream time!') | |||
this.window.loadURL(this.uiUrl) | |||
} else { | |||
logger.warn(`The server reported: ${response.statusCode}`) | |||
logger.warn(`Server reported: ${response.statusCode}`) | |||
setTimeout(this.pollUi.bind(this), 300) | |||
} | |||
}) | |||
.on('error', (error) => { | |||
logger.warn('Poll error', error) | |||
logger.warn('Server error', error) | |||
setTimeout(this.pollUi.bind(this), 300) | |||
}) | |||
} |
@@ -14,7 +14,7 @@ import { | |||
import { AppError } from './app-error' | |||
import { paths, system } from './tools' | |||
const logger = require('logplease').create('electron:scripts:services:settings') | |||
const logger = require('logplease').create('settings') | |||
/** | |||
* User settings. | |||
@@ -64,6 +64,7 @@ class Settings { | |||
*/ | |||
async save() { | |||
fs.writeJsonSync(this.path, this.payload, { spaces: 2 }) | |||
logger.debug('Settings saved!') | |||
} | |||
/** | |||
@@ -91,14 +92,6 @@ class Settings { | |||
this.save() | |||
} | |||
/** | |||
* Setup service | |||
*/ | |||
async setup() { | |||
await this._create() | |||
await this._upgrade() | |||
} | |||
/** | |||
* | |||
*/ | |||
@@ -107,14 +100,21 @@ class Settings { | |||
await this.load() | |||
} | |||
/** | |||
* Setup service | |||
*/ | |||
async setup() { | |||
await this._create() | |||
await this._upgrade() | |||
} | |||
/** | |||
* Load the default configuration (and the current version) | |||
*/ | |||
_loadDefault() { | |||
const hasGPU = system.graphics.length > 0 | |||
const cores = round(system.cores / 2) || 1 | |||
const uuid = require('uuid') | |||
const hasGPU = system.graphics.length > 0 | |||
const cores = round(system.cpu?.cores / 2) || 1 | |||
this.payload = { | |||
version: 6, | |||
@@ -148,7 +148,6 @@ class Settings { | |||
telemetry: { | |||
bugs: true, | |||
dom: true, | |||
domPrivate: false, | |||
}, | |||
processing: { | |||
@@ -169,28 +168,48 @@ class Settings { | |||
}, | |||
boobs: { | |||
size: '1', | |||
randomize: true, | |||
size: 1, | |||
randomize: { | |||
enabled: true, | |||
min: 0.3, | |||
max: 2, | |||
}, | |||
progressive: true, | |||
}, | |||
areola: { | |||
size: '1', | |||
randomize: false, | |||
size: 1, | |||
randomize: { | |||
enabled: true, | |||
min: 0.3, | |||
max: 2, | |||
}, | |||
progressive: true, | |||
}, | |||
nipple: { | |||
size: '1', | |||
randomize: false, | |||
size: 1, | |||
randomize: { | |||
enabled: true, | |||
min: 0.3, | |||
max: 2, | |||
}, | |||
progressive: true, | |||
}, | |||
vagina: { | |||
size: '0.75', | |||
randomize: true, | |||
size: 0.75, | |||
randomize: { | |||
enabled: true, | |||
min: 0.3, | |||
max: 1.5, | |||
}, | |||
progressive: true, | |||
}, | |||
pubicHair: { | |||
size: '1', | |||
randomize: true, | |||
size: 1, | |||
randomize: { | |||
enabled: true, | |||
min: 0, | |||
max: 2, | |||
}, | |||
progressive: true, | |||
}, | |||
}, | |||
@@ -215,6 +234,9 @@ class Settings { | |||
return | |||
} | |||
// default settings now with the system information. | |||
this._loadDefault() | |||
try { | |||
fs.outputFileSync(this.path, JSON.stringify(this._default, null, 2)) | |||
} catch (error) { | |||
@@ -323,7 +345,38 @@ class Settings { | |||
this.payload.telemetry = { | |||
bugs: this.payload.enabled, | |||
dom: true, | |||
domPrivate: false, | |||
} | |||
const { body } = this.payload.preferences | |||
body.boobs.randomize = { | |||
enabled: true, | |||
min: 0.3, | |||
max: 2, | |||
} | |||
body.areola.randomize = { | |||
enabled: true, | |||
min: 0.3, | |||
max: 2, | |||
} | |||
body.nipple.randomize = { | |||
enabled: true, | |||
min: 0.3, | |||
max: 2, | |||
} | |||
body.vagina.randomize = { | |||
enabled: true, | |||
min: 0.3, | |||
max: 1.5, | |||
} | |||
body.pubicHair.randomize = { | |||
enabled: true, | |||
min: 0, | |||
max: 2, | |||
} | |||
} | |||
@@ -331,12 +384,47 @@ class Settings { | |||
} | |||
} | |||
export const settingsRaw = new Settings | |||
const saveHandler = { | |||
get(target, property, receiver) { | |||
try { | |||
return new Proxy(target[property], saveHandler) | |||
} catch (err) { | |||
return Reflect.get(target, property, receiver) | |||
} | |||
}, | |||
defineProperty(target, property, descriptor) { | |||
settingsRaw.save() | |||
return Reflect.defineProperty(target, property, descriptor) | |||
}, | |||
} | |||
const settingsHandler = { | |||
get(target, property, receiver) { | |||
try { | |||
if (property in target) { | |||
return target[property] | |||
} | |||
if (property in target.payload) { | |||
return new Proxy(target.payload[property], saveHandler) | |||
} | |||
// eslint-disable-next-line no-empty | |||
} catch (err) { } | |||
return Reflect.get(target, property, receiver) | |||
}, | |||
} | |||
/** | |||
* Create a new instance with a Proxy. | |||
* | |||
* @return {Proxy} | |||
*/ | |||
export function make(obj) { | |||
return new Proxy(obj, settingsHandler) | |||
/* | |||
return new Proxy(obj, { | |||
get: (obj, prop) => { | |||
if (prop in obj) { | |||
@@ -351,6 +439,11 @@ export function make(obj) { | |||
}, | |||
set: (obj, prop, value) => { | |||
console.log({ | |||
prop, | |||
value, | |||
}) | |||
if (!isNil(obj.payload)) { | |||
if (prop in obj.payload) { | |||
obj.payload[prop] = value | |||
@@ -364,8 +457,7 @@ export function make(obj) { | |||
return true | |||
}, | |||
}) | |||
*/ | |||
} | |||
export const settingsRaw = new Settings | |||
export const settings = make(settingsRaw) |
@@ -1,9 +1,6 @@ | |||
import { attempt } from 'lodash' | |||
import { basename, join } from 'path' | |||
import { | |||
readFileSync, writeFileSync, | |||
existsSync, | |||
createWriteStream, createReadStream, | |||
} from 'fs-extra' | |||
import fs from 'fs-extra' | |||
import { app, dialog } from 'electron' | |||
import axios from 'axios' | |||
import deferred from 'deferred' | |||
@@ -35,7 +32,7 @@ export function getBase64Data(dataURL) { | |||
* @param {string} encoding | |||
*/ | |||
export function read(path, encoding = 'utf-8') { | |||
return readFileSync(path, { encoding }) | |||
return fs.readFileSync(path, { encoding }) | |||
} | |||
/** | |||
@@ -45,7 +42,7 @@ export function read(path, encoding = 'utf-8') { | |||
*/ | |||
export function writeDataURL(path, dataURL) { | |||
const data = this.getBase64Data(dataURL) | |||
return writeFileSync(path, data, 'base64') | |||
return fs.writeFileSync(path, data, 'base64') | |||
} | |||
/** | |||
@@ -58,7 +55,7 @@ export function extractZip(path, destinationPath) { | |||
const def = deferred() | |||
const stream = createReadStream(path).pipe(unzipper.Extract({ path: destinationPath })) | |||
const stream = fs.createReadStream(path).pipe(unzipper.Extract({ path: destinationPath })) | |||
stream.on('close', () => { | |||
def.resolve() | |||
@@ -140,7 +137,7 @@ export function download(url, options = {}) { | |||
}) | |||
} | |||
const writeStream = createWriteStream(filepath) | |||
const writeStream = fs.createWriteStream(filepath) | |||
axios.request({ | |||
url, | |||
@@ -173,7 +170,7 @@ export function download(url, options = {}) { | |||
return | |||
} | |||
if (!existsSync(filepath)) { | |||
if (!fs.existsSync(filepath)) { | |||
throw new AppError('The file was not saved correctly.', { title: 'Download failed.' }) | |||
} | |||
@@ -185,18 +182,24 @@ export function download(url, options = {}) { | |||
bus.on('cancel', () => { | |||
cancelled = true | |||
writeStream.destroy() | |||
data.destroy() | |||
attempt(() => { | |||
writeStream.destroy() | |||
data.destroy() | |||
fs.unlinkSync(filepath) | |||
}) | |||
logger.info('Download canceled by user.') | |||
logger.info('Download cancelled by user.') | |||
bus.emit('cancelled') | |||
}) | |||
return true | |||
}).catch((err) => { | |||
writeStream.destroy(err) | |||
attempt(() => { | |||
writeStream.destroy(err) | |||
fs.unlinkSync(filepath) | |||
}) | |||
logger.warn('Download canceled due to an error.', err) | |||
logger.warn('Download cancelled due to an error.', err) | |||
bus.emit('error', null, err) | |||
}) | |||
@@ -16,7 +16,7 @@ import * as fs from 'fs-extra' | |||
import { getPowerPath } from './paths' | |||
import { settings } from '../settings' | |||
const logger = require('logplease').create('electron:power') | |||
const logger = require('logplease').create('power') | |||
export function exec(args, options = {}) { | |||
args.push('--debug') |
@@ -36,6 +36,19 @@ class System { | |||
*/ | |||
memory | |||
/** | |||
* @type {Object} | |||
*/ | |||
snapshot = { | |||
load: null, | |||
cpu: { | |||
speed: null, | |||
temperature: null, | |||
}, | |||
memory: null, | |||
online: false, | |||
} | |||
/** | |||
* @type {boolean} | |||
*/ | |||
@@ -45,11 +58,13 @@ class System { | |||
* | |||
*/ | |||
async setup() { | |||
logger.debug('Collecting system information...') | |||
const [ | |||
graphics, | |||
os, | |||
cpu, | |||
mem, | |||
memory, | |||
online, | |||
] = await Promise.all([ | |||
si.graphics(), | |||
@@ -62,13 +77,43 @@ class System { | |||
this._graphics = graphics | |||
this.os = os | |||
this.cpu = cpu | |||
this.memory = mem | |||
this.memory = memory | |||
this.online = online | |||
logger.info(`GPU devices: ${this.graphics.length}`) | |||
logger.info(`RAM: ${this.memory.total} bytes.`) | |||
logger.info(`Internet connection: ${this.online}`) | |||
logger.debug(this) | |||
logger.info(`GPU:`, this.graphics) | |||
logger.info(`RAM: ${memory.total} bytes.`) | |||
logger.info(`Online: ${online}`) | |||
} | |||
/** | |||
* | |||
*/ | |||
async takeSnapshot() { | |||
logger.info('Taking snapshot...') | |||
const [load, cpuSpeed, cpuTemperature, memory, online] = await Promise.all([ | |||
si.currentLoad(), | |||
si.cpuCurrentspeed(), | |||
si.cpuTemperature(), | |||
si.mem(), | |||
isOnline(), | |||
]) | |||
this.snapshot = { | |||
load, | |||
cpu: { | |||
speed: cpuSpeed, | |||
temperature: cpuTemperature, | |||
}, | |||
memory, | |||
online, | |||
} | |||
logger.info(`Current load:`, load) | |||
logger.info(`CPU Speed:`, cpuSpeed) | |||
logger.info(`CPU Temperature:`, cpuTemperature) | |||
logger.info(`Memory:`, memory) | |||
logger.info(`Online: ${online}`) | |||
} | |||
/** |
@@ -9,8 +9,6 @@ | |||
</template> | |||
<script> | |||
export default { | |||
} | |||
@@ -31,7 +29,7 @@ export default { | |||
.layout__content { | |||
@apply relative overflow-hidden overflow-y-auto; | |||
@apply p-6; | |||
@apply p-6 border-t border-dark-500; | |||
grid-area: content; | |||
height: calc(100vh - 30px); | |||
} | |||
@@ -40,12 +38,16 @@ export default { | |||
<style lang="scss"> | |||
.layout__header { | |||
@apply flex justify-center items-center; | |||
@apply text-3xl font-semibold mb-6; | |||
height: 60px; | |||
@apply flex flex-col justify-center items-center; | |||
@apply text-3xl font-semibold mb-8; | |||
height: 80px; | |||
h1 { | |||
.title { | |||
@apply text-xl text-white; | |||
} | |||
.subtitle { | |||
@apply text-lg; | |||
} | |||
} | |||
</style> |
@@ -50,10 +50,9 @@ export default function ({ route, redirect }) { | |||
if (route.path !== '/wizard/telemetry') { | |||
redirect('/wizard/telemetry') | |||
} | |||
return | |||
} | |||
/* | |||
if (!wizard.user) { | |||
if (route.path !== '/wizard/user') { | |||
redirect('/wizard/user') | |||
@@ -61,10 +60,5 @@ export default function ({ route, redirect }) { | |||
return | |||
} | |||
if (!wizard.telemetry) { | |||
if (route.path !== '/wizard/telemetry') { | |||
redirect('/wizard/telemetry') | |||
} | |||
} | |||
*/ | |||
} |
@@ -1,4 +1,3 @@ | |||
export { AppError } from './app-error' | |||
export { events } from './events' | |||
export { File } from './file' | |||
export { Timer } from './timer' |
@@ -21,7 +21,7 @@ import { Photo } from './photo' | |||
import { File } from '../file' | |||
import { getFilesMetadata } from '~/workers/fs' | |||
const logger = require('logplease').create('nudify') | |||
const consola = Consola.create('nudify') | |||
const { settings } = $provider | |||
@@ -139,7 +139,7 @@ export class Nudify { | |||
this.photos.unshift(photo) | |||
logger.debug('Photo added!', photo.file.fullname) | |||
consola.debug(`Photo ${photo.file.fullname} added!`) | |||
this.emitUpdate() | |||
@@ -176,7 +176,7 @@ export class Nudify { | |||
this.add(file) | |||
} catch (err) { | |||
if (multiple) { | |||
logger.warn('Error adding a photo, skipped.', err) | |||
consola.warn('Error adding a photo.', err) | |||
} else { | |||
throw err | |||
} | |||
@@ -216,7 +216,7 @@ export class Nudify { | |||
*/ | |||
static async addUrl(url) { | |||
if (!startsWith(url, 'http://') && !startsWith(url, 'https://')) { | |||
throw new AppError('Please enter a valid web address.', { title: 'Upload failed.', level: 'warning' }) | |||
throw new Warning('Upload failed.', 'Please enter a valid web address.') | |||
} | |||
Swal.fire({ | |||
@@ -234,7 +234,7 @@ export class Nudify { | |||
this.add(file) | |||
} catch (error) { | |||
throw new AppError('There was a problem trying to download the file. Make sure you have an Internet connection.', { title: 'Upload failed.', level: 'warning', error }) | |||
throw new Warning('Upload failed.', 'Unable to download the photo, please verify that the address is correct and that you are connected to the Internet.', error) | |||
} | |||
} | |||
@@ -317,7 +317,7 @@ export class Nudify { | |||
return | |||
} | |||
logger.debug(`Forgetting ${photo.file.fullname}...`) | |||
consola.debug(`Forgetting ${photo.file.fullname}...`) | |||
this.forget(photo) | |||
}) |
@@ -152,9 +152,15 @@ export class PhotoRun { | |||
start() { | |||
const def = deferred() | |||
const { consola } = this.photo | |||
const onSpawnError = (error) => { | |||
def.reject(new AppError('There was a problem trying to start DreamPower, make sure you have everything set up correctly.', { title: 'DreamPower failed!', error, level: 'warn' })) | |||
consola | |||
.withError(error) | |||
.warn('There was a problem trying to start DreamPower, make sure you have everything set up correctly.') | |||
.show('DreamPower failed!') | |||
def.reject() | |||
} | |||
this.beforeStart() | |||
@@ -199,14 +205,14 @@ export class PhotoRun { | |||
this.maskfinFile.open(), | |||
]) | |||
nucleus.track('DREAM_COMPLETED') | |||
window.consola.track('DREAM_COMPLETED') | |||
def.resolve() | |||
}) | |||
this.process.on('fail', (fileError) => { | |||
if (fileError) { | |||
def.reject(new AppError('DreamPower has transformed the photo but could not save it.', { title: `Run ${this.id} failed!`, level: 'warn' })) | |||
def.reject(new Warning(`Run ${this.id} failed!`, 'DreamPower has transformed the photo but could not save it.', fileError)) | |||
} else { | |||
def.reject(this.getPowerError()) | |||
} | |||
@@ -227,14 +233,16 @@ export class PhotoRun { | |||
const preferences = this.preferences.body | |||
if (preferences.randomize) { | |||
// randomize | |||
// randomize. | |||
forIn(preferencesConfig, (payload, key) => { | |||
if (preferences[key].randomize) { | |||
preferences[key].size = rand(payload.min, payload.max) | |||
const { enabled, min, max } = preferences[key].randomize | |||
if (enabled) { | |||
preferences[key].size = rand(min, max) | |||
} | |||
}) | |||
} else if (preferences.progressive.enabled) { | |||
// progressive | |||
// progressive. | |||
const add = preferences.progressive.rate * (this.id - 1) | |||
forIn(preferencesConfig, (payload, key) => { | |||
@@ -281,21 +289,22 @@ export class PhotoRun { | |||
return null | |||
} | |||
const { consola } = this.photo | |||
const title = `Run ${this.id} has failed.` | |||
const options = { | |||
error: new Error(message), | |||
terminal: this.cli.lines, | |||
} | |||
for (const payload of cliErrors) { | |||
if (message.includes(payload.error)) { | |||
return new AppError(payload.message, { | |||
title: `Run ${this.id} has failed.`, | |||
level: payload.level, | |||
error: new Error(message), | |||
}) | |||
return new LogError(consola, title, payload.message, payload.level, options) | |||
} | |||
} | |||
return new AppError(`DreamPower has been interrupted by an unknown error, this may be caused by a corrupt installation, please check the console for more information.\n<pre>${message}</pre>`, { | |||
title: `Run ${this.id} has failed.`, | |||
error: new Error(message), | |||
terminal: this.cli.lines, | |||
}) | |||
return new Exception(consola, title, 'DreamPower has been interrupted by an unknown error.', options) | |||
} | |||
_sendNotification() { |
@@ -14,6 +14,7 @@ import Queue from 'better-queue' | |||
import MemoryStore from 'better-queue-memory' | |||
import Logger from 'logplease' | |||
import EventBus from 'js-event-bus' | |||
import { Consola } from '../system' | |||
import { Nudify } from './nudify' | |||
import { PhotoRun } from './photo-run' | |||
import { File } from '../file' | |||
@@ -113,6 +114,11 @@ export class Photo { | |||
*/ | |||
_logger | |||
/** | |||
* @type {Consola} | |||
*/ | |||
consola | |||
get folderName() { | |||
// todo: implement models | |||
return 'Uncategorized' | |||
@@ -202,7 +208,9 @@ export class Photo { | |||
this.fileCrop = new File(getCropPath(`${this.id}-crop${file.extension}`), 'crop') | |||
this._logger = Logger.create(`nudify:photo:${file.fullname}`) | |||
this._logger = Logger.create(file.fullname) | |||
this.consola = Consola.create(file.fullname) | |||
this._setupPreferences(isMaskfin) | |||
@@ -287,16 +295,16 @@ export class Photo { | |||
const { exists, mimetype, path } = this.file | |||
if (!exists) { | |||
throw new AppError(`The file "${path}" does not exists.`, { title: 'Upload failed.', level: 'warn' }) | |||
throw new Warning('Upload failed.', `The file "${path}" does not exists.`) | |||
} | |||
if (mimetype !== 'image/jpeg' && mimetype !== 'image/png' && mimetype !== 'image/gif') { | |||
throw new AppError(`The file "${path}" is not a valid photo. Only jpeg, png or gif.`, { title: 'Upload failed.', level: 'warn' }) | |||
throw new Warning('Upload failed.', `The file "${path}" is not a valid photo. Only jpeg, png or gif.`) | |||
} | |||
} | |||
_setupQueue() { | |||
let maxTimeout = settings.processing.device === 'GPU' ? (3 * 60 * 1000) : (10 * 60 * 1000) | |||
let maxTimeout = settings.processing.device === 'GPU' ? (3 * 60 * 1000) : (20 * 60 * 1000) | |||
if (this.file.mimetype === 'image/gif') { | |||
maxTimeout += (30 * 60 * 1000) | |||
@@ -304,8 +312,6 @@ export class Photo { | |||
this.queue = new Queue(this._run, { | |||
maxTimeout, | |||
// maxRetries: 2, | |||
// retryDelay: 1000, | |||
afterProcessDelay: 500, | |||
batchSize: 1, | |||
concurrent: 1, | |||
@@ -335,9 +341,11 @@ export class Photo { | |||
this._logger.warn(`Run #${runId} failed!`, error) | |||
run.onFail() | |||
/* | |||
if (error !== 'cancelled') { | |||
AppError.handle(error) | |||
} | |||
*/ | |||
}) | |||
} | |||
@@ -35,7 +35,7 @@ class LogRocketService extends BaseService { | |||
* @type {boolean} | |||
*/ | |||
get can() { | |||
return isString(this.accessToken) | |||
return isString(this.accessToken) && process.env.name === 'production' | |||
} | |||
/** |
@@ -15,8 +15,6 @@ const { system, settings } = $provider | |||
const logger = require('logplease').create('services:nucleus') | |||
console.log(settings) | |||
/** | |||
* https://nucleus.sh | |||
* Analytics and bug tracking for Javascript desktop apps. |
@@ -36,7 +36,7 @@ class RollbarService extends BaseService { | |||
* @type {boolean} | |||
*/ | |||
get can() { | |||
return isString(this.accessToken) | |||
return isString(this.accessToken) && process.env.name === 'production' | |||
} | |||
/** |
@@ -0,0 +1,175 @@ | |||
// DreamTime. | |||
// Copyright (C) DreamNet. All rights reserved. | |||
// | |||
// This program is free software: you can redistribute it and/or modify | |||
// it under the terms of the GNU General Public License 3.0 as published by | |||
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html> | |||
// | |||
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019. | |||
import { | |||
isError, isString, isNil, isPlainObject, | |||
} from 'lodash' | |||
import Logger from 'logplease' | |||
import { Log } from './log' | |||
import { nucleus, logrocket } from '../services' | |||
export class Consola { | |||
/** | |||
* @type {string} | |||
*/ | |||
category | |||
/** | |||
* @type {Logger.Logger} | |||
*/ | |||
logger | |||
/** | |||
* | |||
* @param {string} [category] | |||
*/ | |||
static create(category) { | |||
return new this(category) | |||
} | |||
/** | |||
* | |||
* @param {Array} args | |||
*/ | |||
static parseArgs(args) { | |||
let title | |||
let message | |||
let error | |||
let options = {} | |||
args.forEach((value) => { | |||
if (isError(value)) { | |||
error = value | |||
} else if (isPlainObject(value)) { | |||
options = value | |||
} else if (isString(value) && isNil(message)) { | |||
message = value | |||
} else if (isString(value) && isNil(title)) { | |||
title = message | |||
message = value | |||
} | |||
}) | |||
if (isNil(message) &a |