diff --git a/Dockerfile b/Dockerfile index 3f34f85f2a..d5fa236b44 100755 --- a/Dockerfile +++ b/Dockerfile @@ -17,16 +17,12 @@ RUN apk update && \ g++ \ tini \ unzip - COPY --from=build /client/dist /client/dist COPY index.js package* / COPY server server - ARG TARGETPLATFORM - ENV NUSQLITE3_DIR="/usr/local/lib/nusqlite3" ENV NUSQLITE3_PATH="${NUSQLITE3_DIR}/libnusqlite3.so" - RUN case "$TARGETPLATFORM" in \ "linux/amd64") \ curl -L -o /tmp/library.zip "https://github.com/mikiher/nunicode-sqlite/releases/download/v1.2/libnusqlite3-linux-musl-x64.zip" ;; \ @@ -36,9 +32,12 @@ RUN case "$TARGETPLATFORM" in \ esac && \ unzip /tmp/library.zip -d $NUSQLITE3_DIR && \ rm /tmp/library.zip - RUN npm ci --only=production RUN apk del make python3 g++ EXPOSE 80 +ENV PORT=80 +ENV CONFIG_PATH="/config" +ENV METADATA_PATH="/metadata" +ENV SOURCE="docker" ENTRYPOINT ["tini", "--"] CMD ["node", "index.js"] diff --git a/client/components/modals/libraries/ScheduleScan.vue b/client/components/modals/libraries/ScheduleScan.vue index b02fd48fed..e4f4810e51 100755 --- a/client/components/modals/libraries/ScheduleScan.vue +++ b/client/components/modals/libraries/ScheduleScan.vue @@ -5,6 +5,9 @@ +
+

{{ $strings.MessageScheduleLibraryScanNote }}

+
diff --git a/client/strings/en-us.json b/client/strings/en-us.json index 0334adb910..99cf44d0a4 100755 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -839,6 +839,7 @@ "MessageResetChaptersConfirm": "Are you sure you want to reset chapters and undo the changes you made?", "MessageRestoreBackupConfirm": "Are you sure you want to restore the backup created on", "MessageRestoreBackupWarning": "Restoring a backup will overwrite the entire database located at /config and cover images in /metadata/items & /metadata/authors.

Backups do not modify any files in your library folders. If you have enabled server settings to store cover art and metadata in your library folders then those are not backed up or overwritten.

