diff --git a/client/components/modals/libraries/EditModal.vue b/client/components/modals/libraries/EditModal.vue
index 9910a236..6733b7b8 100644
--- a/client/components/modals/libraries/EditModal.vue
+++ b/client/components/modals/libraries/EditModal.vue
@@ -111,7 +111,6 @@ export default {
},
updateLibrary(library) {
this.mapLibraryToCopy(library)
- console.log('Updated library', this.libraryCopy)
},
getNewLibraryData() {
return {
@@ -128,7 +127,9 @@ export default {
autoScanCronExpression: null,
hideSingleBookSeries: false,
onlyShowLaterBooksInContinueSeries: false,
- metadataPrecedence: ['folderStructure', 'audioMetatags', 'nfoFile', 'txtFiles', 'opfFile', 'absMetadata']
+ metadataPrecedence: ['folderStructure', 'audioMetatags', 'nfoFile', 'txtFiles', 'opfFile', 'absMetadata'],
+ markAsFinishedPercentComplete: null,
+ markAsFinishedTimeRemaining: 10
}
}
},
@@ -236,7 +237,6 @@ export default {
this.show = false
this.$toast.success(this.$getString('ToastLibraryCreateSuccess', [res.name]))
if (!this.$store.state.libraries.currentLibraryId) {
- console.log('Setting initially library id', res.id)
// First library added
this.$store.dispatch('libraries/fetch', res.id)
}
diff --git a/client/components/modals/libraries/LibrarySettings.vue b/client/components/modals/libraries/LibrarySettings.vue
index 896a6837..d3b40de9 100644
--- a/client/components/modals/libraries/LibrarySettings.vue
+++ b/client/components/modals/libraries/LibrarySettings.vue
@@ -75,6 +75,20 @@
+
+
+
+
+
+
+
+
+
+
{{ markAsFinishedWhen === 'timeRemaining' ? '' : '%' }}
+
+
+
+
@@ -99,7 +113,9 @@ export default {
epubsAllowScriptedContent: false,
hideSingleBookSeries: false,
onlyShowLaterBooksInContinueSeries: false,
- podcastSearchRegion: 'us'
+ podcastSearchRegion: 'us',
+ markAsFinishedWhen: 'timeRemaining',
+ markAsFinishedValue: 10
}
},
computed: {
@@ -121,10 +137,34 @@ export default {
providers() {
if (this.mediaType === 'podcast') return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
+ },
+ maskAsFinishedWhenItems() {
+ return [
+ {
+ text: this.$strings.LabelSettingsLibraryMarkAsFinishedTimeRemaining,
+ value: 'timeRemaining'
+ },
+ {
+ text: this.$strings.LabelSettingsLibraryMarkAsFinishedPercentComplete,
+ value: 'percentComplete'
+ }
+ ]
}
},
methods: {
+ markAsFinishedWhenChanged(val) {
+ if (val === 'percentComplete' && this.markAsFinishedValue > 100) {
+ this.markAsFinishedValue = 100
+ }
+ this.formUpdated()
+ },
+ markAsFinishedChanged(val) {
+ this.formUpdated()
+ },
getLibraryData() {
+ let markAsFinishedTimeRemaining = this.markAsFinishedWhen === 'timeRemaining' ? Number(this.markAsFinishedValue) : null
+ let markAsFinishedPercentComplete = this.markAsFinishedWhen === 'percentComplete' ? Number(this.markAsFinishedValue) : null
+
return {
settings: {
coverAspectRatio: this.useSquareBookCovers ? this.$constants.BookCoverAspectRatio.SQUARE : this.$constants.BookCoverAspectRatio.STANDARD,
@@ -135,7 +175,9 @@ export default {
epubsAllowScriptedContent: !!this.epubsAllowScriptedContent,
hideSingleBookSeries: !!this.hideSingleBookSeries,
onlyShowLaterBooksInContinueSeries: !!this.onlyShowLaterBooksInContinueSeries,
- podcastSearchRegion: this.podcastSearchRegion
+ podcastSearchRegion: this.podcastSearchRegion,
+ markAsFinishedTimeRemaining: markAsFinishedTimeRemaining,
+ markAsFinishedPercentComplete: markAsFinishedPercentComplete
}
}
},
@@ -152,6 +194,11 @@ export default {
this.hideSingleBookSeries = !!this.librarySettings.hideSingleBookSeries
this.onlyShowLaterBooksInContinueSeries = !!this.librarySettings.onlyShowLaterBooksInContinueSeries
this.podcastSearchRegion = this.librarySettings.podcastSearchRegion || 'us'
+ this.markAsFinishedWhen = this.librarySettings.markAsFinishedTimeRemaining ? 'timeRemaining' : 'percentComplete'
+ if (!this.librarySettings.markAsFinishedTimeRemaining && !this.librarySettings.markAsFinishedPercentComplete) {
+ this.markAsFinishedWhen = 'timeRemaining'
+ }
+ this.markAsFinishedValue = this.librarySettings.markAsFinishedTimeRemaining || this.librarySettings.markAsFinishedPercentComplete || 10
}
},
mounted() {
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index 301c4ad1..918bf685 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -562,6 +562,9 @@
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
"LabelSettingsHomePageBookshelfView": "Home page use bookshelf view",
"LabelSettingsLibraryBookshelfView": "Library use bookshelf view",
+ "LabelSettingsLibraryMarkAsFinishedPercentComplete": "Percent complete is greater than",
+ "LabelSettingsLibraryMarkAsFinishedTimeRemaining": "Time remaining is less than (seconds)",
+ "LabelSettingsLibraryMarkAsFinishedWhen": "Mark media item as finished when",
"LabelSettingsOnlyShowLaterBooksInContinueSeries": "Skip earlier books in Continue Series",
"LabelSettingsOnlyShowLaterBooksInContinueSeriesHelp": "The Continue Series home page shelf shows the first book not started in series that have at least one book finished and no books in progress. Enabling this setting will continue series from the furthest completed book instead of the first book not started.",
"LabelSettingsParseSubtitles": "Parse subtitles",
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index bf535bba..82fd34f0 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -255,15 +255,18 @@ class LibraryController {
}
// Validate settings
+ const defaultLibrarySettings = Database.libraryModel.getDefaultLibrarySettingsForMediaType(req.library.mediaType)
const updatedSettings = {
- ...(req.library.settings || Database.libraryModel.getDefaultLibrarySettingsForMediaType(req.library.mediaType))
+ ...(req.library.settings || defaultLibrarySettings)
}
let hasUpdates = false
let hasUpdatedDisableWatcher = false
let hasUpdatedScanCron = false
if (req.body.settings) {
for (const key in req.body.settings) {
- if (updatedSettings[key] === undefined) continue
+ if (!Object.keys(defaultLibrarySettings).includes(key)) {
+ continue
+ }
if (key === 'metadataPrecedence') {
if (!Array.isArray(req.body.settings[key])) {
@@ -285,6 +288,28 @@ class LibraryController {
updatedSettings[key] = req.body.settings[key]
Logger.debug(`[LibraryController] Library "${req.library.name}" updating setting "${key}" to "${updatedSettings[key]}"`)
}
+ } else if (key === 'markAsFinishedPercentComplete') {
+ if (req.body.settings[key] !== null && isNaN(req.body.settings[key])) {
+ return res.status(400).send(`Invalid request. Setting "${key}" must be a number`)
+ } else if (req.body.settings[key] !== null && (Number(req.body.settings[key]) < 0 || Number(req.body.settings[key]) > 100)) {
+ return res.status(400).send(`Invalid request. Setting "${key}" must be between 0 and 100`)
+ }
+ if (req.body.settings[key] !== updatedSettings[key]) {
+ hasUpdates = true
+ updatedSettings[key] = Number(req.body.settings[key])
+ Logger.debug(`[LibraryController] Library "${req.library.name}" updating setting "${key}" to "${updatedSettings[key]}"`)
+ }
+ } else if (key === 'markAsFinishedTimeRemaining') {
+ if (req.body.settings[key] !== null && isNaN(req.body.settings[key])) {
+ return res.status(400).send(`Invalid request. Setting "${key}" must be a number`)
+ } else if (req.body.settings[key] !== null && Number(req.body.settings[key]) < 0) {
+ return res.status(400).send(`Invalid request. Setting "${key}" must be greater than or equal to 0`)
+ }
+ if (req.body.settings[key] !== updatedSettings[key]) {
+ hasUpdates = true
+ updatedSettings[key] = Number(req.body.settings[key])
+ Logger.debug(`[LibraryController] Library "${req.library.name}" updating setting "${key}" to "${updatedSettings[key]}"`)
+ }
} else {
if (typeof req.body.settings[key] !== typeof updatedSettings[key]) {
return res.status(400).send(`Invalid request. Setting "${key}" must be of type ${typeof updatedSettings[key]}`)
diff --git a/server/managers/PlaybackSessionManager.js b/server/managers/PlaybackSessionManager.js
index a26a5c81..2ffda937 100644
--- a/server/managers/PlaybackSessionManager.js
+++ b/server/managers/PlaybackSessionManager.js
@@ -349,7 +349,7 @@ class PlaybackSessionManager {
progress: session.progress
// TODO: Add support for passing in these values from library settings
// markAsFinishedTimeRemaining: 5,
- // markAsFinishedPercentageComplete: 95
+ // markAsFinishedPercentComplete: 95
})
if (updateResponse.mediaProgress) {
SocketAuthority.clientEmitter(user.id, 'user_item_progress_updated', {
diff --git a/server/models/Library.js b/server/models/Library.js
index 90dd2512..36d462cf 100644
--- a/server/models/Library.js
+++ b/server/models/Library.js
@@ -12,6 +12,8 @@ const Logger = require('../Logger')
* @property {boolean} hideSingleBookSeries Do not show series that only have 1 book
* @property {boolean} onlyShowLaterBooksInContinueSeries Skip showing books that are earlier than the max sequence read
* @property {string[]} metadataPrecedence
+ * @property {number} markAsFinishedTimeRemaining Time remaining in seconds to mark as finished. (defaults to 10s)
+ * @property {number} markAsFinishedPercentComplete Percent complete to mark as finished (0-100). If this is set it will be used over markAsFinishedTimeRemaining.
*/
class Library extends Model {
@@ -57,7 +59,9 @@ class Library extends Model {
coverAspectRatio: 1, // Square
disableWatcher: false,
autoScanCronExpression: null,
- podcastSearchRegion: 'us'
+ podcastSearchRegion: 'us',
+ markAsFinishedPercentComplete: null,
+ markAsFinishedTimeRemaining: 10
}
} else {
return {
@@ -70,7 +74,9 @@ class Library extends Model {
epubsAllowScriptedContent: false,
hideSingleBookSeries: false,
onlyShowLaterBooksInContinueSeries: false,
- metadataPrecedence: this.defaultMetadataPrecedence
+ metadataPrecedence: this.defaultMetadataPrecedence,
+ markAsFinishedPercentComplete: null,
+ markAsFinishedTimeRemaining: 10
}
}
}
diff --git a/server/models/MediaProgress.js b/server/models/MediaProgress.js
index 052c8f74..72c7f7e2 100644
--- a/server/models/MediaProgress.js
+++ b/server/models/MediaProgress.js
@@ -229,18 +229,18 @@ class MediaProgress extends Model {
const timeRemaining = this.duration - this.currentTime
// Check if progress is far enough to mark as finished
- // - If markAsFinishedPercentageComplete is provided, use that otherwise use markAsFinishedTimeRemaining (default 5 seconds)
+ // - If markAsFinishedPercentComplete is provided, use that otherwise use markAsFinishedTimeRemaining (default 10 seconds)
let shouldMarkAsFinished = false
if (!this.isFinished && this.duration) {
- if (!isNullOrNaN(progressPayload.markAsFinishedPercentageComplete)) {
- const markAsFinishedPercentageComplete = Number(progressPayload.markAsFinishedPercentageComplete) / 100
- shouldMarkAsFinished = markAsFinishedPercentageComplete <= this.progress
+ if (!isNullOrNaN(progressPayload.markAsFinishedPercentComplete)) {
+ const markAsFinishedPercentComplete = Number(progressPayload.markAsFinishedPercentComplete) / 100
+ shouldMarkAsFinished = markAsFinishedPercentComplete < this.progress
if (shouldMarkAsFinished) {
- Logger.debug(`[MediaProgress] Marking media progress as finished because progress (${this.progress}) is greater than ${markAsFinishedPercentageComplete}`)
+ Logger.debug(`[MediaProgress] Marking media progress as finished because progress (${this.progress}) is greater than ${markAsFinishedPercentComplete}`)
}
} else {
- const markAsFinishedTimeRemaining = isNullOrNaN(progressPayload.markAsFinishedTimeRemaining) ? 5 : Number(progressPayload.markAsFinishedTimeRemaining)
- shouldMarkAsFinished = timeRemaining <= markAsFinishedTimeRemaining
+ const markAsFinishedTimeRemaining = isNullOrNaN(progressPayload.markAsFinishedTimeRemaining) ? 10 : Number(progressPayload.markAsFinishedTimeRemaining)
+ shouldMarkAsFinished = timeRemaining < markAsFinishedTimeRemaining
if (shouldMarkAsFinished) {
Logger.debug(`[MediaProgress] Marking media progress as finished because time remaining (${timeRemaining}) is less than ${markAsFinishedTimeRemaining} seconds`)
}
diff --git a/server/models/User.js b/server/models/User.js
index aa63aea8..8bd3f742 100644
--- a/server/models/User.js
+++ b/server/models/User.js
@@ -28,7 +28,7 @@ const { DataTypes, Model } = sequelize
* @property {string} [finishedAt]
* @property {number} [lastUpdate]
* @property {number} [markAsFinishedTimeRemaining]
- * @property {number} [markAsFinishedPercentageComplete]
+ * @property {number} [markAsFinishedPercentComplete]
*/
class User extends Model {