Testing video media type

This commit is contained in:
advplyr 2022-05-30 19:26:53 -05:00
parent 705aac40d7
commit acf22ca4fa
27 changed files with 1124 additions and 92 deletions

View file

@ -56,7 +56,7 @@ class Library {
else if (this.icon.endsWith('s') && availableIcons.includes(this.icon.slice(0, -1))) this.icon = this.icon.slice(0, -1)
else this.icon = 'database'
}
if (!this.mediaType || (this.mediaType !== 'podcast' && this.mediaType !== 'book')) {
if (!this.mediaType || (this.mediaType !== 'podcast' && this.mediaType !== 'book' && this.mediaType !== 'video')) {
this.mediaType = 'book'
}
}

View file

@ -6,6 +6,7 @@ const abmetadataGenerator = require('../utils/abmetadataGenerator')
const LibraryFile = require('./files/LibraryFile')
const Book = require('./mediaTypes/Book')
const Podcast = require('./mediaTypes/Podcast')
const Video = require('./mediatypes/Video')
const { areEquivalent, copyValue, getId } = require('../utils/index')
class LibraryItem {
@ -67,11 +68,12 @@ class LibraryItem {
this.mediaType = libraryItem.mediaType
if (this.mediaType === 'book') {
this.media = new Book(libraryItem.media)
this.media.libraryItemId = this.id
} else if (this.mediaType === 'podcast') {
this.media = new Podcast(libraryItem.media)
this.media.libraryItemId = this.id
} else if (this.mediaType === 'video') {
this.media = new Video(libraryItem.media)
}
this.media.libraryItemId = this.id
this.libraryFiles = libraryItem.libraryFiles.map(f => new LibraryFile(f))
}
@ -175,16 +177,15 @@ class LibraryItem {
// Data comes from scandir library item data
setData(libraryMediaType, payload) {
this.id = getId('li')
if (libraryMediaType === 'podcast') {
this.mediaType = 'podcast'
this.mediaType = libraryMediaType
if (libraryMediaType === 'video') {
this.media = new Video()
} else if (libraryMediaType === 'podcast') {
this.media = new Podcast()
this.media.libraryItemId = this.id
} else {
this.mediaType = 'book'
this.media = new Book()
this.media.libraryItemId = this.id
}
this.media.libraryItemId = this.id
for (const key in payload) {
if (key === 'libraryFiles') {
@ -460,6 +461,8 @@ class LibraryItem {
// Saves metadata.abs file
async saveMetadata() {
if (this.mediaType === 'video') return
if (this.isSavingMetadata) return
this.isSavingMetadata = true

View file

@ -4,6 +4,7 @@ const { PlayMethod } = require('../utils/constants')
const BookMetadata = require('./metadata/BookMetadata')
const PodcastMetadata = require('./metadata/PodcastMetadata')
const DeviceInfo = require('./DeviceInfo')
const VideoMetadata = require('./metadata/VideoMetadata')
class PlaybackSession {
constructor(session) {
@ -38,6 +39,7 @@ class PlaybackSession {
// Not saved in DB
this.lastSave = 0
this.audioTracks = []
this.videoTrack = null
this.stream = null
if (session) {
@ -97,6 +99,7 @@ class PlaybackSession {
startedAt: this.startedAt,
updatedAt: this.updatedAt,
audioTracks: this.audioTracks.map(at => at.toJSON()),
videoTrack: this.videoTrack ? this.videoTrack.toJSON() : null,
libraryItem: libraryItem.toJSONExpanded()
}
}
@ -120,6 +123,8 @@ class PlaybackSession {
this.mediaMetadata = new BookMetadata(session.mediaMetadata)
} else if (this.mediaType === 'podcast') {
this.mediaMetadata = new PodcastMetadata(session.mediaMetadata)
} else if (this.mediaType === 'video') {
this.mediaMetadata = new VideoMetadata(session.mediaMetadata)
}
}
this.displayTitle = session.displayTitle || ''

View file

@ -40,13 +40,14 @@ class LibraryFile {
if (globals.SupportedImageTypes.includes(this.metadata.format)) return 'image'
if (globals.SupportedAudioTypes.includes(this.metadata.format)) return 'audio'
if (globals.SupportedEbookTypes.includes(this.metadata.format)) return 'ebook'
if (globals.SupportedVideoTypes.includes(this.metadata.format)) return 'video'
if (globals.TextFileTypes.includes(this.metadata.format)) return 'text'
if (globals.MetadataFileTypes.includes(this.metadata.format)) return 'metadata'
return 'unknown'
}
get isMediaFile() {
return this.fileType === 'audio' || this.fileType === 'ebook'
return this.fileType === 'audio' || this.fileType === 'ebook' || this.fileType === 'video'
}
get isOPFFile() {

View file

@ -0,0 +1,109 @@
const { VideoMimeType } = require('../../utils/constants')
const FileMetadata = require('../metadata/FileMetadata')
class VideoFile {
constructor(data) {
this.index = null
this.ino = null
this.metadata = null
this.addedAt = null
this.updatedAt = null
this.format = null
this.duration = null
this.bitRate = null
this.language = null
this.codec = null
this.timeBase = null
this.frameRate = null
this.width = null
this.height = null
this.embeddedCoverArt = null
this.invalid = false
this.error = null
if (data) {
this.construct(data)
}
}
toJSON() {
return {
index: this.index,
ino: this.ino,
metadata: this.metadata.toJSON(),
addedAt: this.addedAt,
updatedAt: this.updatedAt,
invalid: !!this.invalid,
error: this.error || null,
format: this.format,
duration: this.duration,
bitRate: this.bitRate,
language: this.language,
codec: this.codec,
timeBase: this.timeBase,
frameRate: this.frameRate,
width: this.width,
height: this.height,
embeddedCoverArt: this.embeddedCoverArt,
mimeType: this.mimeType
}
}
construct(data) {
this.index = data.index
this.ino = data.ino
this.metadata = new FileMetadata(data.metadata || {})
this.addedAt = data.addedAt
this.updatedAt = data.updatedAt
this.invalid = !!data.invalid
this.error = data.error || null
this.format = data.format
this.duration = data.duration
this.bitRate = data.bitRate
this.language = data.language
this.codec = data.codec || null
this.timeBase = data.timeBase
this.frameRate = data.frameRate
this.width = data.width
this.height = data.height
this.embeddedCoverArt = data.embeddedCoverArt || null
}
get mimeType() {
var format = this.metadata.format.toUpperCase()
if (VideoMimeType[format]) {
return VideoMimeType[format]
} else {
return VideoMimeType.MP4
}
}
clone() {
return new VideoFile(this.toJSON())
}
setDataFromProbe(libraryFile, probeData) {
this.ino = libraryFile.ino || null
this.metadata = libraryFile.metadata.clone()
this.addedAt = Date.now()
this.updatedAt = Date.now()
const videoStream = probeData.videoStream
this.format = probeData.format
this.duration = probeData.duration
this.bitRate = videoStream.bit_rate || probeData.bitRate || null
this.language = probeData.language
this.codec = videoStream.codec || null
this.timeBase = videoStream.time_base
this.frameRate = videoStream.frame_rate || null
this.width = videoStream.width || null
this.height = videoStream.height || null
this.embeddedCoverArt = probeData.embeddedCoverArt
}
}
module.exports = VideoFile

View file

@ -0,0 +1,42 @@
const Path = require('path')
const { encodeUriPath } = require('../../utils/index')
class VideoTrack {
constructor() {
this.index = null
this.duration = null
this.title = null
this.contentUrl = null
this.mimeType = null
this.metadata = null
}
toJSON() {
return {
index: this.index,
duration: this.duration,
title: this.title,
contentUrl: this.contentUrl,
mimeType: this.mimeType,
metadata: this.metadata ? this.metadata.toJSON() : null
}
}
setData(itemId, videoFile) {
this.index = videoFile.index
this.duration = videoFile.duration
this.title = videoFile.metadata.filename || ''
this.contentUrl = Path.join(`/s/item/${itemId}`, encodeUriPath(videoFile.metadata.relPath))
this.mimeType = videoFile.mimeType
this.metadata = videoFile.metadata.clone()
}
setFromStream(title, duration, contentUrl) {
this.index = 1
this.duration = duration
this.title = title
this.contentUrl = contentUrl
this.mimeType = 'application/vnd.apple.mpegurl'
}
}
module.exports = VideoTrack

View file

@ -0,0 +1,145 @@
const Logger = require('../../Logger')
const VideoFile = require('../files/VideoFile')
const VideoTrack = require('../files/VideoTrack')
const VideoMetadata = require('../metadata/VideoMetadata')
const { areEquivalent, copyValue } = require('../../utils/index')
class Video {
constructor(video) {
this.libraryItemId = null
this.metadata = null
this.coverPath = null
this.tags = []
this.episodes = []
this.autoDownloadEpisodes = false
this.lastEpisodeCheck = 0
this.lastCoverSearch = null
this.lastCoverSearchQuery = null
if (video) {
this.construct(video)
}
}
construct(video) {
this.libraryItemId = video.libraryItemId
this.metadata = new VideoMetadata(video.metadata)
this.coverPath = video.coverPath
this.tags = [...video.tags]
this.videoFile = new VideoFile(video.videoFile)
}
toJSON() {
return {
libraryItemId: this.libraryItemId,
metadata: this.metadata.toJSONExpanded(),
coverPath: this.coverPath,
tags: [...this.tags],
videoFile: this.videoFile.toJSON()
}
}
toJSONMinified() {
return {
metadata: this.metadata.toJSONMinified(),
coverPath: this.coverPath,
tags: [...this.tags],
videoFile: this.videoFile.toJSON(),
size: this.size
}
}
toJSONExpanded() {
return {
libraryItemId: this.libraryItemId,
metadata: this.metadata.toJSONExpanded(),
coverPath: this.coverPath,
tags: [...this.tags],
videoFile: this.videoFile.toJSON(),
size: this.size
}
}
get size() {
return this.videoFile.metadata.size
}
get hasMediaEntities() {
return true
}
get shouldSearchForCover() {
return false
}
get hasEmbeddedCoverArt() {
return false
}
get hasIssues() {
return false
}
get duration() {
return 0
}
update(payload) {
var json = this.toJSON()
var hasUpdates = false
for (const key in json) {
if (payload[key] !== undefined) {
if (key === 'metadata') {
if (this.metadata.update(payload.metadata)) {
hasUpdates = true
}
} else if (!areEquivalent(payload[key], json[key])) {
this[key] = copyValue(payload[key])
Logger.debug('[Video] Key updated', key, this[key])
hasUpdates = true
}
}
}
return hasUpdates
}
updateCover(coverPath) {
coverPath = coverPath.replace(/\\/g, '/')
if (this.coverPath === coverPath) return false
this.coverPath = coverPath
return true
}
removeFileWithInode(inode) {
}
findFileWithInode(inode) {
return null
}
setVideoFile(videoFile) {
this.videoFile = videoFile
}
setData(mediaMetadata) {
this.metadata = new VideoMetadata()
if (mediaMetadata.metadata) {
this.metadata.setData(mediaMetadata.metadata)
}
this.coverPath = mediaMetadata.coverPath || null
}
getPlaybackTitle() {
return this.metadata.title
}
getPlaybackAuthor() {
return ''
}
getVideoTrack() {
var track = new VideoTrack()
track.setData(this.libraryItemId, this.videoFile)
return track
}
}
module.exports = Video

View file

@ -0,0 +1,97 @@
const Logger = require('../../Logger')
const { areEquivalent, copyValue } = require('../../utils/index')
class VideoMetadata {
constructor(metadata) {
this.title = null
this.description = null
this.explicit = false
this.language = null
if (metadata) {
this.construct(metadata)
}
}
construct(metadata) {
this.title = metadata.title
this.description = metadata.description
this.explicit = metadata.explicit
this.language = metadata.language || null
}
toJSON() {
return {
title: this.title,
description: this.description,
explicit: this.explicit,
language: this.language
}
}
toJSONMinified() {
return {
title: this.title,
titleIgnorePrefix: this.titleIgnorePrefix,
description: this.description,
explicit: this.explicit,
language: this.language
}
}
toJSONExpanded() {
return this.toJSONMinified()
}
clone() {
return new VideoMetadata(this.toJSON())
}
get titleIgnorePrefix() {
if (!this.title) return ''
var prefixesToIgnore = global.ServerSettings.sortingPrefixes || []
for (const prefix of prefixesToIgnore) {
// e.g. for prefix "the". If title is "The Book Title" return "Book Title, The"
if (this.title.toLowerCase().startsWith(`${prefix} `)) {
return this.title.substr(prefix.length + 1) + `, ${prefix.substr(0, 1).toUpperCase() + prefix.substr(1)}`
}
}
return this.title
}
searchQuery(query) { // Returns key if match is found
var keysToCheck = ['title']
for (var key of keysToCheck) {
if (this[key] && String(this[key]).toLowerCase().includes(query)) {
return {
matchKey: key,
matchText: this[key]
}
}
}
return null
}
setData(mediaMetadata = {}) {
this.title = mediaMetadata.title || null
this.description = mediaMetadata.description || null
this.explicit = !!mediaMetadata.explicit
this.language = mediaMetadata.language || null
}
update(payload) {
var json = this.toJSON()
var hasUpdates = false
for (const key in json) {
if (payload[key] !== undefined) {
if (!areEquivalent(payload[key], json[key])) {
this[key] = copyValue(payload[key])
Logger.debug('[VideoMetadata] Key updated', key, this[key])
hasUpdates = true
}
}
}
return hasUpdates
}
}
module.exports = VideoMetadata

View file

@ -343,6 +343,7 @@ class User {
checkCanAccessLibraryItem(libraryItem) {
if (!this.checkCanAccessLibrary(libraryItem.libraryId)) return false
if (libraryItem.media.metadata.explicit && !this.canAccessExplicitContent) return false
return this.checkCanAccessLibraryItemWithTags(libraryItem.media.tags)
}