Fix episodeId download issue, add draggable for local tracks

This commit is contained in:
advplyr 2022-04-13 20:24:54 -05:00
parent 331a3b4a1a
commit ccba8dc3c7
11 changed files with 150 additions and 15 deletions

View file

@ -5,10 +5,7 @@ import com.audiobookshelf.app.MainActivity
import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.server.ApiHandler import com.audiobookshelf.app.server.ApiHandler
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.getcapacitor.JSObject import com.getcapacitor.*
import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin import com.getcapacitor.annotation.CapacitorPlugin
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -202,6 +199,27 @@ class AbsDatabase : Plugin() {
} }
} }
@PluginMethod
fun updateLocalTrackOrder(call:PluginCall) {
var localLibraryItemId = call.getString("localLibraryItemId", "") ?: ""
var localLibraryItem = DeviceManager.dbManager.getLocalLibraryItem(localLibraryItemId)
if (localLibraryItem == null) {
call.resolve()
return
}
var tracks:JSArray = call.getArray("tracks") ?: JSArray()
Log.d(tag, "updateLocalTrackOrder $tracks")
for (i in 0..tracks.length()) {
var track = tracks.getJSONObject(i)
var localFileId = track.getString("localFileId")
Log.d(tag, "LOCAL FILE ID $localFileId")
}
call.resolve()
}
// //
// Generic Webview calls to db // Generic Webview calls to db
// //

View file

