mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-11-08 09:04:43 +01:00
Update:Creating user playlists modal
This commit is contained in:
parent
f9b87b94bf
commit
1131bfa751
214
client/components/modals/playlists/AddCreateModal.vue
Normal file
214
client/components/modals/playlists/AddCreateModal.vue
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
<template>
|
||||||
|
<modals-modal v-model="show" name="playlists" :processing="processing" :width="500" :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">{{ title }}</p>
|
||||||
|
</div>
|
||||||
|
</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 v-if="!isBatch" class="text-2xl">{{ $strings.LabelAddToPlaylist }}</h1>
|
||||||
|
<h1 v-else class="text-2xl">{{ $getString('LabelAddToPlaylistBatch', [selectedPlaylistItems.length]) }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="w-full overflow-y-auto overflow-x-hidden max-h-96">
|
||||||
|
<transition-group name="list-complete" tag="div">
|
||||||
|
<template v-for="playlist in sortedPlaylists">
|
||||||
|
<modals-playlists-user-playlist-item :key="playlist.id" :playlist="playlist" :book-cover-aspect-ratio="bookCoverAspectRatio" class="list-complete-item" @add="addToPlaylist" @remove="removeFromPlaylist" @close="show = false" />
|
||||||
|
</template>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
<div v-if="!playlists.length" class="flex h-32 items-center justify-center">
|
||||||
|
<p class="text-xl">{{ $strings.MessageNoUserPlaylists }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="w-full h-px bg-white bg-opacity-10" />
|
||||||
|
<form @submit.prevent="submitCreatePlaylist">
|
||||||
|
<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="newPlaylistName" :placeholder="$strings.PlaceholderNewPlaylist" class="w-full" />
|
||||||
|
</div>
|
||||||
|
<ui-btn type="submit" color="success" :padding-x="4" class="h-10">{{ $strings.ButtonCreate }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</modals-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
newPlaylistName: '',
|
||||||
|
processing: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.loadPlaylists()
|
||||||
|
this.newPlaylistName = ''
|
||||||
|
} else {
|
||||||
|
this.$store.commit('globals/setSelectedPlaylistItems', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
show: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.globals.showPlaylistsModal
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$store.commit('globals/setShowPlaylistsModal', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title() {
|
||||||
|
if (!this.selectedPlaylistItems.length) return ''
|
||||||
|
if (this.isBatch) {
|
||||||
|
return this.$getString('MessageItemsSelected', [this.selectedPlaylistItems.length])
|
||||||
|
}
|
||||||
|
const selectedPlaylistItem = this.selectedPlaylistItems[0]
|
||||||
|
if (selectedPlaylistItem.episode) {
|
||||||
|
return selectedPlaylistItem.episode.title
|
||||||
|
}
|
||||||
|
return selectedPlaylistItem.libraryItem.media.metadata.title || ''
|
||||||
|
},
|
||||||
|
playlists() {
|
||||||
|
return this.$store.state.user.playlists || []
|
||||||
|
},
|
||||||
|
bookCoverAspectRatio() {
|
||||||
|
return this.$store.getters['libraries/getBookCoverAspectRatio']
|
||||||
|
},
|
||||||
|
sortedPlaylists() {
|
||||||
|
return this.playlists
|
||||||
|
.map((playlist) => {
|
||||||
|
const includesItem = !this.selectedPlaylistItems.some((item) => {
|
||||||
|
return !this.checkIsItemInPlaylist(playlist, item)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
isItemIncluded: includesItem,
|
||||||
|
...playlist
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sort((a, b) => (a.isItemIncluded ? -1 : 1))
|
||||||
|
},
|
||||||
|
isBatch() {
|
||||||
|
return this.selectedPlaylistItems.length > 1
|
||||||
|
},
|
||||||
|
selectedPlaylistItems() {
|
||||||
|
return this.$store.state.globals.selectedPlaylistItems || []
|
||||||
|
},
|
||||||
|
currentLibraryId() {
|
||||||
|
return this.$store.state.libraries.currentLibraryId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkIsItemInPlaylist(playlist, item) {
|
||||||
|
if (item.episode) {
|
||||||
|
return playlist.items.some((i) => i.libraryItemId === item.libraryItem.id && i.episodeId === item.episode.id)
|
||||||
|
}
|
||||||
|
return playlist.items.some((i) => i.libraryItemId === item.libraryItem.id)
|
||||||
|
},
|
||||||
|
loadPlaylists() {
|
||||||
|
if (!this.playlists.length) {
|
||||||
|
this.processing = true
|
||||||
|
this.$axios
|
||||||
|
.$get(`/api/playlists`)
|
||||||
|
.then((data) => {
|
||||||
|
this.$store.commit('user/setPlaylists', data.playlists || [])
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to get playlists', error)
|
||||||
|
this.$toast.error('Failed to load user playlists')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeFromPlaylist(playlist) {
|
||||||
|
if (!this.selectedPlaylistItems.length) return
|
||||||
|
this.processing = true
|
||||||
|
|
||||||
|
const itemObjects = this.selectedPlaylistItems.map((pi) => ({ libraryItemId: pi.libraryItem.id, episodeId: pi.episode ? pi.episode.id : null }))
|
||||||
|
this.$axios
|
||||||
|
.$post(`/api/playlists/${playlist.id}/batch/remove`, { items: itemObjects })
|
||||||
|
.then((updatedPlaylist) => {
|
||||||
|
console.log(`Items removed from playlist`, updatedPlaylist)
|
||||||
|
this.$toast.success('Playlist item(s) added')
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to remove items from playlist', error)
|
||||||
|
this.$toast.error('Failed to remove playlist item(s)')
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
addToPlaylist(playlist) {
|
||||||
|
if (!this.selectedPlaylistItems.length) return
|
||||||
|
this.processing = true
|
||||||
|
|
||||||
|
const itemObjects = this.selectedPlaylistItems.map((pi) => ({ libraryItemId: pi.libraryItem.id, episodeId: pi.episode ? pi.episode.id : null }))
|
||||||
|
this.$axios
|
||||||
|
.$post(`/api/playlists/${playlist.id}/batch/add`, { items: itemObjects })
|
||||||
|
.then((updatedPlaylist) => {
|
||||||
|
console.log(`Items added to playlist`, updatedPlaylist)
|
||||||
|
this.$toast.success('Items added to playlist')
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to add items to playlist', error)
|
||||||
|
this.$toast.error('Failed to add items to playlist')
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitCreatePlaylist() {
|
||||||
|
if (!this.newPlaylistName || !this.selectedPlaylistItems.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.processing = true
|
||||||
|
|
||||||
|
const itemObjects = this.selectedPlaylistItems.map((pi) => ({ libraryItemId: pi.libraryItem.id, episodeId: pi.episode ? pi.episode.id : null }))
|
||||||
|
const newPlaylist = {
|
||||||
|
items: itemObjects,
|
||||||
|
libraryId: this.currentLibraryId,
|
||||||
|
name: this.newPlaylistName
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$axios
|
||||||
|
.$post('/api/playlists', newPlaylist)
|
||||||
|
.then((data) => {
|
||||||
|
console.log('New playlist created', data)
|
||||||
|
this.$toast.success(`Playlist "${data.name}" created`)
|
||||||
|
this.processing = false
|
||||||
|
this.newPlaylistName = ''
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to create playlist', error)
|
||||||
|
var errMsg = error.response ? error.response.data || '' : ''
|
||||||
|
this.$toast.error(`Failed to create playlist: ${errMsg}`)
|
||||||
|
this.processing = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.list-complete-item {
|
||||||
|
transition: all 0.8s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-complete-enter-from,
|
||||||
|
.list-complete-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-complete-leave-active {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
58
client/components/modals/playlists/UserPlaylistItem.vue
Normal file
58
client/components/modals/playlists/UserPlaylistItem.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex items-center px-4 py-2 justify-start relative hover:bg-bg" @mouseover="mouseover" @mouseleave="mouseleave">
|
||||||
|
<div v-if="isItemIncluded" class="absolute top-0 left-0 h-full w-1 bg-success z-10" />
|
||||||
|
<div class="w-20 max-w-20 text-center">
|
||||||
|
<!-- <covers-collection-cover :book-items="books" :width="80" :height="40 * bookCoverAspectRatio" :book-cover-aspect-ratio="bookCoverAspectRatio" /> -->
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow overflow-hidden px-2">
|
||||||
|
<nuxt-link :to="`/playlist/${playlist.id}`" class="pl-2 pr-2 truncate hover:underline cursor-pointer" @click.native="clickNuxtLink">{{ playlist.name }}</nuxt-link>
|
||||||
|
</div>
|
||||||
|
<div class="h-full flex items-center justify-end transform" :class="isHovering ? 'transition-transform translate-0 w-16' : 'translate-x-40 w-0'">
|
||||||
|
<ui-btn v-if="!isItemIncluded" color="success" :padding-x="3" small class="h-9" @click.stop="clickAdd"><span class="material-icons text-2xl pt-px">add</span></ui-btn>
|
||||||
|
<ui-btn v-else color="error" :padding-x="3" class="h-9" small @click.stop="clickRem"><span class="material-icons text-2xl pt-px">remove</span></ui-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
playlist: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
bookCoverAspectRatio: Number
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isHovering: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isItemIncluded() {
|
||||||
|
return !!this.playlist.isItemIncluded
|
||||||
|
},
|
||||||
|
items() {
|
||||||
|
return this.playlist.items || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clickNuxtLink() {
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
mouseover() {
|
||||||
|
this.isHovering = true
|
||||||
|
},
|
||||||
|
mouseleave() {
|
||||||
|
this.isHovering = false
|
||||||
|
},
|
||||||
|
clickAdd() {
|
||||||
|
this.$emit('add', this.playlist)
|
||||||
|
},
|
||||||
|
clickRem() {
|
||||||
|
this.$emit('remove', this.playlist)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
<modals-item-edit-modal />
|
<modals-item-edit-modal />
|
||||||
<modals-collections-add-create-modal />
|
<modals-collections-add-create-modal />
|
||||||
|
<modals-playlists-add-create-modal />
|
||||||
<modals-edit-collection-modal />
|
<modals-edit-collection-modal />
|
||||||
<modals-podcast-edit-episode />
|
<modals-podcast-edit-episode />
|
||||||
<modals-podcast-view-episode />
|
<modals-podcast-view-episode />
|
||||||
|
@ -138,7 +138,7 @@
|
|||||||
</ui-btn>
|
</ui-btn>
|
||||||
|
|
||||||
<ui-tooltip v-if="showQueueBtn" :text="isQueued ? $strings.ButtonQueueRemoveItem : $strings.ButtonQueueAddItem" direction="top">
|
<ui-tooltip v-if="showQueueBtn" :text="isQueued ? $strings.ButtonQueueRemoveItem : $strings.ButtonQueueAddItem" direction="top">
|
||||||
<ui-icon-btn :icon="isQueued ? 'playlist_add_check' : 'playlist_add'" class="mx-0.5" :class="isQueued ? 'text-success' : ''" @click="queueBtnClick" />
|
<ui-icon-btn :icon="isQueued ? 'playlist_add_check' : 'playlist_play'" :bg-color="isQueued ? 'primary' : 'success bg-opacity-60'" class="mx-0.5" :class="isQueued ? 'text-success' : ''" @click="queueBtnClick" />
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
<ui-btn v-if="showReadButton" color="info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook">
|
<ui-btn v-if="showReadButton" color="info" :padding-x="4" small class="flex items-center h-9 mr-2" @click="openEbook">
|
||||||
@ -158,6 +158,10 @@
|
|||||||
<ui-icon-btn icon="collections_bookmark" class="mx-0.5" outlined @click="collectionsClick" />
|
<ui-icon-btn icon="collections_bookmark" class="mx-0.5" outlined @click="collectionsClick" />
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
|
<ui-tooltip v-if="!isPodcast" :text="$strings.LabelPlaylists" direction="top">
|
||||||
|
<ui-icon-btn icon="playlist_add" class="mx-0.5" outlined @click="playlistsClick" />
|
||||||
|
</ui-tooltip>
|
||||||
|
|
||||||
<!-- Only admin or root user can download new episodes -->
|
<!-- Only admin or root user can download new episodes -->
|
||||||
<ui-tooltip v-if="isPodcast && userIsAdminOrUp" :text="$strings.LabelFindEpisodes" direction="top">
|
<ui-tooltip v-if="isPodcast && userIsAdminOrUp" :text="$strings.LabelFindEpisodes" direction="top">
|
||||||
<ui-icon-btn icon="search" class="mx-0.5" :loading="fetchingRSSFeed" outlined @click="findEpisodesClick" />
|
<ui-icon-btn icon="search" class="mx-0.5" :loading="fetchingRSSFeed" outlined @click="findEpisodesClick" />
|
||||||
@ -608,6 +612,10 @@ export default {
|
|||||||
this.$store.commit('setSelectedLibraryItem', this.libraryItem)
|
this.$store.commit('setSelectedLibraryItem', this.libraryItem)
|
||||||
this.$store.commit('globals/setShowCollectionsModal', true)
|
this.$store.commit('globals/setShowCollectionsModal', true)
|
||||||
},
|
},
|
||||||
|
playlistsClick() {
|
||||||
|
this.$store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: this.libraryItem }])
|
||||||
|
this.$store.commit('globals/setShowPlaylistsModal', true)
|
||||||
|
},
|
||||||
clickRSSFeed() {
|
clickRSSFeed() {
|
||||||
this.showRssFeedModal = true
|
this.showRssFeedModal = true
|
||||||
},
|
},
|
||||||
|
@ -4,12 +4,14 @@ export const state = () => ({
|
|||||||
showBatchCollectionModal: false,
|
showBatchCollectionModal: false,
|
||||||
showCollectionsModal: false,
|
showCollectionsModal: false,
|
||||||
showEditCollectionModal: false,
|
showEditCollectionModal: false,
|
||||||
|
showPlaylistsModal: false,
|
||||||
showEditPodcastEpisode: false,
|
showEditPodcastEpisode: false,
|
||||||
showViewPodcastEpisodeModal: false,
|
showViewPodcastEpisodeModal: false,
|
||||||
showConfirmPrompt: false,
|
showConfirmPrompt: false,
|
||||||
confirmPromptOptions: null,
|
confirmPromptOptions: null,
|
||||||
showEditAuthorModal: false,
|
showEditAuthorModal: false,
|
||||||
selectedEpisode: null,
|
selectedEpisode: null,
|
||||||
|
selectedPlaylistItems: null,
|
||||||
selectedCollection: null,
|
selectedCollection: null,
|
||||||
selectedAuthor: null,
|
selectedAuthor: null,
|
||||||
isCasting: false, // Actively casting
|
isCasting: false, // Actively casting
|
||||||
@ -79,6 +81,9 @@ export const mutations = {
|
|||||||
setShowEditCollectionModal(state, val) {
|
setShowEditCollectionModal(state, val) {
|
||||||
state.showEditCollectionModal = val
|
state.showEditCollectionModal = val
|
||||||
},
|
},
|
||||||
|
setShowPlaylistsModal(state, val) {
|
||||||
|
state.showPlaylistsModal = val
|
||||||
|
},
|
||||||
setShowEditPodcastEpisodeModal(state, val) {
|
setShowEditPodcastEpisodeModal(state, val) {
|
||||||
state.showEditPodcastEpisode = val
|
state.showEditPodcastEpisode = val
|
||||||
},
|
},
|
||||||
@ -99,6 +104,9 @@ export const mutations = {
|
|||||||
setSelectedEpisode(state, episode) {
|
setSelectedEpisode(state, episode) {
|
||||||
state.selectedEpisode = episode
|
state.selectedEpisode = episode
|
||||||
},
|
},
|
||||||
|
setSelectedPlaylistItems(state, items) {
|
||||||
|
state.selectedPlaylistItems = items
|
||||||
|
},
|
||||||
showEditAuthorModal(state, author) {
|
showEditAuthorModal(state, author) {
|
||||||
state.selectedAuthor = author
|
state.selectedAuthor = author
|
||||||
state.showEditAuthorModal = true
|
state.showEditAuthorModal = true
|
||||||
|
@ -9,7 +9,8 @@ export const state = () => ({
|
|||||||
collapseSeries: false,
|
collapseSeries: false,
|
||||||
collapseBookSeries: false
|
collapseBookSeries: false
|
||||||
},
|
},
|
||||||
settingsListeners: []
|
settingsListeners: [],
|
||||||
|
playlists: []
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
@ -163,5 +164,19 @@ export const mutations = {
|
|||||||
},
|
},
|
||||||
removeSettingsListener(state, listenerId) {
|
removeSettingsListener(state, listenerId) {
|
||||||
state.settingsListeners = state.settingsListeners.filter(l => l.id !== listenerId)
|
state.settingsListeners = state.settingsListeners.filter(l => l.id !== listenerId)
|
||||||
|
},
|
||||||
|
setPlaylists(state, playlists) {
|
||||||
|
state.playlists = playlists
|
||||||
|
},
|
||||||
|
addUpdatePlaylist(state, playlist) {
|
||||||
|
const indexOf = state.playlists.findIndex(p => p.id == playlist.id)
|
||||||
|
if (indexOf >= 0) {
|
||||||
|
state.playlists.splice(indexOf, 1, playlist)
|
||||||
|
} else {
|
||||||
|
state.playlists.push(playlist)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removePlaylist(state, playlist) {
|
||||||
|
state.playlists = state.playlists.filter(p => p.id !== playlist.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -147,6 +147,8 @@
|
|||||||
"LabelAddedAt": "Added At",
|
"LabelAddedAt": "Added At",
|
||||||
"LabelAddToCollection": "Add to Collection",
|
"LabelAddToCollection": "Add to Collection",
|
||||||
"LabelAddToCollectionBatch": "Add {0} Books to Collection",
|
"LabelAddToCollectionBatch": "Add {0} Books to Collection",
|
||||||
|
"LabelAddToPlaylist": "Add to Playlist",
|
||||||
|
"LabelAddToPlaylistBatch": "Add {0} Items to Playlist",
|
||||||
"LabelAll": "All",
|
"LabelAll": "All",
|
||||||
"LabelAllUsers": "All Users",
|
"LabelAllUsers": "All Users",
|
||||||
"LabelAuthor": "Author",
|
"LabelAuthor": "Author",
|
||||||
@ -282,6 +284,7 @@
|
|||||||
"LabelPermissionsUpdate": "Can Update",
|
"LabelPermissionsUpdate": "Can Update",
|
||||||
"LabelPermissionsUpload": "Can Upload",
|
"LabelPermissionsUpload": "Can Upload",
|
||||||
"LabelPhotoPathURL": "Photo Path/URL",
|
"LabelPhotoPathURL": "Photo Path/URL",
|
||||||
|
"LabelPlaylists": "Playlists",
|
||||||
"LabelPlayMethod": "Play Method",
|
"LabelPlayMethod": "Play Method",
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
@ -468,6 +471,7 @@
|
|||||||
"MessageNotYetImplemented": "Not yet implemented",
|
"MessageNotYetImplemented": "Not yet implemented",
|
||||||
"MessageNoUpdateNecessary": "No update necessary",
|
"MessageNoUpdateNecessary": "No update necessary",
|
||||||
"MessageNoUpdatesWereNecessary": "No updates were necessary",
|
"MessageNoUpdatesWereNecessary": "No updates were necessary",
|
||||||
|
"MessageNoUserPlaylists": "You have no playlists",
|
||||||
"MessageOr": "or",
|
"MessageOr": "or",
|
||||||
"MessagePauseChapter": "Pause chapter playback",
|
"MessagePauseChapter": "Pause chapter playback",
|
||||||
"MessagePlayChapter": "Listen to beginning of chapter",
|
"MessagePlayChapter": "Listen to beginning of chapter",
|
||||||
@ -503,6 +507,7 @@
|
|||||||
"NoteUploaderUnsupportedFiles": "Unsupported files are ignored. When choosing or dropping a folder, other files that are not in an item folder are ignored.",
|
"NoteUploaderUnsupportedFiles": "Unsupported files are ignored. When choosing or dropping a folder, other files that are not in an item folder are ignored.",
|
||||||
"PlaceholderNewCollection": "New collection name",
|
"PlaceholderNewCollection": "New collection name",
|
||||||
"PlaceholderNewFolderPath": "New folder path",
|
"PlaceholderNewFolderPath": "New folder path",
|
||||||
|
"PlaceholderNewPlaylist": "New playlist name",
|
||||||
"PlaceholderSearch": "Search..",
|
"PlaceholderSearch": "Search..",
|
||||||
"ToastAccountUpdateFailed": "Failed to update account",
|
"ToastAccountUpdateFailed": "Failed to update account",
|
||||||
"ToastAccountUpdateSuccess": "Account updated",
|
"ToastAccountUpdateSuccess": "Account updated",
|
||||||
|
Loading…
Reference in New Issue
Block a user