Add profile import and export

This commit is contained in:
Kevin Thomas
2022-04-06 17:06:28 -07:00
parent 6be2169cb3
commit 2569227beb
3 changed files with 310 additions and 0 deletions

View File

@@ -77,6 +77,66 @@ router.post('/profiles', (req, res) => {
})
})
router.post('/profiles/import', (req, res) => {
if (!req.user) {
return res.sendStatus(401)
}
let profileID = 0
db.serialize(() => {
db.run(`INSERT INTO profiles (
name,
user,
timer_enabled,
duration,
volume,
noise_color,
filter_enabled,
filter_type,
filter_cutoff,
lfo_filter_cutoff_enabled,
lfo_filter_cutoff_frequency,
lfo_filter_cutoff_low,
lfo_filter_cutoff_high,
tremolo_enabled,
tremolo_frequency,
tremolo_depth)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
req.body.name,
req.user.id,
req.body.isTimerEnabled ? 1 : 0,
req.body.duration,
req.body.volume,
req.body.noiseColor,
req.body.isFilterEnabled ? 1 : 0,
req.body.filterType,
req.body.filterCutoff,
req.body.isLFOFilterCutoffEnabled ? 1 : 0,
req.body.lfoFilterCutoffFrequency,
req.body.lfoFilterCutoffLow,
req.body.lfoFilterCutoffHigh,
req.body.isTremoloEnabled ? 1 : 0,
req.body.tremoloFrequency,
req.body.tremoloDepth
],
function (err) {
if (err) {
logger.error(err)
if (err.code === 'SQLITE_CONSTRAINT') {
return res.sendStatus(409)
} else {
return res.sendStatus(500)
}
}
profileID = this.lastID
return res.json({ id: profileID })
})
})
})
router.put('/profiles/:profileId', (req, res) => {
if (!req.user) {
return res.sendStatus(401)

View File

@@ -123,6 +123,149 @@
</v-card>
</v-form>
</v-dialog>
<v-dialog
v-model="profileMoreDialog"
max-width="600px"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
v-bind="attrs"
class="mx-3 my-3"
:disabled="playDisabled"
v-on="on"
>
More...
</v-btn>
</template>
<v-card>
<v-card-title>
<span class="text-h5">More Profile Options</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-list-item-group>
<v-list-item
@click="openImportDialog"
>
<v-list-item-icon>
<v-icon>mdi-file-import</v-icon>
</v-list-item-icon>
<v-list-item-title>
Import Profile
</v-list-item-title>
</v-list-item>
<v-list-item
@click="openExportDialog"
>
<v-list-item-icon>
<v-icon>mdi-file-export</v-icon>
</v-list-item-icon>
<v-list-item-title>
Export Profile
</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
text
@click="profileMoreDialog = false"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog
v-model="importDialog"
max-width="600px"
>
<v-form
ref="importForm"
v-model="isImportValid"
>
<v-card>
<v-card-title>
<span class="text-h5">Import Profile</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-file-input
v-model="importedProfile"
accept=".json"
label="Upload a profile file!"
:rules="[rules.required()]"
/>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
text
@click="importDialog = false"
>
Close
</v-btn>
<v-btn
text
:disabled="!isImportValid"
@click="importProfile"
>
Import
</v-btn>
</v-card-actions>
</v-card>
</v-form>
</v-dialog>
<v-dialog
v-model="exportDialog"
max-width="600px"
>
<v-card>
<v-card-title>
<span class="text-h5">Export Profile</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-select
v-model="exportedProfile"
:items="profileItems"
return-object
label="Profiles"
class="mx-3 mb-5"
/>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
text
@click="exportDialog = false"
>
Close
</v-btn>
<v-btn
text
@click="exportProfile"
>
Export
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-col>
<v-col cols="12">

View File

@@ -10,6 +10,12 @@ export default {
profileDialog: false,
profileName: '',
isProfileValid: false,
profileMoreDialog: false,
importDialog: false,
isImportValid: false,
exportDialog: false,
importedProfile: null,
exportedProfile: {},
infoSnackbar: false,
infoSnackbarText: '',
playDisabled: false,
@@ -213,6 +219,7 @@ export default {
} else {
this.selectedProfile = this.profileItems.find(p => p.id === profileId)
}
this.exportedProfile = this.profileItems[0]
this.loadProfile()
}
}
@@ -311,6 +318,10 @@ export default {
this.loadedSamples = profile.samples
}
})
.catch(() => {
this.errorSnackbarText = 'Error Loading Profile'
this.errorSnackbar = true
})
},
deleteProfile () {
this.$http.delete('/profiles/'.concat(this.selectedProfile.id))
@@ -403,6 +414,102 @@ export default {
if (this.$refs.uploadSampleForm) {
this.$refs.uploadSampleForm.reset()
}
},
openImportDialog () {
this.profileMoreDialog = false
this.importDialog = true
},
openExportDialog () {
this.profileMoreDialog = false
this.exportDialog = true
},
async importProfile () {
const fileContents = await this.readFile(this.importedProfile)
const profileJSON = JSON.parse(fileContents)
this.$http.post('/profiles/import', {
name: profileJSON.name,
isTimerEnabled: profileJSON.isTimerEnabled,
duration: profileJSON.duration,
volume: profileJSON.volume,
noiseColor: profileJSON.noiseColor,
isFilterEnabled: profileJSON.isFilterEnabled,
filterType: profileJSON.filterType,
filterCutoff: profileJSON.filterCutoff,
isLFOFilterCutoffEnabled: profileJSON.isLFOFilterCutoffEnabled,
lfoFilterCutoffFrequency: profileJSON.lfoFilterCutoffFrequency,
lfoFilterCutoffLow: profileJSON.lfoFilterCutoffLow,
lfoFilterCutoffHigh: profileJSON.lfoFilterCutoffHigh,
isTremoloEnabled: profileJSON.isTremoloEnabled,
tremoloFrequency: profileJSON.tremoloFrequency,
tremoloDepth: profileJSON.tremoloDepth
}).then(response => {
if (response.status === 200) {
this.importDialog = false
this.populateProfileItems(response.data.id)
this.infoSnackbarText = 'Profile Imported and Saved'
this.infoSnackbar = true
}
})
.catch(() => {
this.errorSnackbarText = 'Error Saving Profile'
this.errorSnackbar = true
})
if (this.$refs.importForm) {
this.$refs.importForm.reset()
}
},
readFile (file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = res => {
resolve(res.target.result)
}
reader.onerror = err => reject(err)
reader.readAsText(file)
})
},
exportProfile () {
this.$http.get('/profiles/'.concat(this.exportedProfile.id))
.then(response => {
if (response.status === 200) {
const profile = response.data.profile
const profileJSON = {}
profileJSON.name = this.exportedProfile.text
profileJSON.isTimerEnabled = profile.isTimerEnabled
profileJSON.duration = profile.duration
profileJSON.volume = profile.volume
profileJSON.noiseColor = profile.noiseColor
profileJSON.isFilterEnabled = profile.isFilterEnabled
profileJSON.filterType = profile.filterType
profileJSON.filterCutoff = profile.filterCutoff
profileJSON.isLFOFilterCutoffEnabled = profile.isLFOFilterCutoffEnabled
profileJSON.lfoFilterCutoffFrequency = profile.lfoFilterCutoffFrequency
profileJSON.lfoFilterCutoffLow = profile.lfoFilterCutoffLow
profileJSON.lfoFilterCutoffHigh = profile.lfoFilterCutoffHigh
profileJSON.isTremoloEnabled = profile.isTremoloEnabled
profileJSON.tremoloFrequency = profile.tremoloFrequency
profileJSON.tremoloDepth = profile.tremoloDepth
const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(profileJSON))
const downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute('href', dataStr)
downloadAnchorNode.setAttribute('download', profileJSON.name + '.json')
document.body.appendChild(downloadAnchorNode) // required for firefox
downloadAnchorNode.click()
downloadAnchorNode.remove()
}
})
.catch(() => {
this.errorSnackbarText = 'Error Loading Profile'
this.errorSnackbar = true
})
this.exportDialog = false
}
}
}