Resume videos for non-logged in users (#3885)

* client: resume videos for non-logged in users

closes #3866

* fix build for embeded

* Update client/src/app/app.component.ts

* fix review comments
pull/3909/head
kontrollanten 2021-03-31 11:26:32 +02:00 committed by GitHub
parent d794137057
commit 58b9ce3080
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 8 deletions

View File

@ -29,7 +29,7 @@ import { MetaService } from '@ngx-meta/core'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
import { ServerConfig, ServerErrorCode, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' import { ServerConfig, ServerErrorCode, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models'
import { getStoredP2PEnabled, getStoredTheater } from '../../../assets/player/peertube-player-local-storage' import { cleanupVideoWatch, getStoredP2PEnabled, getStoredTheater, getStoredVideoWatchHistory } from '../../../assets/player/peertube-player-local-storage'
import { import {
CustomizationOptions, CustomizationOptions,
P2PMediaLoaderOptions, P2PMediaLoaderOptions,
@ -195,6 +195,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.theaterEnabled = getStoredTheater() this.theaterEnabled = getStoredTheater()
this.hooks.runAction('action:video-watch.init', 'video-watch') this.hooks.runAction('action:video-watch.init', 'video-watch')
setTimeout(cleanupVideoWatch, 1500) // Run in timeout to ensure we're not blocking the UI
} }
ngOnDestroy () { ngOnDestroy () {
@ -768,9 +770,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
const getStartTime = () => { const getStartTime = () => {
const byUrl = urlOptions.startTime !== undefined const byUrl = urlOptions.startTime !== undefined
const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined) const byHistory = video.userHistory && (!this.playlist || urlOptions.resume !== undefined)
const byLocalStorage = getStoredVideoWatchHistory(video.uuid)
if (byUrl) return timeToInt(urlOptions.startTime) if (byUrl) return timeToInt(urlOptions.startTime)
if (byHistory) return video.userHistory.currentTime if (byHistory) return video.userHistory.currentTime
if (byLocalStorage) return byLocalStorage.duration
return 0 return 0
} }
@ -828,7 +832,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
serverUrl: environment.apiUrl, serverUrl: environment.apiUrl,
videoCaptions: playerCaptions videoCaptions: playerCaptions,
videoUUID: video.uuid
}, },
webtorrent: { webtorrent: {

View File

@ -68,6 +68,51 @@ function getStoredLastSubtitle () {
return getLocalStorage('last-subtitle') return getLocalStorage('last-subtitle')
} }
function saveVideoWatchHistory(videoUUID: string, duration: number) {
return setLocalStorage(`video-watch-history`, JSON.stringify({
...getStoredVideoWatchHistory(),
[videoUUID]: {
duration,
date: `${(new Date()).toISOString()}`
}
}))
}
function getStoredVideoWatchHistory(videoUUID?: string) {
let data
try {
data = JSON.parse(getLocalStorage('video-watch-history'))
} catch (error) {
console.error('Cannot parse video watch history from local storage: ', error)
}
data = data || {}
if (videoUUID) return data[videoUUID]
return data
}
function cleanupVideoWatch() {
const data = getStoredVideoWatchHistory()
const newData = Object.keys(data).reduce((acc, videoUUID) => {
const date = Date.parse(data[videoUUID].date)
const diff = Math.ceil(((new Date()).getTime() - date) / (1000 * 3600 * 24))
if (diff > 30) return acc
return {
...acc,
[videoUUID]: data[videoUUID]
}
}, {})
setLocalStorage('video-watch-history', JSON.stringify(newData))
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export { export {
@ -81,7 +126,10 @@ export {
saveAverageBandwidth, saveAverageBandwidth,
getAverageBandwidthInStore, getAverageBandwidthInStore,
saveLastSubtitle, saveLastSubtitle,
getStoredLastSubtitle getStoredLastSubtitle,
saveVideoWatchHistory,
getStoredVideoWatchHistory,
cleanupVideoWatch
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -106,6 +106,8 @@ export interface CommonOptions extends CustomizationOptions {
videoCaptions: VideoJSCaption[] videoCaptions: VideoJSCaption[]
videoUUID: string
userWatching?: UserWatching userWatching?: UserWatching
serverUrl: string serverUrl: string
@ -231,7 +233,8 @@ export class PeertubePlayerManager {
subtitle: commonOptions.subtitle, subtitle: commonOptions.subtitle,
videoCaptions: commonOptions.videoCaptions, videoCaptions: commonOptions.videoCaptions,
stopTime: commonOptions.stopTime, stopTime: commonOptions.stopTime,
isLive: commonOptions.isLive isLive: commonOptions.isLive,
videoUUID: commonOptions.videoUUID
} }
} }

View File

@ -13,6 +13,7 @@ import {
getStoredVolume, getStoredVolume,
saveLastSubtitle, saveLastSubtitle,
saveMuteInStore, saveMuteInStore,
saveVideoWatchHistory,
saveVolumeInStore saveVolumeInStore
} from './peertube-player-local-storage' } from './peertube-player-local-storage'
@ -120,7 +121,7 @@ class PeerTubePlugin extends Plugin {
this.initializePlayer() this.initializePlayer()
this.runViewAdd() this.runViewAdd()
if (options.userWatching) this.runUserWatchVideo(options.userWatching) this.runUserWatchVideo(options.userWatching, options.videoUUID)
}) })
} }
@ -178,7 +179,7 @@ class PeerTubePlugin extends Plugin {
}, 1000) }, 1000)
} }
private runUserWatchVideo (options: UserWatching) { private runUserWatchVideo (options: UserWatching, videoUUID: string) {
let lastCurrentTime = 0 let lastCurrentTime = 0
this.userWatchingVideoInterval = setInterval(() => { this.userWatchingVideoInterval = setInterval(() => {
@ -187,8 +188,12 @@ class PeerTubePlugin extends Plugin {
if (currentTime - lastCurrentTime >= 1) { if (currentTime - lastCurrentTime >= 1) {
lastCurrentTime = currentTime lastCurrentTime = currentTime
if (options) {
this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader) this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader)
.catch(err => console.error('Cannot notify user is watching.', err)) .catch(err => console.error('Cannot notify user is watching.', err))
} else {
saveVideoWatchHistory(videoUUID, currentTime)
}
} }
}, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL) }, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL)
} }

View File

@ -108,6 +108,8 @@ type PeerTubePluginOptions = {
stopTime: number | string stopTime: number | string
isLive: boolean isLive: boolean
videoUUID: string
} }
type PlaylistPluginOptions = { type PlaylistPluginOptions = {

View File

@ -531,6 +531,7 @@ export class PeerTubeEmbed {
videoCaptions, videoCaptions,
inactivityTimeout: 2500, inactivityTimeout: 2500,
videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views', videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views',
videoUUID: videoInfo.uuid,
isLive: videoInfo.isLive, isLive: videoInfo.isLive,