+
-
-
+
-
-
+
+
@@ -127,6 +127,10 @@ export default {
})
return extensions
},
+ isIOS() {
+ const ua = window.navigator.userAgent
+ return /iPad|iPhone|iPod/.test(ua) && !window.MSStream
+ },
streamLibraryItem() {
return this.$store.state.streamLibraryItem
},
diff --git a/client/strings/ar.json b/client/strings/ar.json
index c4891f195..5bcd4c3a1 100644
--- a/client/strings/ar.json
+++ b/client/strings/ar.json
@@ -127,5 +127,30 @@
"HeaderCollectionItems": "عناصر المجموعة",
"HeaderCover": "الغلاف",
"HeaderCurrentDownloads": "التنزيلات الجارية",
- "HeaderCustomMessageOnLogin": "رسالة مخصصة عند تسجيل الدخول"
+ "HeaderCustomMessageOnLogin": "رسالة مخصصة عند تسجيل الدخول",
+ "HeaderCustomMetadataProviders": "مقدمو البيانات الوصفية المخصصة",
+ "HeaderDetails": "التفاصيل",
+ "HeaderDownloadQueue": "تنزيل قائمة الانتظار",
+ "HeaderEbookFiles": "ملفات الكتب الإلكترونية",
+ "HeaderEmail": "البريد الإلكتروني",
+ "HeaderEmailSettings": "إعدادات البريد الإلكتروني",
+ "HeaderEpisodes": "الحلقات",
+ "HeaderEreaderDevices": "أجهزة قراءة الكتب الإلكترونية",
+ "HeaderEreaderSettings": "إعدادات القارئ الإلكتروني",
+ "HeaderFiles": "ملفات",
+ "HeaderFindChapters": "البحث عن الفصول",
+ "HeaderIgnoredFiles": "الملفات المتجاهلة",
+ "HeaderItemFiles": "ملفات العنصر",
+ "HeaderItemMetadataUtils": "بيانات تعريف العنصر",
+ "HeaderLastListeningSession": "آخر جلسة استماع",
+ "HeaderLatestEpisodes": "أحدث الحلقات",
+ "HeaderLibraries": "المكتبات",
+ "HeaderLibraryFiles": "ملفات المكتبة",
+ "HeaderLibraryStats": "إحصائيات المكتبة",
+ "HeaderListeningSessions": "جلسات الاستماع",
+ "HeaderListeningStats": "جلسات الاستماع",
+ "HeaderLogin": "تسجيل الدخول",
+ "HeaderLogs": "السجلات",
+ "HeaderManageGenres": "إدارة الانواع",
+ "HeaderManageTags": "إدارة العلامات"
}
diff --git a/client/strings/en-us.json b/client/strings/en-us.json
index 8eb375500..0c077ed67 100644
--- a/client/strings/en-us.json
+++ b/client/strings/en-us.json
@@ -663,6 +663,7 @@
"LabelUpdateDetailsHelp": "Allow overwriting of existing details for the selected books when a match is located",
"LabelUpdatedAt": "Updated At",
"LabelUploaderDragAndDrop": "Drag & drop files or folders",
+ "LabelUploaderDragAndDropFilesOnly": "Drag & drop files",
"LabelUploaderDropFiles": "Drop files",
"LabelUploaderItemFetchMetadataHelp": "Automatically fetch title, author, and series",
"LabelUseAdvancedOptions": "Use Advanced Options",
diff --git a/client/strings/he.json b/client/strings/he.json
index 23b9fb723..a93d3f058 100644
--- a/client/strings/he.json
+++ b/client/strings/he.json
@@ -18,7 +18,8 @@
"ButtonChooseAFolder": "בחר תיקייה",
"ButtonChooseFiles": "בחר קבצים",
"ButtonClearFilter": "נקה סינון",
- "ButtonCloseFeed": "סגור פיד",
+ "ButtonCloseFeed": "סגור ערוץ",
+ "ButtonCloseSession": "סגור סשן פתוח",
"ButtonCollections": "אוספים",
"ButtonConfigureScanner": "הגדר סורק",
"ButtonCreate": "צור",
@@ -28,6 +29,7 @@
"ButtonEdit": "ערוך",
"ButtonEditChapters": "ערוך פרקים",
"ButtonEditPodcast": "ערוך פודקאסט",
+ "ButtonEnable": "הפעל",
"ButtonForceReScan": "סרוק מחדש בכוח",
"ButtonFullPath": "נתיב מלא",
"ButtonHide": "הסתר",
@@ -46,19 +48,24 @@
"ButtonNevermind": "לא משנה",
"ButtonNext": "הבא",
"ButtonNextChapter": "פרק הבא",
+ "ButtonNextItemInQueue": "פריט הבא בתור",
"ButtonOk": "אישור",
"ButtonOpenFeed": "פתח פיד",
"ButtonOpenManager": "פתח מנהל",
"ButtonPause": "השהה",
"ButtonPlay": "נגן",
+ "ButtonPlayAll": "נגן הכל",
"ButtonPlaying": "מנגן",
"ButtonPlaylists": "רשימות השמעה",
"ButtonPrevious": "קודם",
"ButtonPreviousChapter": "פרק קודם",
+ "ButtonProbeAudioFile": "בדוק קובץ אודיו",
"ButtonPurgeAllCache": "נקה את כל המטמון",
"ButtonPurgeItemsCache": "נקה את מטמון הפריטים",
"ButtonQueueAddItem": "הוסף לתור",
"ButtonQueueRemoveItem": "הסר מהתור",
+ "ButtonQuickEmbed": "הטמעה מהירה",
+ "ButtonQuickEmbedMetadata": "הטמעת מטא נתונים מהירה",
"ButtonQuickMatch": "התאמה מהירה",
"ButtonReScan": "סרוק מחדש",
"ButtonRead": "קרא",
@@ -88,8 +95,10 @@
"ButtonShow": "הצג",
"ButtonStartM4BEncode": "התחל קידוד M4B",
"ButtonStartMetadataEmbed": "התחל הטמעת מטא-נתונים",
+ "ButtonStats": "סטטיסטיקות",
"ButtonSubmit": "שלח",
"ButtonTest": "בדיקה",
+ "ButtonUnlinkOpenId": "נתק OpenID",
"ButtonUpload": "העלה",
"ButtonUploadBackup": "העלה גיבוי",
"ButtonUploadCover": "העלה כריכה",
@@ -102,6 +111,7 @@
"ErrorUploadFetchMetadataNoResults": "לא ניתן לשלוף מטא-נתונים - נסה לעדכן כותרת ו/או יוצר",
"ErrorUploadLacksTitle": "חובה לתת כותרת",
"HeaderAccount": "חשבון",
+ "HeaderAddCustomMetadataProvider": "הוסף ספק מטא-נתונים מותאם אישית",
"HeaderAdvanced": "מתקדם",
"HeaderAppriseNotificationSettings": "הגדרות התראות של Apprise",
"HeaderAudioTracks": "רצועות קול",
@@ -147,13 +157,17 @@
"HeaderMetadataToEmbed": "מטא-נתונים להטמעה",
"HeaderNewAccount": "חשבון חדש",
"HeaderNewLibrary": "ספרייה חדשה",
+ "HeaderNotificationCreate": "צור התראה",
+ "HeaderNotificationUpdate": "עדכון התראה",
"HeaderNotifications": "התראות",
"HeaderOpenIDConnectAuthentication": "אימות OpenID Connect",
+ "HeaderOpenListeningSessions": "פתח הפעלות האזנה",
"HeaderOpenRSSFeed": "פתח ערוץ RSS",
"HeaderOtherFiles": "קבצים אחרים",
"HeaderPasswordAuthentication": "אימות סיסמה",
"HeaderPermissions": "הרשאות",
"HeaderPlayerQueue": "תור ניגון",
+ "HeaderPlayerSettings": "הגדרות נגן",
"HeaderPlaylist": "רשימת השמעה",
"HeaderPlaylistItems": "פריטי רשימת השמעה",
"HeaderPodcastsToAdd": "פודקאסטים להוספה",
@@ -165,6 +179,7 @@
"HeaderRemoveEpisodes": "הסר {0} פרקים",
"HeaderSavedMediaProgress": "התקדמות מדיה שמורה",
"HeaderSchedule": "תיזמון",
+ "HeaderScheduleEpisodeDownloads": "תזמן הורדת פרקים אוטומטית",
"HeaderScheduleLibraryScans": "קבע סריקות ספרייה אוטומטיות",
"HeaderSession": "הפעלה",
"HeaderSetBackupSchedule": "קבע לוח זמנים לגיבוי",
@@ -190,6 +205,9 @@
"HeaderYearReview": "שנת {0} בסקירה",
"HeaderYourStats": "הסטטיסטיקות שלך",
"LabelAbridged": "מקוצר",
+ "LabelAbridgedChecked": "מקוצר (מסומן)",
+ "LabelAbridgedUnchecked": "בלתי מקוצר (לא מסומן)",
+ "LabelAccessibleBy": "נגיש על ידי",
"LabelAccountType": "סוג חשבון",
"LabelAccountTypeAdmin": "מנהל",
"LabelAccountTypeGuest": "אורח",
@@ -200,13 +218,18 @@
"LabelAddToPlaylist": "הוסף לרשימת השמעה",
"LabelAddToPlaylistBatch": "הוסף {0} פריטים לרשימת השמעה",
"LabelAddedAt": "נוסף בתאריך",
+ "LabelAddedDate": "נוסף ב-{0}",
"LabelAdminUsersOnly": "רק מנהלים",
"LabelAll": "הכל",
"LabelAllUsers": "כל המשתמשים",
"LabelAllUsersExcludingGuests": "כל המשתמשים, ללא אורחים",
"LabelAllUsersIncludingGuests": "כל המשתמשים כולל אורחים",
"LabelAlreadyInYourLibrary": "כבר קיים בספרייה שלך",
+ "LabelApiToken": "טוקן API",
"LabelAppend": "הוסף לסוף",
+ "LabelAudioBitrate": "קצב סיביות (לדוגמא 128k)",
+ "LabelAudioChannels": "ערוצי קול (1 או 2)",
+ "LabelAudioCodec": "קידוד קול",
"LabelAuthor": "יוצר",
"LabelAuthorFirstLast": "יוצר (שם פרטי שם משפחה)",
"LabelAuthorLastFirst": "יוצר (שם משפחה, שם פרטי)",
diff --git a/client/strings/it.json b/client/strings/it.json
index 42e83fe06..70490e3b0 100644
--- a/client/strings/it.json
+++ b/client/strings/it.json
@@ -66,13 +66,13 @@
"ButtonPurgeItemsCache": "Elimina la Cache selezionata",
"ButtonQueueAddItem": "Aggiungi alla Coda",
"ButtonQueueRemoveItem": "Rimuovi dalla Coda",
- "ButtonQuickEmbed": "Quick Embed",
+ "ButtonQuickEmbed": "Incorporazione Rapida",
"ButtonQuickEmbedMetadata": "Incorporamento rapido Metadati",
"ButtonQuickMatch": "Controlla Metadata Auto",
"ButtonReScan": "Ri-scansiona",
"ButtonRead": "Leggi",
- "ButtonReadLess": "Leggi di Meno",
- "ButtonReadMore": "Leggi di Più",
+ "ButtonReadLess": "Riduci",
+ "ButtonReadMore": "Espandi",
"ButtonRefresh": "Aggiorna",
"ButtonRemove": "Rimuovi",
"ButtonRemoveAll": "Rimuovi Tutto",
@@ -220,7 +220,7 @@
"LabelAddToPlaylist": "Aggiungi alla playlist",
"LabelAddToPlaylistBatch": "Aggiungi {0} file alla Playlist",
"LabelAddedAt": "Aggiunto il",
- "LabelAddedDate": "{0} aggiunti",
+ "LabelAddedDate": "Aggiunti {0}",
"LabelAdminUsersOnly": "Solo utenti Amministratori",
"LabelAll": "Tutti",
"LabelAllUsers": "Tutti gli Utenti",
@@ -495,7 +495,7 @@
"LabelProviderAuthorizationValue": "Authorization Header Value",
"LabelPubDate": "Data di pubblicazione",
"LabelPublishYear": "Anno di pubblicazione",
- "LabelPublishedDate": "{0} pubblicati",
+ "LabelPublishedDate": "Pubblicati {0}",
"LabelPublishedDecade": "Decennio di pubblicazione",
"LabelPublishedDecades": "Decenni di pubblicazione",
"LabelPublisher": "Editore",
@@ -682,7 +682,7 @@
"LabelXBooks": "{0} libri",
"LabelXItems": "{0} oggetti",
"LabelYearReviewHide": "Nascondi Anno in rassegna",
- "LabelYearReviewShow": "Vedi Anno in rassegna",
+ "LabelYearReviewShow": "Mostra Anno in rassegna",
"LabelYourAudiobookDuration": "La durata dell'audiolibro",
"LabelYourBookmarks": "I tuoi preferiti",
"LabelYourPlaylists": "le tue Playlist",
@@ -779,7 +779,7 @@
"MessageNoBackups": "Nessun Backup",
"MessageNoBookmarks": "Nessun preferito",
"MessageNoChapters": "Nessun capitolo",
- "MessageNoCollections": "Nessuna Raccolta",
+ "MessageNoCollections": "Nessuna Collezione",
"MessageNoCoversFound": "Nessuna Cover Trovata",
"MessageNoDescription": "Nessuna descrizione",
"MessageNoDevices": "nessun dispositivo",
diff --git a/package-lock.json b/package-lock.json
index 96d85ecec..3f9f7a44c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "audiobookshelf",
- "version": "2.17.1",
+ "version": "2.17.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "audiobookshelf",
- "version": "2.17.1",
+ "version": "2.17.2",
"license": "GPL-3.0",
"dependencies": {
"axios": "^0.27.2",
diff --git a/package.json b/package.json
index ec1538890..8cbbb029f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf",
- "version": "2.17.1",
+ "version": "2.17.2",
"buildNumber": 1,
"description": "Self-hosted audiobook and podcast server",
"main": "index.js",
diff --git a/readme.md b/readme.md
index e2d1f1076..e62aba033 100644
--- a/readme.md
+++ b/readme.md
@@ -41,6 +41,13 @@ Is there a feature you are looking for? [Suggest it](https://github.com/advplyr/
Join us on [Discord](https://discord.gg/HQgCbd6E75)
+### Demo
+
+Check out the web client demo: https://audiobooks.dev/ (thanks for hosting [@Vito0912](https://github.com/Vito0912)!)
+
+Username/password: `demo`/`demo` (user account)
+
+
### Android App (beta)
Try it out on the [Google Play Store](https://play.google.com/store/apps/details?id=com.audiobookshelf.app)
diff --git a/server/Database.js b/server/Database.js
index 9bce26050..95e13c6b4 100644
--- a/server/Database.js
+++ b/server/Database.js
@@ -406,11 +406,6 @@ class Database {
return Promise.all(oldBooks.map((oldBook) => this.models.book.saveFromOld(oldBook)))
}
- removeLibrary(libraryId) {
- if (!this.sequelize) return false
- return this.models.library.removeById(libraryId)
- }
-
createBulkCollectionBooks(collectionBooks) {
if (!this.sequelize) return false
return this.models.collectionBook.bulkCreate(collectionBooks)
diff --git a/server/Server.js b/server/Server.js
index e40e7c574..ae9746d8d 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -194,18 +194,21 @@ class Server {
const app = express()
- /**
- * @temporary
- * This is necessary for the ebook & cover API endpoint in the mobile apps
- * The mobile app ereader is using fetch api in Capacitor that is currently difficult to switch to native requests
- * so we have to allow cors for specific origins to the /api/items/:id/ebook endpoint
- * The cover image is fetched with XMLHttpRequest in the mobile apps to load into a canvas and extract colors
- * @see https://ionicframework.com/docs/troubleshooting/cors
- *
- * Running in development allows cors to allow testing the mobile apps in the browser
- * or env variable ALLOW_CORS = '1'
- */
app.use((req, res, next) => {
+ // Prevent clickjacking by disallowing iframes
+ res.setHeader('Content-Security-Policy', "frame-ancestors 'self'")
+
+ /**
+ * @temporary
+ * This is necessary for the ebook & cover API endpoint in the mobile apps
+ * The mobile app ereader is using fetch api in Capacitor that is currently difficult to switch to native requests
+ * so we have to allow cors for specific origins to the /api/items/:id/ebook endpoint
+ * The cover image is fetched with XMLHttpRequest in the mobile apps to load into a canvas and extract colors
+ * @see https://ionicframework.com/docs/troubleshooting/cors
+ *
+ * Running in development allows cors to allow testing the mobile apps in the browser
+ * or env variable ALLOW_CORS = '1'
+ */
if (Logger.isDev || req.path.match(/\/api\/items\/([a-z0-9-]{36})\/(ebook|cover)(\/[0-9]+)?/)) {
const allowedOrigins = ['capacitor://localhost', 'http://localhost']
if (global.AllowCors || Logger.isDev || allowedOrigins.some((o) => o === req.get('origin'))) {
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index 0bd499f1c..84d6193d5 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -504,8 +504,21 @@ class LibraryController {
await this.handleDeleteLibraryItem(libraryItem.mediaType, libraryItem.id, mediaItemIds)
}
+ // Set PlaybackSessions libraryId to null
+ const [sessionsUpdated] = await Database.playbackSessionModel.update(
+ {
+ libraryId: null
+ },
+ {
+ where: {
+ libraryId: req.library.id
+ }
+ }
+ )
+ Logger.info(`[LibraryController] Updated ${sessionsUpdated} playback sessions to remove library id`)
+
const libraryJson = req.library.toOldJSON()
- await Database.removeLibrary(req.library.id)
+ await req.library.destroy()
// Re-order libraries
await Database.libraryModel.resetDisplayOrder()
diff --git a/server/migrations/v2.17.0-uuid-replacement.js b/server/migrations/v2.17.0-uuid-replacement.js
index 6460b7952..4316cd769 100644
--- a/server/migrations/v2.17.0-uuid-replacement.js
+++ b/server/migrations/v2.17.0-uuid-replacement.js
@@ -27,10 +27,14 @@ async function up({ context: { queryInterface, logger } }) {
type: 'UUID'
})
- logger.info('[2.17.0 migration] Changing mediaItemShares.mediaItemId column to UUID')
- await queryInterface.changeColumn('mediaItemShares', 'mediaItemId', {
- type: 'UUID'
- })
+ if (await queryInterface.tableExists('mediaItemShares')) {
+ logger.info('[2.17.0 migration] Changing mediaItemShares.mediaItemId column to UUID')
+ await queryInterface.changeColumn('mediaItemShares', 'mediaItemId', {
+ type: 'UUID'
+ })
+ } else {
+ logger.info('[2.17.0 migration] mediaItemShares table does not exist, skipping column change')
+ }
logger.info('[2.17.0 migration] Changing playbackSessions.mediaItemId column to UUID')
await queryInterface.changeColumn('playbackSessions', 'mediaItemId', {
diff --git a/server/models/Library.js b/server/models/Library.js
index 4a69e4cdc..708880aad 100644
--- a/server/models/Library.js
+++ b/server/models/Library.js
@@ -107,19 +107,6 @@ class Library extends Model {
})
}
- /**
- * Destroy library by id
- * @param {string} libraryId
- * @returns
- */
- static removeById(libraryId) {
- return this.destroy({
- where: {
- id: libraryId
- }
- })
- }
-
/**
* Get all library ids
* @returns {Promise
} array of library ids
diff --git a/server/models/LibraryItem.js b/server/models/LibraryItem.js
index 5b96ad521..10395c49c 100644
--- a/server/models/LibraryItem.js
+++ b/server/models/LibraryItem.js
@@ -479,7 +479,7 @@ class LibraryItem extends Model {
{
model: this.sequelize.models.series,
through: {
- attributes: ['sequence']
+ attributes: ['id', 'sequence']
}
}
],
diff --git a/server/models/User.js b/server/models/User.js
index 259f841ca..b2a4fd2bc 100644
--- a/server/models/User.js
+++ b/server/models/User.js
@@ -611,7 +611,7 @@ class User extends Model {
*/
getOldMediaProgress(libraryItemId, episodeId = null) {
const mediaProgress = this.mediaProgresses?.find((mp) => {
- if (episodeId && mp.mediaItemId === episodeId) return true
+ if (episodeId && mp.mediaItemId !== episodeId) return false
return mp.extraData?.libraryItemId === libraryItemId
})
return mediaProgress?.getOldMediaProgress() || null