From e2d869bb1971a633fc374b49c8b5e002504f965d Mon Sep 17 00:00:00 2001
From: Cassie Esposito
Date: Thu, 19 May 2022 19:10:51 -0700
Subject: [PATCH 01/14] Added support for npm run watch to automatically reset
server during development.
---
package.json | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index fce51228..cfa5bad4 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,11 @@
"version": "2.0.14",
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
+ "watch": {
+ "dev": "server/{*,*/*}/*.js"
+ },
"scripts": {
+ "watch": "npm-watch",
"dev": "node index.js",
"start": "node index.js",
"client": "cd client && npm install && npm run generate",
@@ -53,5 +57,7 @@
"watcher": "^1.2.0",
"xml2js": "^0.4.23"
},
- "devDependencies": {}
-}
\ No newline at end of file
+ "devDependencies": {
+ "npm-watch": "^0.11.0"
+ }
+}
From 302870a101fd906f4f7ffb21c643eef84a999133 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Fri, 20 May 2022 15:55:03 -0500
Subject: [PATCH 02/14] Fix:Continue series shelf show next book in series #608
---
server/utils/libraryHelpers.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js
index 459e28e4..46ea9d1f 100644
--- a/server/utils/libraryHelpers.js
+++ b/server/utils/libraryHelpers.js
@@ -418,7 +418,7 @@ module.exports = {
books: [libraryItemJson],
inProgress: bookInProgress,
bookInProgressLastUpdate: bookInProgress ? mediaProgress.lastUpdate : null,
- firstBookUnread: bookInProgress ? libraryItemJson : null
+ firstBookUnread: bookInProgress ? null : libraryItemJson
}
seriesMap[librarySeries.id] = series
From 796602d1b215bdcbd06f2b71d90c7c137fa5a472 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Fri, 20 May 2022 16:34:51 -0500
Subject: [PATCH 03/14] Add:Enable e-reader server setting to allow all users
to access experimental e-reader #614
---
client/components/AudioPlayer.vue | 3 ---
client/components/app/SideRail.vue | 3 ---
client/components/app/StreamContainer.vue | 3 ---
client/components/cards/GroupCard.vue | 5 -----
client/components/cards/LazyBookCard.vue | 7 ++++--
client/components/covers/GroupCover.vue | 3 ---
client/components/modals/item/EditModal.vue | 3 +--
client/pages/audiobook/_id/edit.vue | 3 ---
client/pages/config/index.vue | 25 ++++++++++++++++-----
client/pages/config/users/_id.vue | 3 ---
client/pages/item/_id/index.vue | 16 ++++++++-----
server/objects/settings/ServerSettings.js | 15 ++++++-------
12 files changed, 44 insertions(+), 45 deletions(-)
diff --git a/client/components/AudioPlayer.vue b/client/components/AudioPlayer.vue
index 4bb39c7d..54bb1ec1 100644
--- a/client/components/AudioPlayer.vue
+++ b/client/components/AudioPlayer.vue
@@ -153,9 +153,6 @@ export default {
},
currentChapterName() {
return this.currentChapter ? this.currentChapter.title : ''
- },
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
}
},
methods: {
diff --git a/client/components/app/SideRail.vue b/client/components/app/SideRail.vue
index 46e1c4cd..c6b54c5a 100644
--- a/client/components/app/SideRail.vue
+++ b/client/components/app/SideRail.vue
@@ -89,9 +89,6 @@ export default {
offsetTop() {
return 64
},
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
- },
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
diff --git a/client/components/app/StreamContainer.vue b/client/components/app/StreamContainer.vue
index ea140a78..88049003 100644
--- a/client/components/app/StreamContainer.vue
+++ b/client/components/app/StreamContainer.vue
@@ -74,9 +74,6 @@ export default {
}
},
computed: {
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
- },
coverAspectRatio() {
return this.$store.getters['getServerSetting']('coverAspectRatio')
},
diff --git a/client/components/cards/GroupCard.vue b/client/components/cards/GroupCard.vue
index 5b31ed0e..ff12c561 100644
--- a/client/components/cards/GroupCard.vue
+++ b/client/components/cards/GroupCard.vue
@@ -109,19 +109,14 @@ export default {
hasValidCovers() {
var validCovers = this.bookItems.map((bookItem) => bookItem.media.coverPath)
return !!validCovers.length
- },
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
}
},
methods: {
mouseoverCard() {
this.isHovering = true
- // if (this.$refs.groupcover) this.$refs.groupcover.setHover(true)
},
mouseleaveCard() {
this.isHovering = false
- // if (this.$refs.groupcover) this.$refs.groupcover.setHover(false)
},
clickCard() {
this.$emit('click', this.group)
diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue
index dda73474..34b0be60 100644
--- a/client/components/cards/LazyBookCard.vue
+++ b/client/components/cards/LazyBookCard.vue
@@ -147,6 +147,9 @@ export default {
showExperimentalFeatures() {
return this.store.state.showExperimentalFeatures
},
+ enableEReader() {
+ return this.store.getters['getServerSetting']('enableEReader')
+ },
_libraryItem() {
return this.libraryItem || {}
},
@@ -287,13 +290,13 @@ export default {
return this.store.getters['getlibraryItemIdStreaming'] === this.libraryItemId
},
showReadButton() {
- return !this.isSelectionMode && this.showExperimentalFeatures && !this.showPlayButton && this.hasEbook
+ return !this.isSelectionMode && !this.showPlayButton && this.hasEbook && (this.showExperimentalFeatures || this.enableEReader)
},
showPlayButton() {
return !this.isSelectionMode && !this.isMissing && !this.isInvalid && !this.isStreaming && (this.numTracks || this.recentEpisode)
},
showSmallEBookIcon() {
- return !this.isSelectionMode && this.showExperimentalFeatures && this.hasEbook
+ return !this.isSelectionMode && this.hasEbook && (this.showExperimentalFeatures || this.enableEReader)
},
isMissing() {
return this._libraryItem.isMissing
diff --git a/client/components/covers/GroupCover.vue b/client/components/covers/GroupCover.vue
index 015e539f..671616f4 100644
--- a/client/components/covers/GroupCover.vue
+++ b/client/components/covers/GroupCover.vue
@@ -59,9 +59,6 @@ export default {
if (this.bookCoverAspectRatio === 1) return this.width / (120 * 1.6 * 2)
return this.width / 240
},
- showExperimentalFeatures() {
- return this.store.state.showExperimentalFeatures
- },
store() {
return this.$store || this.$nuxt.$store
},
diff --git a/client/components/modals/item/EditModal.vue b/client/components/modals/item/EditModal.vue
index f4b33b9f..548ddf4e 100644
--- a/client/components/modals/item/EditModal.vue
+++ b/client/components/modals/item/EditModal.vue
@@ -64,8 +64,7 @@ export default {
{
id: 'manage',
title: 'Manage',
- component: 'modals-item-tabs-manage',
- experimental: true
+ component: 'modals-item-tabs-manage'
}
]
}
diff --git a/client/pages/audiobook/_id/edit.vue b/client/pages/audiobook/_id/edit.vue
index d63e4eb8..887391d8 100644
--- a/client/pages/audiobook/_id/edit.vue
+++ b/client/pages/audiobook/_id/edit.vue
@@ -126,9 +126,6 @@ export default {
}
},
computed: {
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
- },
media() {
return this.libraryItem.media || {}
},
diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue
index b7e57ca6..e678c29b 100644
--- a/client/pages/config/index.vue
+++ b/client/pages/config/index.vue
@@ -122,6 +122,20 @@
+
+
+
Experimental Feature Settings
+
+
+
+
updateSettingsKey('enableEReader', val)" />
+
+
+ Enable e-reader for all users
+ info_outlined
+
+
+
@@ -172,7 +186,9 @@
Experimental Features
- info_outlined
+
+ info_outlined
+
@@ -207,6 +223,7 @@ export default {
isPurgingCache: false,
newServerSettings: {},
tooltips: {
+ experimentalFeatures: 'Features in development that could use your feedback and help testing. Click to open github discussion.',
scannerDisableWatcher: 'Disables the automatic adding/updating of items when file changes are detected. *Requires server restart',
scannerPreferOpfMetadata: 'OPF file metadata will be used for book details over folder names',
scannerPreferAudioMetadata: 'Audio file ID3 meta tags will be used for book details over folder names',
@@ -216,7 +233,8 @@ export default {
bookshelfView: 'Alternative view without wooden bookshelf',
storeCoverWithItem: 'By default covers are stored in /metadata/items, enabling this setting will store covers in your library item folder. Only one file named "cover" will be kept',
storeMetadataWithItem: 'By default metadata files are stored in /metadata/items, enabling this setting will store metadata files in your library item folders. Uses .abs file extension',
- coverAspectRatio: 'Prefer to use square covers over standard 1.6:1 book covers'
+ coverAspectRatio: 'Prefer to use square covers over standard 1.6:1 book covers',
+ enableEReader: 'E-reader is still a work in progress, but use this setting to open it up to all your users (or use the "Experimental Features" toggle below just for you)'
},
showConfirmPurgeCache: false
}
@@ -229,9 +247,6 @@ export default {
}
},
computed: {
- experimentalFeaturesTooltip() {
- return 'Features in development that could use your feedback and help testing.'
- },
serverSettings() {
return this.$store.state.serverSettings
},
diff --git a/client/pages/config/users/_id.vue b/client/pages/config/users/_id.vue
index 5b953ff0..b44bd900 100644
--- a/client/pages/config/users/_id.vue
+++ b/client/pages/config/users/_id.vue
@@ -104,9 +104,6 @@ export default {
bookCoverAspectRatio() {
return this.coverAspectRatio === this.$constants.BookCoverAspectRatio.SQUARE ? 1 : 1.6
},
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
- },
username() {
return this.user.username
},
diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue
index 2b25d0a1..12b9d9ee 100644
--- a/client/pages/item/_id/index.vue
+++ b/client/pages/item/_id/index.vue
@@ -135,7 +135,7 @@
{{ isMissing ? 'Missing' : 'Incomplete' }}
-
+
auto_stories
Read
@@ -223,6 +223,12 @@ export default {
}
},
computed: {
+ showExperimentalFeatures() {
+ return this.$store.state.showExperimentalFeatures
+ },
+ enableEReader() {
+ return this.store.getters['getServerSetting']('enableEReader')
+ },
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
@@ -241,9 +247,6 @@ export default {
isDeveloperMode() {
return this.$store.state.developerMode
},
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
- },
isPodcast() {
return this.libraryItem.mediaType === 'podcast'
},
@@ -262,6 +265,9 @@ export default {
if (this.isPodcast) return this.podcastEpisodes.length
return this.tracks.length
},
+ showReadButton() {
+ return this.ebookFile && (this.showExperimentalFeatures || this.enableEReader)
+ },
libraryId() {
return this.libraryItem.libraryId
},
@@ -342,7 +348,7 @@ export default {
return this.media.ebookFile
},
showExperimentalReadAlert() {
- return !this.tracks.length && this.ebookFile && !this.showExperimentalFeatures
+ return !this.tracks.length && this.ebookFile && !this.showExperimentalFeatures && !this.enableEReader
},
description() {
return this.mediaMetadata.description || ''
diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js
index e1e20e12..bea7b6ed 100644
--- a/server/objects/settings/ServerSettings.js
+++ b/server/objects/settings/ServerSettings.js
@@ -5,10 +5,6 @@ class ServerSettings {
constructor(settings) {
this.id = 'server-settings'
- // Misc/Unused
- this.autoTagNew = false
- this.newTagExpireDays = 15
-
// Scanner
this.scannerParseSubtitle = false
this.scannerFindCovers = false
@@ -43,11 +39,16 @@ class ServerSettings {
// Podcasts
this.podcastEpisodeSchedule = '0 * * * *' // Every hour
+ // Sorting
this.sortingIgnorePrefix = false
this.sortingPrefixes = ['the', 'a']
+ // Misc Flags
this.chromecastEnabled = false
+ this.enableEReader = false
+
this.logLevel = Logger.logLevel
+
this.version = null
if (settings) {
@@ -56,8 +57,6 @@ class ServerSettings {
}
construct(settings) {
- this.autoTagNew = settings.autoTagNew
- this.newTagExpireDays = settings.newTagExpireDays
this.scannerFindCovers = !!settings.scannerFindCovers
this.scannerCoverProvider = settings.scannerCoverProvider || 'google'
this.scannerParseSubtitle = settings.scannerParseSubtitle
@@ -91,6 +90,7 @@ class ServerSettings {
this.sortingIgnorePrefix = !!settings.sortingIgnorePrefix
this.sortingPrefixes = settings.sortingPrefixes || ['the', 'a']
this.chromecastEnabled = !!settings.chromecastEnabled
+ this.enableEReader = !!settings.enableEReader
this.logLevel = settings.logLevel || Logger.logLevel
this.version = settings.version || null
@@ -102,8 +102,6 @@ class ServerSettings {
toJSON() {
return {
id: this.id,
- autoTagNew: this.autoTagNew,
- newTagExpireDays: this.newTagExpireDays,
scannerFindCovers: this.scannerFindCovers,
scannerCoverProvider: this.scannerCoverProvider,
scannerParseSubtitle: this.scannerParseSubtitle,
@@ -125,6 +123,7 @@ class ServerSettings {
sortingIgnorePrefix: this.sortingIgnorePrefix,
sortingPrefixes: [...this.sortingPrefixes],
chromecastEnabled: this.chromecastEnabled,
+ enableEReader: this.enableEReader,
logLevel: this.logLevel,
version: this.version
}
From dabcad5ebd94ef4ce76cb5f41d2d6c66feb9733c Mon Sep 17 00:00:00 2001
From: advplyr
Date: Fri, 20 May 2022 16:47:00 -0500
Subject: [PATCH 04/14] Update experimental e-reader alert
---
client/components/modals/item/tabs/Manage.vue | 7 +++++--
client/pages/config/index.vue | 2 +-
client/pages/item/_id/index.vue | 5 +++--
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/client/components/modals/item/tabs/Manage.vue b/client/components/modals/item/tabs/Manage.vue
index de97ec49..644d3f44 100644
--- a/client/components/modals/item/tabs/Manage.vue
+++ b/client/components/modals/item/tabs/Manage.vue
@@ -26,7 +26,7 @@
-
+
Split M4B to MP3's
@@ -51,7 +51,7 @@
-
+
Embed Metadata
@@ -113,6 +113,9 @@ export default {
}
},
computed: {
+ showExperimentalFeatures() {
+ return this.$store.state.showExperimentalFeatures
+ },
libraryItemId() {
return this.libraryItem ? this.libraryItem.id : null
},
diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue
index e678c29b..87a5d969 100644
--- a/client/pages/config/index.vue
+++ b/client/pages/config/index.vue
@@ -183,7 +183,7 @@
-
+
Experimental Features
diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue
index 12b9d9ee..c41087dc 100644
--- a/client/pages/item/_id/index.vue
+++ b/client/pages/item/_id/index.vue
@@ -92,7 +92,8 @@
warning_amber
-
Book has no audio tracks but has valid ebook files. The e-reader is experimental and can be turned on in config.
+
Book has no audio tracks but has an ebook. The experimental e-reader can be enabled in config.
+
Book has no audio tracks but has an ebook. The experimental e-reader must be enabled by a server admin.
@@ -227,7 +228,7 @@ export default {
return this.$store.state.showExperimentalFeatures
},
enableEReader() {
- return this.store.getters['getServerSetting']('enableEReader')
+ return this.$store.getters['getServerSetting']('enableEReader')
},
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
From f083d4b5f6f9e602b3d9c188f8c93201d052735c Mon Sep 17 00:00:00 2001
From: advplyr
Date: Fri, 20 May 2022 18:15:54 -0500
Subject: [PATCH 05/14] Update dockerfile failing with dev dependency
---
Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index 39111c19..42a7e1b1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,6 +14,6 @@ COPY index.js index.js
COPY package-lock.json package-lock.json
COPY package.json package.json
COPY server server
-RUN npm ci --production
+RUN npm ci --only=production
EXPOSE 80
CMD ["npm", "start"]
From 169b637720b4b4a71061b75f8c4319f71ab36a32 Mon Sep 17 00:00:00 2001
From: Cassie Esposito
Date: Sat, 21 May 2022 08:06:06 -0700
Subject: [PATCH 06/14] Removed dependency erroniously added by IDE
---
server/utils/scandir.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/server/utils/scandir.js b/server/utils/scandir.js
index faeab015..9d52227a 100644
--- a/server/utils/scandir.js
+++ b/server/utils/scandir.js
@@ -4,7 +4,6 @@ const Logger = require('../Logger')
const { recurseFiles, getFileTimestampsWithIno } = require('./fileUtils')
const globals = require('./globals')
const LibraryFile = require('../objects/files/LibraryFile')
-const { response } = require('express')
function isMediaFile(mediaType, ext) {
// if (!path) return false
From 8beac53f5f367e12875c6baadf236d154c8e018d Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 21 May 2022 11:21:03 -0500
Subject: [PATCH 07/14] Update:Send source back with auth request
---
client/components/app/ConfigSideNav.vue | 13 ++++++++++---
client/components/modals/libraries/EditLibrary.vue | 6 ++++--
client/pages/login.vue | 3 ++-
client/store/index.js | 4 ++++
package.json | 8 ++++----
server/Auth.js | 3 ++-
server/Server.js | 2 +-
server/controllers/MiscController.js | 3 ++-
8 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/client/components/app/ConfigSideNav.vue b/client/components/app/ConfigSideNav.vue
index 8de98961..57c9f66c 100644
--- a/client/components/app/ConfigSideNav.vue
+++ b/client/components/app/ConfigSideNav.vue
@@ -9,9 +9,13 @@
-
@@ -25,6 +29,9 @@ export default {
return {}
},
computed: {
+ Source() {
+ return this.$store.state.Source
+ },
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId
},
diff --git a/client/components/modals/libraries/EditLibrary.vue b/client/components/modals/libraries/EditLibrary.vue
index 60d7875f..db87d1db 100644
--- a/client/components/modals/libraries/EditLibrary.vue
+++ b/client/components/modals/libraries/EditLibrary.vue
@@ -28,10 +28,9 @@
-
Browse for Folder
+
Browse for Folder
-
@@ -77,6 +76,9 @@ export default {
}
},
methods: {
+ browseForFolder() {
+ this.showDirectoryPicker = true
+ },
getLibraryData() {
return {
name: this.name,
diff --git a/client/pages/login.vue b/client/pages/login.vue
index ea9e995c..cf604a57 100644
--- a/client/pages/login.vue
+++ b/client/pages/login.vue
@@ -124,8 +124,9 @@ export default {
location.reload()
},
- setUser({ user, userDefaultLibraryId, serverSettings }) {
+ setUser({ user, userDefaultLibraryId, serverSettings, Source }) {
this.$store.commit('setServerSettings', serverSettings)
+ this.$store.commit('setSource', Source)
if (serverSettings.chromecastEnabled) {
console.log('Chromecast enabled import script')
diff --git a/client/store/index.js b/client/store/index.js
index 2ac4a312..2b9a70ea 100644
--- a/client/store/index.js
+++ b/client/store/index.js
@@ -2,6 +2,7 @@ import { checkForUpdate } from '@/plugins/version'
import Vue from 'vue'
export const state = () => ({
+ Source: null,
versionData: null,
serverSettings: null,
streamLibraryItem: null,
@@ -81,6 +82,9 @@ export const actions = {
}
export const mutations = {
+ setSource(state, source) {
+ state.Source = source
+ },
setLastBookshelfScrollData(state, { scrollTop, path, name }) {
state.lastBookshelfScrollData[name] = { scrollTop, path }
},
diff --git a/package.json b/package.json
index c1ddcffc..678f8cb8 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,9 @@
"watch": "npm-watch",
"dev": "node index.js",
"start": "node index.js",
- "client": "cd client && npm install && npm run generate",
- "prod": "npm run client && npm install && node prod.js",
- "build-win": "pkg -t node16-win-x64 -o ./dist/win/audiobookshelf -C GZip .",
+ "client": "cd client && npm ci && npm run generate",
+ "prod": "npm run client && npm ci && node prod.js",
+ "build-win": "npm run client && pkg -t node16-win-x64 -o ./dist/win/audiobookshelf -C GZip .",
"build-linux": "build/linuxpackager",
"docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --push . -t advplyr/audiobookshelf",
"deploy": "node dist/autodeploy"
@@ -60,4 +60,4 @@
"devDependencies": {
"npm-watch": "^0.11.0"
}
-}
+}
\ No newline at end of file
diff --git a/server/Auth.js b/server/Auth.js
index b30e060f..1548854b 100644
--- a/server/Auth.js
+++ b/server/Auth.js
@@ -96,7 +96,8 @@ class Auth {
return {
user: user.toJSONForBrowser(),
userDefaultLibraryId: user.getDefaultLibraryId(this.db.libraries),
- serverSettings: this.db.serverSettings.toJSON()
+ serverSettings: this.db.serverSettings.toJSON(),
+ Source: global.Source
}
}
diff --git a/server/Server.js b/server/Server.js
index 41496f53..c34fde26 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -35,9 +35,9 @@ const RssFeedManager = require('./managers/RssFeedManager')
class Server {
constructor(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH) {
- this.Source = SOURCE
this.Port = PORT
this.Host = HOST
+ global.Source = SOURCE
global.Uid = isNaN(UID) ? 0 : Number(UID)
global.Gid = isNaN(GID) ? 0 : Number(GID)
global.ConfigPath = Path.normalize(CONFIG_PATH)
diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js
index 852af27d..d16c9c8f 100644
--- a/server/controllers/MiscController.js
+++ b/server/controllers/MiscController.js
@@ -242,7 +242,8 @@ class MiscController {
const userResponse = {
user: req.user,
userDefaultLibraryId: req.user.getDefaultLibraryId(this.db.libraries),
- serverSettings: this.db.serverSettings.toJSON()
+ serverSettings: this.db.serverSettings.toJSON(),
+ Source: global.Source
}
res.json(userResponse)
}
From d22e9e32ed63aaa139021bc0f86aa68d1528cbe1 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sat, 21 May 2022 11:36:08 -0500
Subject: [PATCH 08/14] Remove dev dependency from package.json
---
package.json | 7 -------
1 file changed, 7 deletions(-)
diff --git a/package.json b/package.json
index 678f8cb8..babba376 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,7 @@
"version": "2.0.15",
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
- "watch": {
- "dev": "server/{*,*/*}/*.js"
- },
"scripts": {
- "watch": "npm-watch",
"dev": "node index.js",
"start": "node index.js",
"client": "cd client && npm ci && npm run generate",
@@ -56,8 +52,5 @@
"string-strip-html": "^8.3.0",
"watcher": "^1.2.0",
"xml2js": "^0.4.23"
- },
- "devDependencies": {
- "npm-watch": "^0.11.0"
}
}
\ No newline at end of file
From 4d227cbade4d1cf61a3c82ab500c26fb1b5a6c92 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sun, 22 May 2022 08:05:39 -0500
Subject: [PATCH 09/14] Add copy to clipboard fallback
---
client/plugins/init.client.js | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/client/plugins/init.client.js b/client/plugins/init.client.js
index 90dd7814..0fab85aa 100644
--- a/client/plugins/init.client.js
+++ b/client/plugins/init.client.js
@@ -163,17 +163,26 @@ Vue.prototype.$sanitizeSlug = (str) => {
Vue.prototype.$copyToClipboard = (str, ctx) => {
return new Promise((resolve) => {
if (!navigator.clipboard) {
- console.warn('Clipboard not supported')
- return resolve(false)
+ navigator.clipboard.writeText(str).then(() => {
+ if (ctx) ctx.$toast.success('Copied to clipboard')
+ resolve(true)
+ }, (err) => {
+ console.error('Clipboard copy failed', str, err)
+ resolve(false)
+ })
+ } else {
+ const el = document.createElement('textarea')
+ el.value = str
+ el.setAttribute('readonly', '')
+ el.style.position = 'absolute'
+ el.style.left = '-9999px'
+ document.body.appendChild(el)
+ el.select()
+ document.execCommand('copy')
+ document.body.removeChild(el)
+
+ if (ctx) ctx.$toast.success('Copied to clipboard')
}
- navigator.clipboard.writeText(str).then(() => {
- console.log('Clipboard copy success', str)
- ctx.$toast.success('Copied to clipboard')
- resolve(true)
- }, (err) => {
- console.error('Clipboard copy failed', str, err)
- resolve(false)
- })
})
}
From a8d5b543d7c092d9505b93874508102b9ec21aa3 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Sun, 22 May 2022 19:17:21 -0500
Subject: [PATCH 10/14] Update:Parsing sequence from folder will strip leading
zeros #562
---
client/components/app/LazyBookshelf.vue | 2 --
server/utils/scandir.js | 4 ++++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue
index 112efef7..3f8f7d71 100644
--- a/client/components/app/LazyBookshelf.vue
+++ b/client/components/app/LazyBookshelf.vue
@@ -43,7 +43,6 @@ export default {
mixins: [bookshelfCardsHelpers],
data() {
return {
- routeName: null,
routeFullPath: null,
initialized: false,
bookshelfHeight: 0,
@@ -632,7 +631,6 @@ export default {
mounted() {
this.initListeners()
- this.routeName = this.$route.name // beforeDestroy will have the new route name already, so need to store this
this.routeFullPath = window.location.pathname + (window.location.search || '')
},
updated() {
diff --git a/server/utils/scandir.js b/server/utils/scandir.js
index 9d52227a..699a47b9 100644
--- a/server/utils/scandir.js
+++ b/server/utils/scandir.js
@@ -260,6 +260,10 @@ function getBookDataFromDir(folderPath, relPath, parseSubtitle = false) {
title = title.replace(replaceChunk, '').trim()
}
}
+
+ if (volumeNumber != null && !isNaN(volumeNumber)) {
+ volumeNumber = String(Number(volumeNumber)) // Strips leading zeros
+ }
}
var publishedYear = null
From 0ad7a98fc7f8e1ce2475c2b3c68ce8c888bbc1f4 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 23 May 2022 18:15:15 -0500
Subject: [PATCH 11/14] Add:Support for single book files to be detected by
Watcher #610, Fix:Single media file in library folder root is only supported
for books not podcasts
---
server/Watcher.js | 7 ---
server/scanner/Scanner.js | 18 +++++---
server/utils/scandir.js | 91 +++++++++++++++++++++++----------------
3 files changed, 68 insertions(+), 48 deletions(-)
diff --git a/server/Watcher.js b/server/Watcher.js
index 555dce06..d2166b6e 100644
--- a/server/Watcher.js
+++ b/server/Watcher.js
@@ -162,13 +162,6 @@ class FolderWatcher extends EventEmitter {
}
var folderFullPath = folder.fullPath.replace(/\\/g, '/')
- // Check if file was added to root directory
- var dir = Path.dirname(path)
- if (dir === folderFullPath) {
- Logger.warn(`[Watcher] New file "${Path.basename(path)}" added to folder root - ignoring it`)
- return
- }
-
var relPath = path.replace(folderFullPath, '')
var hasDotPath = relPath.split('/').find(p => p.startsWith('.'))
diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js
index 7e9ffc2e..74e181eb 100644
--- a/server/scanner/Scanner.js
+++ b/server/scanner/Scanner.js
@@ -62,7 +62,8 @@ class Scanner {
}
async scanLibraryItem(libraryMediaType, folder, libraryItem) {
- var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, libraryItem.path, this.db.serverSettings)
+ // TODO: Support for single media item
+ var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, libraryItem.path, false, this.db.serverSettings)
if (!libraryItemData) {
return ScanResult.NOTHING
}
@@ -499,7 +500,11 @@ class Scanner {
continue;
}
var relFilePaths = folderGroups[folderId].fileUpdates.map(fileUpdate => fileUpdate.relPath)
- var fileUpdateGroup = groupFilesIntoLibraryItemPaths(relFilePaths, true)
+ var fileUpdateGroup = groupFilesIntoLibraryItemPaths(library.mediaType, relFilePaths)
+ if (!Object.keys(fileUpdateGroup).length) {
+ Logger.info(`[Scanner] No important changes to scan for in folder "${folderId}"`)
+ continue;
+ }
var folderScanResults = await this.scanFolderUpdates(library, folder, fileUpdateGroup)
Logger.debug(`[Scanner] Folder scan results`, folderScanResults)
}
@@ -513,6 +518,8 @@ class Scanner {
// Test Case: Moving audio files from library item folder to author folder should trigger a re-scan of the item
var updateGroup = { ...fileUpdateGroup }
for (const itemDir in updateGroup) {
+ if (itemDir == fileUpdateGroup[itemDir]) continue; // Media in root path
+
var itemDirNestedFiles = fileUpdateGroup[itemDir].filter(b => b.includes('/'))
if (!itemDirNestedFiles.length) continue;
@@ -582,7 +589,8 @@ class Scanner {
}
Logger.debug(`[Scanner] Folder update group must be a new item "${itemDir}" in library "${library.name}"`)
- var newLibraryItem = await this.scanPotentialNewLibraryItem(library.mediaType, folder, fullPath)
+ var isSingleMediaItem = itemDir === fileUpdateGroup[itemDir]
+ var newLibraryItem = await this.scanPotentialNewLibraryItem(library.mediaType, folder, fullPath, isSingleMediaItem)
if (newLibraryItem) {
await this.createNewAuthorsAndSeries(newLibraryItem)
await this.db.insertLibraryItem(newLibraryItem)
@@ -594,8 +602,8 @@ class Scanner {
return itemGroupingResults
}
- async scanPotentialNewLibraryItem(libraryMediaType, folder, fullPath) {
- var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, fullPath, this.db.serverSettings)
+ async scanPotentialNewLibraryItem(libraryMediaType, folder, fullPath, isSingleMediaItem = false) {
+ var libraryItemData = await getLibraryItemFileData(libraryMediaType, folder, fullPath, isSingleMediaItem, this.db.serverSettings)
if (!libraryItemData) return null
var serverSettings = this.db.serverSettings
return this.scanNewLibraryItem(libraryItemData, libraryMediaType, serverSettings.scannerPreferAudioMetadata, serverSettings.scannerPreferOpfMetadata, serverSettings.scannerFindCovers)
diff --git a/server/utils/scandir.js b/server/utils/scandir.js
index 699a47b9..0c68dacf 100644
--- a/server/utils/scandir.js
+++ b/server/utils/scandir.js
@@ -17,11 +17,14 @@ function isMediaFile(mediaType, ext) {
// TODO: Function needs to be re-done
// Input: array of relative file paths
// Output: map of files grouped into potential item dirs
-function groupFilesIntoLibraryItemPaths(paths) {
- // Step 1: Clean path, Remove leading "/", Filter out files in root dir
+function groupFilesIntoLibraryItemPaths(mediaType, paths) {
+ // Step 1: Clean path, Remove leading "/", Filter out non-media files in root dir
var pathsFiltered = paths.map(path => {
return path.startsWith('/') ? path.slice(1) : path
- }).filter(path => Path.parse(path).dir)
+ }).filter(path => {
+ let parsedPath = Path.parse(path)
+ return parsedPath.dir || (mediaType === 'book' && isMediaFile(mediaType, parsedPath.ext))
+ })
// Step 2: Sort by least number of directories
pathsFiltered.sort((a, b) => {
@@ -33,25 +36,30 @@ function groupFilesIntoLibraryItemPaths(paths) {
// Step 3: Group files in dirs
var itemGroup = {}
pathsFiltered.forEach((path) => {
- var dirparts = Path.dirname(path).split('/')
+ var dirparts = Path.dirname(path).split('/').filter(p => !!p && p !== '.') // dirname returns . if no directory
var numparts = dirparts.length
var _path = ''
- // Iterate over directories in path
- for (let i = 0; i < numparts; i++) {
- var dirpart = dirparts.shift()
- _path = Path.posix.join(_path, dirpart)
+ if (!numparts) {
+ // Media file in root
+ itemGroup[path] = path
+ } else {
+ // Iterate over directories in path
+ for (let i = 0; i < numparts; i++) {
+ var dirpart = dirparts.shift()
+ _path = Path.posix.join(_path, dirpart)
- if (itemGroup[_path]) { // Directory already has files, add file
- var relpath = Path.posix.join(dirparts.join('/'), Path.basename(path))
- itemGroup[_path].push(relpath)
- return
- } else if (!dirparts.length) { // This is the last directory, create group
- itemGroup[_path] = [Path.basename(path)]
- return
- } else if (dirparts.length === 1 && /^cd\d{1,3}$/i.test(dirparts[0])) { // Next directory is the last and is a CD dir, create group
- itemGroup[_path] = [Path.posix.join(dirparts[0], Path.basename(path))]
- return
+ if (itemGroup[_path]) { // Directory already has files, add file
+ var relpath = Path.posix.join(dirparts.join('/'), Path.basename(path))
+ itemGroup[_path].push(relpath)
+ return
+ } else if (!dirparts.length) { // This is the last directory, create group
+ itemGroup[_path] = [Path.basename(path)]
+ return
+ } else if (dirparts.length === 1 && /^cd\d{1,3}$/i.test(dirparts[0])) { // Next directory is the last and is a CD dir, create group
+ itemGroup[_path] = [Path.posix.join(dirparts[0], Path.basename(path))]
+ return
+ }
}
}
})
@@ -62,9 +70,9 @@ module.exports.groupFilesIntoLibraryItemPaths = groupFilesIntoLibraryItemPaths
// Input: array of relative file items (see recurseFiles)
// Output: map of files grouped into potential libarary item dirs
function groupFileItemsIntoLibraryItemDirs(mediaType, fileItems) {
- // Step 1: Filter out non-media files in root dir (with depth of 0)
+ // Step 1: Filter out non-book-media files in root dir (with depth of 0)
var itemsFiltered = fileItems.filter(i => {
- return i.deep > 0 || isMediaFile(mediaType, i.extension)
+ return i.deep > 0 || (mediaType === 'book' && isMediaFile(mediaType, i.extension))
})
// Step 2: Seperate media files and other files
@@ -147,16 +155,6 @@ async function scanFolder(libraryMediaType, folder, serverSettings = {}) {
}
var fileItems = await recurseFiles(folderPath)
- var basePath = folderPath
-
- const isOpenAudibleFolder = fileItems.find(fi => fi.deep === 0 && fi.name === 'books.json')
- if (isOpenAudibleFolder) {
- Logger.info(`[scandir] Detected Open Audible Folder, looking in books folder`)
- basePath = Path.posix.join(folderPath, 'books')
- fileItems = await recurseFiles(basePath)
- Logger.debug(`[scandir] ${fileItems.length} files found in books folder`)
- }
-
var libraryItemGrouping = groupFileItemsIntoLibraryItemDirs(libraryMediaType, fileItems)
if (!Object.keys(libraryItemGrouping).length) {
@@ -175,10 +173,10 @@ async function scanFolder(libraryMediaType, folder, serverSettings = {}) {
mediaMetadata: {
title: Path.basename(libraryItemPath, Path.extname(libraryItemPath))
},
- path: Path.posix.join(basePath, libraryItemPath),
+ path: Path.posix.join(folderPath, libraryItemPath),
relPath: libraryItemPath
}
- fileObjs = await cleanFileObjects(basePath, [libraryItemPath])
+ fileObjs = await cleanFileObjects(folderPath, [libraryItemPath])
isFile = true
} else {
libraryItemData = getDataFromMediaDir(libraryMediaType, folderPath, libraryItemPath, serverSettings)
@@ -335,14 +333,34 @@ function getDataFromMediaDir(libraryMediaType, folderPath, relPath, serverSettin
}
// Called from Scanner.js
-async function getLibraryItemFileData(libraryMediaType, folder, libraryItemPath, serverSettings = {}) {
- var fileItems = await recurseFiles(libraryItemPath)
-
+async function getLibraryItemFileData(libraryMediaType, folder, libraryItemPath, isSingleMediaItem, serverSettings = {}) {
libraryItemPath = libraryItemPath.replace(/\\/g, '/')
var folderFullPath = folder.fullPath.replace(/\\/g, '/')
var libraryItemDir = libraryItemPath.replace(folderFullPath, '').slice(1)
- var libraryItemData = getDataFromMediaDir(libraryMediaType, folderFullPath, libraryItemDir, serverSettings)
+ var libraryItemData = {}
+
+ var fileItems = []
+
+ if (isSingleMediaItem) { // Single media item in root of folder
+ fileItems = [
+ {
+ fullpath: libraryItemPath,
+ path: libraryItemDir // actually the relPath (only filename here)
+ }
+ ]
+ libraryItemData = {
+ path: libraryItemPath, // full path
+ relPath: libraryItemDir, // only filename
+ mediaMetadata: {
+ title: Path.basename(libraryItemDir, Path.extname(libraryItemDir))
+ }
+ }
+ } else {
+ fileItems = await recurseFiles(libraryItemPath)
+ libraryItemData = getDataFromMediaDir(libraryMediaType, folderFullPath, libraryItemDir, serverSettings)
+ }
+
var libraryItemDirStats = await getFileTimestampsWithIno(libraryItemData.path)
var libraryItem = {
ino: libraryItemDirStats.ino,
@@ -353,6 +371,7 @@ async function getLibraryItemFileData(libraryMediaType, folder, libraryItemPath,
libraryId: folder.libraryId,
path: libraryItemData.path,
relPath: libraryItemData.relPath,
+ isFile: isSingleMediaItem,
media: {
metadata: libraryItemData.mediaMetadata || null
},
From 6cfe583535217a8f4aa31329e57fa01208e282b4 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 23 May 2022 18:31:11 -0500
Subject: [PATCH 12/14] Fix:Static router for downloading single file library
items #627
---
server/routers/StaticRouter.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/server/routers/StaticRouter.js b/server/routers/StaticRouter.js
index b571869f..24b6f6da 100644
--- a/server/routers/StaticRouter.js
+++ b/server/routers/StaticRouter.js
@@ -17,7 +17,9 @@ class StaticRouter {
if (!item) return res.status(404).send('Item not found with id ' + req.params.id)
var remainingPath = req.params['0']
- var fullPath = Path.join(item.path, remainingPath)
+ var fullPath = null
+ if (item.isFile) fullPath = item.path
+ else fullPath = Path.join(item.path, remainingPath)
res.sendFile(fullPath)
})
}
From 3c465994fe094c11d0fb991fec074e3fa7542e85 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 23 May 2022 19:12:40 -0500
Subject: [PATCH 13/14] Fix:Hide remove icon from author images with no image
---
client/components/modals/authors/EditModal.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/components/modals/authors/EditModal.vue b/client/components/modals/authors/EditModal.vue
index 503f8b12..ef3eb03e 100644
--- a/client/components/modals/authors/EditModal.vue
+++ b/client/components/modals/authors/EditModal.vue
@@ -11,7 +11,7 @@
-
From 3e98b6f7499c8cdf96ccaed29a3fe474e37d0566 Mon Sep 17 00:00:00 2001
From: advplyr
Date: Mon, 23 May 2022 19:28:00 -0500
Subject: [PATCH 14/14] Update:Remove manual sorting of podcast episodes and
default to sort by published at
---
.../components/controls/EpisodeSortSelect.vue | 8 +-
.../components/modals/authors/EditModal.vue | 2 +-
.../tables/podcast/EpisodeTableRow.vue | 29 +++---
.../tables/podcast/EpisodesTable.vue | 93 ++-----------------
server/controllers/LibraryItemController.js | 14 ---
server/objects/mediaTypes/Podcast.js | 12 +--
server/routers/ApiRouter.js | 1 -
7 files changed, 23 insertions(+), 136 deletions(-)
diff --git a/client/components/controls/EpisodeSortSelect.vue b/client/components/controls/EpisodeSortSelect.vue
index 27aced3f..7819e4a3 100644
--- a/client/components/controls/EpisodeSortSelect.vue
+++ b/client/components/controls/EpisodeSortSelect.vue
@@ -33,8 +33,8 @@ export default {
showMenu: false,
items: [
{
- text: 'Current',
- value: 'index'
+ text: 'Pub Date',
+ value: 'publishedAt'
},
{
text: 'Title',
@@ -47,10 +47,6 @@ export default {
{
text: 'Episode',
value: 'episode'
- },
- {
- text: 'Pub Date',
- value: 'publishedAt'
}
]
}
diff --git a/client/components/modals/authors/EditModal.vue b/client/components/modals/authors/EditModal.vue
index ef3eb03e..3657bc85 100644
--- a/client/components/modals/authors/EditModal.vue
+++ b/client/components/modals/authors/EditModal.vue
@@ -6,7 +6,7 @@