From c6e9fe6513f1b2248fc4f4814fdef08f097e7e13 Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 2 Nov 2022 17:28:26 -0500 Subject: [PATCH 001/256] Update:Chapter lookup modal show # of chapters found vs current # of chapters #1070 --- client/pages/audiobook/_id/chapters.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/pages/audiobook/_id/chapters.vue b/client/pages/audiobook/_id/chapters.vue index 6ef6ce8ac..2ec6aec8d 100644 --- a/client/pages/audiobook/_id/chapters.vue +++ b/client/pages/audiobook/_id/chapters.vue @@ -134,10 +134,14 @@

- Duration found: {{ $secondsToTimestamp(chapterData.runtimeLengthSec) }} + Duration found: {{ $secondsToTimestamp(chapterData.runtimeLengthSec) }}
+ {{ chapterData.chapters.length }} chapters found

- Your audiobook duration: {{ $secondsToTimestamp(mediaDurationRounded) }} + Your audiobook duration: {{ $secondsToTimestamp(mediaDurationRounded) }}
+ Your audiobook has {{ chapters.length }} chapters

Your audiobook duration is shorter than duration found From e97171d95365581ae822c635a01e7e7175676ae2 Mon Sep 17 00:00:00 2001 From: Scott Ruoti Date: Sat, 29 Oct 2022 23:36:52 -0400 Subject: [PATCH 002/256] Makes the dev target support auto reloading of the server --- package-lock.json | 642 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 5 +- 2 files changed, 645 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9db9a655..2103ccea5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,9 @@ }, "bin": { "audiobookshelf": "prod.js" + }, + "devDependencies": { + "nodemon": "^2.0.20" } }, "node_modules/@types/component-emitter": { @@ -41,6 +44,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz", "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -53,6 +62,19 @@ "node": ">= 0.6" } }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -66,6 +88,12 @@ "follow-redirects": "^1.14.8" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -74,6 +102,15 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/body-parser": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", @@ -97,6 +134,28 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -117,11 +176,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -377,6 +469,18 @@ "node": ">= 0.10.0" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -429,6 +533,20 @@ "node": ">= 0.6" } }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -447,6 +565,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -463,6 +593,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -518,6 +657,12 @@ "node": ">=0.10.0" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -531,6 +676,48 @@ "node": ">= 0.10" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -582,6 +769,18 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -600,6 +799,73 @@ "resolved": "https://registry.npmjs.org/node-tone/-/node-tone-1.0.1.tgz", "integrity": "sha512-wi7L0taDZMN6tM5l85TDKHsYzdhqJTtPNgvgpk2zHeZzPt6ZIUZ9vBLTJRRDpm0xzCvbsvFHjAaudeQjLHTE4w==" }, + "node_modules/nodemon": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -640,6 +906,18 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -652,6 +930,12 @@ "node": ">= 0.10" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "node_modules/qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -688,6 +972,18 @@ "node": ">= 0.8" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -717,6 +1013,15 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -777,6 +1082,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-update-notifier": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz", + "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==", + "dev": true, + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/socket.io": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", @@ -861,6 +1187,30 @@ "node": ">= 0.8" } }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -869,6 +1219,18 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -881,6 +1243,12 @@ "node": ">= 0.6" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -967,6 +1335,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz", "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==" }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -976,6 +1350,16 @@ "negotiator": "0.6.3" } }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -989,11 +1373,23 @@ "follow-redirects": "^1.14.8" } }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "body-parser": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", @@ -1013,6 +1409,25 @@ "unpipe": "1.0.0" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1027,11 +1442,33 @@ "get-intrinsic": "^1.0.2" } }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1220,6 +1657,15 @@ "vary": "~1.1.2" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", @@ -1249,6 +1695,13 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1264,6 +1717,15 @@ "has-symbols": "^1.0.1" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -1277,6 +1739,12 @@ "function-bind": "^1.1.1" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -1313,6 +1781,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1323,6 +1797,36 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1356,6 +1860,15 @@ "mime-db": "1.52.0" } }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1371,6 +1884,56 @@ "resolved": "https://registry.npmjs.org/node-tone/-/node-tone-1.0.1.tgz", "integrity": "sha512-wi7L0taDZMN6tM5l85TDKHsYzdhqJTtPNgvgpk2zHeZzPt6ZIUZ9vBLTJRRDpm0xzCvbsvFHjAaudeQjLHTE4w==" }, + "nodemon": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1399,6 +1962,12 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1408,6 +1977,12 @@ "ipaddr.js": "1.9.1" } }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -1432,6 +2007,15 @@ "unpipe": "1.0.0" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1447,6 +2031,12 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, "send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -1500,6 +2090,23 @@ "object-inspect": "^1.9.0" } }, + "simple-update-notifier": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz", + "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==", + "dev": true, + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, "socket.io": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", @@ -1563,11 +2170,38 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1577,6 +2211,12 @@ "mime-types": "~2.1.24" } }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1613,4 +2253,4 @@ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index d4f78f6d3..cb5403083 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Self-hosted audiobook and podcast server", "main": "index.js", "scripts": { - "dev": "node index.js", + "dev": "nodemon --watch server index.js", "start": "node index.js", "client": "cd client && npm ci && npm run generate", "prod": "npm run client && npm ci && node prod.js", @@ -37,5 +37,8 @@ "node-tone": "^1.0.1", "socket.io": "^4.4.1", "xml2js": "^0.4.23" + }, + "devDependencies": { + "nodemon": "^2.0.20" } } \ No newline at end of file From c1938f78c24feff8d81cf7bb4b7c17a714d8ad79 Mon Sep 17 00:00:00 2001 From: Keagan Hilliard Date: Wed, 2 Nov 2022 19:40:50 -0600 Subject: [PATCH 003/256] Added json file support --- server/managers/AbMergeManager.js | 22 +++++---- server/managers/AudioMetadataManager.js | 25 ++++------ server/utils/toneHelpers.js | 65 +++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 25 deletions(-) diff --git a/server/managers/AbMergeManager.js b/server/managers/AbMergeManager.js index 1b3801c04..25e571e25 100644 --- a/server/managers/AbMergeManager.js +++ b/server/managers/AbMergeManager.js @@ -120,20 +120,22 @@ class AbMergeManager { } } - var chaptersFilePath = null - if (libraryItem.media.chapters.length) { - chaptersFilePath = Path.join(task.data.itemCachePath, 'chapters.txt') - try { - await toneHelpers.writeToneChaptersFile(libraryItem.media.chapters, chaptersFilePath) - } catch (error) { - Logger.error(`[AbMergeManager] Write chapters.txt failed`, error) - chaptersFilePath = null - } + var toneJsonPath = null + try { + toneJsonPath = Path.join(itemCacheDir, 'metadata.json') + await toneHelpers.writeToneMetadataJsonFile(libraryItem, toneJsonPath) + } catch (error) { + Logger.error(`[AudioMetadataManager] Write metadata.json failed`, error) + toneJsonPath = null } const toneMetadataObject = toneHelpers.getToneMetadataObject(libraryItem, chaptersFilePath) toneMetadataObject.TrackNumber = 1 task.data.toneMetadataObject = toneMetadataObject + task.data.toneJsonObject = { + 'ToneJsonFile': toneJsonPath, + 'TrackNumber': 1, + } Logger.debug(`[AbMergeManager] Book "${libraryItem.media.metadata.title}" tone metadata object=`, toneMetadataObject) @@ -190,7 +192,7 @@ class AbMergeManager { } // Write metadata to merged file - const success = await toneHelpers.tagAudioFile(task.data.tempFilepath, task.data.toneMetadataObject) + const success = await toneHelpers.tagAudioFile(task.data.tempFilepath, task.data.toneJsonObject) if (!success) { Logger.error(`[AbMergeManager] Failed to write metadata to file "${task.data.tempFilepath}"`) task.setFailed('Failed to write metadata to m4b file') diff --git a/server/managers/AudioMetadataManager.js b/server/managers/AudioMetadataManager.js index 02a44ce19..bcf51aede 100644 --- a/server/managers/AudioMetadataManager.js +++ b/server/managers/AudioMetadataManager.js @@ -43,23 +43,18 @@ class AudioMetadataMangaer { this.emitter('audio_metadata_started', itemAudioMetadataPayload) // Write chapters file - var chaptersFilePath = null + var toneJsonPath = null const itemCacheDir = Path.join(global.MetadataPath, `cache/items/${libraryItem.id}`) await fs.ensureDir(itemCacheDir) - if (libraryItem.media.chapters.length) { - chaptersFilePath = Path.join(itemCacheDir, 'chapters.txt') - try { - await toneHelpers.writeToneChaptersFile(libraryItem.media.chapters, chaptersFilePath) - } catch (error) { - Logger.error(`[AudioMetadataManager] Write chapters.txt failed`, error) - chaptersFilePath = null - } + try { + toneJsonPath = Path.join(itemCacheDir, 'metadata.json') + await toneHelpers.writeToneMetadataJsonFile(libraryItem, toneJsonPath) + } catch (error) { + Logger.error(`[AudioMetadataManager] Write metadata.json failed`, error) + toneJsonPath = null } - const toneMetadataObject = toneHelpers.getToneMetadataObject(libraryItem, chaptersFilePath) - Logger.debug(`[AudioMetadataManager] Book "${libraryItem.media.metadata.title}" tone metadata object=`, toneMetadataObject) - const results = [] for (const af of audioFiles) { const result = await this.updateAudioFileMetadataWithTone(libraryItem.id, af, toneMetadataObject, itemCacheDir) @@ -74,7 +69,7 @@ class AudioMetadataMangaer { this.emitter('audio_metadata_finished', itemAudioMetadataPayload) } - async updateAudioFileMetadataWithTone(libraryItemId, audioFile, toneMetadataObject, itemCacheDir) { + async updateAudioFileMetadataWithTone(libraryItemId, audioFile, toneJsonPath, itemCacheDir) { const resultPayload = { libraryItemId, index: audioFile.index, @@ -93,8 +88,8 @@ class AudioMetadataMangaer { } const _toneMetadataObject = { - ...toneMetadataObject, - 'TrackNumber': audioFile.index + 'ToneJsonFile': toneJsonPath, + 'TrackNumber': audioFile.index, } resultPayload.success = await toneHelpers.tagAudioFile(audioFile.metadata.path, _toneMetadataObject) diff --git a/server/utils/toneHelpers.js b/server/utils/toneHelpers.js index fc88c169b..54add6bdb 100644 --- a/server/utils/toneHelpers.js +++ b/server/utils/toneHelpers.js @@ -72,6 +72,71 @@ module.exports.getToneMetadataObject = (libraryItem, chaptersFile) => { return metadataObject } +module.exports.writeToneMetadataJsonFile = (libraryItem, filePath) => { + const bookMetadata = libraryItem.media.metadata + const coverPath = libraryItem.media.coverPath + const chapters = libraryItem.media.chapters + + const metadataObject = { + 'album': bookMetadata.title || '', + 'title': bookMetadata.title || '', + 'trackTotal': libraryItem.media.tracks.length, + 'additionalFields': {} + } + if (bookMetadata.subtitle) { + metadataObject['subtitle'] = bookMetadata.subtitle + } + if (bookMetadata.authorName) { + metadataObject['artist'] = bookMetadata.authorName + metadataObject['albumArtist'] = bookMetadata.authorName + } + if (bookMetadata.description) { + metadataObject['comment'] = bookMetadata.description + metadataObject['description'] = bookMetadata.description + } + if (bookMetadata.narratorName) { + metadataObject['narrator'] = bookMetadata.narratorName + metadataObject['composer'] = bookMetadata.narratorName + } + if (bookMetadata.firstSeriesName) { + metadataObject['movementName'] = bookMetadata.firstSeriesName + } + if (bookMetadata.firstSeriesSequence) { + metadataObject['movement'] = bookMetadata.firstSeriesSequence + } + if (bookMetadata.genres.length) { + metadataObject['genre'] = bookMetadata.genres.join('/') + } + if (bookMetadata.publisher) { + metadataObject['publisher'] = bookMetadata.publisher + } + if (bookMetadata.asin) { + metadataObject.additionalFields['asin'] = bookMetadata.asin + } + if (bookMetadata.isbn) { + metadataObject.additionalFields['isbn'] = bookMetadata.isbn + } + if (coverPath) { + metadataObject['coverFile'] = coverPath + } + if (parsePublishedYear(bookMetadata.publishedYear)) { + metadataObject['publishingDate'] = parsePublishedYear(bookMetadata.publishedYear) + } + if (chapters && chapters.length > 0) { + let metadataChapters = [] + for (const chapter of chapters) { + metadataChapters.push({ + start: chapter.start, + length: chapter.end - chapter.start, + title: chapter.title, + }) + } + metadataObject['chapters'] = chaptersFile + } + + return fs.writeFile(filePath, JSON.stringify({ metadata: metadataObject })) +} + module.exports.tagAudioFile = (filePath, payload) => { return tone.tag(filePath, payload).then((data) => { return true From 473257f65eabf88a87a2ffb6fb3bf6750872ec9c Mon Sep 17 00:00:00 2001 From: Scott Ruoti Date: Thu, 3 Nov 2022 00:14:07 -0400 Subject: [PATCH 004/256] Makes it so the when sorting and ignoring prefixes, they are actually ignored --- server/controllers/LibraryController.js | 2 +- server/objects/metadata/BookMetadata.js | 16 +++++++++++++--- server/objects/metadata/PodcastMetadata.js | 18 +++++++----------- server/objects/metadata/VideoMetadata.js | 18 +++++++----------- server/utils/index.js | 18 ++++++++++++++---- server/utils/libraryHelpers.js | 5 +++-- 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index 1988b9f75..e06fbe92f 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -288,7 +288,7 @@ class LibraryController { } else if (payload.sortBy === 'addedAt') { return se.addedAt } else { // sort by name - return this.db.serverSettings.sortingIgnorePrefix ? se.nameIgnorePrefix : se.name + return this.db.serverSettings.sortingIgnorePrefix ? se.nameIgnorePrefixSort : se.name } } } diff --git a/server/objects/metadata/BookMetadata.js b/server/objects/metadata/BookMetadata.js index 0dbec4c98..22a28d876 100644 --- a/server/objects/metadata/BookMetadata.js +++ b/server/objects/metadata/BookMetadata.js @@ -1,5 +1,5 @@ const Logger = require('../../Logger') -const { areEquivalent, copyValue, cleanStringForSearch, getTitleIgnorePrefix } = require('../../utils/index') +const { areEquivalent, copyValue, cleanStringForSearch, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index') const parseNameString = require('../../utils/parsers/parseNameString') class BookMetadata { constructor(metadata) { @@ -62,7 +62,7 @@ class BookMetadata { toJSONMinified() { return { title: this.title, - titleIgnorePrefix: this.titleIgnorePrefix, + titleIgnorePrefix: this.titlePrefixAtEnd, subtitle: this.subtitle, authorName: this.authorName, authorNameLF: this.authorNameLF, @@ -83,7 +83,7 @@ class BookMetadata { toJSONExpanded() { return { title: this.title, - titleIgnorePrefix: this.titleIgnorePrefix, + titleIgnorePrefix: this.titlePrefixAtEnd, subtitle: this.subtitle, authors: this.authors.map(a => ({ ...a })), // Author JSONMinimal with name and id narrators: [...this.narrators], @@ -111,6 +111,9 @@ class BookMetadata { get titleIgnorePrefix() { return getTitleIgnorePrefix(this.title) } + get titlePrefixAtEnd() { + return getTitlePrefixAtEnd(this.title) + } get authorName() { if (!this.authors.length) return '' return this.authors.map(au => au.name).join(', ') @@ -133,6 +136,13 @@ class BookMetadata { return `${getTitleIgnorePrefix(se.name)} #${se.sequence}` }).join(', ') } + get seriesNamePrefixAtEnd() { + if (!this.series.length) return '' + return this.series.map(se => { + if (!se.sequence) return getTitlePrefixAtEnd(se.name) + return `${getTitlePrefixAtEnd(se.name)} #${se.sequence}` + }).join(', ') + } get firstSeriesName() { if (!this.series.length) return '' return this.series[0].name diff --git a/server/objects/metadata/PodcastMetadata.js b/server/objects/metadata/PodcastMetadata.js index 3bbd7f2c5..3de2fe336 100644 --- a/server/objects/metadata/PodcastMetadata.js +++ b/server/objects/metadata/PodcastMetadata.js @@ -1,5 +1,5 @@ const Logger = require('../../Logger') -const { areEquivalent, copyValue, cleanStringForSearch } = require('../../utils/index') +const { areEquivalent, copyValue, cleanStringForSearch, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index') class PodcastMetadata { constructor(metadata) { @@ -56,7 +56,7 @@ class PodcastMetadata { toJSONMinified() { return { title: this.title, - titleIgnorePrefix: this.titleIgnorePrefix, + titleIgnorePrefix: this.titlePrefixAtEnd, author: this.author, description: this.description, releaseDate: this.releaseDate, @@ -80,15 +80,11 @@ class PodcastMetadata { } 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 + return getTitleIgnorePrefix(this.title) + } + + get titlePrefixAtEnd() { + return getTitlePrefixAtEnd(this.title) } searchQuery(query) { // Returns key if match is found diff --git a/server/objects/metadata/VideoMetadata.js b/server/objects/metadata/VideoMetadata.js index 400b4c6dd..15d57fbe2 100644 --- a/server/objects/metadata/VideoMetadata.js +++ b/server/objects/metadata/VideoMetadata.js @@ -1,5 +1,5 @@ const Logger = require('../../Logger') -const { areEquivalent, copyValue } = require('../../utils/index') +const { areEquivalent, copyValue, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index') class VideoMetadata { constructor(metadata) { @@ -32,7 +32,7 @@ class VideoMetadata { toJSONMinified() { return { title: this.title, - titleIgnorePrefix: this.titleIgnorePrefix, + titleIgnorePrefix: this.titlePrefixAtEnd, description: this.description, explicit: this.explicit, language: this.language @@ -48,15 +48,11 @@ class VideoMetadata { } 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 + return getTitleIgnorePrefix(this.title) + } + + get titlePrefixAtEnd() { + return getTitlePrefixAtEnd(this.title) } searchQuery(query) { // Returns key if match is found diff --git a/server/utils/index.js b/server/utils/index.js index 48474c8b4..21c959dc1 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -140,14 +140,24 @@ module.exports.cleanStringForSearch = (str) => { return str.toLowerCase().replace(/[\'\.\`\",]/g, '').trim() } -module.exports.getTitleIgnorePrefix = (title) => { - if (!title) return '' +const getTitleParts = (title) => { + if (!title) return ['', null] var prefixesToIgnore = global.ServerSettings.sortingPrefixes || [] + prefixes = [] for (const prefix of prefixesToIgnore) { // e.g. for prefix "the". If title is "The Book" return "Book, The" if (title.toLowerCase().startsWith(`${prefix} `)) { - return title.substr(prefix.length + 1) + `, ${prefix.substr(0, 1).toUpperCase() + prefix.substr(1)}` + return [title.substr(prefix.length + 1), `${prefix.substr(0, 1).toUpperCase() + prefix.substr(1)}`] } } - return title + return [title, null] +} + +module.exports.getTitleIgnorePrefix = (title) => { + return getTitleParts(title)[0] +} + +module.exports.getTitlePrefixAtEnd = (title) => { + let [sort, prefix] = getTitleParts(title) + return prefix ? `${sort}, ${prefix}` : title } \ No newline at end of file diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js index 21a136838..cb5fb0d46 100644 --- a/server/utils/libraryHelpers.js +++ b/server/utils/libraryHelpers.js @@ -1,5 +1,5 @@ const { sort, createNewSortInstance } = require('../libs/fastSort') -const { getTitleIgnorePrefix, isNullOrNaN } = require('../utils/index') +const { getTitlePrefixAtEnd, isNullOrNaN, getTitleIgnorePrefix } = require('../utils/index') const naturalSort = createNewSortInstance({ comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare }) @@ -183,7 +183,8 @@ module.exports = { _series[bookSeriesObj.id] = { id: bookSeriesObj.id, name: bookSeriesObj.name, - nameIgnorePrefix: getTitleIgnorePrefix(bookSeriesObj.name), + nameIgnorePrefix: getTitlePrefixAtEnd(bookSeriesObj.name), + nameIgnorePrefixSort: getTitleIgnorePrefix(bookSeriesObj.name), type: 'series', books: [abJson], addedAt: series ? series.addedAt : 0, From d57effe97cb6dbee7ad7a28a4688bc85aa4e5554 Mon Sep 17 00:00:00 2001 From: Keagan Hilliard Date: Thu, 3 Nov 2022 09:32:50 -0600 Subject: [PATCH 005/256] Fixed a couple of issues, should be working well now --- server/managers/AudioMetadataManager.js | 2 +- server/utils/toneHelpers.js | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server/managers/AudioMetadataManager.js b/server/managers/AudioMetadataManager.js index bcf51aede..88bb83e97 100644 --- a/server/managers/AudioMetadataManager.js +++ b/server/managers/AudioMetadataManager.js @@ -57,7 +57,7 @@ class AudioMetadataMangaer { const results = [] for (const af of audioFiles) { - const result = await this.updateAudioFileMetadataWithTone(libraryItem.id, af, toneMetadataObject, itemCacheDir) + const result = await this.updateAudioFileMetadataWithTone(libraryItem.id, af, toneJsonPath, itemCacheDir) results.push(result) } diff --git a/server/utils/toneHelpers.js b/server/utils/toneHelpers.js index 54add6bdb..ff57bf1ec 100644 --- a/server/utils/toneHelpers.js +++ b/server/utils/toneHelpers.js @@ -126,19 +126,20 @@ module.exports.writeToneMetadataJsonFile = (libraryItem, filePath) => { let metadataChapters = [] for (const chapter of chapters) { metadataChapters.push({ - start: chapter.start, - length: chapter.end - chapter.start, + start: Math.round(chapter.start * 1000), + length: Math.round((chapter.end - chapter.start) * 1000), title: chapter.title, }) } - metadataObject['chapters'] = chaptersFile + metadataObject['chapters'] = metadataChapters } - return fs.writeFile(filePath, JSON.stringify({ metadata: metadataObject })) + return fs.writeFile(filePath, JSON.stringify({ meta: metadataObject })) } module.exports.tagAudioFile = (filePath, payload) => { return tone.tag(filePath, payload).then((data) => { + Logger.info('Tone results: ', data) return true }).catch((error) => { Logger.error(`[toneHelpers] tagAudioFile: Failed for "${filePath}"`, error) From 586c8a550a9b53487aabd13fa47d44626cf1da09 Mon Sep 17 00:00:00 2001 From: Keagan Hilliard Date: Thu, 3 Nov 2022 10:09:49 -0600 Subject: [PATCH 006/256] Removed a noisy log and limit chapter embedding to items with only 1 audiofile --- server/managers/AudioMetadataManager.js | 2 +- server/utils/toneHelpers.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/server/managers/AudioMetadataManager.js b/server/managers/AudioMetadataManager.js index 88bb83e97..c76e3fe27 100644 --- a/server/managers/AudioMetadataManager.js +++ b/server/managers/AudioMetadataManager.js @@ -49,7 +49,7 @@ class AudioMetadataMangaer { try { toneJsonPath = Path.join(itemCacheDir, 'metadata.json') - await toneHelpers.writeToneMetadataJsonFile(libraryItem, toneJsonPath) + await toneHelpers.writeToneMetadataJsonFile(libraryItem, audioFiles.length == 1 && libraryItem.media.chapters, toneJsonPath) } catch (error) { Logger.error(`[AudioMetadataManager] Write metadata.json failed`, error) toneJsonPath = null diff --git a/server/utils/toneHelpers.js b/server/utils/toneHelpers.js index ff57bf1ec..fcdd14405 100644 --- a/server/utils/toneHelpers.js +++ b/server/utils/toneHelpers.js @@ -72,10 +72,9 @@ module.exports.getToneMetadataObject = (libraryItem, chaptersFile) => { return metadataObject } -module.exports.writeToneMetadataJsonFile = (libraryItem, filePath) => { +module.exports.writeToneMetadataJsonFile = (libraryItem, chapters, filePath) => { const bookMetadata = libraryItem.media.metadata const coverPath = libraryItem.media.coverPath - const chapters = libraryItem.media.chapters const metadataObject = { 'album': bookMetadata.title || '', @@ -139,7 +138,6 @@ module.exports.writeToneMetadataJsonFile = (libraryItem, filePath) => { module.exports.tagAudioFile = (filePath, payload) => { return tone.tag(filePath, payload).then((data) => { - Logger.info('Tone results: ', data) return true }).catch((error) => { Logger.error(`[toneHelpers] tagAudioFile: Failed for "${filePath}"`, error) From 3824154c15fa6f86d121ef8db5da77c2c3154079 Mon Sep 17 00:00:00 2001 From: Keagan Hilliard Date: Thu, 3 Nov 2022 10:20:32 -0600 Subject: [PATCH 007/256] Forgot to update the merge --- server/managers/AbMergeManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/managers/AbMergeManager.js b/server/managers/AbMergeManager.js index 25e571e25..0bc89be31 100644 --- a/server/managers/AbMergeManager.js +++ b/server/managers/AbMergeManager.js @@ -123,7 +123,7 @@ class AbMergeManager { var toneJsonPath = null try { toneJsonPath = Path.join(itemCacheDir, 'metadata.json') - await toneHelpers.writeToneMetadataJsonFile(libraryItem, toneJsonPath) + await toneHelpers.writeToneMetadataJsonFile(libraryItem, libraryItem.media.chapters, toneJsonPath) } catch (error) { Logger.error(`[AudioMetadataManager] Write metadata.json failed`, error) toneJsonPath = null From af9a87f8bdd51613fa6a7985d7b16973fd891367 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 5 Nov 2022 07:36:53 -0500 Subject: [PATCH 008/256] Tone version bump v0.1.2 --- Dockerfile | 2 +- build/debian/DEBIAN/preinst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index f1e9452bf..db7642bb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ RUN npm ci && npm cache clean --force RUN npm run generate ### STAGE 1: Build server ### -FROM sandreas/tone:v0.1.1 AS tone +FROM sandreas/tone:v0.1.2 AS tone FROM node:16-alpine ENV NODE_ENV=production diff --git a/build/debian/DEBIAN/preinst b/build/debian/DEBIAN/preinst index fc7bb0a28..be2d8b794 100644 --- a/build/debian/DEBIAN/preinst +++ b/build/debian/DEBIAN/preinst @@ -50,7 +50,7 @@ install_ffmpeg() { echo "Starting FFMPEG Install" WGET="wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz --output-document=ffmpeg-git-amd64-static.tar.xz" - WGET_TONE="wget https://github.com/sandreas/tone/releases/download/v0.1.1/tone-0.1.1-linux-x64.tar.gz --output-document=tone-0.1.1-linux-x64.tar.gz" + WGET_TONE="wget https://github.com/sandreas/tone/releases/download/v0.1.2/tone-0.1.2-linux-x64.tar.gz --output-document=tone-0.1.2-linux-x64.tar.gz" if ! cd "$FFMPEG_INSTALL_DIR"; then echo "Creating ffmpeg install dir at $FFMPEG_INSTALL_DIR" @@ -66,8 +66,8 @@ install_ffmpeg() { # Temp downloading tone library to the ffmpeg dir echo "Getting tone.." $WGET_TONE - tar xvf tone-0.1.1-linux-x64.tar.gz --strip-components=1 - rm tone-0.1.1-linux-x64.tar.gz + tar xvf tone-0.1.2-linux-x64.tar.gz --strip-components=1 + rm tone-0.1.2-linux-x64.tar.gz echo "Good to go on Ffmpeg (& tone)... hopefully" } From 1118b8b78200704d2c16c5c36f7fccc70e5d0f4f Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 5 Nov 2022 13:13:52 -0500 Subject: [PATCH 009/256] Metadata embed and m4b merge fixes and cleanup --- client/pages/audiobook/_id/manage.vue | 4 ++++ server/controllers/LibraryItemController.js | 3 ++- server/managers/AbMergeManager.js | 13 ++++--------- server/managers/AudioMetadataManager.js | 9 +++++---- server/utils/toneHelpers.js | 6 +++--- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/client/pages/audiobook/_id/manage.vue b/client/pages/audiobook/_id/manage.vue index 9f858a81d..a45979dc1 100644 --- a/client/pages/audiobook/_id/manage.vue +++ b/client/pages/audiobook/_id/manage.vue @@ -91,6 +91,10 @@ A backup of your original audio files will be stored in /metadata/cache/items/{{ libraryItemId }}/. Make sure to periodically purge items cache.

+
+ star +

Chapters are not embedded in multi-track audiobooks.

+
star

Encoding can take up to 30 minutes.

diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index b1f3f77d1..56b181c27 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -398,7 +398,8 @@ class LibraryItemController { } const useTone = req.query.tone === '1' - this.audioMetadataManager.updateMetadataForItem(req.user, req.libraryItem, useTone) + const forceEmbedChapters = req.query.forceEmbedChapters === '1' + this.audioMetadataManager.updateMetadataForItem(req.user, req.libraryItem, useTone, forceEmbedChapters) res.sendStatus(200) } diff --git a/server/managers/AbMergeManager.js b/server/managers/AbMergeManager.js index 0bc89be31..72343d50a 100644 --- a/server/managers/AbMergeManager.js +++ b/server/managers/AbMergeManager.js @@ -62,7 +62,7 @@ class AbMergeManager { targetFilename, targetFilepath: Path.join(libraryItem.path, targetFilename), itemCachePath, - toneMetadataObject: null + toneJsonObject: null } const taskDescription = `Encoding audiobook "${libraryItem.media.metadata.title}" into a single m4b file.` task.setData('encode-m4b', 'Encoding M4b', taskDescription, taskData) @@ -122,23 +122,18 @@ class AbMergeManager { var toneJsonPath = null try { - toneJsonPath = Path.join(itemCacheDir, 'metadata.json') - await toneHelpers.writeToneMetadataJsonFile(libraryItem, libraryItem.media.chapters, toneJsonPath) + toneJsonPath = Path.join(task.data.itemCachePath, 'metadata.json') + await toneHelpers.writeToneMetadataJsonFile(libraryItem, libraryItem.media.chapters, toneJsonPath, 1) } catch (error) { - Logger.error(`[AudioMetadataManager] Write metadata.json failed`, error) + Logger.error(`[AbMergeManager] Write metadata.json failed`, error) toneJsonPath = null } - const toneMetadataObject = toneHelpers.getToneMetadataObject(libraryItem, chaptersFilePath) - toneMetadataObject.TrackNumber = 1 - task.data.toneMetadataObject = toneMetadataObject task.data.toneJsonObject = { 'ToneJsonFile': toneJsonPath, 'TrackNumber': 1, } - Logger.debug(`[AbMergeManager] Book "${libraryItem.media.metadata.title}" tone metadata object=`, toneMetadataObject) - var workerData = { inputs: ffmpegInputs, options: ffmpegOptions, diff --git a/server/managers/AudioMetadataManager.js b/server/managers/AudioMetadataManager.js index c76e3fe27..25a93583c 100644 --- a/server/managers/AudioMetadataManager.js +++ b/server/managers/AudioMetadataManager.js @@ -15,9 +15,9 @@ class AudioMetadataMangaer { this.clientEmitter = clientEmitter } - updateMetadataForItem(user, libraryItem, useTone = true) { + updateMetadataForItem(user, libraryItem, useTone, forceEmbedChapters) { if (useTone) { - this.updateMetadataForItemWithTone(user, libraryItem) + this.updateMetadataForItemWithTone(user, libraryItem, forceEmbedChapters) } else { this.updateMetadataForItemWithFfmpeg(user, libraryItem) } @@ -30,7 +30,7 @@ class AudioMetadataMangaer { return toneHelpers.getToneMetadataObject(libraryItem) } - async updateMetadataForItemWithTone(user, libraryItem) { + async updateMetadataForItemWithTone(user, libraryItem, forceEmbedChapters) { var audioFiles = libraryItem.media.includedAudioFiles const itemAudioMetadataPayload = { @@ -49,7 +49,8 @@ class AudioMetadataMangaer { try { toneJsonPath = Path.join(itemCacheDir, 'metadata.json') - await toneHelpers.writeToneMetadataJsonFile(libraryItem, audioFiles.length == 1 && libraryItem.media.chapters, toneJsonPath) + const chapters = (audioFiles.length == 1 || forceEmbedChapters) ? libraryItem.media.chapters : null + await toneHelpers.writeToneMetadataJsonFile(libraryItem, chapters, toneJsonPath, audioFiles.length) } catch (error) { Logger.error(`[AudioMetadataManager] Write metadata.json failed`, error) toneJsonPath = null diff --git a/server/utils/toneHelpers.js b/server/utils/toneHelpers.js index fcdd14405..dac6fa40f 100644 --- a/server/utils/toneHelpers.js +++ b/server/utils/toneHelpers.js @@ -72,14 +72,14 @@ module.exports.getToneMetadataObject = (libraryItem, chaptersFile) => { return metadataObject } -module.exports.writeToneMetadataJsonFile = (libraryItem, chapters, filePath) => { +module.exports.writeToneMetadataJsonFile = (libraryItem, chapters, filePath, trackTotal) => { const bookMetadata = libraryItem.media.metadata const coverPath = libraryItem.media.coverPath const metadataObject = { 'album': bookMetadata.title || '', 'title': bookMetadata.title || '', - 'trackTotal': libraryItem.media.tracks.length, + 'trackTotal': trackTotal, 'additionalFields': {} } if (bookMetadata.subtitle) { @@ -133,7 +133,7 @@ module.exports.writeToneMetadataJsonFile = (libraryItem, chapters, filePath) => metadataObject['chapters'] = metadataChapters } - return fs.writeFile(filePath, JSON.stringify({ meta: metadataObject })) + return fs.writeFile(filePath, JSON.stringify({ meta: metadataObject }, null, 2)) } module.exports.tagAudioFile = (filePath, payload) => { From a3dc79121ea3a0df15dbd3bb26143c277cd06298 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 5 Nov 2022 15:06:10 -0500 Subject: [PATCH 010/256] Version bump 2.2.2 --- client/package-lock.json | 6 +++--- client/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 9443270dd..5a835a29e 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf-client", - "version": "2.2.1", + "version": "2.2.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "audiobookshelf-client", - "version": "2.2.1", + "version": "2.2.2", "license": "ISC", "dependencies": { "@nuxtjs/axios": "^5.13.6", @@ -30804,4 +30804,4 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } } -} +} \ No newline at end of file diff --git a/client/package.json b/client/package.json index 9e68accf3..d4a7580ab 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "2.2.1", + "version": "2.2.2", "description": "Self-hosted audiobook and podcast client", "main": "index.js", "scripts": { diff --git a/package-lock.json b/package-lock.json index 2103ccea5..884386d30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf", - "version": "2.2.1", + "version": "2.2.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "audiobookshelf", - "version": "2.2.1", + "version": "2.2.2", "license": "GPL-3.0", "dependencies": { "axios": "^0.26.1", @@ -2253,4 +2253,4 @@ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index cb5403083..e0624eaa3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "2.2.1", + "version": "2.2.2", "description": "Self-hosted audiobook and podcast server", "main": "index.js", "scripts": { From d64932dad70c0be31375b99c92fd2c994007bb58 Mon Sep 17 00:00:00 2001 From: Scott Ruoti Date: Sat, 29 Oct 2022 22:54:31 -0400 Subject: [PATCH 011/256] Fixes bug when titles are in multiple series being collapsed --- server/utils/libraryHelpers.js | 43 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js index cb5fb0d46..19e0cd6fa 100644 --- a/server/utils/libraryHelpers.js +++ b/server/utils/libraryHelpers.js @@ -283,32 +283,31 @@ module.exports = { collapseBookSeries(libraryItems, series) { var seriesObjects = this.getSeriesFromBooks(libraryItems, series, null, null, true) var seriesToUse = {} - var libraryItemIdsToHide = [] - seriesObjects.forEach((series) => { - series.firstBook = series.books.find(b => !seriesToUse[b.id]) // Find first book not already used - if (series.firstBook) { - seriesToUse[series.firstBook.id] = series - libraryItemIdsToHide = libraryItemIdsToHide.concat(series.books.filter(b => !seriesToUse[b.id]).map(b => b.id)) - } - }) + var collapsedLibraryItems = [] - return libraryItems.map((li) => { + libraryItems.forEach((li) => { if (li.mediaType != 'book') return - var libraryItemJson = li.toJSONMinified() - if (libraryItemIdsToHide.includes(li.id)) { - return null - } - if (seriesToUse[li.id]) { + + // Handle when this is the first book in a series + seriesObjects.filter(s => s.books[0].id == li.id).forEach(series => { + let libraryItemJson = li.toJSONMinified() libraryItemJson.collapsedSeries = { - id: seriesToUse[li.id].id, - name: seriesToUse[li.id].name, - nameIgnorePrefix: seriesToUse[li.id].nameIgnorePrefix, - libraryItemIds: seriesToUse[li.id].books.map(b => b.id), - numBooks: seriesToUse[li.id].books.length + id: series.id, + name: series.name, + nameIgnorePrefix: series.nameIgnorePrefix, + libraryItemIds: series.books.map(b => b.id), + numBooks: series.books.length } - } - return libraryItemJson - }).filter(li => li) + collapsedLibraryItems.push(libraryItemJson); + }); + + // Ignore books contained in series + if (li.media.metadata.series.length) return + + collapsedLibraryItems.push(li.toJSONMinified()) + }); + + return collapsedLibraryItems }, buildPersonalizedShelves(user, libraryItems, mediaType, allSeries, allAuthors, maxEntitiesPerShelf = 10) { From b322d0207bdea39007122b70e7d239638ac95986 Mon Sep 17 00:00:00 2001 From: Scott Ruoti Date: Sun, 30 Oct 2022 10:21:12 -0400 Subject: [PATCH 012/256] Fixed sorting to be more consistent for multiple series (and generally) --- server/controllers/LibraryController.js | 94 +++++++++++++++---------- server/utils/libraryHelpers.js | 36 +++++----- 2 files changed, 76 insertions(+), 54 deletions(-) diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index e06fbe92f..a5bcfe73a 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -160,17 +160,30 @@ class LibraryController { minified: req.query.minified === '1', collapseseries: req.query.collapseseries === '1' } + var mediaIsBook = payload.mediaType === 'book' var filterSeries = null if (payload.filterBy) { // If filtering by series, will include seriesName and seriesSequence on media metadata - filterSeries = (payload.mediaType == 'book' && payload.filterBy.startsWith('series.')) ? libraryHelpers.decode(payload.filterBy.replace('series.', '')) : null + filterSeries = (mediaIsBook && payload.filterBy.startsWith('series.')) ? libraryHelpers.decode(payload.filterBy.replace('series.', '')) : null if (filterSeries === 'No Series') filterSeries = null libraryItems = libraryHelpers.getFilteredLibraryItems(libraryItems, payload.filterBy, req.user, this.rssFeedManager.feedsArray) payload.total = libraryItems.length } + if (payload.collapseseries) { + libraryItems = libraryHelpers.collapseBookSeries(libraryItems, this.db.series, filterSeries) + payload.total = libraryItems.length + } + + + var sortArray = [] + if (filterSeries) { + // Book media when filtering series will sort by the series sequence + sortArray.push({ asc: (li) => li.media.metadata.getSeries(filterSeries).sequence }) + } + if (payload.sortBy) { var sortKey = payload.sortBy @@ -186,29 +199,37 @@ class LibraryController { sortKey += 'IgnorePrefix' } - // Start sort - var direction = payload.sortDesc ? 'desc' : 'asc' - var sortArray = [ - { - [direction]: (li) => { - // When collapsing by series and sorting by title use the series name instead of the book title - if (payload.mediaType === 'book' && payload.collapseseries && li.media.metadata.seriesName) { - if (sortByTitle) { - return this.db.serverSettings.sortingIgnorePrefix ? li.media.metadata.seriesNameIgnorePrefix : li.media.metadata.seriesName - } else { - // When not sorting by title always show the collapsed series at the end - return direction === 'desc' ? -1 : 'zzzz' - } + // If series are collapsed and not sorting by title, sort all collapsed series to the end in alphabetical order + if (payload.collapseseries && !sortByTitle) { + sortArray.push({ + asc: (li) => { + if (li.collapsedSeries) { + return this.db.serverSettings.sortingIgnorePrefix ? + li.collapsedSeries.nameIgnorePrefix : + li.collapsedSeries.name + } else { + return '' } + } + }) + } + var direction = payload.sortDesc ? 'desc' : 'asc' + sortArray.push({ + [direction]: (li) => { + if (mediaIsBook && sortByTitle && li.collapsedSeries) { + return this.db.serverSettings.sortingIgnorePrefix ? + li.collapsedSeries.nameIgnorePrefix : + li.collapsedSeries.name + } else { // Supports dot notation strings i.e. "media.metadata.title" return sortKey.split('.').reduce((a, b) => a[b], li) } } - ] + }) // Secondary sort when sorting by book author use series sort title - if (payload.mediaType === 'book' && payload.sortBy.includes('author')) { + if (mediaIsBook && payload.sortBy.includes('author')) { sortArray.push({ asc: (li) => { if (li.media.metadata.series && li.media.metadata.series.length) { @@ -218,30 +239,31 @@ class LibraryController { } }) } + } + + // Sort the items + if (sortArray.length) { libraryItems = naturalSort(libraryItems).by(sortArray) } - if (payload.collapseseries) { - libraryItems = libraryHelpers.collapseBookSeries(libraryItems, this.db.series) - payload.total = libraryItems.length - } else if (filterSeries) { - // Book media when filtering series will include series object on media metadata - libraryItems = libraryItems.map(li => { - var series = li.media.metadata.getSeries(filterSeries) - var liJson = payload.minified ? li.toJSONMinified() : li.toJSON() - liJson.media.metadata.series = series - return liJson - }) - libraryItems = naturalSort(libraryItems).asc(li => li.media.metadata.series.sequence) - } else { - libraryItems = libraryItems.map(li => payload.minified ? li.toJSONMinified() : li.toJSON()) - } + payload.results = libraryItems.map(li => { + let json = payload.minified ? li.toJSONMinified() : li.toJSON() + + if (li.collapsedSeries) { + json.collapsedSeries = { + id: li.collapsedSeries.id, + name: li.collapsedSeries.name, + nameIgnorePrefix: li.collapsedSeries.nameIgnorePrefix, + libraryItemIds: li.collapsedSeries.books.map(b => b.id), + numBooks: li.collapsedSeries.books.length + } + } else if (filterSeries) { + json.media.metadata.series = li.media.metadata.getSeries(filterSeries) + } + + return json + }) - if (payload.limit) { - var startIndex = payload.page * payload.limit - libraryItems = libraryItems.slice(startIndex, startIndex + payload.limit) - } - payload.results = libraryItems res.json(payload) } diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js index 19e0cd6fa..f3d28acf3 100644 --- a/server/utils/libraryHelpers.js +++ b/server/utils/libraryHelpers.js @@ -280,34 +280,34 @@ module.exports = { return totalSize }, - collapseBookSeries(libraryItems, series) { - var seriesObjects = this.getSeriesFromBooks(libraryItems, series, null, null, true) - var seriesToUse = {} - var collapsedLibraryItems = [] + + collapseBookSeries(libraryItems, series, filterSeries) { + // Get series from the library items. If this list is being collapsed after filtering for a series, + // don't collapse that series, only books that are in other series. + var seriesObjects = this + .getSeriesFromBooks(libraryItems, series, null, null, true) + .filter(s => s.id != filterSeries) + + var filteredLibraryItems = [] libraryItems.forEach((li) => { if (li.mediaType != 'book') return // Handle when this is the first book in a series seriesObjects.filter(s => s.books[0].id == li.id).forEach(series => { - let libraryItemJson = li.toJSONMinified() - libraryItemJson.collapsedSeries = { - id: series.id, - name: series.name, - nameIgnorePrefix: series.nameIgnorePrefix, - libraryItemIds: series.books.map(b => b.id), - numBooks: series.books.length - } - collapsedLibraryItems.push(libraryItemJson); + // Clone the library item as we need to attach data to it, but don't + // want to change the global copy of the library item + filteredLibraryItems.push(Object.assign( + Object.create(Object.getPrototypeOf(li)), + li, { collapsedSeries: series })) }); - // Ignore books contained in series - if (li.media.metadata.series.length) return - - collapsedLibraryItems.push(li.toJSONMinified()) + // Only included books not contained in series + if (!seriesObjects.some(s => s.books.some(b => b.id == li.id))) + filteredLibraryItems.push(li) }); - return collapsedLibraryItems + return filteredLibraryItems }, buildPersonalizedShelves(user, libraryItems, mediaType, allSeries, allAuthors, maxEntitiesPerShelf = 10) { From c1035d97e8d988269b896bc78a112e7ca1232cff Mon Sep 17 00:00:00 2001 From: Scott Ruoti Date: Sun, 30 Oct 2022 11:38:00 -0400 Subject: [PATCH 013/256] Show book sequences for collapsed series when filtering by series --- client/components/app/LazyBookshelf.vue | 28 ++++-- client/components/cards/LazyBookCard.vue | 117 +++++++++++++++++------ server/controllers/LibraryController.js | 58 ++++++++--- server/utils/libraryHelpers.js | 7 +- 4 files changed, 159 insertions(+), 51 deletions(-) diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue index 08ed1496d..c0798d19a 100644 --- a/client/components/app/LazyBookshelf.vue +++ b/client/components/app/LazyBookshelf.vue @@ -1,12 +1,15 @@