mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Add:Listening session modal with all details
This commit is contained in:
parent
f002532c1e
commit
b2aab06e01
150
client/components/modals/ListeningSessionModal.vue
Normal file
150
client/components/modals/ListeningSessionModal.vue
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<template>
|
||||||
|
<modals-modal v-model="show" name="listening-session-modal" :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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden p-6" style="max-height: 80vh">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<p class="text-base text-gray-200">{{ _session.displayTitle }}</p>
|
||||||
|
<p v-if="_session.displayAuthor" class="text-xs text-gray-400 px-4">by {{ _session.displayAuthor }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<div class="flex items-center -mx-1 mb-1">
|
||||||
|
<div class="w-40 px-1 text-gray-200">Started At</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="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="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="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="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>
|
||||||
|
<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="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="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="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="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="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="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="mb-1">{{ playMethodName }}</p>
|
||||||
|
<p class="mb-1">{{ _session.mediaPlayer }}</p>
|
||||||
|
|
||||||
|
<p class="font-semibold uppercase text-xs text-gray-400 tracking-wide mt-6 mb-2">Device</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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</modals-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: Boolean,
|
||||||
|
session: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
show: {
|
||||||
|
get() {
|
||||||
|
return this.value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('input', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_session() {
|
||||||
|
return this.session || {}
|
||||||
|
},
|
||||||
|
deviceInfo() {
|
||||||
|
return this._session.deviceInfo || {}
|
||||||
|
},
|
||||||
|
osDisplayName() {
|
||||||
|
if (!this.deviceInfo.osName) return null
|
||||||
|
return `${this.deviceInfo.osName} ${this.deviceInfo.osVersion}`
|
||||||
|
},
|
||||||
|
clientDisplayName() {
|
||||||
|
if (!this.deviceInfo.manufacturer || !this.deviceInfo.model) return null
|
||||||
|
return `${this.deviceInfo.manufacturer} ${this.deviceInfo.model}`
|
||||||
|
},
|
||||||
|
playMethodName() {
|
||||||
|
const playMethod = this._session.playMethod
|
||||||
|
if (playMethod === this.$constants.PlayMethod.DIRECTPLAY) return 'Direct Play'
|
||||||
|
else if (playMethod === this.$constants.PlayMethod.TRANSCODE) return 'Transcode'
|
||||||
|
else if (playMethod === this.$constants.PlayMethod.DIRECTSTREAM) return 'Direct Stream'
|
||||||
|
else if (playMethod === this.$constants.PlayMethod.LOCAL) return 'Local'
|
||||||
|
return 'Unknown'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
@ -37,7 +37,11 @@
|
|||||||
<div class="flex flex-col md:flex-row overflow-hidden max-w-full">
|
<div class="flex flex-col md:flex-row overflow-hidden max-w-full">
|
||||||
<stats-daily-listening-chart :listening-stats="listeningStats" class="origin-top-left transform scale-75 lg:scale-100" />
|
<stats-daily-listening-chart :listening-stats="listeningStats" class="origin-top-left transform scale-75 lg:scale-100" />
|
||||||
<div class="w-80 my-6 mx-auto">
|
<div class="w-80 my-6 mx-auto">
|
||||||
<h1 class="text-2xl mb-4 font-book">Recent Listening Sessions</h1>
|
<div class="flex mb-4 items-center">
|
||||||
|
<h1 class="text-2xl font-book">Recent Sessions</h1>
|
||||||
|
<div class="flex-grow" />
|
||||||
|
<ui-btn :to="`/config/users/${user.id}/sessions`" class="text-xs" :padding-x="1.5" :padding-y="1">View All</ui-btn>
|
||||||
|
</div>
|
||||||
<p v-if="!mostRecentListeningSessions.length">No Listening Sessions</p>
|
<p v-if="!mostRecentListeningSessions.length">No Listening Sessions</p>
|
||||||
<template v-for="(item, index) in mostRecentListeningSessions">
|
<template v-for="(item, index) in mostRecentListeningSessions">
|
||||||
<div :key="item.id" class="w-full py-0.5">
|
<div :key="item.id" class="w-full py-0.5">
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-full h-px bg-white bg-opacity-10 my-2" />
|
<div class="w-full h-px bg-white bg-opacity-10 my-2" />
|
||||||
<div class="py-2">
|
<div class="py-2">
|
||||||
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Item Progress</h1>
|
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Saved Media Progress</h1>
|
||||||
<table v-if="mediaProgress.length" class="userAudiobooksTable">
|
<table v-if="mediaProgress.length" class="userAudiobooksTable">
|
||||||
<tr class="bg-primary bg-opacity-40">
|
<tr class="bg-primary bg-opacity-40">
|
||||||
<th class="w-16 text-left">Item</th>
|
<th class="w-16 text-left">Item</th>
|
||||||
|
@ -17,24 +17,23 @@
|
|||||||
<div class="w-full h-px bg-white bg-opacity-10 my-2" />
|
<div class="w-full h-px bg-white bg-opacity-10 my-2" />
|
||||||
|
|
||||||
<div class="py-2">
|
<div class="py-2">
|
||||||
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Listening Sessions</h1>
|
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Listening Sessions ({{ listeningSessions.length }})</h1>
|
||||||
<table v-if="listeningSessions.length" class="userSessionsTable">
|
<table v-if="listeningSessions.length" class="userSessionsTable">
|
||||||
<tr class="bg-primary bg-opacity-40">
|
<tr class="bg-primary bg-opacity-40">
|
||||||
<th class="flex-grow text-left">Item</th>
|
<th class="flex-grow text-left">Item</th>
|
||||||
<th class="w-40 text-left hidden md:table-cell">Play Method</th>
|
<th class="w-32 text-left hidden md:table-cell">Play Method</th>
|
||||||
<th class="w-40 text-left hidden sm:table-cell">Device Info</th>
|
<th class="w-40 text-left hidden sm:table-cell">Device Info</th>
|
||||||
<th class="w-20">Listening Time</th>
|
<th class="w-20">Listened</th>
|
||||||
<th class="w-20">Last Time</th>
|
<th class="w-20">Last Time</th>
|
||||||
<!-- <th class="w-40 hidden sm:table-cell">Started At</th> -->
|
|
||||||
<th class="w-40 hidden sm:table-cell">Last Update</th>
|
<th class="w-40 hidden sm:table-cell">Last Update</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-for="session in listeningSessions" :key="session.id">
|
<tr v-for="session in listeningSessions" :key="session.id" class="cursor-pointer" @click="showSession(session)">
|
||||||
<td class="py-1">
|
<td class="py-1">
|
||||||
<p class="text-sm text-gray-200">{{ session.displayTitle }}</p>
|
<p class="text-sm text-gray-200">{{ session.displayTitle }}</p>
|
||||||
<p class="text-xs text-gray-400">{{ session.displayAuthor }}</p>
|
<p class="text-xs text-gray-400">{{ session.displayAuthor }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden md:table-cell">
|
<td class="hidden md:table-cell">
|
||||||
<p class="text-xs">{{ getPlayMethodName(session.playMethod) }} with {{ session.mediaPlayer }}</p>
|
<p class="text-xs">{{ getPlayMethodName(session.playMethod) }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden sm:table-cell">
|
<td class="hidden sm:table-cell">
|
||||||
<p class="text-xs" v-html="getDeviceInfoString(session.deviceInfo)" />
|
<p class="text-xs" v-html="getDeviceInfoString(session.deviceInfo)" />
|
||||||
@ -45,11 +44,6 @@
|
|||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
<p class="text-xs font-mono">{{ $secondsToTimestamp(session.currentTime) }}</p>
|
||||||
</td>
|
</td>
|
||||||
<!-- <td class="text-center hidden sm:table-cell">
|
|
||||||
<ui-tooltip v-if="session.startedAt" direction="top" :text="$formatDate(session.startedAt, 'MMMM do, yyyy HH:mm')">
|
|
||||||
<p class="text-xs">{{ $dateDistanceFromNow(session.startedAt) }}</p>
|
|
||||||
</ui-tooltip>
|
|
||||||
</td> -->
|
|
||||||
<td class="text-center hidden sm:table-cell">
|
<td class="text-center hidden sm:table-cell">
|
||||||
<ui-tooltip v-if="session.updatedAt" direction="top" :text="$formatDate(session.updatedAt, 'MMMM do, yyyy HH:mm')">
|
<ui-tooltip v-if="session.updatedAt" direction="top" :text="$formatDate(session.updatedAt, 'MMMM do, yyyy HH:mm')">
|
||||||
<p class="text-xs">{{ $dateDistanceFromNow(session.updatedAt) }}</p>
|
<p class="text-xs">{{ $dateDistanceFromNow(session.updatedAt) }}</p>
|
||||||
@ -60,6 +54,8 @@
|
|||||||
<p v-else class="text-white text-opacity-50">No sessions yet...</p>
|
<p v-else class="text-white text-opacity-50">No sessions yet...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<modals-listening-session-modal v-model="showSessionModal" :session="selectedSession" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -77,6 +73,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
showSessionModal: false,
|
||||||
|
selectedSession: null,
|
||||||
listeningSessions: []
|
listeningSessions: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -89,6 +87,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
showSession(session) {
|
||||||
|
this.selectedSession = session
|
||||||
|
this.showSessionModal = true
|
||||||
|
},
|
||||||
getDeviceInfoString(deviceInfo) {
|
getDeviceInfoString(deviceInfo) {
|
||||||
if (!deviceInfo) return ''
|
if (!deviceInfo) return ''
|
||||||
var lines = []
|
var lines = []
|
||||||
@ -127,12 +129,15 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid #474747;
|
border: 1px solid #474747;
|
||||||
}
|
}
|
||||||
.userSessionsTable tr:nth-child(even) {
|
.userSessionsTable tr:first-child {
|
||||||
background-color: #2e2e2e;
|
background-color: #272727;
|
||||||
}
|
}
|
||||||
.userSessionsTable tr:not(:first-child) {
|
.userSessionsTable tr:not(:first-child) {
|
||||||
background-color: #373838;
|
background-color: #373838;
|
||||||
}
|
}
|
||||||
|
.userSessionsTable tr:not(:first-child):nth-child(odd) {
|
||||||
|
background-color: #2f2f2f;
|
||||||
|
}
|
||||||
.userSessionsTable tr:hover:not(:first-child) {
|
.userSessionsTable tr:hover:not(:first-child) {
|
||||||
background-color: #474747;
|
background-color: #474747;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ class PlaybackSession {
|
|||||||
constructor(session) {
|
constructor(session) {
|
||||||
this.id = null
|
this.id = null
|
||||||
this.userId = null
|
this.userId = null
|
||||||
|
this.libraryId = null
|
||||||
this.libraryItemId = null
|
this.libraryItemId = null
|
||||||
this.episodeId = null
|
this.episodeId = null
|
||||||
|
|
||||||
@ -47,8 +48,8 @@ class PlaybackSession {
|
|||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
sessionType: this.sessionType,
|
|
||||||
userId: this.userId,
|
userId: this.userId,
|
||||||
|
libraryId: this.libraryId,
|
||||||
libraryItemId: this.libraryItemId,
|
libraryItemId: this.libraryItemId,
|
||||||
episodeId: this.episodeId,
|
episodeId: this.episodeId,
|
||||||
mediaType: this.mediaType,
|
mediaType: this.mediaType,
|
||||||
@ -74,8 +75,8 @@ class PlaybackSession {
|
|||||||
toJSONForClient(libraryItem) {
|
toJSONForClient(libraryItem) {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
sessionType: this.sessionType,
|
|
||||||
userId: this.userId,
|
userId: this.userId,
|
||||||
|
libraryId: this.libraryId,
|
||||||
libraryItemId: this.libraryItemId,
|
libraryItemId: this.libraryItemId,
|
||||||
episodeId: this.episodeId,
|
episodeId: this.episodeId,
|
||||||
mediaType: this.mediaType,
|
mediaType: this.mediaType,
|
||||||
@ -102,8 +103,8 @@ class PlaybackSession {
|
|||||||
|
|
||||||
construct(session) {
|
construct(session) {
|
||||||
this.id = session.id
|
this.id = session.id
|
||||||
this.sessionType = session.sessionType
|
|
||||||
this.userId = session.userId
|
this.userId = session.userId
|
||||||
|
this.libraryId = session.libraryId || null
|
||||||
this.libraryItemId = session.libraryItemId
|
this.libraryItemId = session.libraryItemId
|
||||||
this.episodeId = session.episodeId
|
this.episodeId = session.episodeId
|
||||||
this.mediaType = session.mediaType
|
this.mediaType = session.mediaType
|
||||||
@ -143,6 +144,7 @@ class PlaybackSession {
|
|||||||
setData(libraryItem, user, mediaPlayer, deviceInfo, startTime, episodeId = null) {
|
setData(libraryItem, user, mediaPlayer, deviceInfo, startTime, episodeId = null) {
|
||||||
this.id = getId('play')
|
this.id = getId('play')
|
||||||
this.userId = user.id
|
this.userId = user.id
|
||||||
|
this.libraryId = libraryItem.libraryId
|
||||||
this.libraryItemId = libraryItem.id
|
this.libraryItemId = libraryItem.id
|
||||||
this.episodeId = episodeId
|
this.episodeId = episodeId
|
||||||
this.mediaType = libraryItem.mediaType
|
this.mediaType = libraryItem.mediaType
|
||||||
@ -161,7 +163,6 @@ class PlaybackSession {
|
|||||||
this.mediaPlayer = mediaPlayer
|
this.mediaPlayer = mediaPlayer
|
||||||
this.deviceInfo = deviceInfo || new DeviceInfo()
|
this.deviceInfo = deviceInfo || new DeviceInfo()
|
||||||
|
|
||||||
|
|
||||||
this.timeListening = 0
|
this.timeListening = 0
|
||||||
this.startTime = startTime
|
this.startTime = startTime
|
||||||
this.currentTime = startTime
|
this.currentTime = startTime
|
||||||
|
Loading…
Reference in New Issue
Block a user