mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-15 22:01:37 +00:00
Merge branch 'rich-text-descriptions' of https://github.com/MxMarx/audiobookshelf into rich-text-descriptions
This commit is contained in:
commit
089e4b47db
22 changed files with 165 additions and 104 deletions
|
|
@ -191,7 +191,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
search() {},
|
submit() {},
|
||||||
inputUpdate() {
|
inputUpdate() {
|
||||||
clearTimeout(this.searchTimeout)
|
clearTimeout(this.searchTimeout)
|
||||||
this.searchTimeout = setTimeout(() => {
|
this.searchTimeout = setTimeout(() => {
|
||||||
|
|
|
||||||
4
client/package-lock.json
generated
4
client/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxtjs/axios": "^5.13.6",
|
"@nuxtjs/axios": "^5.13.6",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"description": "Self-hosted audiobook and podcast client",
|
"description": "Self-hosted audiobook and podcast client",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -99,11 +99,11 @@
|
||||||
"HeaderDetails": "Details",
|
"HeaderDetails": "Details",
|
||||||
"HeaderDownloadQueue": "Download-wachtrij",
|
"HeaderDownloadQueue": "Download-wachtrij",
|
||||||
"HeaderEbookFiles": "Ebook Files",
|
"HeaderEbookFiles": "Ebook Files",
|
||||||
"HeaderEmail": "Email",
|
"HeaderEmail": "E-mail",
|
||||||
"HeaderEmailSettings": "Email Settings",
|
"HeaderEmailSettings": "E-mail instellingen",
|
||||||
"HeaderEpisodes": "Afleveringen",
|
"HeaderEpisodes": "Afleveringen",
|
||||||
"HeaderEreaderDevices": "Ereader Devices",
|
"HeaderEreaderDevices": "Ereader-apparaten",
|
||||||
"HeaderEreaderSettings": "Ereader Settings",
|
"HeaderEreaderSettings": "Ereader-instellingen",
|
||||||
"HeaderFiles": "Bestanden",
|
"HeaderFiles": "Bestanden",
|
||||||
"HeaderFindChapters": "Zoek hoofdstukken",
|
"HeaderFindChapters": "Zoek hoofdstukken",
|
||||||
"HeaderIgnoredFiles": "Genegeerde bestanden",
|
"HeaderIgnoredFiles": "Genegeerde bestanden",
|
||||||
|
|
@ -138,7 +138,7 @@
|
||||||
"HeaderRemoveEpisodes": "Verwijder {0} afleveringen",
|
"HeaderRemoveEpisodes": "Verwijder {0} afleveringen",
|
||||||
"HeaderRSSFeedGeneral": "RSS-details",
|
"HeaderRSSFeedGeneral": "RSS-details",
|
||||||
"HeaderRSSFeedIsOpen": "RSS-feed is open",
|
"HeaderRSSFeedIsOpen": "RSS-feed is open",
|
||||||
"HeaderRSSFeeds": "RSS Feeds",
|
"HeaderRSSFeeds": "RSS-feeds",
|
||||||
"HeaderSavedMediaProgress": "Opgeslagen mediavoortgang",
|
"HeaderSavedMediaProgress": "Opgeslagen mediavoortgang",
|
||||||
"HeaderSchedule": "Schema",
|
"HeaderSchedule": "Schema",
|
||||||
"HeaderScheduleLibraryScans": "Schema automatische bibliotheekscans",
|
"HeaderScheduleLibraryScans": "Schema automatische bibliotheekscans",
|
||||||
|
|
@ -156,7 +156,7 @@
|
||||||
"HeaderStatsRecentSessions": "Recente sessies",
|
"HeaderStatsRecentSessions": "Recente sessies",
|
||||||
"HeaderStatsTop10Authors": "Top 10 auteurs",
|
"HeaderStatsTop10Authors": "Top 10 auteurs",
|
||||||
"HeaderStatsTop5Genres": "Top 5 genres",
|
"HeaderStatsTop5Genres": "Top 5 genres",
|
||||||
"HeaderTableOfContents": "Table of Contents",
|
"HeaderTableOfContents": "Inhoudsopgave",
|
||||||
"HeaderTools": "Tools",
|
"HeaderTools": "Tools",
|
||||||
"HeaderUpdateAccount": "Account bijwerken",
|
"HeaderUpdateAccount": "Account bijwerken",
|
||||||
"HeaderUpdateAuthor": "Auteur bijwerken",
|
"HeaderUpdateAuthor": "Auteur bijwerken",
|
||||||
|
|
@ -179,14 +179,14 @@
|
||||||
"LabelAll": "Alle",
|
"LabelAll": "Alle",
|
||||||
"LabelAllUsers": "Alle gebruikers",
|
"LabelAllUsers": "Alle gebruikers",
|
||||||
"LabelAlreadyInYourLibrary": "Reeds in je bibliotheek",
|
"LabelAlreadyInYourLibrary": "Reeds in je bibliotheek",
|
||||||
"LabelAppend": "Append",
|
"LabelAppend": "Achteraan toevoegen",
|
||||||
"LabelAuthor": "Auteur",
|
"LabelAuthor": "Auteur",
|
||||||
"LabelAuthorFirstLast": "Auteur (Voornaam Achternaam)",
|
"LabelAuthorFirstLast": "Auteur (Voornaam Achternaam)",
|
||||||
"LabelAuthorLastFirst": "Auteur (Achternaam, Voornaam)",
|
"LabelAuthorLastFirst": "Auteur (Achternaam, Voornaam)",
|
||||||
"LabelAuthors": "Auteurs",
|
"LabelAuthors": "Auteurs",
|
||||||
"LabelAutoDownloadEpisodes": "Afleveringen automatisch downloaden",
|
"LabelAutoDownloadEpisodes": "Afleveringen automatisch downloaden",
|
||||||
"LabelBackToUser": "Terug naar gebruiker",
|
"LabelBackToUser": "Terug naar gebruiker",
|
||||||
"LabelBackupLocation": "Backup Location",
|
"LabelBackupLocation": "Back-up locatie",
|
||||||
"LabelBackupsEnableAutomaticBackups": "Automatische back-ups inschakelen",
|
"LabelBackupsEnableAutomaticBackups": "Automatische back-ups inschakelen",
|
||||||
"LabelBackupsEnableAutomaticBackupsHelp": "Back-ups opgeslagen in /metadata/backups",
|
"LabelBackupsEnableAutomaticBackupsHelp": "Back-ups opgeslagen in /metadata/backups",
|
||||||
"LabelBackupsMaxBackupSize": "Maximale back-up-grootte (in GB)",
|
"LabelBackupsMaxBackupSize": "Maximale back-up-grootte (in GB)",
|
||||||
|
|
@ -208,7 +208,7 @@
|
||||||
"LabelComplete": "Compleet",
|
"LabelComplete": "Compleet",
|
||||||
"LabelConfirmPassword": "Bevestig wachtwoord",
|
"LabelConfirmPassword": "Bevestig wachtwoord",
|
||||||
"LabelContinueListening": "Verder luisteren",
|
"LabelContinueListening": "Verder luisteren",
|
||||||
"LabelContinueReading": "Continue Reading",
|
"LabelContinueReading": "Verder luisteren",
|
||||||
"LabelContinueSeries": "Ga verder met serie",
|
"LabelContinueSeries": "Ga verder met serie",
|
||||||
"LabelCover": "Cover",
|
"LabelCover": "Cover",
|
||||||
"LabelCoverImageURL": "Coverafbeelding URL",
|
"LabelCoverImageURL": "Coverafbeelding URL",
|
||||||
|
|
@ -225,7 +225,7 @@
|
||||||
"LabelDirectory": "Map",
|
"LabelDirectory": "Map",
|
||||||
"LabelDiscFromFilename": "Schijf uit bestandsnaam",
|
"LabelDiscFromFilename": "Schijf uit bestandsnaam",
|
||||||
"LabelDiscFromMetadata": "Schijf uit metadata",
|
"LabelDiscFromMetadata": "Schijf uit metadata",
|
||||||
"LabelDiscover": "Discover",
|
"LabelDiscover": "Ontdek",
|
||||||
"LabelDownload": "Download",
|
"LabelDownload": "Download",
|
||||||
"LabelDownloadNEpisodes": "Download {0} episodes",
|
"LabelDownloadNEpisodes": "Download {0} episodes",
|
||||||
"LabelDuration": "Duur",
|
"LabelDuration": "Duur",
|
||||||
|
|
@ -234,10 +234,10 @@
|
||||||
"LabelEbooks": "Ebooks",
|
"LabelEbooks": "Ebooks",
|
||||||
"LabelEdit": "Wijzig",
|
"LabelEdit": "Wijzig",
|
||||||
"LabelEmail": "Email",
|
"LabelEmail": "Email",
|
||||||
"LabelEmailSettingsFromAddress": "From Address",
|
"LabelEmailSettingsFromAddress": "Van-adres",
|
||||||
"LabelEmailSettingsSecure": "Secure",
|
"LabelEmailSettingsSecure": "Veilig",
|
||||||
"LabelEmailSettingsSecureHelp": "If true the connection will use TLS when connecting to server. If false then TLS is used if server supports the STARTTLS extension. In most cases set this value to true if you are connecting to port 465. For port 587 or 25 keep it false. (from nodemailer.com/smtp/#authentication)",
|
"LabelEmailSettingsSecureHelp": "Als 'waar', dan gebruikt de verbinding TLS om met de server te verbinden. Als 'onwaar', dan wordt TLS gebruikt als de server de STARTTLS-extensie ondersteunt. In de meeste gevallen kies je voor 'waar' verbindt met poort 465. Voo poort 587 of 25, laat op 'onwaar'. (van nodemailer.com/smtp/#authentication)",
|
||||||
"LabelEmailSettingsTestAddress": "Test Address",
|
"LabelEmailSettingsTestAddress": "Test-adres",
|
||||||
"LabelEmbeddedCover": "Ingesloten cover",
|
"LabelEmbeddedCover": "Ingesloten cover",
|
||||||
"LabelEnable": "Inschakelen",
|
"LabelEnable": "Inschakelen",
|
||||||
"LabelEnd": "Einde",
|
"LabelEnd": "Einde",
|
||||||
|
|
@ -256,13 +256,13 @@
|
||||||
"LabelFinished": "Voltooid",
|
"LabelFinished": "Voltooid",
|
||||||
"LabelFolder": "Map",
|
"LabelFolder": "Map",
|
||||||
"LabelFolders": "Mappen",
|
"LabelFolders": "Mappen",
|
||||||
"LabelFontScale": "Font scale",
|
"LabelFontScale": "Lettertype schaal",
|
||||||
"LabelFormat": "Format",
|
"LabelFormat": "Formaat",
|
||||||
"LabelGenre": "Genre",
|
"LabelGenre": "Genre",
|
||||||
"LabelGenres": "Genres",
|
"LabelGenres": "Genres",
|
||||||
"LabelHardDeleteFile": "Hard-delete bestand",
|
"LabelHardDeleteFile": "Hard-delete bestand",
|
||||||
"LabelHasEbook": "Has ebook",
|
"LabelHasEbook": "Heeft ebook",
|
||||||
"LabelHasSupplementaryEbook": "Has supplementary ebook",
|
"LabelHasSupplementaryEbook": "Heeft supplementair ebook",
|
||||||
"LabelHost": "Host",
|
"LabelHost": "Host",
|
||||||
"LabelHour": "Uur",
|
"LabelHour": "Uur",
|
||||||
"LabelIcon": "Icoon",
|
"LabelIcon": "Icoon",
|
||||||
|
|
@ -289,15 +289,15 @@
|
||||||
"LabelLastTime": "Laatste keer",
|
"LabelLastTime": "Laatste keer",
|
||||||
"LabelLastUpdate": "Laatste update",
|
"LabelLastUpdate": "Laatste update",
|
||||||
"LabelLayout": "Layout",
|
"LabelLayout": "Layout",
|
||||||
"LabelLayoutSinglePage": "Single page",
|
"LabelLayoutSinglePage": "Enkele pagina",
|
||||||
"LabelLayoutSplitPage": "Split page",
|
"LabelLayoutSplitPage": "Gesplitste pagina",
|
||||||
"LabelLess": "Minder",
|
"LabelLess": "Minder",
|
||||||
"LabelLibrariesAccessibleToUser": "Voor gebruiker toegankelijke bibliotheken",
|
"LabelLibrariesAccessibleToUser": "Voor gebruiker toegankelijke bibliotheken",
|
||||||
"LabelLibrary": "Bibliotheek",
|
"LabelLibrary": "Bibliotheek",
|
||||||
"LabelLibraryItem": "Library Item",
|
"LabelLibraryItem": "Bibliotheekonderdeel",
|
||||||
"LabelLibraryName": "Library Name",
|
"LabelLibraryName": "Bibliotheeknaam",
|
||||||
"LabelLimit": "Limiet",
|
"LabelLimit": "Limiet",
|
||||||
"LabelLineSpacing": "Line spacing",
|
"LabelLineSpacing": "Regelruimte",
|
||||||
"LabelListenAgain": "Luister opnieuw",
|
"LabelListenAgain": "Luister opnieuw",
|
||||||
"LabelLogLevelDebug": "Debug",
|
"LabelLogLevelDebug": "Debug",
|
||||||
"LabelLogLevelInfo": "Info",
|
"LabelLogLevelInfo": "Info",
|
||||||
|
|
@ -322,7 +322,7 @@
|
||||||
"LabelNewPassword": "Nieuw wachtwoord",
|
"LabelNewPassword": "Nieuw wachtwoord",
|
||||||
"LabelNextBackupDate": "Volgende back-up datum",
|
"LabelNextBackupDate": "Volgende back-up datum",
|
||||||
"LabelNextScheduledRun": "Volgende geplande run",
|
"LabelNextScheduledRun": "Volgende geplande run",
|
||||||
"LabelNoEpisodesSelected": "No episodes selected",
|
"LabelNoEpisodesSelected": "Geen afleveringen geselecteerd",
|
||||||
"LabelNotes": "Notities",
|
"LabelNotes": "Notities",
|
||||||
"LabelNotFinished": "Niet Voltooid",
|
"LabelNotFinished": "Niet Voltooid",
|
||||||
"LabelNotificationAppriseURL": "Apprise URL(s)",
|
"LabelNotificationAppriseURL": "Apprise URL(s)",
|
||||||
|
|
@ -354,18 +354,18 @@
|
||||||
"LabelPodcast": "Podcast",
|
"LabelPodcast": "Podcast",
|
||||||
"LabelPodcasts": "Podcasts",
|
"LabelPodcasts": "Podcasts",
|
||||||
"LabelPodcastType": "Podcasttype",
|
"LabelPodcastType": "Podcasttype",
|
||||||
"LabelPort": "Port",
|
"LabelPort": "Poort",
|
||||||
"LabelPrefixesToIgnore": "Te negeren voorzetsels (ongeacht hoofdlettergebruik)",
|
"LabelPrefixesToIgnore": "Te negeren voorzetsels (ongeacht hoofdlettergebruik)",
|
||||||
"LabelPreventIndexing": "Voorkom indexering van je feed door iTunes- en Google podcastmappen",
|
"LabelPreventIndexing": "Voorkom indexering van je feed door iTunes- en Google podcastmappen",
|
||||||
"LabelPrimaryEbook": "Primary ebook",
|
"LabelPrimaryEbook": "Primair ebook",
|
||||||
"LabelProgress": "Voortgang",
|
"LabelProgress": "Voortgang",
|
||||||
"LabelProvider": "Bron",
|
"LabelProvider": "Bron",
|
||||||
"LabelPubDate": "Publicatiedatum",
|
"LabelPubDate": "Publicatiedatum",
|
||||||
"LabelPublisher": "Uitgever",
|
"LabelPublisher": "Uitgever",
|
||||||
"LabelPublishYear": "Jaar van uitgave",
|
"LabelPublishYear": "Jaar van uitgave",
|
||||||
"LabelRead": "Read",
|
"LabelRead": "Lees",
|
||||||
"LabelReadAgain": "Read Again",
|
"LabelReadAgain": "Lees opnieuw",
|
||||||
"LabelReadEbookWithoutProgress": "Read ebook without keeping progress",
|
"LabelReadEbookWithoutProgress": "Lees ebook zonder voortgang bij te houden",
|
||||||
"LabelRecentlyAdded": "Recent toegevoegd",
|
"LabelRecentlyAdded": "Recent toegevoegd",
|
||||||
"LabelRecentSeries": "Recente series",
|
"LabelRecentSeries": "Recente series",
|
||||||
"LabelRecommended": "Aangeraden",
|
"LabelRecommended": "Aangeraden",
|
||||||
|
|
@ -382,32 +382,32 @@
|
||||||
"LabelSearchTitle": "Zoek titel",
|
"LabelSearchTitle": "Zoek titel",
|
||||||
"LabelSearchTitleOrASIN": "Zoek titel of ASIN",
|
"LabelSearchTitleOrASIN": "Zoek titel of ASIN",
|
||||||
"LabelSeason": "Seizoen",
|
"LabelSeason": "Seizoen",
|
||||||
"LabelSelectAllEpisodes": "Select all episodes",
|
"LabelSelectAllEpisodes": "Selecteer alle afleveringen",
|
||||||
"LabelSelectEpisodesShowing": "Select {0} episodes showing",
|
"LabelSelectEpisodesShowing": "Selecteer {0} afleveringen laten zien",
|
||||||
"LabelSendEbookToDevice": "Send Ebook to...",
|
"LabelSendEbookToDevice": "Stuur ebook naar...",
|
||||||
"LabelSequence": "Sequentie",
|
"LabelSequence": "Sequentie",
|
||||||
"LabelSeries": "Serie",
|
"LabelSeries": "Serie",
|
||||||
"LabelSeriesName": "Naam serie",
|
"LabelSeriesName": "Naam serie",
|
||||||
"LabelSeriesProgress": "Voortgang serie",
|
"LabelSeriesProgress": "Voortgang serie",
|
||||||
"LabelSetEbookAsPrimary": "Set as primary",
|
"LabelSetEbookAsPrimary": "Stel in als primair",
|
||||||
"LabelSetEbookAsSupplementary": "Set as supplementary",
|
"LabelSetEbookAsSupplementary": "Stel in als supplementair",
|
||||||
"LabelSettingsAudiobooksOnly": "Audiobooks only",
|
"LabelSettingsAudiobooksOnly": "Alleen audiobooks",
|
||||||
"LabelSettingsAudiobooksOnlyHelp": "Enabling this setting will ignore ebook files unless they are inside an audiobook folder in which case they will be set as supplementary ebooks",
|
"LabelSettingsAudiobooksOnlyHelp": "Deze instelling inschakelen zorgt ervoor dat ebook-bestanden genegeerd worden tenzij ze in een audiobook-map staan, in welk geval ze worden ingesteld als supplementaire ebooks",
|
||||||
"LabelSettingsBookshelfViewHelp": "Skeumorphisch design met houten planken",
|
"LabelSettingsBookshelfViewHelp": "Skeumorphisch design met houten planken",
|
||||||
"LabelSettingsChromecastSupport": "Chromecast support",
|
"LabelSettingsChromecastSupport": "Chromecast support",
|
||||||
"LabelSettingsDateFormat": "Datum format",
|
"LabelSettingsDateFormat": "Datum format",
|
||||||
"LabelSettingsDisableWatcher": "Watcher uitschakelen",
|
"LabelSettingsDisableWatcher": "Watcher uitschakelen",
|
||||||
"LabelSettingsDisableWatcherForLibrary": "Map-watcher voor bibliotheek uitschakelen",
|
"LabelSettingsDisableWatcherForLibrary": "Map-watcher voor bibliotheek uitschakelen",
|
||||||
"LabelSettingsDisableWatcherHelp": "Schakelt het automatisch toevoegen/bijwerken van onderdelen wanneer bestandswijzigingen gedetecteerd zijn uit. *Vereist herstart server",
|
"LabelSettingsDisableWatcherHelp": "Schakelt het automatisch toevoegen/bijwerken van onderdelen wanneer bestandswijzigingen gedetecteerd zijn uit. *Vereist herstart server",
|
||||||
"LabelSettingsEnableWatcher": "Enable Watcher",
|
"LabelSettingsEnableWatcher": "Watcher inschakelen",
|
||||||
"LabelSettingsEnableWatcherForLibrary": "Enable folder watcher for library",
|
"LabelSettingsEnableWatcherForLibrary": "Map-watcher voor bibliotheek inschakelen",
|
||||||
"LabelSettingsEnableWatcherHelp": "Enables the automatic adding/updating of items when file changes are detected. *Requires server restart",
|
"LabelSettingsEnableWatcherHelp": "Zorgt voor het automatisch toevoegen/bijwerken van onderdelen als bestandswijzigingen worden gedetecteerd. *Vereist herstarten van server",
|
||||||
"LabelSettingsExperimentalFeatures": "Experimentele functies",
|
"LabelSettingsExperimentalFeatures": "Experimentele functies",
|
||||||
"LabelSettingsExperimentalFeaturesHelp": "Functies in ontwikkeling die je feedback en testing kunnen gebruiken. Klik om de Github-discussie te openen.",
|
"LabelSettingsExperimentalFeaturesHelp": "Functies in ontwikkeling die je feedback en testing kunnen gebruiken. Klik om de Github-discussie te openen.",
|
||||||
"LabelSettingsFindCovers": "Zoek covers",
|
"LabelSettingsFindCovers": "Zoek covers",
|
||||||
"LabelSettingsFindCoversHelp": "Als je audioboek geen ingesloten cover of cover in de map heeft, zal de scanner proberen een cover te vinden.<br>Opmerking: Dit zal de scan-duur verlengen",
|
"LabelSettingsFindCoversHelp": "Als je audioboek geen ingesloten cover of cover in de map heeft, zal de scanner proberen een cover te vinden.<br>Opmerking: Dit zal de scan-duur verlengen",
|
||||||
"LabelSettingsHideSingleBookSeries": "Hide single book series",
|
"LabelSettingsHideSingleBookSeries": "Verberg series met een enkel boek",
|
||||||
"LabelSettingsHideSingleBookSeriesHelp": "Series that have a single book will be hidden from the series page and home page shelves.",
|
"LabelSettingsHideSingleBookSeriesHelp": "Series die slechts een enkel boek bevatten worden verborgen op de seriespagina en de homepagina-planken.",
|
||||||
"LabelSettingsHomePageBookshelfView": "Boekenplank-view voor homepagina",
|
"LabelSettingsHomePageBookshelfView": "Boekenplank-view voor homepagina",
|
||||||
"LabelSettingsLibraryBookshelfView": "Boekenplank-view voor bibliotheek",
|
"LabelSettingsLibraryBookshelfView": "Boekenplank-view voor bibliotheek",
|
||||||
"LabelSettingsOverdriveMediaMarkers": "Gebruik Overdrive media markers voor hoofdstukken",
|
"LabelSettingsOverdriveMediaMarkers": "Gebruik Overdrive media markers voor hoofdstukken",
|
||||||
|
|
@ -461,9 +461,9 @@
|
||||||
"LabelTagsAccessibleToUser": "Tags toegankelijk voor de gebruiker",
|
"LabelTagsAccessibleToUser": "Tags toegankelijk voor de gebruiker",
|
||||||
"LabelTagsNotAccessibleToUser": "Tags niet toegankelijk voor de gebruiker",
|
"LabelTagsNotAccessibleToUser": "Tags niet toegankelijk voor de gebruiker",
|
||||||
"LabelTasks": "Lopende taken",
|
"LabelTasks": "Lopende taken",
|
||||||
"LabelTheme": "Theme",
|
"LabelTheme": "Thema",
|
||||||
"LabelThemeDark": "Dark",
|
"LabelThemeDark": "Donker",
|
||||||
"LabelThemeLight": "Light",
|
"LabelThemeLight": "Licht",
|
||||||
"LabelTimeBase": "Tijdsbasis",
|
"LabelTimeBase": "Tijdsbasis",
|
||||||
"LabelTimeListened": "Tijd geluisterd",
|
"LabelTimeListened": "Tijd geluisterd",
|
||||||
"LabelTimeListenedToday": "Tijd geluisterd vandaag",
|
"LabelTimeListenedToday": "Tijd geluisterd vandaag",
|
||||||
|
|
@ -482,8 +482,8 @@
|
||||||
"LabelTrackFromMetadata": "Track vanuit metadata",
|
"LabelTrackFromMetadata": "Track vanuit metadata",
|
||||||
"LabelTracks": "Tracks",
|
"LabelTracks": "Tracks",
|
||||||
"LabelTracksMultiTrack": "Multi-track",
|
"LabelTracksMultiTrack": "Multi-track",
|
||||||
"LabelTracksNone": "No tracks",
|
"LabelTracksNone": "Geen tracks",
|
||||||
"LabelTracksSingleTrack": "Single-track",
|
"LabelTracksSingleTrack": "Enkele track",
|
||||||
"LabelType": "Type",
|
"LabelType": "Type",
|
||||||
"LabelUnabridged": "Onverkort",
|
"LabelUnabridged": "Onverkort",
|
||||||
"LabelUnknown": "Onbekend",
|
"LabelUnknown": "Onbekend",
|
||||||
|
|
@ -525,16 +525,16 @@
|
||||||
"MessageCheckingCron": "Cron aan het checken...",
|
"MessageCheckingCron": "Cron aan het checken...",
|
||||||
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?",
|
"MessageConfirmCloseFeed": "Are you sure you want to close this feed?",
|
||||||
"MessageConfirmDeleteBackup": "Weet je zeker dat je de backup voor {0} wil verwijderen?",
|
"MessageConfirmDeleteBackup": "Weet je zeker dat je de backup voor {0} wil verwijderen?",
|
||||||
"MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?",
|
"MessageConfirmDeleteFile": "Dit verwijdert het bestand uit het bestandssysteem. Weet je het zeker?",
|
||||||
"MessageConfirmDeleteLibrary": "Weet je zeker dat je de bibliotheek \"{0}\" permanent wil verwijderen?",
|
"MessageConfirmDeleteLibrary": "Weet je zeker dat je de bibliotheek \"{0}\" permanent wil verwijderen?",
|
||||||
"MessageConfirmDeleteSession": "Weet je zeker dat je deze sessie wil verwijderen?",
|
"MessageConfirmDeleteSession": "Weet je zeker dat je deze sessie wil verwijderen?",
|
||||||
"MessageConfirmForceReScan": "Weet je zeker dat je geforceerd opnieuw wil scannen?",
|
"MessageConfirmForceReScan": "Weet je zeker dat je geforceerd opnieuw wil scannen?",
|
||||||
"MessageConfirmMarkAllEpisodesFinished": "Are you sure you want to mark all episodes as finished?",
|
"MessageConfirmMarkAllEpisodesFinished": "Weet je zeker dat je alle afleveringen als voltooid wil markeren?",
|
||||||
"MessageConfirmMarkAllEpisodesNotFinished": "Are you sure you want to mark all episodes as not finished?",
|
"MessageConfirmMarkAllEpisodesNotFinished": "Weet je zeker dat je alle afleveringen als niet-voltooid wil markeren?",
|
||||||
"MessageConfirmMarkSeriesFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als voltooid?",
|
"MessageConfirmMarkSeriesFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als voltooid?",
|
||||||
"MessageConfirmMarkSeriesNotFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als niet voltooid?",
|
"MessageConfirmMarkSeriesNotFinished": "Weet je zeker dat je alle boeken in deze serie wil markeren als niet voltooid?",
|
||||||
"MessageConfirmRemoveAllChapters": "Weet je zeker dat je alle hoofdstukken wil verwijderen?",
|
"MessageConfirmRemoveAllChapters": "Weet je zeker dat je alle hoofdstukken wil verwijderen?",
|
||||||
"MessageConfirmRemoveAuthor": "Are you sure you want to remove author \"{0}\"?",
|
"MessageConfirmRemoveAuthor": "Weet je zeker dat je auteur \"{0}\" wil verwijderen?",
|
||||||
"MessageConfirmRemoveCollection": "Weet je zeker dat je de collectie \"{0}\" wil verwijderen?",
|
"MessageConfirmRemoveCollection": "Weet je zeker dat je de collectie \"{0}\" wil verwijderen?",
|
||||||
"MessageConfirmRemoveEpisode": "Weet je zeker dat je de aflevering \"{0}\" wil verwijderen?",
|
"MessageConfirmRemoveEpisode": "Weet je zeker dat je de aflevering \"{0}\" wil verwijderen?",
|
||||||
"MessageConfirmRemoveEpisodes": "Weet je zeker dat je {0} afleveringen wil verwijderen?",
|
"MessageConfirmRemoveEpisodes": "Weet je zeker dat je {0} afleveringen wil verwijderen?",
|
||||||
|
|
@ -546,7 +546,7 @@
|
||||||
"MessageConfirmRenameTag": "Weet je zeker dat je tag \"{0}\" wil hernoemen naar\"{1}\" voor alle onderdelen?",
|
"MessageConfirmRenameTag": "Weet je zeker dat je tag \"{0}\" wil hernoemen naar\"{1}\" voor alle onderdelen?",
|
||||||
"MessageConfirmRenameTagMergeNote": "Opmerking: Deze tag bestaat al, dus zullen ze worden samengevoegd.",
|
"MessageConfirmRenameTagMergeNote": "Opmerking: Deze tag bestaat al, dus zullen ze worden samengevoegd.",
|
||||||
"MessageConfirmRenameTagWarning": "Waarschuwing! Een gelijknamige tag met ander hoofdlettergebruik bestaat al: \"{0}\".",
|
"MessageConfirmRenameTagWarning": "Waarschuwing! Een gelijknamige tag met ander hoofdlettergebruik bestaat al: \"{0}\".",
|
||||||
"MessageConfirmSendEbookToDevice": "Are you sure you want to send {0} ebook \"{1}\" to device \"{2}\"?",
|
"MessageConfirmSendEbookToDevice": "Weet je zeker dat je {0} ebook \"{1}\" naar apparaat \"{2}\" wil sturen?",
|
||||||
"MessageDownloadingEpisode": "Aflevering aan het dowloaden",
|
"MessageDownloadingEpisode": "Aflevering aan het dowloaden",
|
||||||
"MessageDragFilesIntoTrackOrder": "Sleep bestanden in de juiste trackvolgorde",
|
"MessageDragFilesIntoTrackOrder": "Sleep bestanden in de juiste trackvolgorde",
|
||||||
"MessageEmbedFinished": "Insluiting voltooid!",
|
"MessageEmbedFinished": "Insluiting voltooid!",
|
||||||
|
|
@ -565,8 +565,8 @@
|
||||||
"MessageM4BFailed": "M4B mislukt!",
|
"MessageM4BFailed": "M4B mislukt!",
|
||||||
"MessageM4BFinished": "M4B voltooid!",
|
"MessageM4BFinished": "M4B voltooid!",
|
||||||
"MessageMapChapterTitles": "Map hoofdstuktitels naar je bestaande audioboekhoofdstukken zonder aanpassing van tijden",
|
"MessageMapChapterTitles": "Map hoofdstuktitels naar je bestaande audioboekhoofdstukken zonder aanpassing van tijden",
|
||||||
"MessageMarkAllEpisodesFinished": "Mark all episodes finished",
|
"MessageMarkAllEpisodesFinished": "Markeer alle afleveringen als voltooid",
|
||||||
"MessageMarkAllEpisodesNotFinished": "Mark all episodes not finished",
|
"MessageMarkAllEpisodesNotFinished": "Markeer alle afleveringen als niet voltooid",
|
||||||
"MessageMarkAsFinished": "Markeer als Voltooid",
|
"MessageMarkAsFinished": "Markeer als Voltooid",
|
||||||
"MessageMarkAsNotFinished": "Markeer als Niet Voltooid",
|
"MessageMarkAsNotFinished": "Markeer als Niet Voltooid",
|
||||||
"MessageMatchBooksDescription": "zal proberen boeken in de bibliotheek te matchen met een boek uit de geselecteerde bron en lege details en coverafbeelding te vullen. Overschrijft details niet.",
|
"MessageMatchBooksDescription": "zal proberen boeken in de bibliotheek te matchen met een boek uit de geselecteerde bron en lege details en coverafbeelding te vullen. Overschrijft details niet.",
|
||||||
|
|
@ -702,8 +702,8 @@
|
||||||
"ToastRemoveItemFromCollectionSuccess": "Onderdeel verwijderd uit collectie",
|
"ToastRemoveItemFromCollectionSuccess": "Onderdeel verwijderd uit collectie",
|
||||||
"ToastRSSFeedCloseFailed": "Sluiten RSS-feed mislukt",
|
"ToastRSSFeedCloseFailed": "Sluiten RSS-feed mislukt",
|
||||||
"ToastRSSFeedCloseSuccess": "RSS-feed gesloten",
|
"ToastRSSFeedCloseSuccess": "RSS-feed gesloten",
|
||||||
"ToastSendEbookToDeviceFailed": "Failed to Send Ebook to device",
|
"ToastSendEbookToDeviceFailed": "Ebook naar apparaat sturen mislukt",
|
||||||
"ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"",
|
"ToastSendEbookToDeviceSuccess": "Ebook verstuurd naar apparaat \"{0}\"",
|
||||||
"ToastSeriesUpdateFailed": "Bijwerken serie mislukt",
|
"ToastSeriesUpdateFailed": "Bijwerken serie mislukt",
|
||||||
"ToastSeriesUpdateSuccess": "Bijwerken serie gelukt",
|
"ToastSeriesUpdateSuccess": "Bijwerken serie gelukt",
|
||||||
"ToastSessionDeleteFailed": "Verwijderen sessie mislukt",
|
"ToastSessionDeleteFailed": "Verwijderen sessie mislukt",
|
||||||
|
|
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "2.4.3",
|
"version": "2.4.4",
|
||||||
"description": "Self-hosted audiobook and podcast server",
|
"description": "Self-hosted audiobook and podcast server",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ class Logger {
|
||||||
* @param {...any} args
|
* @param {...any} args
|
||||||
*/
|
*/
|
||||||
dev(...args) {
|
dev(...args) {
|
||||||
if (!this.isDev) return
|
if (!this.isDev || process.env.HIDE_DEV_LOGS === '1') return
|
||||||
console.log(`[${this.timestamp}] DEV:`, ...args)
|
console.log(`[${this.timestamp}] DEV:`, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ class FolderWatcher extends EventEmitter {
|
||||||
this.ignoreDirs = []
|
this.ignoreDirs = []
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
this.pendingDirsToRemoveFromIgnore = []
|
this.pendingDirsToRemoveFromIgnore = []
|
||||||
|
/** @type {NodeJS.Timeout} */
|
||||||
|
this.removeFromIgnoreTimer = null
|
||||||
|
|
||||||
this.disabled = false
|
this.disabled = false
|
||||||
}
|
}
|
||||||
|
|
@ -240,9 +242,12 @@ class FolderWatcher extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
addIgnoreDir(path) {
|
addIgnoreDir(path) {
|
||||||
path = this.cleanDirPath(path)
|
path = this.cleanDirPath(path)
|
||||||
if (this.ignoreDirs.includes(path)) return
|
|
||||||
this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path)
|
this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path)
|
||||||
Logger.debug(`[Watcher] Ignoring directory "${path}"`)
|
if (this.ignoreDirs.includes(path)) {
|
||||||
|
// Already ignoring dir
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Logger.debug(`[Watcher] addIgnoreDir: Ignoring directory "${path}"`)
|
||||||
this.ignoreDirs.push(path)
|
this.ignoreDirs.push(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,18 +260,24 @@ class FolderWatcher extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
removeIgnoreDir(path) {
|
removeIgnoreDir(path) {
|
||||||
path = this.cleanDirPath(path)
|
path = this.cleanDirPath(path)
|
||||||
if (!this.ignoreDirs.includes(path) || this.pendingDirsToRemoveFromIgnore.includes(path)) return
|
if (!this.ignoreDirs.includes(path)) {
|
||||||
|
Logger.debug(`[Watcher] removeIgnoreDir: Path is not being ignored "${path}"`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Add a 5 second delay before removing the ignore from this dir
|
// Add a 5 second delay before removing the ignore from this dir
|
||||||
this.pendingDirsToRemoveFromIgnore.push(path)
|
if (!this.pendingDirsToRemoveFromIgnore.includes(path)) {
|
||||||
setTimeout(() => {
|
this.pendingDirsToRemoveFromIgnore.push(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTimeout(this.removeFromIgnoreTimer)
|
||||||
|
this.removeFromIgnoreTimer = setTimeout(() => {
|
||||||
if (this.pendingDirsToRemoveFromIgnore.includes(path)) {
|
if (this.pendingDirsToRemoveFromIgnore.includes(path)) {
|
||||||
this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path)
|
this.pendingDirsToRemoveFromIgnore = this.pendingDirsToRemoveFromIgnore.filter(p => p !== path)
|
||||||
Logger.debug(`[Watcher] No longer ignoring directory "${path}"`)
|
Logger.debug(`[Watcher] removeIgnoreDir: No longer ignoring directory "${path}"`)
|
||||||
this.ignoreDirs = this.ignoreDirs.filter(p => p !== path)
|
this.ignoreDirs = this.ignoreDirs.filter(p => p !== path)
|
||||||
}
|
}
|
||||||
}, 5000)
|
}, 5000)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports = FolderWatcher
|
module.exports = FolderWatcher
|
||||||
|
|
@ -259,7 +259,6 @@ class LibraryItemController {
|
||||||
|
|
||||||
// Check if library item media has a cover path
|
// Check if library item media has a cover path
|
||||||
if (!libraryItem.media.coverPath || !await fs.pathExists(libraryItem.media.coverPath)) {
|
if (!libraryItem.media.coverPath || !await fs.pathExists(libraryItem.media.coverPath)) {
|
||||||
Logger.debug(`[LibraryItemController] getCover: Library item "${req.params.id}" has no cover path`)
|
|
||||||
return res.sendStatus(404)
|
return res.sendStatus(404)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,12 +279,6 @@ class LibraryItemController {
|
||||||
return CacheManager.handleCoverCache(res, libraryItem.id, libraryItem.media.coverPath, options)
|
return CacheManager.handleCoverCache(res, libraryItem.id, libraryItem.media.coverPath, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/items/:id/stream
|
|
||||||
openStream(req, res) {
|
|
||||||
// this.streamManager.openStreamApiRequest(res, req.user, req.libraryItem)
|
|
||||||
res.sendStatus(500)
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST: api/items/:id/play
|
// POST: api/items/:id/play
|
||||||
startPlaybackSession(req, res) {
|
startPlaybackSession(req, res) {
|
||||||
if (!req.libraryItem.media.numTracks && req.libraryItem.mediaType !== 'video') {
|
if (!req.libraryItem.media.numTracks && req.libraryItem.mediaType !== 'video') {
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ class MeController {
|
||||||
|
|
||||||
const libraryItem = await Database.libraryItemModel.getOldById(localProgress.libraryItemId)
|
const libraryItem = await Database.libraryItemModel.getOldById(localProgress.libraryItemId)
|
||||||
if (!libraryItem) {
|
if (!libraryItem) {
|
||||||
Logger.error(`[MeController] syncLocalMediaProgress invalid local media progress object no library item`, localProgress)
|
Logger.error(`[MeController] syncLocalMediaProgress invalid local media progress object no library item with id "${localProgress.libraryItemId}"`, localProgress)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ class PodcastController {
|
||||||
|
|
||||||
res.json(libraryItem.toJSONExpanded())
|
res.json(libraryItem.toJSONExpanded())
|
||||||
|
|
||||||
if (payload.episodesToDownload && payload.episodesToDownload.length) {
|
if (payload.episodesToDownload?.length) {
|
||||||
Logger.info(`[PodcastController] Podcast created now starting ${payload.episodesToDownload.length} episode downloads`)
|
Logger.info(`[PodcastController] Podcast created now starting ${payload.episodesToDownload.length} episode downloads`)
|
||||||
this.podcastManager.downloadPodcastEpisodes(libraryItem, payload.episodesToDownload)
|
this.podcastManager.downloadPodcastEpisodes(libraryItem, payload.episodesToDownload)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class SearchController {
|
||||||
|
|
||||||
let results = null
|
let results = null
|
||||||
if (podcast) results = await PodcastFinder.findCovers(query.title)
|
if (podcast) results = await PodcastFinder.findCovers(query.title)
|
||||||
else results = await BookFinder.findCovers(query.provider || 'google', query.title, query.author || null)
|
else results = await BookFinder.findCovers(query.provider || 'google', query.title, query.author || '')
|
||||||
res.json({
|
res.json({
|
||||||
results
|
results
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ class BookFinder {
|
||||||
if (!books.length && maxFuzzySearches > 0) {
|
if (!books.length && maxFuzzySearches > 0) {
|
||||||
// normalize title and author
|
// normalize title and author
|
||||||
title = title.trim().toLowerCase()
|
title = title.trim().toLowerCase()
|
||||||
author = author.trim().toLowerCase()
|
author = author?.trim().toLowerCase() || ''
|
||||||
|
|
||||||
// Now run up to maxFuzzySearches fuzzy searches
|
// Now run up to maxFuzzySearches fuzzy searches
|
||||||
let candidates = new Set()
|
let candidates = new Set()
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ class BackupManager {
|
||||||
let entries
|
let entries
|
||||||
try {
|
try {
|
||||||
entries = await zip.entries()
|
entries = await zip.entries()
|
||||||
} catch(error){
|
} catch (error) {
|
||||||
// Not a valid zip file
|
// Not a valid zip file
|
||||||
Logger.error('[BackupManager] Failed to read backup file - backup might not be a valid .zip file', tempPath, error)
|
Logger.error('[BackupManager] Failed to read backup file - backup might not be a valid .zip file', tempPath, error)
|
||||||
return res.status(400).send('Failed to read backup file - backup might not be a valid .zip file')
|
return res.status(400).send('Failed to read backup file - backup might not be a valid .zip file')
|
||||||
|
|
@ -182,7 +182,6 @@ class BackupManager {
|
||||||
data = await zip.entryData('details')
|
data = await zip.entryData('details')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.error(`[BackupManager] Failed to unzip backup "${fullFilePath}"`, error)
|
Logger.error(`[BackupManager] Failed to unzip backup "${fullFilePath}"`, error)
|
||||||
await zip.close()
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -794,6 +794,9 @@ class LibraryItem extends Model {
|
||||||
{
|
{
|
||||||
fields: ['libraryId', 'mediaType']
|
fields: ['libraryId', 'mediaType']
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fields: ['libraryId', 'mediaId', 'mediaType']
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fields: ['birthtime']
|
fields: ['birthtime']
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,13 @@ class PlaybackSession {
|
||||||
this.currentTime = session.currentTime || 0
|
this.currentTime = session.currentTime || 0
|
||||||
|
|
||||||
this.startedAt = session.startedAt
|
this.startedAt = session.startedAt
|
||||||
this.updatedAt = session.updatedAt || null
|
this.updatedAt = session.updatedAt || session.startedAt
|
||||||
|
|
||||||
|
// Local playback sessions dont set this date field so set using updatedAt
|
||||||
|
if (!this.date && session.updatedAt) {
|
||||||
|
this.date = date.format(new Date(session.updatedAt), 'YYYY-MM-DD')
|
||||||
|
this.dayOfWeek = date.format(new Date(session.updatedAt), 'dddd')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get mediaItemId() {
|
get mediaItemId() {
|
||||||
|
|
|
||||||
|
|
@ -339,9 +339,9 @@ class Stream extends EventEmitter {
|
||||||
} else {
|
} else {
|
||||||
Logger.error('Ffmpeg Err', '"' + err.message + '"')
|
Logger.error('Ffmpeg Err', '"' + err.message + '"')
|
||||||
|
|
||||||
// Temporary workaround for https://github.com/advplyr/audiobookshelf/issues/172
|
// Temporary workaround for https://github.com/advplyr/audiobookshelf/issues/172 and https://github.com/advplyr/audiobookshelf/issues/2157
|
||||||
const aacErrorMsg = 'ffmpeg exited with code 1: Could not write header for output file #0 (incorrect codec parameters ?)'
|
const aacErrorMsg = 'ffmpeg exited with code 1:'
|
||||||
if (audioCodec === 'copy' && this.isAACEncodable && err.message && err.message.startsWith(aacErrorMsg)) {
|
if (audioCodec === 'copy' && this.isAACEncodable && err.message?.startsWith(aacErrorMsg)) {
|
||||||
Logger.info(`[Stream] Re-attempting stream with AAC encode`)
|
Logger.info(`[Stream] Re-attempting stream with AAC encode`)
|
||||||
this.transcodeOptions.forceAAC = true
|
this.transcodeOptions.forceAAC = true
|
||||||
this.reset(this.startTime)
|
this.reset(this.startTime)
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ class ServerSettings {
|
||||||
loggerScannerLogsToKeep: this.loggerScannerLogsToKeep,
|
loggerScannerLogsToKeep: this.loggerScannerLogsToKeep,
|
||||||
homeBookshelfView: this.homeBookshelfView,
|
homeBookshelfView: this.homeBookshelfView,
|
||||||
bookshelfView: this.bookshelfView,
|
bookshelfView: this.bookshelfView,
|
||||||
|
podcastEpisodeSchedule: this.podcastEpisodeSchedule,
|
||||||
sortingIgnorePrefix: this.sortingIgnorePrefix,
|
sortingIgnorePrefix: this.sortingIgnorePrefix,
|
||||||
sortingPrefixes: [...this.sortingPrefixes],
|
sortingPrefixes: [...this.sortingPrefixes],
|
||||||
chromecastEnabled: this.chromecastEnabled,
|
chromecastEnabled: this.chromecastEnabled,
|
||||||
|
|
|
||||||
|
|
@ -168,9 +168,7 @@ class BookScanner {
|
||||||
hasMediaChanges = true
|
hasMediaChanges = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When metadata file is stored in /metadata/items/{libraryItemId}.[abs|json] we should load this
|
const bookMetadata = await this.getBookMetadataFromScanData(media.audioFiles, libraryItemData, libraryScan, existingLibraryItem.id)
|
||||||
// TODO: store an additional array of metadata keys that the user has changed manually so we know what not to override
|
|
||||||
const bookMetadata = await this.getBookMetadataFromScanData(media.audioFiles, libraryItemData, libraryScan)
|
|
||||||
let authorsUpdated = false
|
let authorsUpdated = false
|
||||||
const bookAuthorsRemoved = []
|
const bookAuthorsRemoved = []
|
||||||
let seriesUpdated = false
|
let seriesUpdated = false
|
||||||
|
|
@ -550,9 +548,10 @@ class BookScanner {
|
||||||
* @param {import('../models/Book').AudioFileObject[]} audioFiles
|
* @param {import('../models/Book').AudioFileObject[]} audioFiles
|
||||||
* @param {import('./LibraryItemScanData')} libraryItemData
|
* @param {import('./LibraryItemScanData')} libraryItemData
|
||||||
* @param {LibraryScan} libraryScan
|
* @param {LibraryScan} libraryScan
|
||||||
|
* @param {string} [existingLibraryItemId]
|
||||||
* @returns {Promise<BookMetadataObject>}
|
* @returns {Promise<BookMetadataObject>}
|
||||||
*/
|
*/
|
||||||
async getBookMetadataFromScanData(audioFiles, libraryItemData, libraryScan) {
|
async getBookMetadataFromScanData(audioFiles, libraryItemData, libraryScan, existingLibraryItemId = null) {
|
||||||
// First set book metadata from folder/file names
|
// First set book metadata from folder/file names
|
||||||
const bookMetadata = {
|
const bookMetadata = {
|
||||||
title: libraryItemData.mediaMetadata.title,
|
title: libraryItemData.mediaMetadata.title,
|
||||||
|
|
@ -722,11 +721,31 @@ class BookScanner {
|
||||||
|
|
||||||
// If metadata.json or metadata.abs use this for metadata
|
// If metadata.json or metadata.abs use this for metadata
|
||||||
const metadataLibraryFile = libraryItemData.metadataJsonLibraryFile || libraryItemData.metadataAbsLibraryFile
|
const metadataLibraryFile = libraryItemData.metadataJsonLibraryFile || libraryItemData.metadataAbsLibraryFile
|
||||||
const metadataText = metadataLibraryFile ? await readTextFile(metadataLibraryFile.metadata.path) : null
|
let metadataText = metadataLibraryFile ? await readTextFile(metadataLibraryFile.metadata.path) : null
|
||||||
|
let metadataFilePath = metadataLibraryFile?.metadata.path
|
||||||
|
let metadataFileFormat = libraryItemData.metadataJsonLibraryFile ? 'json' : 'abs'
|
||||||
|
|
||||||
|
// When metadata file is not stored with library item then check in the /metadata/items folder for it
|
||||||
|
if (!metadataText && existingLibraryItemId) {
|
||||||
|
let metadataPath = Path.join(global.MetadataPath, 'items', existingLibraryItemId)
|
||||||
|
|
||||||
|
let altFormat = global.ServerSettings.metadataFileFormat === 'json' ? 'abs' : 'json'
|
||||||
|
// First check the metadata format set in server settings, fallback to the alternate
|
||||||
|
metadataFilePath = Path.join(metadataPath, `metadata.${global.ServerSettings.metadataFileFormat}`)
|
||||||
|
metadataFileFormat = global.ServerSettings.metadataFileFormat
|
||||||
|
if (await fsExtra.pathExists(metadataFilePath)) {
|
||||||
|
metadataText = await readTextFile(metadataFilePath)
|
||||||
|
} else if (await fsExtra.pathExists(Path.join(metadataPath, `metadata.${altFormat}`))) {
|
||||||
|
metadataFilePath = Path.join(metadataPath, `metadata.${altFormat}`)
|
||||||
|
metadataFileFormat = altFormat
|
||||||
|
metadataText = await readTextFile(metadataFilePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (metadataText) {
|
if (metadataText) {
|
||||||
libraryScan.addLog(LogLevel.INFO, `Found metadata file "${metadataLibraryFile.metadata.path}" - preferring`)
|
libraryScan.addLog(LogLevel.INFO, `Found metadata file "${metadataFilePath}" - preferring`)
|
||||||
let abMetadata = null
|
let abMetadata = null
|
||||||
if (!!libraryItemData.metadataJsonLibraryFile) {
|
if (metadataFileFormat === 'json') {
|
||||||
abMetadata = abmetadataGenerator.parseJson(metadataText)
|
abMetadata = abmetadataGenerator.parseJson(metadataText)
|
||||||
} else {
|
} else {
|
||||||
abMetadata = abmetadataGenerator.parse(metadataText, 'book')
|
abMetadata = abmetadataGenerator.parse(metadataText, 'book')
|
||||||
|
|
@ -1092,7 +1111,7 @@ class BookScanner {
|
||||||
const result = await CoverManager.downloadCoverFromUrlNew(results[i], libraryItemId, libraryItemPath)
|
const result = await CoverManager.downloadCoverFromUrlNew(results[i], libraryItemId, libraryItemPath)
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
Logger.error(`[Scanner] Failed to download cover from url "${results[i]}" | Attempt ${i + 1}`, result.error)
|
libraryScan.addLog(LogLevel.ERROR, `Failed to download cover from url "${results[i]}" | Attempt ${i + 1}`, result.error)
|
||||||
} else if (result.cover) {
|
} else if (result.cover) {
|
||||||
return result.cover
|
return result.cover
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,8 +145,7 @@ class PodcastScanner {
|
||||||
hasMediaChanges = true
|
hasMediaChanges = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When metadata file is stored in /metadata/items/{libraryItemId}.[abs|json] we should load this
|
const podcastMetadata = await this.getPodcastMetadataFromScanData(existingPodcastEpisodes, libraryItemData, libraryScan, existingLibraryItem.id)
|
||||||
const podcastMetadata = await this.getPodcastMetadataFromScanData(existingPodcastEpisodes, libraryItemData, libraryScan)
|
|
||||||
|
|
||||||
for (const key in podcastMetadata) {
|
for (const key in podcastMetadata) {
|
||||||
// Ignore unset metadata and empty arrays
|
// Ignore unset metadata and empty arrays
|
||||||
|
|
@ -312,9 +311,10 @@ class PodcastScanner {
|
||||||
* @param {PodcastEpisode[]} podcastEpisodes Not the models for new podcasts
|
* @param {PodcastEpisode[]} podcastEpisodes Not the models for new podcasts
|
||||||
* @param {import('./LibraryItemScanData')} libraryItemData
|
* @param {import('./LibraryItemScanData')} libraryItemData
|
||||||
* @param {import('./LibraryScan')} libraryScan
|
* @param {import('./LibraryScan')} libraryScan
|
||||||
|
* @param {string} [existingLibraryItemId]
|
||||||
* @returns {Promise<PodcastMetadataObject>}
|
* @returns {Promise<PodcastMetadataObject>}
|
||||||
*/
|
*/
|
||||||
async getPodcastMetadataFromScanData(podcastEpisodes, libraryItemData, libraryScan) {
|
async getPodcastMetadataFromScanData(podcastEpisodes, libraryItemData, libraryScan, existingLibraryItemId = null) {
|
||||||
const podcastMetadata = {
|
const podcastMetadata = {
|
||||||
title: libraryItemData.mediaMetadata.title,
|
title: libraryItemData.mediaMetadata.title,
|
||||||
titleIgnorePrefix: getTitleIgnorePrefix(libraryItemData.mediaMetadata.title),
|
titleIgnorePrefix: getTitleIgnorePrefix(libraryItemData.mediaMetadata.title),
|
||||||
|
|
@ -389,11 +389,31 @@ class PodcastScanner {
|
||||||
|
|
||||||
// If metadata.json or metadata.abs use this for metadata
|
// If metadata.json or metadata.abs use this for metadata
|
||||||
const metadataLibraryFile = libraryItemData.metadataJsonLibraryFile || libraryItemData.metadataAbsLibraryFile
|
const metadataLibraryFile = libraryItemData.metadataJsonLibraryFile || libraryItemData.metadataAbsLibraryFile
|
||||||
const metadataText = metadataLibraryFile ? await readTextFile(metadataLibraryFile.metadata.path) : null
|
let metadataText = metadataLibraryFile ? await readTextFile(metadataLibraryFile.metadata.path) : null
|
||||||
|
let metadataFilePath = metadataLibraryFile?.metadata.path
|
||||||
|
let metadataFileFormat = libraryItemData.metadataJsonLibraryFile ? 'json' : 'abs'
|
||||||
|
|
||||||
|
// When metadata file is not stored with library item then check in the /metadata/items folder for it
|
||||||
|
if (!metadataText && existingLibraryItemId) {
|
||||||
|
let metadataPath = Path.join(global.MetadataPath, 'items', existingLibraryItemId)
|
||||||
|
|
||||||
|
let altFormat = global.ServerSettings.metadataFileFormat === 'json' ? 'abs' : 'json'
|
||||||
|
// First check the metadata format set in server settings, fallback to the alternate
|
||||||
|
metadataFilePath = Path.join(metadataPath, `metadata.${global.ServerSettings.metadataFileFormat}`)
|
||||||
|
metadataFileFormat = global.ServerSettings.metadataFileFormat
|
||||||
|
if (await fsExtra.pathExists(metadataFilePath)) {
|
||||||
|
metadataText = await readTextFile(metadataFilePath)
|
||||||
|
} else if (await fsExtra.pathExists(Path.join(metadataPath, `metadata.${altFormat}`))) {
|
||||||
|
metadataFilePath = Path.join(metadataPath, `metadata.${altFormat}`)
|
||||||
|
metadataFileFormat = altFormat
|
||||||
|
metadataText = await readTextFile(metadataFilePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (metadataText) {
|
if (metadataText) {
|
||||||
libraryScan.addLog(LogLevel.INFO, `Found metadata file "${metadataLibraryFile.metadata.path}" - preferring`)
|
libraryScan.addLog(LogLevel.INFO, `Found metadata file "${metadataFilePath}" - preferring`)
|
||||||
let abMetadata = null
|
let abMetadata = null
|
||||||
if (!!libraryItemData.metadataJsonLibraryFile) {
|
if (metadataFileFormat === 'json') {
|
||||||
abMetadata = abmetadataGenerator.parseJson(metadataText)
|
abMetadata = abmetadataGenerator.parseJson(metadataText)
|
||||||
} else {
|
} else {
|
||||||
abMetadata = abmetadataGenerator.parse(metadataText, 'podcast')
|
abMetadata = abmetadataGenerator.parse(metadataText, 'podcast')
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,15 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Handle library setting to hide single book series
|
||||||
|
// TODO: Merge with existing query
|
||||||
|
if (library.settings.hideSingleBookSeries) {
|
||||||
|
seriesWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM books b, bookSeries bs WHERE bs.seriesId = series.id AND bs.bookId = b.id)`), {
|
||||||
|
[Sequelize.Op.gt]: 1
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
// Handle user permissions to only include series with at least 1 book
|
// Handle user permissions to only include series with at least 1 book
|
||||||
// TODO: Simplify to a single query
|
// TODO: Simplify to a single query
|
||||||
if (userPermissionBookWhere.bookWhere.length) {
|
if (userPermissionBookWhere.bookWhere.length) {
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ module.exports = {
|
||||||
podcastEpisodeWhere['$mediaProgresses.isFinished$'] = true
|
podcastEpisodeWhere['$mediaProgresses.isFinished$'] = true
|
||||||
}
|
}
|
||||||
} else if (filterGroup === 'recent') {
|
} else if (filterGroup === 'recent') {
|
||||||
libraryItemWhere['createdAt'] = {
|
podcastEpisodeWhere['createdAt'] = {
|
||||||
[Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago
|
[Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue