Update:More localization strings #1103

This commit is contained in:
advplyr 2022-11-08 17:10:08 -06:00
parent c1b3d7779b
commit 400e34a4c7
63 changed files with 681 additions and 584 deletions

View file

@ -125,20 +125,6 @@ export default {
processing: false,
newUser: {},
isNew: true,
accountTypes: [
{
text: 'Guest',
value: 'guest'
},
{
text: 'User',
value: 'user'
},
{
text: 'Admin',
value: 'admin'
}
],
tags: [],
loadingTags: false
}
@ -161,6 +147,22 @@ export default {
this.$emit('input', val)
}
},
accountTypes() {
return [
{
text: this.$strings.LabelAccountTypeGuest,
value: 'guest'
},
{
text: this.$strings.LabelAccountTypeUser,
value: 'user'
},
{
text: this.$strings.LabelAccountTypeAdmin,
value: 'admin'
}
]
},
user() {
return this.$store.state.user.user
},
@ -249,7 +251,7 @@ export default {
.then((data) => {
this.processing = false
if (data.error) {
this.$toast.error(`Failed to update account: ${data.error}`)
this.$toast.error(`${this.$strings.ToastAccountUpdateFailed}: ${data.error}`)
} else {
console.log('Account updated', data.user)
@ -258,7 +260,7 @@ export default {
this.$store.commit('user/setUserToken', data.user.token)
}
this.$toast.success('Account updated')
this.$toast.success(this.$strings.ToastAccountUpdateSuccess)
this.show = false
}
})

View file

@ -2,14 +2,14 @@
<modals-modal v-model="show" name="backup-scheduler" :width="700" :height="'unset'" :processing="processing">
<template #outer>
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
<p class="font-book text-3xl text-white truncate">Set Backup Schedule</p>
<p class="font-book text-3xl text-white truncate">{{ $strings.HeaderSetBackupSchedule }}</p>
</div>
</template>
<div v-if="show && newCronExpression" class="p-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden" style="min-height: 400px; max-height: 80vh">
<widgets-cron-expression-builder ref="expressionBuilder" v-model="newCronExpression" @input="expressionUpdated" />
<div class="flex items-center justify-end">
<ui-btn :disabled="!isUpdated" @click="submit">{{ isUpdated ? 'Save Backup Schedule' : 'No update necessary' }}</ui-btn>
<ui-btn :disabled="!isUpdated" @click="submit">{{ isUpdated ? $strings.ButtonSave : $strings.MessageNoUpdateNecessary }}</ui-btn>
</div>
</div>
</modals-modal>

View file

@ -7,40 +7,36 @@
</template>
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div v-if="show" class="w-full h-full">
<div class="py-4 px-4">
<h1 class="text-2xl">Quick Match {{ selectedBookIds.length }} Books</h1>
</div>
<div v-if="show" class="w-full h-full py-4">
<div class="w-full overflow-y-auto overflow-x-hidden max-h-96">
<div class="flex px-8 items-center py-2">
<p class="pr-4">Provider</p>
<p class="pr-4">{{ $strings.LabelProvider }}</p>
<ui-dropdown v-model="options.provider" :items="providers" small />
</div>
<p class="text-base px-8 py-2">Quick Match will attempt to add missing covers and metadata for the selected books. Enable the options below to allow Quick Match to overwrite existing covers and/or metadata.</p>
<p class="text-base px-8 py-2">{{ $strings.MessageBatchQuickMatchDescription }}</p>
<div class="flex px-8 items-end py-2">
<ui-toggle-switch v-model="options.overrideCover"/>
<ui-tooltip :text="tooltips.updateCovers">
<ui-toggle-switch v-model="options.overrideCover" />
<ui-tooltip :text="$strings.LabelUpdateCoverHelp">
<p class="pl-4">
Update Covers
{{ $strings.LabelUpdateCover }}
<span class="material-icons icon-text text-sm">info_outlined</span>
</p>
</ui-tooltip>
</div>
<div class="flex px-8 items-end py-2">
<ui-toggle-switch v-model="options.overrideDetails"/>
<ui-tooltip :text="tooltips.updateDetails">
<ui-toggle-switch v-model="options.overrideDetails" />
<ui-tooltip :text="$strings.LabelUpdateDetailsHelp">
<p class="pl-4">
Update Details
{{ $strings.LabelUpdateDetails }}
<span class="material-icons icon-text text-sm">info_outlined</span>
</p>
</ui-tooltip>
</div>
<div class="mt-4 py-4 border-b border-white border-opacity-10 text-white text-opacity-80 border-t border-white border-opacity-5">
<div class="mt-4 pt-4 text-white text-opacity-80 border-t border-white border-opacity-5">
<div class="flex items-center px-4">
<ui-btn type="button" @click="show = false">Cancel</ui-btn>
<ui-btn type="button" @click="show = false">{{ $strings.ButtonCancel }}</ui-btn>
<div class="flex-grow" />
<ui-btn color="success" @click="doBatchQuickMatch">Continue</ui-btn>
<ui-btn color="success" @click="doBatchQuickMatch">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
@ -60,10 +56,6 @@ export default {
overrideDetails: true,
overrideCover: true,
overrideDefaults: true
},
tooltips: {
updateCovers: 'Allow overwriting of existing covers for the selected books when a match is located.',
updateDetails: 'Allow overwriting of existing details for the selected books when a match is located.'
}
}
},
@ -84,7 +76,7 @@ export default {
}
},
title() {
return `${this.selectedBookIds.length} Items Selected`
return this.$getString('MessageItemsSelected', [this.selectedBookIds.length])
},
showBatchQuickMatchModal() {
return this.$store.state.globals.showBatchQuickMatchModal
@ -107,7 +99,7 @@ export default {
init() {
// If we don't have a set provider (first open of dialog) or we've switched library, set
// the selected provider to the current library default provider
if (!this.options.provider || (this.options.lastUsedLibrary != this.currentLibraryId)) {
if (!this.options.provider || this.options.lastUsedLibrary != this.currentLibraryId) {
this.options.lastUsedLibrary = this.currentLibraryId
this.options.provider = this.libraryProvider
}
@ -115,7 +107,7 @@ export default {
doBatchQuickMatch() {
if (!this.selectedBookIds.length) return
if (this.processing) return
this.processing = true
this.$store.commit('setProcessingBatch', true)
this.$axios
@ -125,10 +117,12 @@ export default {
})
.then(() => {
this.$toast.info('Batch quick match of ' + this.selectedBookIds.length + ' books started!')
}).catch((error) => {
})
.catch((error) => {
this.$toast.error('Batch quick match failed')
console.error('Failed to batch quick match', error)
}).finally(() => {
})
.finally(() => {
this.processing = false
this.$store.commit('setProcessingBatch', false)
this.show = false

View file

@ -85,10 +85,10 @@ export default {
this.$axios
.$delete(`/api/me/item/${this.libraryItemId}/bookmark/${bm.time}`)
.then(() => {
this.$toast.success('Bookmark removed')
this.$toast.success(this.$strings.ToastBookmarkRemoveSuccess)
})
.catch((error) => {
this.$toast.error(`Failed to remove bookmark`)
this.$toast.error(this.$strings.ToastBookmarkRemoveFailed)
console.error(error)
})
this.show = false
@ -101,10 +101,10 @@ export default {
this.$axios
.$patch(`/api/me/item/${this.libraryItemId}/bookmark`, bookmark)
.then(() => {
this.$toast.success('Bookmark updated')
this.$toast.success(this.$strings.ToastBookmarkUpdateSuccess)
})
.catch((error) => {
this.$toast.error(`Failed to update bookmark`)
this.$toast.error(this.$strings.ToastBookmarkUpdateFailed)
console.error(error)
})
this.show = false
@ -120,10 +120,10 @@ export default {
this.$axios
.$post(`/api/me/item/${this.libraryItemId}/bookmark`, bookmark)
.then(() => {
this.$toast.success('Bookmark added')
this.$toast.success(this.$strings.ToastBookmarkCreateSuccess)
})
.catch((error) => {
this.$toast.error(`Failed to create bookmark`)
this.$toast.error(this.$strings.ToastBookmarkCreateFailed)
console.error(error)
})

View file

@ -96,20 +96,19 @@ export default {
this.newCollectionDescription = this.collection.description || ''
},
removeClick() {
if (confirm(`Are you sure you want to remove collection "${this.collectionName}"?`)) {
if (confirm(this.$getString('MessageConfirmRemoveCollection', [this.collectionName]))) {
this.processing = true
var collectionName = this.collectionName
this.$axios
.$delete(`/api/collections/${this.collection.id}`)
.then(() => {
this.processing = false
this.show = false
this.$toast.success(`Collection "${collectionName}" Removed`)
this.$toast.success(this.$strings.ToastCollectionRemoveSuccess)
})
.catch((error) => {
console.error('Failed to remove collection', error)
this.processing = false
this.$toast.error(`Failed to remove collection`)
this.$toast.error(this.$strings.ToastCollectionRemoveFailed)
})
}
},
@ -133,12 +132,12 @@ export default {
console.log('Collection Updated', collection)
this.processing = false
this.show = false
this.$toast.success(`Collection "${collection.name}" Updated`)
this.$toast.success(this.$strings.ToastCollectionUpdateSuccess)
})
.catch((error) => {
console.error('Failed to update collection', error)
this.processing = false
this.$toast.error(`Failed to update collection`)
this.$toast.error(this.$strings.ToastCollectionUpdateFailed)
})
}
},

View file

@ -2,7 +2,7 @@
<modals-modal v-model="show" name="listening-session-modal" :processing="processing" :width="700" :height="'unset'">
<template #outer>
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
<p class="font-book text-3xl text-white truncate">Session {{ _session.id }}</p>
<p class="font-book text-3xl text-white truncate">{{ $strings.HeaderSession }} {{ _session.id }}</p>
</div>
</template>
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden p-6" style="max-height: 80vh">
@ -15,90 +15,90 @@
<div class="flex flex-wrap mb-4">
<div class="w-full md:w-2/3">
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mb-2">Details</p>
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mb-2">{{ $strings.HeaderDetails }}</p>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Started At</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelStartedAt }}</div>
<div class="px-1">
{{ $formatDate(_session.startedAt, 'MMMM do, yyyy HH:mm') }}
</div>
</div>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Updated At</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelUpdatedAt }}</div>
<div class="px-1">
{{ $formatDate(_session.updatedAt, 'MMMM do, yyyy HH:mm') }}
</div>
</div>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Listened for</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelTimeListened }}</div>
<div class="px-1">
{{ $elapsedPrettyExtended(_session.timeListening) }}
</div>
</div>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Start Time</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelStartTime }}</div>
<div class="px-1">
{{ $secondsToTimestamp(_session.startTime) }}
</div>
</div>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Last Time</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelLastTime }}</div>
<div class="px-1">
{{ $secondsToTimestamp(_session.currentTime) }}
</div>
</div>
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">Item</p>
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">{{ $strings.LabelItem }}</p>
<div v-if="_session.libraryId" class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Library Id</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelLibrary }} Id</div>
<div class="px-1">
{{ _session.libraryId }}
</div>
</div>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Library Item Id</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelLibraryItem }} Id</div>
<div class="px-1">
{{ _session.libraryItemId }}
</div>
</div>
<div v-if="_session.episodeId" class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Episode Id</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelEpisode }} Id</div>
<div class="px-1">
{{ _session.episodeId }}
</div>
</div>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Media Type</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelMediaType }}</div>
<div class="px-1">
{{ _session.mediaType }}
</div>
</div>
<div class="flex items-center -mx-1 mb-1">
<div class="w-40 px-1 text-gray-200">Duration</div>
<div class="w-40 px-1 text-gray-200">{{ $strings.LabelDuration }}</div>
<div class="px-1">
{{ $elapsedPretty(_session.duration) }}
</div>
</div>
</div>
<div class="w-full md:w-1/3">
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mb-2 mt-6 md:mt-0">User</p>
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mb-2 mt-6 md:mt-0">{{ $strings.LabelUser }}</p>
<p class="mb-1">{{ _session.userId }}</p>
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">Media Player</p>
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">{{ $strings.LabelMediaPlayer }}</p>
<p class="mb-1">{{ playMethodName }}</p>
<p class="mb-1">{{ _session.mediaPlayer }}</p>
<p v-if="hasDeviceInfo" class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">Device</p>
<p v-if="hasDeviceInfo" class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">{{ $strings.LabelDevice }}</p>
<p v-if="deviceInfo.ipAddress" class="mb-1">{{ deviceInfo.ipAddress }}</p>
<p v-if="osDisplayName" class="mb-1">{{ osDisplayName }}</p>
<p v-if="deviceInfo.browserName" class="mb-1">{{ deviceInfo.browserName }}</p>
<p v-if="clientDisplayName" class="mb-1">{{ clientDisplayName }}</p>
<p v-if="deviceInfo.sdkVersion" class="mb-1">SDK Version: {{ deviceInfo.sdkVersion }}</p>
<p v-if="deviceInfo.deviceType" class="mb-1">Type: {{ deviceInfo.deviceType }}</p>
<p v-if="deviceInfo.sdkVersion" class="mb-1">SDK {{ $strings.LabelVersion }}: {{ deviceInfo.sdkVersion }}</p>
<p v-if="deviceInfo.deviceType" class="mb-1">{{ $strings.LabelType }}: {{ deviceInfo.deviceType }}</p>
</div>
</div>
<div class="flex items-center">
<ui-btn small color="error" @click.stop="deleteSessionClick">Delete</ui-btn>
<ui-btn small color="error" @click.stop="deleteSessionClick">{{ $strings.ButtonDelete }}</ui-btn>
</div>
</div>
</modals-modal>
@ -156,7 +156,7 @@ export default {
methods: {
deleteSessionClick() {
const payload = {
message: `Are you sure you want to delete this session?`,
message: this.$strings.MessageConfirmDeleteSession,
callback: (confirmed) => {
if (confirmed) {
this.deleteSession()
@ -172,7 +172,7 @@ export default {
.$delete(`/api/sessions/${this._session.id}`)
.then(() => {
this.processing = false
this.$toast.success('Session deleted successfully')
this.$toast.success(this.$strings.ToastSessionDeleteSuccess)
this.$emit('removedSession')
this.show = false
})
@ -180,7 +180,7 @@ export default {
this.processing = false
console.error('Failed to delete session', error)
var errMsg = error.response ? error.response.data || '' : ''
this.$toast.error(errMsg || 'Failed to delete session')
this.$toast.error(errMsg || this.$strings.ToastSessionDeleteFailed)
})
}
},

View file

@ -2,7 +2,7 @@
<modals-modal v-model="show" name="sleep-timer" :width="350" :height="'unset'">
<template #outer>
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden pointer-events-none">
<p class="font-book text-3xl text-white truncate pointer-events-none">Sleep Timer</p>
<p class="font-book text-3xl text-white truncate pointer-events-none">{{ $strings.HeaderSleepTimer }}</p>
</div>
</template>
@ -32,7 +32,7 @@
<span class="pl-1 text-base font-mono">30m</span>
</ui-btn>
</div>
<ui-btn class="w-full" @click="$emit('cancel')">Cancel</ui-btn>
<ui-btn class="w-full" @click="$emit('cancel')">{{ $strings.ButtonCancel }}</ui-btn>
</div>
</div>
</modals-modal>

View file

@ -9,8 +9,8 @@
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
<div v-if="show" class="w-full h-full">
<div class="py-4 px-4">
<h1 v-if="!showBatchUserCollectionModal" class="text-2xl">Add to Collection</h1>
<h1 v-else class="text-2xl">Add {{ selectedBookIds.length }} Books to Collection</h1>
<h1 v-if="!showBatchUserCollectionModal" class="text-2xl">{{ $strings.LabelAddToCollection }}</h1>
<h1 v-else class="text-2xl">{{ $getString('LabelAddToCollectionBatch', [selectedBookIds.length]) }}</h1>
</div>
<div class="w-full overflow-y-auto overflow-x-hidden max-h-96">
<transition-group name="list-complete" tag="div">
@ -20,15 +20,15 @@
</transition-group>
</div>
<div v-if="!collections.length" class="flex h-32 items-center justify-center">
<p class="text-xl">No Collections</p>
<p class="text-xl">{{ $strings.MessageNoCollections }}</p>
</div>
<div class="w-full h-px bg-white bg-opacity-10" />
<form @submit.prevent="submitCreateCollection">
<div class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80">
<div class="flex-grow px-2">
<ui-text-input v-model="newCollectionName" placeholder="New Collection" class="w-full" />
<ui-text-input v-model="newCollectionName" :placeholder="$strings.PlaceholderNewCollection" class="w-full" />
</div>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10">Create</ui-btn>
<ui-btn type="submit" color="success" :padding-x="4" class="h-10">{{ $strings.ButtonCreate }}</ui-btn>
</div>
</form>
</div>
@ -65,7 +65,7 @@ export default {
},
title() {
if (this.showBatchUserCollectionModal) {
return `${this.selectedBookIds.length} Items Selected`
return this.$getString('MessageItemsSelected', [this.selectedBookIds.length])
}
return this.selectedLibraryItem ? this.selectedLibraryItem.media.metadata.title : ''
},
@ -124,12 +124,12 @@ export default {
.$post(`/api/collections/${collection.id}/batch/remove`, { books: this.selectedBookIds })
.then((updatedCollection) => {
console.log(`Books removed from collection`, updatedCollection)
this.$toast.success('Books removed from collection')
this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess)
this.processing = false
})
.catch((error) => {
console.error('Failed to remove books from collection', error)
this.$toast.error('Failed to remove books from collection')
this.$toast.error(this.$strings.ToastCollectionItemsRemoveFailed)
this.processing = false
})
} else {
@ -138,12 +138,12 @@ export default {
.$delete(`/api/collections/${collection.id}/book/${this.selectedLibraryItemId}`)
.then((updatedCollection) => {
console.log(`Book removed from collection`, updatedCollection)
this.$toast.success('Book removed from collection')
this.$toast.success(this.$strings.ToastCollectionItemsRemoveSuccess)
this.processing = false
})
.catch((error) => {
console.error('Failed to remove book from collection', error)
this.$toast.error('Failed to remove book from collection')
this.$toast.error(this.$strings.ToastCollectionItemsRemoveFailed)
this.processing = false
})
}

View file

@ -19,23 +19,23 @@
<div class="flex-grow">
<div class="flex">
<div class="w-3/4 p-2">
<ui-text-input-with-label v-model="authorCopy.name" :disabled="processing" label="Name" />
<ui-text-input-with-label v-model="authorCopy.name" :disabled="processing" :label="$strings.LabelName" />
</div>
<div class="flex-grow p-2">
<ui-text-input-with-label v-model="authorCopy.asin" :disabled="processing" label="ASIN" />
</div>
</div>
<div class="p-2">
<ui-text-input-with-label v-model="authorCopy.imagePath" :disabled="processing" label="Photo Path/URL" />
<ui-text-input-with-label v-model="authorCopy.imagePath" :disabled="processing" :label="$strings.LabelPhotoPathURL" />
</div>
<div class="p-2">
<ui-textarea-with-label v-model="authorCopy.description" :disabled="processing" label="Description" :rows="8" />
<ui-textarea-with-label v-model="authorCopy.description" :disabled="processing" :label="$strings.LabelDescription" :rows="8" />
</div>
<div class="flex pt-2 px-2">
<ui-btn type="button" @click="searchAuthor">Quick Match</ui-btn>
<ui-btn type="button" @click="searchAuthor">{{ $strings.ButtonQuickMatch }}</ui-btn>
<div class="flex-grow" />
<ui-btn type="submit">Submit</ui-btn>
<ui-btn type="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</div>
@ -84,7 +84,7 @@ export default {
return this.author.id
},
title() {
return 'Edit Author'
return this.$strings.HeaderUpdateAuthor
}
},
methods: {
@ -103,23 +103,23 @@ export default {
}
})
if (!Object.keys(updatePayload).length) {
this.$toast.info('No updates are necessary')
this.$toast.info(this.$strings.MessageNoUpdateNecessary)
return
}
this.processing = true
var result = await this.$axios.$patch(`/api/authors/${this.authorId}`, updatePayload).catch((error) => {
console.error('Failed', error)
this.$toast.error('Failed to update author')
this.$toast.error(this.$strings.ToastAuthorUpdateFailed)
return null
})
if (result) {
if (result.updated) {
this.$toast.success('Author updated')
this.$toast.success(this.$strings.ToastAuthorUpdateSuccess)
this.show = false
} else if (result.merged) {
this.$toast.success('Author merged')
this.$toast.success(this.$strings.ToastAuthorUpdateMerged)
this.show = false
} else this.$toast.info('No updates were needed')
} else this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)
}
this.processing = false
},
@ -131,11 +131,11 @@ export default {
this.processing = true
var result = await this.$axios.$patch(`/api/authors/${this.authorId}`, updatePayload).catch((error) => {
console.error('Failed', error)
this.$toast.error('Failed to remove image')
this.$toast.error(this.$strings.ToastAuthorImageRemoveFailed)
return null
})
if (result && result.updated) {
this.$toast.success('Author image removed')
this.$toast.success(this.$strings.ToastAuthorImageRemoveSuccess)
}
this.processing = false
},
@ -157,8 +157,8 @@ export default {
if (!response) {
this.$toast.error('Author not found')
} else if (response.updated) {
if (response.author.imagePath) this.$toast.success('Author was updated')
else this.$toast.success('Author was updated (no image found)')
if (response.author.imagePath) this.$toast.success(this.$strings.ToastAuthorUpdateSuccess)
else this.$toast.success(this.$strings.ToastAuthorUpdateSuccessNoImageFound)
} else {
this.$toast.info('No updates were made for Author')
}

View file

@ -179,7 +179,7 @@ export default {
this.$toast.success('Item details updated')
return true
} else {
this.$toast.info('No updates were necessary')
this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)
}
}
return false

View file

@ -2,29 +2,29 @@
<div class="w-full h-full overflow-y-auto overflow-x-hidden px-4 py-6">
<div class="w-full mb-4">
<div v-if="userIsAdminOrUp" class="flex items-end justify-end mb-4">
<ui-text-input-with-label ref="lastCheckInput" v-model="lastEpisodeCheckInput" :disabled="checkingNewEpisodes" type="datetime-local" label="Look for new episodes after this date" class="max-w-xs mr-2" />
<ui-text-input-with-label ref="maxEpisodesInput" v-model="maxEpisodesToDownload" :disabled="checkingNewEpisodes" type="number" label="Max episodes" class="w-16 mr-2" input-class="h-10">
<ui-text-input-with-label ref="lastCheckInput" v-model="lastEpisodeCheckInput" :disabled="checkingNewEpisodes" type="datetime-local" :label="$strings.LabelLookForNewEpisodesAfterDate" class="max-w-xs mr-2" />
<ui-text-input-with-label ref="maxEpisodesInput" v-model="maxEpisodesToDownload" :disabled="checkingNewEpisodes" type="number" :label="$strings.LabelLimit" class="w-16 mr-2" input-class="h-10">
<div class="flex -mb-0.5">
<p class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': checkingNewEpisodes }">Limit</p>
<p class="px-1 text-sm font-semibold" :class="{ 'text-gray-400': checkingNewEpisodes }">{{ $strings.LabelLimit }}</p>
<ui-tooltip direction="top" text="Max # of episodes to download. Use 0 for unlimited.">
<span class="material-icons text-base">info_outlined</span>
</ui-tooltip>
</div>
</ui-text-input-with-label>
<ui-btn :loading="checkingNewEpisodes" @click="checkForNewEpisodes">Check & Download New Episodes</ui-btn>
<ui-btn :loading="checkingNewEpisodes" @click="checkForNewEpisodes">{{ $strings.ButtonCheckAndDownloadNewEpisodes }}</ui-btn>
</div>
<div v-if="episodes.length" class="w-full p-4 bg-primary">
<p>Podcast Episodes</p>
<p>{{ $strings.HeaderEpisodes }}</p>
</div>
<div v-if="!episodes.length" class="flex my-4 text-center justify-center text-xl">No Episodes</div>
<div v-if="!episodes.length" class="flex my-4 text-center justify-center text-xl">{{ $strings.MessageNoEpisodes }}</div>
<table v-else class="text-sm tracksTable">
<tr class="font-book">
<th class="text-left">Sort #</th>
<th class="text-left whitespace-nowrap">Episode #</th>
<th class="text-left">Title</th>
<th class="text-center w-28">Duration</th>
<th class="text-center w-28">Size</th>
<th class="text-left whitespace-nowrap">{{ $strings.LabelEpisode }}</th>
<th class="text-left">{{ $strings.EpisodeTitle }}</th>
<th class="text-center w-28">{{ $strings.EpisodeDuration }}</th>
<th class="text-center w-28">{{ $strings.EpisodeSize }}</th>
</tr>
<tr v-for="episode in episodes" :key="episode.id">
<td class="text-left">

