Merge branch 'master' into liaocl

This commit is contained in:
advplyr 2024-02-17 12:56:05 -06:00
commit 2ec52a7a45
119 changed files with 11524 additions and 25894 deletions

View file

@ -1,23 +1,28 @@
const Path = require('path')
const date = require('../libs/dateAndTime')
const fs = require('../libs/fsExtra')
const { readTextFile } = require('../utils/fileUtils')
const fileUtils = require('../utils/fileUtils')
const Logger = require('../Logger')
class DailyLog {
constructor() {
this.id = null
this.datePretty = null
/**
*
* @param {string} dailyLogDirPath Path to daily logs /metadata/logs/daily
*/
constructor(dailyLogDirPath) {
this.id = date.format(new Date(), 'YYYY-MM-DD')
this.dailyLogDirPath = null
this.filename = null
this.path = null
this.fullPath = null
this.dailyLogDirPath = dailyLogDirPath
this.filename = this.id + '.txt'
this.fullPath = Path.join(this.dailyLogDirPath, this.filename)
this.createdAt = null
this.createdAt = Date.now()
/** @type {import('../managers/LogManager').LogObject[]} */
this.logs = []
/** @type {string[]} */
this.bufferedLogLines = []
this.locked = false
}
@ -32,8 +37,6 @@ class DailyLog {
toJSON() {
return {
id: this.id,
datePretty: this.datePretty,
path: this.path,
dailyLogDirPath: this.dailyLogDirPath,
fullPath: this.fullPath,
filename: this.filename,
@ -41,36 +44,34 @@ class DailyLog {
}
}
setData(data) {
this.id = date.format(new Date(), 'YYYY-MM-DD')
this.datePretty = date.format(new Date(), 'ddd, MMM D YYYY')
this.dailyLogDirPath = data.dailyLogDirPath
this.filename = this.id + '.txt'
this.path = Path.join('backups', this.filename)
this.fullPath = Path.join(this.dailyLogDirPath, this.filename)
this.createdAt = Date.now()
}
async appendBufferedLogs() {
var buffered = [...this.bufferedLogLines]
/**
* Append all buffered lines to daily log file
*/
appendBufferedLogs() {
let buffered = [...this.bufferedLogLines]
this.bufferedLogLines = []
var oneBigLog = ''
let oneBigLog = ''
buffered.forEach((logLine) => {
oneBigLog += logLine
})
this.appendLogLine(oneBigLog)
return this.appendLogLine(oneBigLog)
}
async appendLog(logObj) {
/**
*
* @param {import('../managers/LogManager').LogObject} logObj
*/
appendLog(logObj) {
this.logs.push(logObj)
var line = JSON.stringify(logObj) + '\n'
this.appendLogLine(line)
return this.appendLogLine(JSON.stringify(logObj) + '\n')
}
/**
* Append log to daily log file
*
* @param {string} line
*/
async appendLogLine(line) {
if (this.locked) {
this.bufferedLogLines.push(line)
@ -84,24 +85,29 @@ class DailyLog {
this.locked = false
if (this.bufferedLogLines.length) {
this.appendBufferedLogs()
await this.appendBufferedLogs()
}
}
/**
* Load all logs from file
* Parses lines and re-saves the file if bad lines are removed
*/
async loadLogs() {
var exists = await fs.pathExists(this.fullPath)
if (!exists) {
if (!await fs.pathExists(this.fullPath)) {
console.error('Daily log does not exist')
return
}
var text = await readTextFile(this.fullPath)
const text = await fileUtils.readTextFile(this.fullPath)
var hasFailures = false
let hasFailures = false
var logLines = text.split(/\r?\n/)
let logLines = text.split(/\r?\n/)
// remove last log if empty
if (logLines.length && !logLines[logLines.length - 1]) logLines = logLines.slice(0, -1)
// JSON parse log lines
this.logs = logLines.map(t => {
if (!t) {
hasFailures = true
@ -118,7 +124,7 @@ class DailyLog {
// Rewrite log file to remove errors
if (hasFailures) {
var newLogLines = this.logs.map(l => JSON.stringify(l)).join('\n') + '\n'
const newLogLines = this.logs.map(l => JSON.stringify(l)).join('\n') + '\n'
await fs.writeFile(this.fullPath, newLogLines)
console.log('Re-Saved log file to remove bad lines')
}

View file

@ -84,6 +84,24 @@ class Feed {
return episode.fullPath
}
/**
* If chapters for an audiobook match the audio tracks then use chapter titles instead of audio file names
*
* @param {import('../objects/LibraryItem')} libraryItem
* @returns {boolean}
*/
checkUseChapterTitlesForEpisodes(libraryItem) {
const tracks = libraryItem.media.tracks
const chapters = libraryItem.media.chapters
if (tracks.length !== chapters.length) return false
for (let i = 0; i < tracks.length; i++) {
if (Math.abs(chapters[i].start - tracks[i].startOffset) >= 1) {
return false
}
}
return true
}
setFromItem(userId, slug, libraryItem, serverAddress, preventIndexing = true, ownerName = null, ownerEmail = null) {
const media = libraryItem.media
const mediaMetadata = media.metadata
@ -128,9 +146,10 @@ class Feed {
this.episodes.push(feedEpisode)
})
} else { // AUDIOBOOK EPISODES
const useChapterTitles = this.checkUseChapterTitlesForEpisodes(libraryItem)
media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, this.meta)
feedEpisode.setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, this.meta, useChapterTitles)
this.episodes.push(feedEpisode)
})
}
@ -168,9 +187,10 @@ class Feed {
this.episodes.push(feedEpisode)
})
} else { // AUDIOBOOK EPISODES
const useChapterTitles = this.checkUseChapterTitlesForEpisodes(libraryItem)
media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(libraryItem, this.serverAddress, this.slug, audioTrack, this.meta)
feedEpisode.setFromAudiobookTrack(libraryItem, this.serverAddress, this.slug, audioTrack, this.meta, useChapterTitles)
this.episodes.push(feedEpisode)
})
}
@ -214,9 +234,10 @@ class Feed {
itemsWithTracks.forEach((item, index) => {
if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt
const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item)
item.media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, index)
feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, useChapterTitles, index)
this.episodes.push(feedEpisode)
})
})
@ -245,9 +266,10 @@ class Feed {
itemsWithTracks.forEach((item, index) => {
if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt
const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item)
item.media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, index)
feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, useChapterTitles, index)
this.episodes.push(feedEpisode)
})
})
@ -295,9 +317,10 @@ class Feed {
itemsWithTracks.forEach((item, index) => {
if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt
const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item)
item.media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, index)
feedEpisode.setFromAudiobookTrack(item, serverAddress, slug, audioTrack, this.meta, useChapterTitles, index)
this.episodes.push(feedEpisode)
})
})
@ -329,9 +352,10 @@ class Feed {
itemsWithTracks.forEach((item, index) => {
if (item.updatedAt > this.entityUpdatedAt) this.entityUpdatedAt = item.updatedAt
const useChapterTitles = this.checkUseChapterTitlesForEpisodes(item)
item.media.tracks.forEach((audioTrack) => {
const feedEpisode = new FeedEpisode()
feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, index)
feedEpisode.setFromAudiobookTrack(item, this.serverAddress, this.slug, audioTrack, this.meta, useChapterTitles, index)
this.episodes.push(feedEpisode)
})
})