@ -100,8 +100,9 @@ class AbsDownloader : Plugin() {
fun downloadLibraryItem(call: PluginCall) { fun downloadLibraryItem(call: PluginCall) {
var libraryItemId = call.data.getString("libraryItemId").toString() var libraryItemId = call.data.getString("libraryItemId").toString()
var episodeId = call.data.getString("episodeId").toString() var episodeId = call.data.getString("episodeId").toString()
if (episodeId == "null") episodeId = ""
var localFolderId = call.data.getString("localFolderId").toString() var localFolderId = call.data.getString("localFolderId").toString()
Log.d(tag, "Download library item $libraryItemId to folder $localFolderId") Log.d(tag, "Download library item $libraryItemId to folder $localFolderId / episode: $episodeId")
var downloadId = if (episodeId.isNullOrEmpty()) libraryItemId else "$libraryItemId-$episodeId" var downloadId = if (episodeId.isNullOrEmpty()) libraryItemId else "$libraryItemId-$episodeId"
if (downloadQueue.find { it.id == downloadId } != null) { if (downloadQueue.find { it.id == downloadId } != null) {

View file

@ -42,11 +42,9 @@
</div> </div>
<!-- Error widget --> <!-- Error widget -->
<ui-tooltip v-if="showError" :text="errorText" class="absolute bottom-4 left-0 z-10"> <div v-if="showError" :style="{ height: 1.5 * sizeMultiplier + 'rem', width: 2.5 * sizeMultiplier + 'rem' }" class="bg-error rounded-r-full shadow-md flex items-center justify-end border-r border-b border-red-300">
<div :style="{ height: 1.5 * sizeMultiplier + 'rem', width: 2.5 * sizeMultiplier + 'rem' }" class="bg-error rounded-r-full shadow-md flex items-center justify-end border-r border-b border-red-300"> <span class="material-icons text-red-100 pr-1" :style="{ fontSize: 0.875 * sizeMultiplier + 'rem' }">priority_high</span>
<span class="material-icons text-red-100 pr-1" :style="{ fontSize: 0.875 * sizeMultiplier + 'rem' }">priority_high</span> </div>
</div>
</ui-tooltip>
<!-- Volume number --> <!-- Volume number -->
<div v-if="seriesSequence && showSequence && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }"> <div v-if="seriesSequence && showSequence && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }">

View file

@ -24,6 +24,11 @@ export default {
{ hid: 'description', name: 'description', content: '' }, { hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' } { name: 'format-detection', content: 'telephone=no' }
], ],
script: [
{
src: '/libs/sortable.js'
}
],
link: [ link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Ubuntu+Mono&family=Source+Sans+Pro:wght@300;400;600' }, { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Ubuntu+Mono&family=Source+Sans+Pro:wght@300;400;600' },

13
package-lock.json generated
View file

@ -11775,6 +11775,11 @@
} }
} }
}, },
"sortablejs": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
"integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
},
"source-list-map": { "source-list-map": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@ -13190,6 +13195,14 @@
"resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-1.7.14.tgz", "resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-1.7.14.tgz",
"integrity": "sha512-khZR8t3NWZ/JJ2MZxXLbesHrRJ8AKa75PY5Zq8yMifF9x8lHq8ljYkC0d2PD9yahooygQB5tcFyRDkbbIPx8hw==" "integrity": "sha512-khZR8t3NWZ/JJ2MZxXLbesHrRJ8AKa75PY5Zq8yMifF9x8lHq8ljYkC0d2PD9yahooygQB5tcFyRDkbbIPx8hw=="
}, },
"vuedraggable": {
"version": "2.24.3",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
"integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
"requires": {
"sortablejs": "1.10.2"
}
},
"vuex": { "vuex": {
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",

View file

@ -28,7 +28,8 @@
"libarchive.js": "^1.3.0", "libarchive.js": "^1.3.0",
"nuxt": "^2.15.7", "nuxt": "^2.15.7",
"socket.io-client": "^4.1.3", "socket.io-client": "^4.1.3",
"vue-toastification": "^1.7.11" "vue-toastification": "^1.7.11",
"vuedraggable": "^2.24.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.13.15", "@babel/core": "7.13.15",

View file

@ -18,10 +18,36 @@
<div v-if="isScanning" class="w-full text-center p-4"> <div v-if="isScanning" class="w-full text-center p-4">
<p>Scanning...</p> <p>Scanning...</p>
</div> </div>
<div v-else class="w-full media-item-container overflow-y-auto"> <div v-else class="w-full max-w-full media-item-container overflow-y-auto overflow-x-hidden relative">
<div v-if="!isPodcast" class="w-full"> <div v-if="!isPodcast" class="w-full">
<p class="text-base mb-2">Audio Tracks ({{ audioTracks.length }})</p> <p class="text-base mb-2">Audio Tracks ({{ audioTracks.length }})</p>
<template v-for="track in audioTracks">
<draggable v-model="audioTracksCopy" v-bind="dragOptions" handle=".drag-handle" draggable=".item" tag="div" @start="drag = true" @end="drag = false" @update="draggableUpdate">
<transition-group type="transition" :name="!drag ? 'dragtrack' : null">
<template v-for="track in audioTracksCopy">
<div :key="track.localFileId" class="flex items-center my-1 item">
<div class="w-8 h-12 flex items-center justify-center" style="min-width: 32px">
<span class="material-icons drag-handle text-lg text-white text-opacity-50 hover:text-opacity-100">menu</span>
</div>
<div class="w-8 h-12 flex items-center justify-center" style="min-width: 32px">
<p class="font-mono font-bold text-xl">{{ track.index }}</p>
</div>
<div class="flex-grow px-2">
<p class="text-xs">{{ track.title }}</p>
</div>
<div class="w-20 text-center text-gray-300" style="min-width: 80px">
<p class="text-xs">{{ track.mimeType }}</p>
<p class="text-sm">{{ $elapsedPretty(track.duration) }}</p>
</div>
<div class="w-12 h-12 flex items-center justify-center" style="min-width: 48px">
<span class="material-icons" @click="showTrackDialog(track)">more_vert</span>
</div>
</div>
</template>
</transition-group>
</draggable>
<!-- <template v-for="track in audioTracks">
<div :key="track.localFileId" class="flex items-center my-1"> <div :key="track.localFileId" class="flex items-center my-1">
<div class="w-10 h-12 flex items-center justify-center" style="min-width: 48px"> <div class="w-10 h-12 flex items-center justify-center" style="min-width: 48px">
<p class="font-mono font-bold text-xl">{{ track.index }}</p> <p class="font-mono font-bold text-xl">{{ track.index }}</p>
@ -37,7 +63,7 @@
<span class="material-icons" @click="showTrackDialog(track)">more_vert</span> <span class="material-icons" @click="showTrackDialog(track)">more_vert</span>
</div> </div>
</div> </div>
</template> </template> -->
</div> </div>
<div v-else class="w-full"> <div v-else class="w-full">
<p class="text-base mb-2">Episodes ({{ audioTracks.length }})</p> <p class="text-base mb-2">Episodes ({{ audioTracks.length }})</p>
@ -87,11 +113,16 @@
</template> </template>
<script> <script>
import draggable from 'vuedraggable'
import { Capacitor } from '@capacitor/core' import { Capacitor } from '@capacitor/core'
import { Dialog } from '@capacitor/dialog' import { Dialog } from '@capacitor/dialog'
import { AbsFileSystem } from '@/plugins/capacitor' import { AbsFileSystem } from '@/plugins/capacitor'
export default { export default {
components: {
draggable
},
asyncData({ params }) { asyncData({ params }) {
return { return {
localLibraryItemId: params.id localLibraryItemId: params.id
@ -99,8 +130,16 @@ export default {
}, },
data() { data() {
return { return {
drag: false,
dragOptions: {
animation: 200,
group: 'description',
delay: 40,
delayOnTouchOnly: true
},
failed: false, failed: false,
localLibraryItem: null, localLibraryItem: null,
audioTracksCopy: [],
removingItem: false, removingItem: false,
folderId: null, folderId: null,
folder: null, folder: null,
@ -187,6 +226,15 @@ export default {
} }
}, },
methods: { methods: {
draggableUpdate() {
console.log('Draggable update', this.audioTracksCopy)
// var copyOfCopy = this.audioTracksCopy.map((at) => ({ ...at }))
// const payload = {
// localLibraryItemId: this.localLibraryItemId,
// tracks: copyOfCopy
// }
// this.$db.updateLocalTrackOrder(payload)
},
showItemDialog() { showItemDialog() {
this.selectedAudioTrack = null this.selectedAudioTrack = null
this.showDialog = true this.showDialog = true
@ -322,6 +370,8 @@ export default {
return return
} }
this.audioTracksCopy = this.audioTracks.map((at) => ({ ...at }))
this.folderId = this.localLibraryItem.folderId this.folderId = this.localLibraryItem.folderId
this.folder = await this.$db.getLocalFolder(this.folderId) this.folder = await this.$db.getLocalFolder(this.folderId)
} }
@ -332,9 +382,21 @@ export default {
} }
</script> </script>
<style scoped> <style>
.media-item-container { .media-item-container {
height: calc(100vh - 200px); height: calc(100vh - 200px);
max-height: calc(100vh - 200px); max-height: calc(100vh - 200px);
} }
.sortable-ghost {
opacity: 0.5;
}
.dragtrack-enter-from,
.dragtrack-leave-to {
opacity: 0;
transform: translateX(30px);
}
.dragtrack-leave-active {
position: absolute;
}
</style> </style>

View file

@ -111,6 +111,30 @@ class AbsDatabaseWeb extends WebPlugin {
isLocal: true, isLocal: true,
localFileId: 'lf1', localFileId: 'lf1',
audioProbeResult: {} audioProbeResult: {}
},
{
index: 2,
startOffset: 0,
duration: 15000,
title: 'Track Title 2',
contentUrl: 'test2',
mimeType: 'audio/mpeg',
metadata: null,
isLocal: true,
localFileId: 'lf2',
audioProbeResult: {}
},
{
index: 3,
startOffset: 0,
duration: 20000,
title: 'Track Title 3',
contentUrl: 'test3',
mimeType: 'audio/mpeg',
metadata: null,
isLocal: true,
localFileId: 'lf3',
audioProbeResult: {}
} }
] ]
}, },
@ -170,6 +194,10 @@ class AbsDatabaseWeb extends WebPlugin {
async syncLocalMediaProgressWithServer() { async syncLocalMediaProgressWithServer() {
return null return null
} }
async updateLocalTrackOrder({ localLibraryItemId, tracks }) {
return []
}
} }
const AbsDatabase = registerPlugin('AbsDatabase', { const AbsDatabase = registerPlugin('AbsDatabase', {

View file

@ -92,6 +92,10 @@ class DbService {
syncLocalMediaProgressWithServer() { syncLocalMediaProgressWithServer() {
return AbsDatabase.syncLocalMediaProgressWithServer() return AbsDatabase.syncLocalMediaProgressWithServer()
} }
updateLocalTrackOrder(payload) {
return AbsDatabase.updateLocalTrackOrder(payload)
}
} }
export default ({ app, store }, inject) => { export default ({ app, store }, inject) => {

2
static/lib/sortable.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long