View file

@ -474,9 +474,9 @@ export default {
return false
})
if (success) {
this.$toast.success('Item Cover Updated')
this.$toast.success(this.$strings.ToastItemCoverUpdateSuccess)
} else {
this.$toast.error('Item Cover Failed to Update')
this.$toast.error(this.$strings.ToastItemCoverUpdateFailed)
}
console.log('Updated cover')
delete updatePayload.metadata.cover
@ -490,14 +490,14 @@ export default {
})
if (updateResult) {
if (updateResult.updated) {
this.$toast.success('Item details updated')
this.$toast.success(this.$strings.ToastItemDetailsUpdateSuccess)
} else {
this.$toast.info('No detail updates were necessary')
this.$toast.info(this.$strings.ToastItemDetailsUpdateUnneeded)
}
this.clearSelectedMatch()
this.$emit('selectTab', 'details')
} else {
this.$toast.error('Item Details Failed to Update')
this.$toast.error(this.$strings.ToastItemDetailsUpdateFailed)
}
} else {
this.clearSelectedMatch()

View file

@ -153,7 +153,7 @@ export default {
this.$toast.success('Item details updated')
return true
} else {
this.$toast.info('No updates were necessary')
this.$toast.info(this.$strings.MessageNoUpdatesWereNecessary)
}
}
return false

