Compare commits

..
Author SHA1 Message Date
advplyrandGitHub 1b46d68027 Merge pull request #5429 from Vito0912/fix/npmv12
add npmv12 compatibility
2026-08-03 17:14:55 -05:00
Finn Dittmar d511612f21 add npmv12 compatibility 2026-08-03 21:08:18 +02:00
advplyrandGitHub 6caa1aa2da Merge pull request #5409 from mikiher/podcast-scanner-stale-episodes
Fix podcast rescan emitting stale episodes on item_updated
2026-07-28 18:15:19 -04:00
mikiher 32fae4a7c6 Fix podcast rescan emitting stale episodes on item_updated 2026-07-28 12:26:32 +03:00
advplyr 96d4021a3c Version bump v2.36.0 2026-07-27 17:31:12 -05:00
advplyrandGitHub 49826f5163 Merge pull request #5406 from weblate/weblate-audiobookshelf-abs-web-client
Translations update from Hosted Weblate
2026-07-27 18:26:57 -04:00
FiendFEARingandHosted Weblate 76e33a2a4e Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1167 of 1167 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2026-07-27 11:54:06 +02:00
FiendFEARingandHosted Weblate 1a440811c5 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1166 of 1166 strings)

Translation: Audiobookshelf/Abs Web Client
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-web-client/zh_Hans/
2026-07-27 11:54:06 +02:00
advplyrandGitHub 529a16b718 Merge pull request #5407 from nichwall/readme-update
Readme update
2026-07-27 05:53:49 -04:00
Nicholas Wallace 032f0b6b51 Fix installation link 2026-07-26 21:41:51 -07:00
Nicholas Wallace f5bfa1edd6 Add note about frontend rewrite 2026-07-26 21:40:59 -07:00
advplyrandGitHub 4dfad46722 Merge pull request #5405 from advplyr/auth_sessions_enhancements
Delete auth session endpoint & paginate sessions
2026-07-26 18:49:25 -04:00
7 changed files with 43 additions and 12 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.35.1", "version": "2.36.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.35.1", "version": "2.36.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@nuxtjs/axios": "^5.13.6", "@nuxtjs/axios": "^5.13.6",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf-client", "name": "audiobookshelf-client",
"version": "2.35.1", "version": "2.36.0",
"buildNumber": 1, "buildNumber": 1,
"description": "Self-hosted audiobook and podcast client", "description": "Self-hosted audiobook and podcast client",
"main": "index.js", "main": "index.js",
+3
View File
@@ -195,6 +195,7 @@
"HeaderScheduleEpisodeDownloads": "设置自动下载剧集", "HeaderScheduleEpisodeDownloads": "设置自动下载剧集",
"HeaderScheduleLibraryScans": "自动扫描媒体库", "HeaderScheduleLibraryScans": "自动扫描媒体库",
"HeaderSession": "会话", "HeaderSession": "会话",
"HeaderSessions": "会话",
"HeaderSetBackupSchedule": "设置备份计划任务", "HeaderSetBackupSchedule": "设置备份计划任务",
"HeaderSettings": "设置", "HeaderSettings": "设置",
"HeaderSettingsDisplay": "显示", "HeaderSettingsDisplay": "显示",
@@ -416,6 +417,7 @@
"LabelIntervalEveryHour": "每小时", "LabelIntervalEveryHour": "每小时",
"LabelIntervalEveryMinute": "每分钟", "LabelIntervalEveryMinute": "每分钟",
"LabelInvert": "倒转", "LabelInvert": "倒转",
"LabelIpAddress": "IP 地址",
"LabelItem": "项目", "LabelItem": "项目",
"LabelJumpBackwardAmount": "向后跳转时长", "LabelJumpBackwardAmount": "向后跳转时长",
"LabelJumpForwardAmount": "向前跳转时长", "LabelJumpForwardAmount": "向前跳转时长",
@@ -775,6 +777,7 @@
"MessageConfirmDeleteSession": "你确定要删除此会话吗?", "MessageConfirmDeleteSession": "你确定要删除此会话吗?",
"MessageConfirmEmbedMetadataInAudioFiles": "你确定要将元数据嵌入到 {0} 个音频文件中吗?", "MessageConfirmEmbedMetadataInAudioFiles": "你确定要将元数据嵌入到 {0} 个音频文件中吗?",
"MessageConfirmForceReScan": "你确定要强制重新扫描吗?", "MessageConfirmForceReScan": "你确定要强制重新扫描吗?",
"MessageConfirmLogoutDevice": "确定要注销 \"{0}\" 吗?",
"MessageConfirmMarkAllEpisodesFinished": "你确定要将所有剧集都标记为已完成吗?", "MessageConfirmMarkAllEpisodesFinished": "你确定要将所有剧集都标记为已完成吗?",
"MessageConfirmMarkAllEpisodesNotFinished": "你确定要将所有剧集都标记为未完成吗?", "MessageConfirmMarkAllEpisodesNotFinished": "你确定要将所有剧集都标记为未完成吗?",
"MessageConfirmMarkItemFinished": "你确定要将 “{0}” 标记为已完成吗?", "MessageConfirmMarkItemFinished": "你确定要将 “{0}” 标记为已完成吗?",
+17 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.35.1", "version": "2.36.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.35.1", "version": "2.36.0",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"axios": "^0.27.2", "axios": "^0.27.2",
@@ -2113,6 +2113,21 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"devOptional": true "devOptional": true
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+4 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf", "name": "audiobookshelf",
"version": "2.35.1", "version": "2.36.0",
"buildNumber": 1, "buildNumber": 1,
"description": "Self-hosted audiobook and podcast server", "description": "Self-hosted audiobook and podcast server",
"main": "index.js", "main": "index.js",
@@ -65,5 +65,8 @@
"nodemon": "^2.0.20", "nodemon": "^2.0.20",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"sinon": "^17.0.1" "sinon": "^17.0.1"
},
"allowScripts": {
"sqlite3@5.1.7": true
} }
} }
+4 -4
View File
@@ -12,6 +12,8 @@
</p> </p>
</div> </div>
## ⚠️ Frontend pull requests are not being reviewed or merged for the existing Vue frontend. The frontend is currently being rewritten and migrated to React and should be available soon.
# About # About
Audiobookshelf is a self-hosted audiobook and podcast server. Audiobookshelf is a self-hosted audiobook and podcast server.
@@ -73,7 +75,7 @@ See [library docs](https://audiobookshelf.org/docs/category/libraries) for suppo
# Installation # Installation
See [install docs](https://www.audiobookshelf.org/docs) See [install docs](https://audiobookshelf.org/docs/category/installation)
<br /> <br />
@@ -93,8 +95,7 @@ See [contributing docs](https://audiobookshelf.org/docs/contributing/general/)
### Localization ### Localization
Thank you to [Weblate](https://hosted.weblate.org/engage/audiobookshelf/) for hosting our localization infrastructure pro-bono. If you want to see Audiobookshelf in your language, please help us localize. Additional information on helping with the translations [here](https://www.audiobookshelf.org/faq#how-do-i-help-with-translations). Thank you to [Weblate](https://hosted.weblate.org/engage/audiobookshelf/) for hosting our localization infrastructure pro-bono. If you want to see Audiobookshelf in your language, please help us localize. Additional information on helping with the translations [here](https://www.audiobookshelf.org/faq#how-do-i-help-with-translations). <a href="https://hosted.weblate.org/engage/audiobookshelf/"> <img src="https://hosted.weblate.org/widget/audiobookshelf/abs-web-client/multi-auto.svg" alt="Translation status" /> </a>
<a href="https://hosted.weblate.org/engage/audiobookshelf/"> <img src="https://hosted.weblate.org/widget/audiobookshelf/abs-web-client/multi-auto.svg" alt="Translation status" /> </a>
<br /> <br />
@@ -191,4 +192,3 @@ If you are using VSCode, this project includes a couple of pre-defined targets t
- `Debug server`—Run the server. - `Debug server`—Run the server.
- `Debug client (nuxt)`—Run the client with live reload. - `Debug client (nuxt)`—Run the client with live reload.
- `Debug server and client (nuxt)`—Runs both the preceding two debug targets. - `Debug server and client (nuxt)`—Runs both the preceding two debug targets.
+12 -2
View File
@@ -58,8 +58,12 @@ class PodcastScanner {
/** @type {AudioFile[]} */ /** @type {AudioFile[]} */
let newAudioFiles = [] let newAudioFiles = []
// Track episode add/remove/update so item_updated includes the current episode list
let hasEpisodeChanges = false
if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) { if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) {
// Filter out and destroy episodes that were removed // Filter out and destroy episodes that were removed.
// filter() returns a new array — reassign to media.podcastEpisodes before emit.
const episodesToRemove = [] const episodesToRemove = []
existingPodcastEpisodes = existingPodcastEpisodes.filter((ep) => { existingPodcastEpisodes = existingPodcastEpisodes.filter((ep) => {
if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) { if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) {
@@ -70,6 +74,7 @@ class PodcastScanner {
}) })
if (episodesToRemove.length) { if (episodesToRemove.length) {
hasEpisodeChanges = true
// Remove episodes from playlists and media progress // Remove episodes from playlists and media progress
const episodeIds = episodesToRemove.map((ep) => ep.id) const episodeIds = episodesToRemove.map((ep) => ep.id)
await Database.playlistModel.removeMediaItemsFromPlaylists(episodeIds) await Database.playlistModel.removeMediaItemsFromPlaylists(episodeIds)
@@ -116,6 +121,7 @@ class PodcastScanner {
AudioFileScanner.setPodcastEpisodeMetadataFromAudioMetaTags(podcastEpisode, libraryScan) AudioFileScanner.setPodcastEpisodeMetadataFromAudioMetaTags(podcastEpisode, libraryScan)
libraryScan.addLog(LogLevel.INFO, `Podcast episode "${podcastEpisode.title}" keys changed [${podcastEpisode.changed()?.join(', ')}]`) libraryScan.addLog(LogLevel.INFO, `Podcast episode "${podcastEpisode.title}" keys changed [${podcastEpisode.changed()?.join(', ')}]`)
await podcastEpisode.save() await podcastEpisode.save()
hasEpisodeChanges = true
} }
} }
@@ -155,9 +161,13 @@ class PodcastScanner {
libraryScan.addLog(LogLevel.INFO, `New Podcast episode "${newPodcastEpisode.title}" added`) libraryScan.addLog(LogLevel.INFO, `New Podcast episode "${newPodcastEpisode.title}" added`)
await newPodcastEpisode.save() await newPodcastEpisode.save()
existingPodcastEpisodes.push(newPodcastEpisode) existingPodcastEpisodes.push(newPodcastEpisode)
hasEpisodeChanges = true
} }
} }
// Keep association in sync for toOldJSONExpanded() / item_updated socket payload
media.podcastEpisodes = existingPodcastEpisodes
let hasMediaChanges = false let hasMediaChanges = false
if (existingPodcastEpisodes.length !== media.numEpisodes) { if (existingPodcastEpisodes.length !== media.numEpisodes) {
media.numEpisodes = existingPodcastEpisodes.length media.numEpisodes = existingPodcastEpisodes.length
@@ -250,7 +260,7 @@ class PodcastScanner {
return { return {
libraryItem: existingLibraryItem, libraryItem: existingLibraryItem,
wasUpdated: hasMediaChanges || libraryItemUpdated wasUpdated: hasMediaChanges || libraryItemUpdated || hasEpisodeChanges
} }
} }