diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..0cd521a5b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,33 @@ + + +## Brief summary + + + +## Which issue is fixed? + + + +## In-depth Description + + + +## How have you tested this? + + + +## Screenshots + + diff --git a/client/components/modals/podcast/ViewEpisode.vue b/client/components/modals/podcast/ViewEpisode.vue index 411e9efd9..af67242ae 100644 --- a/client/components/modals/podcast/ViewEpisode.vue +++ b/client/components/modals/podcast/ViewEpisode.vue @@ -18,6 +18,23 @@

{{ title }}

{{ $strings.MessageNoDescription }}

+ +
+ +
+
+

{{ $strings.LabelFilename }}

+

+ {{ audioFileFilename }} +

+
+
+

{{ $strings.LabelSize }}

+

+ {{ audioFileSize }} +

+
+
@@ -54,7 +71,7 @@ export default { return this.episode.description || '' }, media() { - return this.libraryItem ? this.libraryItem.media || {} : {} + return this.libraryItem?.media || {} }, mediaMetadata() { return this.media.metadata || {} @@ -65,6 +82,14 @@ export default { podcastAuthor() { return this.mediaMetadata.author }, + audioFileFilename() { + return this.episode.audioFile?.metadata?.filename || '' + }, + audioFileSize() { + const size = this.episode.audioFile?.metadata?.size || 0 + + return this.$bytesPretty(size) + }, bookCoverAspectRatio() { return this.$store.getters['libraries/getBookCoverAspectRatio'] } diff --git a/client/components/tables/podcast/LazyEpisodesTable.vue b/client/components/tables/podcast/LazyEpisodesTable.vue index 963cd7c96..0dae11b36 100644 --- a/client/components/tables/podcast/LazyEpisodesTable.vue +++ b/client/components/tables/podcast/LazyEpisodesTable.vue @@ -25,7 +25,6 @@
-
@@ -515,6 +514,10 @@ export default { } }, filterSortChanged() { + // Save filterKey and sortKey to local storage + localStorage.setItem('podcastEpisodesFilter', this.filterKey) + localStorage.setItem('podcastEpisodesSortBy', this.sortKey + (this.sortDesc ? '-desc' : '')) + this.init() }, refresh() { @@ -537,6 +540,11 @@ export default { } }, mounted() { + this.filterKey = localStorage.getItem('podcastEpisodesFilter') || 'incomplete' + const sortBy = localStorage.getItem('podcastEpisodesSortBy') || 'publishedAt-desc' + this.sortKey = sortBy.split('-')[0] + this.sortDesc = sortBy.split('-')[1] === 'desc' + this.episodesCopy = this.episodes.map((ep) => ({ ...ep })) this.initListeners() this.init() diff --git a/client/mixins/uploadHelpers.js b/client/mixins/uploadHelpers.js index 2d7a554f0..994d36c69 100644 --- a/client/mixins/uploadHelpers.js +++ b/client/mixins/uploadHelpers.js @@ -28,10 +28,8 @@ export default { var validOtherFiles = [] var ignoredFiles = [] files.forEach((file) => { - // var filetype = this.checkFileType(file.name) if (!file.filetype) ignoredFiles.push(file) else { - // file.filetype = filetype if (file.filetype === 'audio' || (file.filetype === 'ebook' && mediaType === 'book')) validItemFiles.push(file) else validOtherFiles.push(file) } @@ -165,7 +163,7 @@ export default { var firstBookPath = Path.dirname(firstBookFile.filepath) - var dirs = firstBookPath.split('/').filter(d => !!d && d !== '.') + var dirs = firstBookPath.split('/').filter((d) => !!d && d !== '.') if (dirs.length) { audiobook.title = dirs.pop() if (dirs.length > 1) { @@ -189,7 +187,7 @@ export default { var firstAudioFile = podcast.itemFiles[0] if (!firstAudioFile.filepath) return podcast // No path var firstPath = Path.dirname(firstAudioFile.filepath) - var dirs = firstPath.split('/').filter(d => !!d && d !== '.') + var dirs = firstPath.split('/').filter((d) => !!d && d !== '.') if (dirs.length) { podcast.title = dirs.length > 1 ? dirs[1] : dirs[0] } else { @@ -212,13 +210,15 @@ export default { } var ignoredFiles = itemData.ignoredFiles var index = 1 - var items = itemData.items.filter((ab) => { - if (!ab.itemFiles.length) { - if (ab.otherFiles.length) ignoredFiles = ignoredFiles.concat(ab.otherFiles) - if (ab.ignoredFiles.length) ignoredFiles = ignoredFiles.concat(ab.ignoredFiles) - } - return ab.itemFiles.length - }).map(ab => this.cleanItem(ab, mediaType, index++)) + var items = itemData.items + .filter((ab) => { + if (!ab.itemFiles.length) { + if (ab.otherFiles.length) ignoredFiles = ignoredFiles.concat(ab.otherFiles) + if (ab.ignoredFiles.length) ignoredFiles = ignoredFiles.concat(ab.ignoredFiles) + } + return ab.itemFiles.length + }) + .map((ab) => this.cleanItem(ab, mediaType, index++)) return { items, ignoredFiles @@ -259,7 +259,7 @@ export default { otherFiles.forEach((file) => { var dir = Path.dirname(file.filepath) - var findItem = Object.values(itemMap).find(b => dir.startsWith(b.path)) + var findItem = Object.values(itemMap).find((b) => dir.startsWith(b.path)) if (findItem) { findItem.otherFiles.push(file) } else { @@ -270,18 +270,18 @@ export default { var items = [] var index = 1 // If book media type and all files are audio files then treat each one as an audiobook - if (itemMap[''] && !otherFiles.length && mediaType === 'book' && !itemMap[''].itemFiles.some(f => f.filetype !== 'audio')) { + if (itemMap[''] && !otherFiles.length && mediaType === 'book' && !itemMap[''].itemFiles.some((f) => f.filetype !== 'audio')) { items = itemMap[''].itemFiles.map((audioFile) => { return this.cleanItem({ itemFiles: [audioFile], otherFiles: [], ignoredFiles: [] }, mediaType, index++) }) } else { - items = Object.values(itemMap).map(i => this.cleanItem(i, mediaType, index++)) + items = Object.values(itemMap).map((i) => this.cleanItem(i, mediaType, index++)) } return { items, ignoredFiles: ignoredFiles } - }, + } } -} \ No newline at end of file +} diff --git a/client/package-lock.json b/client/package-lock.json index c7d01fb45..b8b17f3ef 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "audiobookshelf-client", - "version": "2.17.1", + "version": "2.17.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audiobookshelf-client", - "version": "2.17.1", + "version": "2.17.2", "license": "ISC", "dependencies": { "@nuxtjs/axios": "^5.13.6", diff --git a/client/package.json b/client/package.json index 361130eac..924220e0f 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "2.17.1", + "version": "2.17.2", "buildNumber": 1, "description": "Self-hosted audiobook and podcast client", "main": "index.js", diff --git a/client/pages/upload/index.vue b/client/pages/upload/index.vue index 0efa1456d..441ce88ee 100644 --- a/client/pages/upload/index.vue +++ b/client/pages/upload/index.vue @@ -1,20 +1,20 @@ @@ -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