first_progress

This commit is contained in:
kontiko 2024-06-23 23:29:00 +02:00
parent 09d7880779
commit 2d10669700
15 changed files with 643 additions and 108 deletions

View file

@ -0,0 +1,88 @@
import EventEmitter from 'events'
export default class DLNAPlayer extends EventEmitter {
constructor(ctx) {
super()
this.ctx = ctx
this.player = null
this.playerController = null
this.libraryItem = null
this.audioTracks = []
this.currentTrackIndex = 0
this.isHlsTranscode = null
this.currentTime = 0
this.playWhenReady = false
this.defaultPlaybackRate = 1
// TODO: Use canDisplayType on receiver to check mime types
this.playableMimeTypes = ['audio/flac', 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/aac', 'audio/x-ms-wma', 'audio/x-aiff', 'audio/webm']
this.coverUrl = ''
this.castPlayerState = 'IDLE'
// Supported audio codecs for chromecast
this.supportedAudioCodecs = ['opus', 'mp3', 'aac', 'flac', 'webma', 'wav']
this.initialize()
}
get currentTrack() {
return this.audioTracks[this.currentTrackIndex] || {}
}
initialize() {
this.ctx.$root.socket.on('test', this.destroy)
//this.player = this.ctx.$root.castPlayer
}
evtMediaInfoChanged() {}
destroy() {}
async set(libraryItem, tracks, isHlsTranscode, startTime, playWhenReady = false) {
this.libraryItem = libraryItem
this.audioTracks = tracks
this.isHlsTranscode = isHlsTranscode
this.playWhenReady = playWhenReady
this.currentTime = startTime
console.log('Tracks: ')
console.log(tracks.find((at) => at.startOffset <= startTime && at.startOffset + at.duration > startTime))
var coverImg = this.ctx.$store.getters['globals/getLibraryItemCoverSrc'](libraryItem)
if (process.env.NODE_ENV === 'development') {
this.coverUrl = coverImg
} else {
this.coverUrl = `${window.location.origin}${coverImg}`
}
this.ctx.$root.socket.emit('dlna_start', this.audioTracks)
}
resetStream(startTime) {}
playPause() {}
play() {}
pause() {}
getCurrentTime() {
//var currentTrackOffset = this.currentTrack.startOffset || 0
return 0 //this.player ? currentTrackOffset + this.player.currentTime : 0
}
getDuration() {
if (!this.audioTracks.length) return 0
var lastTrack = this.audioTracks[this.audioTracks.length - 1]
return lastTrack.startOffset + lastTrack.duration
}
setPlaybackRate(playbackRate) {
this.defaultPlaybackRate = playbackRate
}
async seek(time, playWhenReady) {}
setVolume(volume) {}
}

View file

@ -113,7 +113,6 @@ export default class LocalAudioPlayer extends EventEmitter {
this.isHlsTranscode = isHlsTranscode
this.playWhenReady = playWhenReady
this.startTime = startTime
if (this.hlsInstance) {
this.destroyHlsInstance()
}
@ -147,7 +146,7 @@ export default class LocalAudioPlayer extends EventEmitter {
timeoutRetry: {
maxNumRetry: 4,
retryDelayMs: 0,
maxRetryDelayMs: 0,
maxRetryDelayMs: 0
},
errorRetry: {
maxNumRetry: 8,
@ -160,7 +159,7 @@ export default class LocalAudioPlayer extends EventEmitter {
}
return retry
}
},
}
}
}
}
@ -194,7 +193,7 @@ export default class LocalAudioPlayer extends EventEmitter {
setDirectPlay() {
// Set initial track and track time offset
var trackIndex = this.audioTracks.findIndex(t => this.startTime >= t.startOffset && this.startTime < (t.startOffset + t.duration))
var trackIndex = this.audioTracks.findIndex((t) => this.startTime >= t.startOffset && this.startTime < t.startOffset + t.duration)
this.currentTrackIndex = trackIndex >= 0 ? trackIndex : 0
this.loadCurrentTrack()
@ -270,7 +269,7 @@ export default class LocalAudioPlayer extends EventEmitter {
// Seeking Direct play
if (time < this.currentTrack.startOffset || time > this.currentTrack.startOffset + this.currentTrack.duration) {
// Change Track
var trackIndex = this.audioTracks.findIndex(t => time >= t.startOffset && time < (t.startOffset + t.duration))
var trackIndex = this.audioTracks.findIndex((t) => time >= t.startOffset && time < t.startOffset + t.duration)
if (trackIndex >= 0) {
this.startTime = time
this.currentTrackIndex = trackIndex
@ -293,7 +292,6 @@ export default class LocalAudioPlayer extends EventEmitter {
this.player.volume = volume
}
// Utils
isValidDuration(duration) {
if (duration && !isNaN(duration) && duration !== Number.POSITIVE_INFINITY && duration !== Number.NEGATIVE_INFINITY) {
@ -338,4 +336,4 @@ export default class LocalAudioPlayer extends EventEmitter {
var last = bufferedRanges[bufferedRanges.length - 1]
return last.end
}
}
}

View file

@ -3,6 +3,7 @@ import LocalVideoPlayer from './LocalVideoPlayer'
import CastPlayer from './CastPlayer'
import AudioTrack from './AudioTrack'
import VideoTrack from './VideoTrack'
import DLNAPlayer from './DLNAPlayer'
export default class PlayerHandler {
constructor(ctx) {
@ -32,14 +33,17 @@ export default class PlayerHandler {
get isCasting() {
return this.ctx.$store.state.globals.isCasting
}
get get_Dlna() {
return this.ctx.$store.state.globals.dlnaDevice
}
get libraryItemId() {
return this.libraryItem ? this.libraryItem.id : null
}
get isPlayingCastedItem() {
return this.libraryItem && (this.player instanceof CastPlayer)
return this.libraryItem && this.player instanceof CastPlayer
}
get isPlayingLocalItem() {
return this.libraryItem && (this.player instanceof LocalAudioPlayer)
return this.libraryItem && this.player instanceof LocalAudioPlayer
}
get userToken() {
return this.ctx.$store.getters['user/getToken']
@ -49,7 +53,7 @@ export default class PlayerHandler {
}
get episode() {
if (!this.episodeId) return null
return this.libraryItem.media.episodes.find(ep => ep.id === this.episodeId)
return this.libraryItem.media.episodes.find((ep) => ep.id === this.episodeId)
}
setSessionId(sessionId) {
@ -66,7 +70,7 @@ export default class PlayerHandler {
this.playWhenReady = playWhenReady
this.initialPlaybackRate = this.isMusic ? 1 : playbackRate
this.startTimeOverride = (startTimeOverride == null || isNaN(startTimeOverride)) ? undefined : Number(startTimeOverride)
this.startTimeOverride = startTimeOverride == null || isNaN(startTimeOverride) ? undefined : Number(startTimeOverride)
if (!this.player) this.switchPlayer(playWhenReady)
else this.prepare()
@ -86,6 +90,22 @@ export default class PlayerHandler {
this.player = new CastPlayer(this.ctx)
this.setPlayerListeners()
if (this.libraryItem) {
// libraryItem was already loaded - prepare for cast
this.playWhenReady = playWhenReady
this.prepare()
}
} else if (this.get_Dlna) {
console.log('[PlayerHandler] Switching to DLNA player')
this.stopPlayInterval()
this.playerStateChange('LOADING')
this.startTime = this.player ? this.player.getCurrentTime() : this.startTime
if (this.player) {
this.player.destroy()
}
this.player = new DLNAPlayer(this.ctx)
this.setPlayerListeners()
if (this.libraryItem) {
// libraryItem was already loaded - prepare for cast
this.playWhenReady = playWhenReady
@ -127,7 +147,7 @@ export default class PlayerHandler {
playerError() {
// Switch to HLS stream on error
if (!this.isCasting && (this.player instanceof LocalAudioPlayer)) {
if (!this.isCasting && this.player instanceof LocalAudioPlayer) {
console.log(`[PlayerHandler] Audio player error switching to HLS stream`)
this.prepare(true)
}
@ -199,15 +219,17 @@ export default class PlayerHandler {
forceTranscode,
forceDirectPlay: this.isCasting || this.isVideo // TODO: add transcode support for chromecast
}
console.log('Payload:', payload)
const path = this.episodeId ? `/api/items/${this.libraryItem.id}/play/${this.episodeId}` : `/api/items/${this.libraryItem.id}/play`
const session = await this.ctx.$axios.$post(path, payload).catch((error) => {
console.error('Failed to start stream', error)
})
console.log(session)
this.prepareSession(session)
}
prepareOpenSession(session, playbackRate) { // Session opened on init socket
prepareOpenSession(session, playbackRate) {
// Session opened on init socket
if (!this.player) this.switchPlayer() // Must set player first for open sessions
this.libraryItem = session.libraryItem
@ -241,14 +263,14 @@ export default class PlayerHandler {
this.player.set(this.libraryItem, videoTrack, this.isHlsTranscode, this.startTime, this.playWhenReady)
} else {
var audioTracks = session.audioTracks.map(at => new AudioTrack(at, this.userToken))
var audioTracks = session.audioTracks.map((at) => new AudioTrack(at, this.userToken))
this.ctx.playerLoading = true
this.isHlsTranscode = true
if (session.playMethod === this.ctx.$constants.PlayMethod.DIRECTPLAY) {
this.isHlsTranscode = false
}
console.log('blub', audioTracks)
this.player.set(this.libraryItem, audioTracks, this.isHlsTranscode, this.startTime, this.playWhenReady)
}
@ -295,7 +317,7 @@ export default class PlayerHandler {
const currentTime = this.player.getCurrentTime()
this.ctx.setCurrentTime(currentTime)
const exactTimeElapsed = ((Date.now() - lastTick) / 1000)
const exactTimeElapsed = (Date.now() - lastTick) / 1000
lastTick = Date.now()
this.listeningTimeSinceSync += exactTimeElapsed
const TimeToWaitBeforeSync = this.lastSyncTime > 0 ? 10 : 20
@ -340,17 +362,20 @@ export default class PlayerHandler {
}
this.listeningTimeSinceSync = 0
this.ctx.$axios.$post(`/api/session/${this.currentSessionId}/sync`, syncData, { timeout: 9000 }).then(() => {
this.failedProgressSyncs = 0
}).catch((error) => {
console.error('Failed to update session progress', error)
// After 4 failed sync attempts show an alert toast
this.failedProgressSyncs++
if (this.failedProgressSyncs >= 4) {
this.ctx.showFailedProgressSyncs()
this.ctx.$axios
.$post(`/api/session/${this.currentSessionId}/sync`, syncData, { timeout: 9000 })
.then(() => {
this.failedProgressSyncs = 0
}
})
})
.catch((error) => {
console.error('Failed to update session progress', error)
// After 4 failed sync attempts show an alert toast
this.failedProgressSyncs++
if (this.failedProgressSyncs >= 4) {
this.ctx.showFailedProgressSyncs()
this.failedProgressSyncs = 0
}
})
}
stopPlayInterval() {
@ -411,4 +436,4 @@ export default class PlayerHandler {
this.sendProgressSync(time)
}
}
}
}