View file

@ -97,7 +97,17 @@ class FeedEpisode {
this.fullPath = episode.audioFile.metadata.path
}
setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, meta, additionalOffset = null) {
/**
*
* @param {import('../objects/LibraryItem')} libraryItem
* @param {string} serverAddress
* @param {string} slug
* @param {import('../objects/files/AudioTrack')} audioTrack
* @param {Object} meta
* @param {boolean} useChapterTitles
* @param {number} [additionalOffset]
*/
setFromAudiobookTrack(libraryItem, serverAddress, slug, audioTrack, meta, useChapterTitles, additionalOffset = null) {
// Example: <pubDate>Fri, 04 Feb 2015 00:00:00 GMT</pubDate>
let timeOffset = isNaN(audioTrack.index) ? 0 : (Number(audioTrack.index) * 1000) // Offset pubdate to ensure correct order
let episodeId = uuidv4()
@ -119,10 +129,10 @@ class FeedEpisode {
if (libraryItem.media.tracks.length == 1) { // If audiobook is a single file, use book title instead of chapter/file title
title = libraryItem.media.metadata.title
} else {
if (libraryItem.media.chapters.length) {
if (useChapterTitles) {
// If audio track start and chapter start are within 1 seconds of eachother then use the chapter title
var matchingChapter = libraryItem.media.chapters.find(ch => Math.abs(ch.start - audioTrack.startOffset) < 1)
if (matchingChapter && matchingChapter.title) title = matchingChapter.title
const matchingChapter = libraryItem.media.chapters.find(ch => Math.abs(ch.start - audioTrack.startOffset) < 1)
if (matchingChapter?.title) title = matchingChapter.title
}
}

View file

@ -55,7 +55,7 @@ class ServerSettings {
this.buildNumber = packageJson.buildNumber
// Auth settings
// Active auth methodes
this.authLoginCustomMessage = null
this.authActiveAuthMethods = ['local']
// openid settings
@ -113,6 +113,7 @@ class ServerSettings {
this.version = settings.version || null
this.buildNumber = settings.buildNumber || 0 // Added v2.4.5
this.authLoginCustomMessage = settings.authLoginCustomMessage || null // Added v2.7.3
this.authActiveAuthMethods = settings.authActiveAuthMethods || ['local']
this.authOpenIDIssuerURL = settings.authOpenIDIssuerURL || null
@ -201,6 +202,7 @@ class ServerSettings {
logLevel: this.logLevel,
version: this.version,
buildNumber: this.buildNumber,
authLoginCustomMessage: this.authLoginCustomMessage,
authActiveAuthMethods: this.authActiveAuthMethods,
authOpenIDIssuerURL: this.authOpenIDIssuerURL,
authOpenIDAuthorizationURL: this.authOpenIDAuthorizationURL,
@ -213,7 +215,7 @@ class ServerSettings {
authOpenIDButtonText: this.authOpenIDButtonText,
authOpenIDAutoLaunch: this.authOpenIDAutoLaunch,
authOpenIDAutoRegister: this.authOpenIDAutoRegister,
authOpenIDMatchExistingBy: this.authOpenIDMatchExistingBy,
authOpenIDMatchExistingBy: this.authOpenIDMatchExistingBy,
authOpenIDMobileRedirectURIs: this.authOpenIDMobileRedirectURIs // Do not return to client
}
}
@ -246,6 +248,7 @@ class ServerSettings {
get authenticationSettings() {
return {
authLoginCustomMessage: this.authLoginCustomMessage,
authActiveAuthMethods: this.authActiveAuthMethods,
authOpenIDIssuerURL: this.authOpenIDIssuerURL,
authOpenIDAuthorizationURL: this.authOpenIDAuthorizationURL,
@ -264,7 +267,9 @@ class ServerSettings {
}
get authFormData() {
const clientFormData = {}
const clientFormData = {
authLoginCustomMessage: this.authLoginCustomMessage
}
if (this.authActiveAuthMethods.includes('openid')) {
clientFormData.authOpenIDButtonText = this.authOpenIDButtonText
clientFormData.authOpenIDAutoLaunch = this.authOpenIDAutoLaunch