All clients using your server will be automatically refreshed.", + "MessageScheduleLibraryScanNote": "For most users, it is recommended to leave this feature disabled and keep the folder watcher setting enabled. The folder watcher will automatically detect changes in your library folders. The folder watcher doesn't work for every file system (like NFS) so scheduled library scans can be used instead.", "MessageSearchResultsFor": "Search results for", "MessageSelected": "{0} selected", "MessageServerCouldNotBeReached": "Server could not be reached", diff --git a/index.js b/index.js index 9a0be347cc..4b3a2648bd 100755 --- a/index.js +++ b/index.js @@ -1,3 +1,18 @@ +const optionDefinitions = [ + { name: 'config', alias: 'c', type: String }, + { name: 'metadata', alias: 'm', type: String }, + { name: 'port', alias: 'p', type: String }, + { name: 'host', alias: 'h', type: String }, + { name: 'source', alias: 's', type: String }, + { name: 'dev', alias: 'd', type: Boolean } +] + +const commandLineArgs = require('./server/libs/commandLineArgs') +const options = commandLineArgs(optionDefinitions) + +const Path = require('path') +process.env.NODE_ENV = options.dev ? 'development' : process.env.NODE_ENV || 'production' + const server = require('./server/Server') global.appRoot = __dirname @@ -17,14 +32,19 @@ if (isDev) { process.env.ROUTER_BASE_PATH = devEnv.RouterBasePath || '' } -const PORT = process.env.PORT || 80 -const HOST = process.env.HOST -const CONFIG_PATH = process.env.CONFIG_PATH || '/config' -const METADATA_PATH = process.env.METADATA_PATH || '/metadata' -const SOURCE = process.env.SOURCE || 'docker' +const inputConfig = options.config ? Path.resolve(options.config) : null +const inputMetadata = options.metadata ? Path.resolve(options.metadata) : null + +const PORT = options.port || process.env.PORT || 3333 +const HOST = options.host || process.env.HOST +const CONFIG_PATH = inputConfig || process.env.CONFIG_PATH || Path.resolve('config') +const METADATA_PATH = inputMetadata || process.env.METADATA_PATH || Path.resolve('metadata') +const SOURCE = options.source || process.env.SOURCE || 'debian' + const ROUTER_BASE_PATH = process.env.ROUTER_BASE_PATH || '' -console.log('Config', CONFIG_PATH, METADATA_PATH) +console.log(`Running in ${process.env.NODE_ENV} mode.`) +console.log(`Options: CONFIG_PATH=${CONFIG_PATH}, METADATA_PATH=${METADATA_PATH}, PORT=${PORT}, HOST=${HOST}, SOURCE=${SOURCE}, ROUTER_BASE_PATH=${ROUTER_BASE_PATH}`) const Server = new server(SOURCE, PORT, HOST, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) Server.start() diff --git a/package-lock.json b/package-lock.json index 64a85a6011..0fcbcf20ef 100755 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "xml2js": "^0.5.0" }, "bin": { - "audiobookshelf": "prod.js" + "audiobookshelf": "index.js" }, "devDependencies": { "chai": "^4.3.10", diff --git a/package.json b/package.json index 1079118118..7d2bf982c0 100755 --- a/package.json +++ b/package.json @@ -5,10 +5,10 @@ "description": "Self-hosted audiobook and podcast server", "main": "index.js", "scripts": { - "dev": "nodemon --watch server index.js", + "dev": "nodemon --watch server index.js -- --dev", "start": "node index.js", "client": "cd client && npm ci && npm run generate", - "prod": "npm run client && npm ci && node prod.js", + "prod": "npm run client && npm ci && node index.js", "build-win": "npm run client && pkg -t node20-win-x64 -o ./dist/win/audiobookshelf -C GZip .", "build-linux": "build/linuxpackager", "docker": "docker buildx build --platform linux/amd64,linux/arm64 --push . -t advplyr/audiobookshelf", @@ -18,7 +18,7 @@ "test": "mocha", "coverage": "nyc mocha" }, - "bin": "prod.js", + "bin": "index.js", "pkg": { "assets": [ "client/dist/**/*", @@ -26,7 +26,7 @@ "server/migrations/*.js" ], "scripts": [ - "prod.js", + "index.js", "server/**/*.js" ] }, diff --git a/server/Server.js b/server/Server.js index e9e77f00f0..c3e73aec82 100755 --- a/server/Server.js +++ b/server/Server.js @@ -85,6 +85,12 @@ class Server { } } + if (process.env.PODCAST_DOWNLOAD_TIMEOUT) { + global.PodcastDownloadTimeout = process.env.PODCAST_DOWNLOAD_TIMEOUT + } else { + global.PodcastDownloadTimeout = 30000 + } + if (!fs.pathExistsSync(global.ConfigPath)) { fs.mkdirSync(global.ConfigPath) } diff --git a/server/utils/comicBookExtractors.js b/server/utils/comicBookExtractors.js index 9c18ebddc2..6fc3739211 100644 --- a/server/utils/comicBookExtractors.js +++ b/server/utils/comicBookExtractors.js @@ -189,8 +189,14 @@ class CbzStreamZipComicBookExtractor extends AbstractComicBookExtractor { } close() { - this.archive?.close() - Logger.debug(`[CbzStreamZipComicBookExtractor] Closed comic book "${this.comicPath}"`) + this.archive + ?.close() + .then(() => { + Logger.debug(`[CbzStreamZipComicBookExtractor] Closed comic book "${this.comicPath}"`) + }) + .catch((error) => { + Logger.error(`[CbzStreamZipComicBookExtractor] Failed to close comic book "${this.comicPath}"`, error) + }) } } diff --git a/server/utils/ffmpegHelpers.js b/server/utils/ffmpegHelpers.js index f81f889c9a..a365ca9e24 100755 --- a/server/utils/ffmpegHelpers.js +++ b/server/utils/ffmpegHelpers.js @@ -110,7 +110,7 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => { headers: { 'User-Agent': 'audiobookshelf (+https://audiobookshelf.org)' }, - timeout: 30000 + timeout: global.PodcastDownloadTimeout }).catch((error) => { Logger.error(`[ffmpegHelpers] Failed to download podcast episode with url "${podcastEpisodeDownload.url}"`, error) return null diff --git a/server/utils/parsers/parseComicMetadata.js b/server/utils/parsers/parseComicMetadata.js index 38a41b51d8..85988ffc5a 100644 --- a/server/utils/parsers/parseComicMetadata.js +++ b/server/utils/parsers/parseComicMetadata.js @@ -43,7 +43,9 @@ async function parse(ebookFile) { archive = createComicBookExtractor(comicPath) await archive.open() - const filePaths = await archive.getFilePaths() + const filePaths = await archive.getFilePaths().catch((error) => { + Logger.error(`[parseComicMetadata] Failed to get file paths from comic at "${comicPath}"`, error) + }) // Sort the file paths in a natural order to get the first image filePaths.sort((a, b) => { diff --git a/server/utils/podcastUtils.js b/server/utils/podcastUtils.js index bc9892b2db..af5229230f 100755 --- a/server/utils/podcastUtils.js +++ b/server/utils/podcastUtils.js @@ -281,7 +281,7 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => { return axios({ url: feedUrl, method: 'GET', - timeout: 12000, + timeout: global.PodcastDownloadTimeout, responseType: 'arraybuffer', headers: { Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8',