View file

@ -50,7 +50,7 @@
</div>
</div>
<p v-if="!mediaTracks.length" class="text-lg text-center my-8">No audio tracks</p>
<p v-if="!mediaTracks.length" class="text-lg text-center my-8">{{ $strings.MessageNoAudioTracks }}</p>
</div>
</template>

View file

@ -53,20 +53,22 @@ export default {
folders: [],
showDirectoryPicker: false,
newFolderPath: '',
mediaType: null,
mediaTypes: [
{
value: 'book',
text: 'Books'
},
{
value: 'podcast',
text: 'Podcasts'
}
]
mediaType: null
}
},
computed: {
mediaTypes() {
return [
{
value: 'book',
text: this.$strings.LabelBooks
},
{
value: 'podcast',
text: this.$strings.LabelPodcasts
}
]
},
folderPaths() {
return this.folders.map((f) => f.fullPath)
},

View file

@ -192,14 +192,14 @@ export default {
.then((res) => {
this.processing = false
this.show = false
this.$toast.success(`Library "${res.name}" updated successfully`)
this.$toast.success(this.$getString('ToastLibraryUpdateSuccess', [res.name]))
})
.catch((error) => {
console.error(error)
if (error.response && error.response.data) {
this.$toast.error(error.response.data)
} else {
this.$toast.error('Failed to update library')
this.$toast.error(this.$strings.ToastLibraryUpdateFailed)
}
this.processing = false
})
@ -211,7 +211,7 @@ export default {
.then((res) => {
this.processing = false
this.show = false
this.$toast.success(`Library "${res.name}" created successfully`)
this.$toast.success(this.$getString('ToastLibraryCreateSuccess', [res.name]))
if (!this.$store.state.libraries.currentLibraryId) {
console.log('Setting initially library id', res.id)
// First library added
@ -223,7 +223,7 @@ export default {
if (error.response && error.response.data) {
this.$toast.error(error.response.data)
} else {
this.$toast.error('Failed to create library')
this.$toast.error(this.$strings.ToastLibraryCreateFailed)
}
this.processing = false
})

View file

@ -26,12 +26,12 @@ export default {
tabs: [
{
id: 'details',
title: 'Details',
title: this.$strings.HeaderDetails,
component: 'modals-podcast-tabs-episode-details'
},
{
id: 'match',
title: 'Match',
title: this.$strings.HeaderMatch,
component: 'modals-podcast-tabs-episode-match'
}
]

View file

@ -7,45 +7,45 @@
</template>
<div ref="wrapper" id="podcast-wrapper" class="p-2 md:p-8 w-full text-sm py-2 rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-x-hidden overflow-y-auto" style="max-height: 80vh">
<div class="w-full">
<p class="text-lg font-semibold mb-2 px-2">Details</p>
<p class="text-lg font-semibold mb-2 px-2">{{ $strings.HeaderDetails }}</p>
<div v-if="podcast.imageUrl" class="p-2 w-full">
<img :src="podcast.imageUrl" class="h-16 w-16 object-contain" />
</div>
<div class="flex flex-wrap">
<div class="w-full md:w-1/2 p-2">
<ui-text-input-with-label v-model="podcast.title" label="Title" @input="titleUpdated" />
<ui-text-input-with-label v-model="podcast.title" :label="$strings.LabelTitle" @input="titleUpdated" />
</div>
<div class="w-full md:w-1/2 p-2">
<ui-text-input-with-label v-model="podcast.author" label="Author" />
<ui-text-input-with-label v-model="podcast.author" :label="$strings.LabelAuthor" />
</div>
</div>
<div class="flex flex-wrap">
<div class="w-full md:w-1/2 p-2">
<ui-text-input-with-label v-model="podcast.feedUrl" label="Feed URL" readonly />
<ui-text-input-with-label v-model="podcast.feedUrl" :label="$strings.LabelFeedURL" readonly />
</div>
<div class="w-full md:w-1/2 p-2">
<ui-multi-select v-model="podcast.genres" :items="podcast.genres" label="Genres" />
<ui-multi-select v-model="podcast.genres" :items="podcast.genres" :label="$strings.LabelGenres" />
</div>
</div>
<div class="p-2 w-full">
<ui-textarea-with-label v-model="podcast.description" label="Description" :rows="3" />
<ui-textarea-with-label v-model="podcast.description" :label="$strings.LabelDescription" :rows="3" />
</div>
<div class="flex flex-wrap">
<div class="w-full md:w-1/2 p-2">
<ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="processing" label="Folder" @input="folderUpdated" />
<ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="processing" :label="$strings.LabelFolder" @input="folderUpdated" />
</div>
<div class="w-full md:w-1/2 p-2">
<ui-text-input-with-label v-model="fullPath" label="Podcast Path" input-class="h-10" readonly />
<ui-text-input-with-label v-model="fullPath" :label="`${$strings.LabelPodcast} ${$strings.LabelPath}`" input-class="h-10" readonly />
</div>
</div>
</div>
<div class="flex items-center py-4 px-2">
<div class="flex-grow" />
<div class="px-4">
<ui-checkbox v-model="podcast.autoDownloadEpisodes" label="Auto Download Episodes" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-sm md:text-base font-semibold" />
<ui-checkbox v-model="podcast.autoDownloadEpisodes" :label="$strings.LabelAutoDownloadEpisodes" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-sm md:text-base font-semibold" />
</div>
<ui-btn color="success" @click="submit">Add Podcast</ui-btn>
<ui-btn color="success" @click="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
</div>
</modals-modal>
@ -182,12 +182,12 @@ export default {
.$post('/api/podcasts', podcastPayload)
.then((libraryItem) => {
this.processing = false
this.$toast.success('Podcast created successfully')
this.$toast.success(this.$strings.ToastPodcastCreateSuccess)
this.show = false
this.$router.push(`/item/${libraryItem.id}`)
})
.catch((error) => {
var errorMsg = error.response && error.response.data ? error.response.data : 'Failed to create podcast'
var errorMsg = error.response && error.response.data ? error.response.data : this.$strings.ToastPodcastCreateFailed
console.error('Failed to create podcast', error)
this.processing = false
this.$toast.error(errorMsg)

View file

@ -9,14 +9,14 @@
<div class="w-full p-4">
<div class="flex items-center -mx-2 mb-2">
<div class="w-full md:w-2/3 p-2">
<ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="processing" label="Folder" />
<ui-dropdown v-model="selectedFolderId" :items="folderItems" :disabled="processing" :label="$strings.LabelFolder" />
</div>
<div class="w-full md:w-1/3 p-2 pt-6">
<ui-checkbox v-model="autoDownloadEpisodes" label="Auto Download New Episodes" checkbox-bg="primary" border-color="gray-600" label-class="text-sm font-semibold pl-2" />
<ui-checkbox v-model="autoDownloadEpisodes" :label="$strings.LabelAutoDownloadEpisodes" checkbox-bg="primary" border-color="gray-600" label-class="text-sm font-semibold pl-2" />
</div>
</div>
<p class="text-lg font-semibold mb-2">Podcasts to Add</p>
<p class="text-lg font-semibold mb-2">{{ $strings.HeaderPodcastsToAdd }}</p>
<div class="w-full overflow-y-auto" style="max-height: 50vh">
<template v-for="(feed, index) in feedMetadata">
@ -26,7 +26,7 @@
</div>
<div class="flex items-center py-4">
<div class="flex-grow" />
<ui-btn color="success" @click="submit">Add Podcasts</ui-btn>
<ui-btn color="success" @click="submit">{{ $strings.ButtonAddPodcasts }}</ui-btn>
</div>
</div>
</modals-modal>
@ -141,10 +141,10 @@ export default {
await this.$axios
.$post('/api/podcasts', podcastPayload)
.then(() => {
this.$toast.success(`${podcastPayload.media.metadata.title}: Podcast created successfully`)
this.$toast.success(`${podcastPayload.media.metadata.title}: ${this.$strings.ToastPodcastCreateSuccess}`)
})
.catch((error) => {
var errorMsg = error.response && error.response.data ? error.response.data : 'Failed to create podcast'
var errorMsg = error.response && error.response.data ? error.response.data : this.$strings.ToastPodcastCreateFailed
console.error('Failed to create podcast', podcastPayload, error)
this.$toast.error(`${podcastPayload.media.metadata.title}: ${errorMsg}`)
})

View file

@ -8,14 +8,13 @@
<div ref="wrapper" class="px-8 py-6 w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden">
<div class="mb-4">
<p v-if="episode" class="text-lg text-gray-200 mb-4">
Are you sure you want to remove episode<br /><span class="text-base">{{ episodeTitle }}</span
>?
{{ $getString('MessageConfirmRemoveEpisode', [episodeTitle]) }}
</p>
<p v-else class="text-lg text-gray-200 mb-4">Are you sure you want to remove {{ episodes.length }} episodes?</p>
<p v-else class="text-lg text-gray-200 mb-4">{{ $getString('MessageConfirmRemoveEpisodes', [episodes.length]) }}</p>
<p class="text-xs font-semibold text-warning text-opacity-90">Note: This does not delete the audio file unless toggling "Hard delete file"</p>
</div>
<div class="flex justify-between items-center pt-4">
<ui-checkbox v-model="hardDeleteFile" label="Hard delete file" check-color="error" checkbox-bg="bg" small label-class="text-base text-gray-200 pl-3" />
<ui-checkbox v-model="hardDeleteFile" :label="$strings.LabelHardDeleteFile" check-color="error" checkbox-bg="bg" small label-class="text-base text-gray-200 pl-3" />
<ui-btn @click="submit">{{ btnText }}</ui-btn>
</div>
@ -61,12 +60,11 @@ export default {
return null
},
title() {
if (this.episodes.length > 1) return `Remove ${this.episodes.length} episodes`
return 'Remove Episode'
if (this.episodes.length > 1) return this.$getString('HeaderRemoveEpisodes', [this.episodes.length])
return this.$strings.HeaderRemoveEpisode
},
btnText() {
if (this.episodes.length > 1) return this.hardDeleteFile ? `Delete ${this.episodes.length} episodes` : `Remove ${this.episodes.length} episodes`
return this.hardDeleteFile ? 'Delete episode' : 'Remove episode'
return this.hardDeleteFile ? this.$strings.ButtonDelete : this.$strings.ButtonRemove
},
episodeTitle() {
return this.episode ? this.episode.title : null

View file

@ -2,7 +2,7 @@
<modals-modal v-model="show" name="podcast-episode-view-modal" :width="800" :height="'unset'" :processing="processing">
<template #outer>
<div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden">
<p class="font-book text-3xl text-white truncate">Episode</p>
<p class="font-book text-3xl text-white truncate">{{ $strings.LabelEpisode }}</p>
</div>
</template>
<div ref="wrapper" class="p-4 w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-y-auto" style="max-height: 80vh">
@ -17,7 +17,7 @@
</div>
<p class="text-lg font-semibold mb-6">{{ title }}</p>
<div v-if="description" class="default-style" v-html="description" />
<p v-else class="mb-2">No description</p>
<p v-else class="mb-2">{{ $strings.MessageNoDescription }}</p>
</div>
</modals-modal>
</template>

View file

@ -2,29 +2,29 @@
<div>
<div class="flex flex-wrap">
<div class="w-1/5 p-1">
<ui-text-input-with-label v-model="newEpisode.season" label="Season" />
<ui-text-input-with-label v-model="newEpisode.season" :label="$strings.LabelSeason" />
</div>
<div class="w-1/5 p-1">
<ui-text-input-with-label v-model="newEpisode.episode" label="Episode" />
<ui-text-input-with-label v-model="newEpisode.episode" :label="$strings.LabelEpisode" />
</div>
<div class="w-1/5 p-1">
<ui-text-input-with-label v-model="newEpisode.episodeType" label="Episode Type" />
<ui-text-input-with-label v-model="newEpisode.episodeType" :label="$strings.LabelEpisodeType" />
</div>
<div class="w-2/5 p-1">
<ui-text-input-with-label v-model="pubDateInput" @input="updatePubDate" type="datetime-local" label="Pub Date" />
<ui-text-input-with-label v-model="pubDateInput" @input="updatePubDate" type="datetime-local" :label="$strings.LabelPubDate" />
</div>
<div class="w-full p-1">
<ui-text-input-with-label v-model="newEpisode.title" label="Title" />
<ui-text-input-with-label v-model="newEpisode.title" :label="$strings.LabelTitle" />
</div>
<div class="w-full p-1">
<ui-textarea-with-label v-model="newEpisode.subtitle" label="Subtitle" :rows="3" />
<ui-textarea-with-label v-model="newEpisode.subtitle" :label="$strings.LabelSubtitle" :rows="3" />
</div>
<div class="w-full p-1 default-style">
<ui-rich-text-editor label="Description" v-model="newEpisode.description" />
<ui-rich-text-editor :label="$strings.LabelDescription" v-model="newEpisode.description" />
</div>
</div>
<div class="flex items-center justify-end pt-4">
<ui-btn @click="submit">Submit</ui-btn>
<ui-btn @click="submit">{{ $strings.ButtonSubmit }}</ui-btn>
</div>
<div v-if="enclosureUrl" class="py-4">
<p class="text-xs text-gray-300 font-semibold">Episode URL from RSS feed</p>

View file

@ -2,18 +2,18 @@
<div style="min-height: 200px">
<template v-if="!podcastFeedUrl">
<div class="py-8">
<widgets-alert type="error">Podcast has no RSS feed url to use for matching</widgets-alert>
<widgets-alert type="error">{{ $strings.MessagePodcastHasNoRSSFeedForMatching }}</widgets-alert>
</div>
</template>
<template v-else>
<form @submit.prevent="submitForm">
<div class="flex mb-2">
<ui-text-input-with-label v-model="episodeTitle" :disabled="isProcessing" label="Episode Title" class="pr-1" />
<ui-btn class="mt-5 ml-1" :loading="isProcessing" type="submit">Search</ui-btn>
<ui-text-input-with-label v-model="episodeTitle" :disabled="isProcessing" :label="$strings.LabelEpisodeTitle" class="pr-1" />
<ui-btn class="mt-5 ml-1" :loading="isProcessing" type="submit">{{ $strings.ButtonSearch }}</ui-btn>
</div>
</form>
<div v-if="!isProcessing && searchedTitle && !episodesFound.length" class="w-full py-8">
<p class="text-center text-lg">No episode matches found</p>
<p class="text-center text-lg">{{ $strings.MessageNoEpisodeMatchesFound }}</p>
</div>
<div v-for="(episode, index) in episodesFound" :key="index" class="w-full py-4 border-b border-white border-opacity-5 hover:bg-gray-300 hover:bg-opacity-10 cursor-pointer px-2" @click.stop="selectEpisode(episode)">
<p v-if="episode.episode" class="font-semibold text-gray-200">#{{ episode.episode }}</p>

View file

@ -7,7 +7,7 @@
</template>
<div ref="wrapper" class="px-8 py-6 w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden">
<div v-if="currentFeedUrl" class="w-full">
<p class="text-lg font-semibold mb-4">Podcast RSS Feed is Open</p>
<p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedIsOpen }}</p>
<div class="w-full relative">
<ui-text-input v-model="currentFeedUrl" readonly />
@ -16,20 +16,20 @@
</div>
</div>
<div v-else class="w-full">
<p class="text-lg font-semibold mb-4">Open RSS Feed</p>
<p class="text-lg font-semibold mb-4">{{ $strings.HeaderOpenRSSFeed }}</p>
<div class="w-full relative mb-2">
<ui-text-input-with-label v-model="newFeedSlug" label="RSS Feed Slug" />
<p class="text-xs text-gray-400 py-0.5 px-1">Feed will be {{ demoFeedUrl }}</p>
<ui-text-input-with-label v-model="newFeedSlug" :label="$strings.LabelRSSFeedSlug" />
<p class="text-xs text-gray-400 py-0.5 px-1">{{ $getString('MessageFeedURLWillBe', [demoFeedUrl]) }}</p>
</div>
<p v-if="isHttp" class="w-full pt-2 text-warning text-xs">Warning: Most podcast apps will require the RSS feed URL is using HTTPS</p>
<p v-if="hasEpisodesWithoutPubDate" class="w-full pt-2 text-warning text-xs">Warning: 1 or more of your episodes do not have a Pub Date. Some podcast apps require this.</p>
<p v-if="isHttp" class="w-full pt-2 text-warning text-xs">{{ $strings.NoteRSSFeedPodcastAppsHttps }}</p>
<p v-if="hasEpisodesWithoutPubDate" class="w-full pt-2 text-warning text-xs">{{ $strings.NoteRSSFeedPodcastAppsPubDate }}</p>
</div>
<div v-show="userIsAdminOrUp" class="flex items-center pt-6">
<div class="flex-grow" />
<ui-btn v-if="currentFeedUrl" color="error" small @click="closeFeed">Close RSS Feed</ui-btn>
<ui-btn v-else color="success" small @click="openFeed">Open RSS Feed</ui-btn>
<ui-btn v-if="currentFeedUrl" color="error" small @click="closeFeed">{{ $strings.ButtonCloseFeed }}</ui-btn>
<ui-btn v-else color="success" small @click="openFeed">{{ $strings.ButtonOpenFeed }}</ui-btn>
</div>
</div>
</modals-modal>
@ -144,14 +144,14 @@ export default {
this.$axios
.$post(`/api/items/${this.libraryItem.id}/close-feed`)
.then(() => {
this.$toast.success('RSS Feed Closed')
this.$toast.success(this.$strings.ToastRSSFeedCloseSuccess)
this.show = false
this.processing = false
})
.catch((error) => {
console.error('Failed to close RSS feed', error)
this.processing = false
this.$toast.error()
this.$toast.error(this.$strings.ToastRSSFeedCloseFailed)
})
},
init() {